天天看點

淺析php反序列化字元串逃逸

前言:

php反序列化字元串逃逸之前沒有詳細的學習過,是以遇到題目看的有點懵,這次好好學習一下。

反序列化的特點

首先要了解一下反序列化的一些特點:

  1. php在反序列化時,底層代碼是以

    ;

    作為字段的分隔,以

    }

    作為結尾,并且是根據長度判斷内容的 ,同時反序列化的過程中必須嚴格按照序列化規則才能成功實作反序列化 。
class A{
    public $name='shy';
    public $pass='123456';
}

$lemon = new A();
echo serialize($lemon);
#反序列化後的結果為:
O:1:"A":2:{s:4:"name";s:3:"shy";s:4:"pass";s:6:"123456";}
           

超出的部分并不會被反序列化成功,如下圖:

淺析php反序列化字元串逃逸

這說明反序列化的過程是有一定識别範圍的,在這個範圍之外的字元都會被忽略,不影響反序列化的正常進行。而且可以看到反序列化字元串都是以

";}

結束的,那如果把

";}

添入到需要反序列化的字元串中(除了結尾處),就能讓反序列化提前閉合結束,後面的内容就相應的丢棄了。

2. 長度不對應的時候會報錯

在反序列化的時候php會根據s所指定的字元長度去讀取後邊的字元。如果指定的長度錯誤則反序列化就會失敗

淺析php反序列化字元串逃逸

3. 可以反序列化類中不存在的元素

<?php
$str='O:1:"A":3:{s:4:"name";s:3:"shy";s:4:"pass";s:6:"123456";s:5:"pass2";s:6:"123456";}';
var_dump(unserialize($str));
           
淺析php反序列化字元串逃逸

這些特點一定要清楚,否則在做題時可能就因為這些基礎知識而做出不來。

字元串逃逸

0x0:特點

這類CTF題目的本質是因為改變序列化字元串的長度,進而導緻反序列化漏洞。

具體的話大緻都是因為php序列化後的字元串經過了

替換或者修改

,導緻字元串長度發生變化。而且總是先進行序列化,再進行替換修改操作。

0x01:過濾後字元變多

實驗代碼:

#參考位元組脈搏實驗室
<?php
function lemon($string){
	$lemon = '/p/i';
	return preg_replace($lemon,'ww',$string);
}
$username = $_GET['a'];
$age = '20';
$user = array($username,$age);
var_dump(serialize($user));
echo "<br>";

$r = lemon(serialize($user));
var_dump($r);
var_dump(unserialize($r));
?>
           

正常輸入的話

淺析php反序列化字元串逃逸

因為我們輸入的是apple,含有兩個

p

,是以會被替換成四個

w

,但是發現長度并沒有變化,是以根據反序列化的特點,指定的長度錯誤則反序列化就會失敗。

但是正是因為存在這個過濾,我們便可以去修改age的值,首先來看一下,原來序列化後

";i:1;s:2:"20";}

長度為16,我們已經知道了當輸入一個

p

會替換成

ww

,是以如果輸入16個p,那麼會生成32個的

w

,是以如果我們輸入16個p再加上構造的相同位數的

";i:1;s:2:"30";}

,恰好是32位,即

32 pppppppppppppppp";i:1;s:2:"30";}
經過替換後
32 wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
           

是以非常明顯了,在過濾後的序列化時會被32個w全部填充,進而使構造的代碼

";i:1;s:2:"30";}

成功逃逸,修改了age的值,而原來的那

";i:1;s:2:"20";}

則被忽略了因為反序列化字元串都是以

";}

結束的,我們傳入的

";i:1;s:2:"30";}

已經在前面成功閉合了

淺析php反序列化字元串逃逸

0x02:過濾後字元變少

搭建一個簡單的實驗環境代碼如下:

#參考Mr. Anonymous師傅的代碼學習
<?php
function str_rep($string){
	return preg_replace( '/lemon|shy/','', $string);
}

$test['name'] = $_GET['name'];
$test['sign'] = $_GET['sign']; 
$test['number'] = '2020';
$temp = str_rep(serialize($test));
printf($temp);
$fake = unserialize($temp);
echo '<br>';
print("name:".$fake['name'].'<br>');
print("sign:".$fake['sign'].'<br>');
print("number:".$fake['number'].'<br>');
?>
           

如果正常輸入的話,回顯出的結果如下:

淺析php反序列化字元串逃逸

已經知道number的值是固定的2020

如果想要修改這個值,就要在sign中加入

";s:6:"number";s:4:"2000";}

,其長度為27,仔細觀察便可以發現是利用反序列化的第一個特點

底層代碼是以

;

作為字段的分隔,以

}

作為結尾

,想要将之前的number擋在序列化之外,進而可以反序列化自己構造的,但直接輸入發現是不行的,并沒有将我們輸入的給反序列化了

淺析php反序列化字元串逃逸

在實驗代碼中有替換功能,當遇到

lemon 或 shy

會自動替換為空,也這裡用

shy

做為name的輸入,故意輸入敏感字元,替換為空之後來實作字元逃逸,

三個字元變成零個字元,吃掉了三個字元

,輸入8個

shy

,也就是騰出了24個字元的空間,利用這個空間來進行構造,由于

";s:4:"sign";s:54:"hello成了

name的内容,是以還要在後面加個

";s:4:"sign";s:4:"eval

作為sign序列化的内容。

http://127.0.0.1/1.php
?name=shyshyshyshyshyshyshyshyshy
&sign=hello123";s:4:"sign";s:4:"eval";s:6:"number";s:4:"2000";}