- 資訊首頁(yè) > 網(wǎng)絡(luò )安全 >
- php中怎么反序列化字符TAOYI
本篇文章給大家分享的是有關(guān)php中怎么反序列化字符TAOYI,小編覺(jué)得挺實(shí)用的,因此分享給大家學(xué)習,希望大家閱讀完這篇文章后可以有所收獲,話(huà)不多說(shuō),跟著(zhù)小編一起來(lái)看看吧。
字符逃逸在理解之后就能夠明白,這是一種閉合的思想,它類(lèi)似SQL中的萬(wàn)能密碼,理解這種原理之后會(huì )變得特別容易。
在SQL注入中,我們常用'、"來(lái)對注入點(diǎn)進(jìn)行一些閉合或者一些試探,從而進(jìn)行各種姿勢的注入,反序列化時(shí),序列化的值是以;作為字段的分隔,在結尾是以}結束,我們稍微了解一下,
<?php class people{ public $name = 'Tom'; public $sex = 'boy'; public $age = '12'; } $a = new people(); print_r(serialize($a));
反序列化的過(guò)程就是碰到;}與最前面的{配對后,便停止反序列化。我們可以將上面的序列化的值稍作改變:
O:6:"people":3:{s:4:"name";s:3:"Tom";s:3:"sex";s:3:"boy";s:3:"age";s:2:"12";}123123
可以看到,并沒(méi)有報錯,而且也順利將這個(gè)對象反序列化出來(lái)了,恰好說(shuō)明了我們以上所說(shuō)的閉合的問(wèn)題,與此同時(shí),修改一些序列化出來(lái)的值可以反序列化出我們所知道的對象中里沒(méi)有的值,在學(xué)習繞過(guò)__wakeup就能過(guò)知道了,這里可以自己去做一些嘗試,去理解。
接下來(lái)就是要說(shuō)到報錯的時(shí)候了,當你的字符串長(cháng)度與所描述的長(cháng)度不一樣時(shí),便會(huì )報錯,比如上圖中s:3:"Tom"變成s:4:"Tom"或s:2:"Tom"便會(huì )報錯,為的就是解決這種報錯,所以在字符逃逸中分為兩類(lèi):
字符變多
字符減少
關(guān)鍵字符增多
在題目中,往往對一些關(guān)鍵字會(huì )進(jìn)行一些過(guò)濾,使用的手段通常替換關(guān)鍵字,使得一些關(guān)鍵字增多,簡(jiǎn)單認識一下,正常序列化查看結果
這里,我們對序列化后的字符串進(jìn)行了替換,使得后來(lái)的反序列化報錯,那我們就需要在Tom這里面的字符串做手腳,在username之后只有一個(gè)age,所以在雙引號里面可以構造我需要的username之后參數的值,這里修改age的值,我們這里將Tom替換為T(mén)om";s:3:"age";s:2:"35";}然后進(jìn)行反序列化,這里指的是在對username傳參的時(shí)候進(jìn)行修改,也就是我們寫(xiě)鏈子的時(shí)候進(jìn)行的操作
可以看到構造出來(lái)的序列化字符串長(cháng)度為25,而在上面的反序列化過(guò)程中,他會(huì )將一個(gè)o變成兩個(gè),oo,那么得到的應該就是s:25:"Toom"我們要做的就是讓這個(gè)雙引號里面的字符串在過(guò)濾替換之后真的有描述的這么長(cháng),讓他不要報錯,再配合反序列化的特點(diǎn),(反序列化的過(guò)程就是碰到;}與最前面的{配對后,便停止反序列化)閉合后忽略后面的age:13的字符串成功使得age被修改為35。
而age的修改需要前面的字符串username的值長(cháng)度與描述的一樣,這需要我們精確的計算,這里是將一個(gè)o變成兩個(gè),以下就只寫(xiě)o不寫(xiě)Tom,效果一致,我們需要知道我們除了雙引號以?xún)鹊?,所構造的字符串長(cháng)度為多少,即";s:3:"age";s:2:"35";}的長(cháng)度22,那就需要22個(gè)o,
總的來(lái)說(shuō)就是22個(gè)o加上后面的字符串長(cháng)度22,總長(cháng)度就為44,在被過(guò)濾替換后,光o就有44個(gè),符合描述的字符串長(cháng)度。下面就說(shuō)明(為什么叫做逃逸)
這里特意寫(xiě)了"將一大串o進(jìn)行與前面的"閉合了,如果直接反序列化,在序列化出來(lái)的值中就包含了";s:3:"age";s:2:"35";}。
反序列的過(guò)程中,所描述的字符串長(cháng)度(這里為44),而后面雙引號包裹的字符串長(cháng)度(這里為22)不夠所描述的長(cháng)度,那么他將會(huì )向后吞噬,他會(huì )將后雙引號吞噬,直至足夠所描述的長(cháng)度,在一切吞噬結束之后,序列化出來(lái)的字符串如果不滿(mǎn)足反序列化的字符串的格式,就會(huì )報錯。我們這里是他吞噬結束后,還滿(mǎn)足這個(gè)格式,所以不報錯。
在這個(gè)例子中,我們利用他對序列化后的值,進(jìn)行增加字符串長(cháng)度的過(guò)濾,讓他填充雙引號內的字符串達到所描述的44這么長(cháng),使得后面的s:3:"age";s:2:"35";不被吞噬,讓這部分代碼逃逸出吞噬,又讓他提前遇到}忽略后面的一些不需要的字符串,結束反序列化。
可以看到,我們構造的payload是成功修改了age,這里是數組,在對對象操作時(shí)也是一樣的。
剛剛說(shuō)到吞噬,在增加字符串的題目中,我們是利用題中的增加操作,阻止他進(jìn)行向后吞噬我們構造的代碼,而在字符減少的過(guò)程中,我們也是利用這個(gè)操作。
關(guān)鍵字符減少
有了前面”吞噬“的一種解釋?zhuān)敲醋址疁p少就很好說(shuō)了 ,同樣的也是因為替換的問(wèn)題,使得參數可以讓我們構造payload
這里的錯誤是因為s:5:"zddo"長(cháng)度不夠,他向后吞噬了一個(gè)雙引號,導致反序列化格式錯誤,從而報錯,我們要做的就是讓他往后去吞噬一些我們構造的一些代碼。以下講具體實(shí)施。
同樣的,我們這里以修改age為例,不同的是與增加字符串傳值的地方有些許不同,我們構造的值是有一部分讓他吞噬的
先正常傳遞值序列化出我們需要修改的值,我們需要的是將age:13改為35
取出";s:3:"age";s:2:"35";}這就是我們需要構造的,接著(zhù)繼續將這部分內容重新傳值,序列化出來(lái),得到下面的結果
選中部分就是我們構造出來(lái),他需要吞噬的代碼,s:22:""這個(gè)雙引號里面我們還有操作的空間,用來(lái)補齊字符串長(cháng)度,接著(zhù)就是計算我們自己所需要吃掉的字符串長(cháng)度為18,根據過(guò)濾,他是將兩個(gè)o變成一個(gè),也就是每吃掉一個(gè)字符,就需要有一個(gè)oo,那我們需要吃掉的是18個(gè)長(cháng)度,那么我們就需要18個(gè)oo,在吞噬結束之后我們的格式又恢復正確,使得真正的字符s:3:"age";s:2:"35";逃逸出來(lái),成功加入反序列化
這就是我們最終的payload,可以看到下圖成功修改了
例題
有了以上基礎,就可以做題了,簡(jiǎn)單的開(kāi)始入手
安恒四月(字符減少)
<?php show_source("index.php"); function write($data) { return str_replace(chr(0) . '*' . chr(0), '\0\0\0', $data); } function read($data) { return str_replace('\0\0\0', chr(0) . '*' . chr(0), $data); } class A{ public $username; public $password; function __construct($a, $b){ $this->username = $a; $this->password = $b; } } class B{ public $b = 'gqy'; function __destruct(){ $c = 'a'.$this->b; echo $c; } } class C{ public $c; function __toString(){ echo file_get_contents($this->c); return 'nice'; } } $a = new A($_GET['a'],$_GET['b']); //省略了存儲序列化數據的過(guò)程,下面是取出來(lái)并反序列化的操作 $b = unserialize(read(write(serialize($a))));
看到上面的代碼,很明顯,我們需要利用file_get_contents();讀取文件,將flag讀取出來(lái),但是他是個(gè)__toString()方法,我們就要讓他觸發(fā)這個(gè)方法,當反序列化出對象后,被當作字符串使用時(shí),就可以觸發(fā),那我們就需要寫(xiě)一個(gè)鏈,與此同時(shí)我們也要知道字符串被刪減了幾個(gè)字符
function write($data) { return str_replace(chr(0) . '*' . chr(0), '\0\0\0', $data); } function read($data) { return str_replace('\0\0\0', chr(0) . '*' . chr(0), $data); }
看這一部分即可了解到,如果發(fā)現不可見(jiàn)字符*不可見(jiàn)字符,字符串就會(huì )增多,接著(zhù)又將\0\0\0的6個(gè)字符變成3個(gè)字符不可見(jiàn)字符*不可見(jiàn)字符,我們自己是不會(huì )去寫(xiě)入不可見(jiàn)字符的在這道題中,相反可以故意寫(xiě)入\0\0\0使得字符串減少,通過(guò)計算逃逸字符,讀取flag文件
題目中序列化的是對象$a,里面有兩個(gè)參數,username和password,我們要傳入的也是這兩個(gè)參數的值,所以我們構造的payload,應該是往password傳我們構造好的字符,而在username傳入的為計算好的\0\0\0個(gè)數,
按照流程來(lái),先寫(xiě)一個(gè)鏈子,某些參數先隨便寫(xiě)
";s:8:"password";O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:5:"/flag";}}}
選中部分就是我們需要重新傳參的地方,我們傳進(jìn)password,再次序列化看看效果
我所選擇的地方就是需要被吞噬的部分,然后才能使得后邊的代碼全部逃逸,長(cháng)度為23,計算后發(fā)現需要8個(gè)\0\0\0,但8個(gè)這樣的符號會(huì )吞噬24個(gè)字符,因此我們可以在s:70:""雙引號里面可以隨意補一個(gè)字符讓它吞噬。
因此我們最終在password里面傳參的應該是:
i";s:8:"password";O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:5:"/flag";}}}
而在username中傳的就是8個(gè)\0\0\0:
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0
最終payload:
?a=\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0&b=i";s:8:"password";O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:5:"/flag";}}}
0CTF piapiapia(字符增加)
掃描目錄發(fā)現www.zip,下載后開(kāi)始,審計源碼
我們根據他的網(wǎng)頁(yè)一步步看源碼,首先他要我們登陸,我們先注冊一個(gè)賬號進(jìn)行登陸
看到上圖的界面,這時(shí)我們看到update.php,都是一些對參數的白名單按要求寫(xiě)即可,圖片也隨便傳一個(gè)符合大小的即可,但是注意nickname是我們要操作的地方,稍后講解,然后有個(gè)序列化數組的過(guò)程
$user->update_profile($username, serialize($profile));
在上傳成功后,他會(huì )到profile.php,我們看到profile.php,有這么一段東西
$profile = unserialize($profile); $phone = $profile['phone']; $email = $profile['email']; $nickname = $profile['nickname']; $photo = base64_encode(file_get_contents($profile['photo']));
這里告訴我們,他反序列化的是profile這個(gè)數組序列化后的值,讀取的是鍵名為photo里面的文件名,而flag在config.php也就是說(shuō)我們需要構造的就是數組中$profile['photo']='config.php'
那么怎樣才能讓他讀這個(gè)config.php呢
我們看到class.php,看到父類(lèi)中:
public function filter($string) { $escape = array('\'', '\\\\'); $escape = '/' . implode('|', $escape) . '/'; $string = preg_replace($escape, '_', $string); $safe = array('select', 'insert', 'update', 'delete', 'where'); $safe = '/' . implode('|', $safe) . '/i'; return preg_replace($safe, 'hacker', $string); }
發(fā)現序列化的值他會(huì )傳遞給user中的方法update_profile,接著(zhù)update_profile又將這個(gè)值傳遞給了父類(lèi)方法filter,顯而易見(jiàn),就是一種過(guò)濾,防止sql注入,但是可以發(fā)現,他是以替換的方式給返回值,在過(guò)濾的字符串中,只有where變成hacker,由5個(gè)字符變成6個(gè),所以是字符增加的逃逸方式,接著(zhù)開(kāi)始構造
選擇部分是需要傳入的,在phone和email都是白名單,傳入的格式受限制,只有nickname是黑名單,所以我們要繞過(guò)這個(gè)黑名單,bp抓包
他使用的是strlen()所以這里有個(gè)方式,這個(gè)函數如果參數是字符串,那么數出來(lái)的就是字符串個(gè)數,如果是數組,那么數出來(lái)的就是數組元素的個(gè)數
將nickname改成nickname[]然后傳參
傳的參數我們需要構造,需要計算,上面分析了我們需要構造的字符串";}s:5:"photo";s:10:"config.php";},但是他是字符增多,我們就需要知道需要逃逸的字符有多少個(gè),計算了一下為34個(gè),(這里因為改為了數組,而數組的序列化格式結束后是一個(gè)大括號,所以我們在一開(kāi)始的”;后面增加了一個(gè)花括號)所以需要34個(gè)where幫助我們逃逸這部分代碼,上傳成功,根據這個(gè)修改放包即可
返回網(wǎng)頁(yè)點(diǎn)擊
之后可以看到一張顯示不出來(lái)的圖片,因為使用base64編碼了,右擊查看圖片信息
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自互聯(lián)網(wǎng)轉載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權請聯(lián)系QQ:712375056 進(jìn)行舉報,并提供相關(guān)證據,一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容。
Copyright ? 2009-2021 56dr.com. All Rights Reserved. 特網(wǎng)科技 特網(wǎng)云 版權所有 珠海市特網(wǎng)科技有限公司 粵ICP備16109289號
域名注冊服務(wù)機構:阿里云計算有限公司(萬(wàn)網(wǎng)) 域名服務(wù)機構:煙臺帝思普網(wǎng)絡(luò )科技有限公司(DNSPod) CDN服務(wù):阿里云計算有限公司 中國互聯(lián)網(wǎng)舉報中心 增值電信業(yè)務(wù)經(jīng)營(yíng)許可證B2
建議您使用Chrome、Firefox、Edge、IE10及以上版本和360等主流瀏覽器瀏覽本網(wǎng)站