33

无字母数字webshell之提高篇

 5 years ago
source link: https://www.leavesongs.com/PENETRATION/webshell-without-alphanum-advanced.html?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

前几天【代码审计知识星球】里有同学提出了一个问题,大概代码如下:

<?php
if(isset($_GET['code'])){
    $code = $_GET['code'];
    if(strlen($code)>35){
        die("Long.");
    }
    if(preg_match("/[A-Za-z0-9_$]+/",$code)){
        die("NO.");
    }
    eval($code);
}else{
    highlight_file(__FILE__);
}

这个代码如果要getshell,怎样利用?

这题可能来自是我曾写过的一篇文章:《 一些不包含数字和字母的webshell 》,里面介绍了如何构造无字母数字的webshell。其中有两个主要的思路:

  1. 利用位运算
  2. 利用自增运算符

当然,这道题多了两个限制:

  1. webshell长度不超过35位
  2. 除了不包含字母数字,还不能包含 $_

难点呼之欲出了,我前面文章中给出的所有方法,都用到了PHP中的变量,需要对变量进行变形、异或、取反等操作,最后动态执行函数。但现在,因为 $ 不能使用了,所以我们无法构造PHP中的变量。

所以,如何解决这个问题?

我们将上述代码放在index.php中,然后执行 docker run --rm -p 9090:80 -v `pwd`:/var/www/html php:7.2-apache ,启动一个php 7.2的服务器。

php7中修改了表达式执行的顺序: http://php.net/manual/zh/migration70.incompatible.php

VzURj2V.png!web

PHP7前是不允许用 ($a)(); 这样的方法来执行动态函数的,但PHP7中增加了对此的支持。所以,我们可以通过 ('phpinfo')(); 来执行函数,第一个括号中可以是任意PHP表达式。

所以很简单了,构造一个可以生成 phpinfo 这个字符串的PHP表达式即可。payload如下(不可见字符用url编码表示):

(~%8F%97%8F%96%91%99%90)();

eqa2iaE.png!web

我们使用 docker run --rm -p 9090:80 -v `pwd`:/var/www/html php:5.6-apach 来运行一个php5.6的web环境。

此时,我们尝试用PHP7的payload,将会得到一个错误:

R7nUfa7.png!web

原因就是php5并不支持这种表达方式。

在我在知识星球里发出帖子的时候,其实还没想到如何用PHP5解决问题,但我有自信解决它,所以先发了这个小挑战。后来关上电脑仔细想想,发现当思路禁锢在一个点的时候,你将会钻进牛角尖;当你用大局观来看待问题,问题就迎刃而解。

当然,我觉得我的方法应该不是唯一的,不过一直没人出来公布答案,我就先抛钻引玉了。

大部分语言都不会是单纯的逻辑语言,一门全功能的语言必然需要和操作系统进行交互。操作系统里包含的最重要的两个功能就是“shell(系统命令)”和“文件系统”,很多木马与远控其实也只实现了这两个功能。

PHP自然也能够和操作系统进行交互,“反引号”就是PHP中最简单的执行shell的方法。那么,在使用PHP无法解决问题的情况下,为何不考虑用“反引号”+“shell”的方式来getshell呢?

因为反引号不属于“字母”、“数字”,所以我们可以执行系统命令,但问题来了:如何利用无字母、数字、 $ 的系统命令来getshell?

好像问题又回到了原点:无字母、数字、 $ ,在shell中仍然是一个难题。

此时我想到了两个有趣的Linux shell知识点:

.

第一点曾在《 小密圈里的那些奇技淫巧 》露出过一角,但我没细讲。 . 或者叫period,它的作用和source一样,就是用当前的shell执行一个文件中的命令。比如,当前运行的shell是bash,则 . file 的意思就是用bash执行file文件中的命令。

. file 执行文件,是不需要file有x权限的。那么,如果目标服务器上有一个我们可控的文件,那不就可以利用 . 来执行它了吗?

这个文件也很好得到,我们可以发送一个上传文件的POST包,此时PHP会将我们上传的文件保存在临时文件夹下,默认的文件名是 /tmp/phpXXXXXX ,文件名最后6个字符是随机的大小写字母。

第二个难题接踵而至,执行 . /tmp/phpXXXXXX ,也是有字母的。此时就可以用到Linux下的glob通配符:

*
?

那么, /tmp/phpXXXXXX 就可以表示为 /*/?????????/???/?????????

但我们尝试执行 . /???/????????? ,却得到如下错误:

fIZbMfq.png!web

这是因为,能够匹配上 /???/????????? 这个通配符的文件有很多,我们可以列出来:

If2Q3qj.png!web

可见,我们要执行的 /tmp/phpcjggLC 排在倒数第二位。然而,在执行第一个匹配上的文件(即 /bin/run-parts )的时候就已经出现了错误,导致整个流程停止,根本不会执行到我们上传的文件。

思路又陷入了僵局,虽然方向没错。

大部分同学对于通配符,可能知道的都只有 *? 。但实际上,阅读Linux的文档( http://man7.org/linux/man-pages/man7/glob.7.html ),可以学到更多有趣的知识点。

其中,glob支持用 [^x] 的方法来构造“这个位置不是字符x”。那么,我们用这个姿势干掉 /bin/run-parts

A7NJrqe.png!web

排除了第4个字符是 - 的文件,同样我们可以排除包含 . 的文件:

BFbyU3U.png!web

现在就剩最后三个文件了。但我们要执行的文件仍然排在最后,但我发现这三个文件名中都不包含特殊字符,那么这个方法似乎行不通了。

继续阅读glob的帮助,我发现另一个有趣的用法:

zEnqIfb.png!web

就跟正则表达式类似,glob支持利用 [0-9] 来表示一个范围。

我们再来看看之前列出可能干扰我们的文件:

v6bM3ev.png!web

所有文件名都是小写,只有PHP生成的临时文件包含大写字母。那么答案就呼之欲出了,我们只要找到一个可以表示“大写字母”的glob通配符,就能精准找到我们要执行的文件。

翻开ascii码表,可见大写字母位于 @[ 之间:

Jz6jqmF.png!web

那么,我们可以利用 [@-[] 来表示大写字母:

FvqMbam.png!web

显然这一招是管用的。

构造POC,执行任意命令

当然,php生成临时文件名是随机的,最后一个字符不一定是大写字母,不过多尝试几次也就行了。

最后,我传入的code为 ?><?=`. /???/????????[@-[]`;?> ,发送数据包如下:

36nAZrf.png!web

成功执行任意命令。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK