這段時間遇到幾個關於反序列化的字符逃逸的程序,今天來分享一下經驗。
<?php
function filter($str){
return str_replace('bb', 'ccc', $str);
}
class A{
public $name='aaaa';
public $pass='123456';
}
$AA=new A();
$res=filter(serialize($AA));
$c=unserialize($res);
echo $c->pass;
?>
以上面代碼為例,如何在不直接修改$pass值的情況下間接修改$pass的值。
代碼的流程為:
先序列化代碼,然后將里面不希望出現的字符替換成自定義的字符串。然后進行反序列化,最后輸出pass變量。
要解決上面這個問題,先來看一下php序列化代碼的特征。
在反序列化的時候php會根據s所指定的字符長度去讀取后邊的字符。
如果指定的長度錯誤則反序列化就會失敗。
此時的name所讀取的數據為aaaa”而正常的語法是需要用”;去閉合當前的變量,而因為長度錯誤所以此時php把閉合的雙引號當做了字符串,所以下一個字符就成了分號,沒能閉合導致拋出了錯誤。
把精力回到開頭所說的代碼,根據剛才講的,如果我們將name變量中添加bb則程序會報錯。
查看過濾前后的代碼能發現,應該是6位長度的name變量在過濾后變成了7位,根據反序列化讀取變量的原則來講,此時的name能讀取到的是aaaacc,末尾處的那個c是讀取不到的,這就形成了一個字符串的逃逸。當我們添加多個bb,每添加一個bb我們就能逃逸一個字符,那我們將逃逸的字符串的長度填充成我們要反序列化的代碼的話那就可以控制反序列化的結果以及類里面的變量值了。
填充如上圖數量的bb即可修改pass的值。
具體分析如下:
逃逸或者說被“頂”出來的payload就會被當做當前類的屬性被執行。