您好,登錄后才能下訂單哦!
本篇文章給大家分享的是有關php中怎么反序列化字符TAOYI,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
字符逃逸在理解之后就能夠明白,這是一種閉合的思想,它類似SQL中的萬能密碼,理解這種原理之后會變得特別容易。
在SQL注入中,我們常用'、"來對注入點進行一些閉合或者一些試探,從而進行各種姿勢的注入,反序列化時,序列化的值是以;作為字段的分隔,在結尾是以}結束,我們稍微了解一下,
<?php class people{ public $name = 'Tom'; public $sex = 'boy'; public $age = '12'; } $a = new people(); print_r(serialize($a));
反序列化的過程就是碰到;}與最前面的{配對后,便停止反序列化。我們可以將上面的序列化的值稍作改變:
O:6:"people":3:{s:4:"name";s:3:"Tom";s:3:"sex";s:3:"boy";s:3:"age";s:2:"12";}123123
可以看到,并沒有報錯,而且也順利將這個對象反序列化出來了,恰好說明了我們以上所說的閉合的問題,與此同時,修改一些序列化出來的值可以反序列化出我們所知道的對象中里沒有的值,在學習繞過__wakeup就能過知道了,這里可以自己去做一些嘗試,去理解。
接下來就是要說到報錯的時候了,當你的字符串長度與所描述的長度不一樣時,便會報錯,比如上圖中s:3:"Tom"變成s:4:"Tom"或s:2:"Tom"便會報錯,為的就是解決這種報錯,所以在字符逃逸中分為兩類:
字符變多
字符減少
關鍵字符增多
在題目中,往往對一些關鍵字會進行一些過濾,使用的手段通常替換關鍵字,使得一些關鍵字增多,簡單認識一下,正常序列化查看結果
這里,我們對序列化后的字符串進行了替換,使得后來的反序列化報錯,那我們就需要在Tom這里面的字符串做手腳,在username之后只有一個age,所以在雙引號里面可以構造我需要的username之后參數的值,這里修改age的值,我們這里將Tom替換為Tom";s:3:"age";s:2:"35";}然后進行反序列化,這里指的是在對username傳參的時候進行修改,也就是我們寫鏈子的時候進行的操作
可以看到構造出來的序列化字符串長度為25,而在上面的反序列化過程中,他會將一個o變成兩個,oo,那么得到的應該就是s:25:"Toom"我們要做的就是讓這個雙引號里面的字符串在過濾替換之后真的有描述的這么長,讓他不要報錯,再配合反序列化的特點,(反序列化的過程就是碰到;}與最前面的{配對后,便停止反序列化)閉合后忽略后面的age:13的字符串成功使得age被修改為35。
而age的修改需要前面的字符串username的值長度與描述的一樣,這需要我們精確的計算,這里是將一個o變成兩個,以下就只寫o不寫Tom,效果一致,我們需要知道我們除了雙引號以內的,所構造的字符串長度為多少,即";s:3:"age";s:2:"35";}的長度22,那就需要22個o,
總的來說就是22個o加上后面的字符串長度22,總長度就為44,在被過濾替換后,光o就有44個,符合描述的字符串長度。下面就說明(為什么叫做逃逸)
這里特意寫了"將一大串o進行與前面的"閉合了,如果直接反序列化,在序列化出來的值中就包含了";s:3:"age";s:2:"35";}。
反序列的過程中,所描述的字符串長度(這里為44),而后面雙引號包裹的字符串長度(這里為22)不夠所描述的長度,那么他將會向后吞噬,他會將后雙引號吞噬,直至足夠所描述的長度,在一切吞噬結束之后,序列化出來的字符串如果不滿足反序列化的字符串的格式,就會報錯。我們這里是他吞噬結束后,還滿足這個格式,所以不報錯。
在這個例子中,我們利用他對序列化后的值,進行增加字符串長度的過濾,讓他填充雙引號內的字符串達到所描述的44這么長,使得后面的s:3:"age";s:2:"35";不被吞噬,讓這部分代碼逃逸出吞噬,又讓他提前遇到}忽略后面的一些不需要的字符串,結束反序列化。
可以看到,我們構造的payload是成功修改了age,這里是數組,在對對象操作時也是一樣的。
剛剛說到吞噬,在增加字符串的題目中,我們是利用題中的增加操作,阻止他進行向后吞噬我們構造的代碼,而在字符減少的過程中,我們也是利用這個操作。
關鍵字符減少
有了前面”吞噬“的一種解釋,那么字符串減少就很好說了 ,同樣的也是因為替換的問題,使得參數可以讓我們構造payload
這里的錯誤是因為s:5:"zddo"長度不夠,他向后吞噬了一個雙引號,導致反序列化格式錯誤,從而報錯,我們要做的就是讓他往后去吞噬一些我們構造的一些代碼。以下講具體實施。
同樣的,我們這里以修改age為例,不同的是與增加字符串傳值的地方有些許不同,我們構造的值是有一部分讓他吞噬的
先正常傳遞值序列化出我們需要修改的值,我們需要的是將age:13改為35
取出";s:3:"age";s:2:"35";}這就是我們需要構造的,接著繼續將這部分內容重新傳值,序列化出來,得到下面的結果
選中部分就是我們構造出來,他需要吞噬的代碼,s:22:""這個雙引號里面我們還有操作的空間,用來補齊字符串長度,接著就是計算我們自己所需要吃掉的字符串長度為18,根據過濾,他是將兩個o變成一個,也就是每吃掉一個字符,就需要有一個oo,那我們需要吃掉的是18個長度,那么我們就需要18個oo,在吞噬結束之后我們的格式又恢復正確,使得真正的字符s:3:"age";s:2:"35";逃逸出來,成功加入反序列化
這就是我們最終的payload,可以看到下圖成功修改了
例題
有了以上基礎,就可以做題了,簡單的開始入手
安恒四月(字符減少)
<?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']); //省略了存儲序列化數據的過程,下面是取出來并反序列化的操作 $b = unserialize(read(write(serialize($a))));
看到上面的代碼,很明顯,我們需要利用file_get_contents();讀取文件,將flag讀取出來,但是他是個__toString()方法,我們就要讓他觸發這個方法,當反序列化出對象后,被當作字符串使用時,就可以觸發,那我們就需要寫一個鏈,與此同時我們也要知道字符串被刪減了幾個字符
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); }
看這一部分即可了解到,如果發現不可見字符*不可見字符,字符串就會增多,接著又將\0\0\0的6個字符變成3個字符不可見字符*不可見字符,我們自己是不會去寫入不可見字符的在這道題中,相反可以故意寫入\0\0\0使得字符串減少,通過計算逃逸字符,讀取flag文件
題目中序列化的是對象$a,里面有兩個參數,username和password,我們要傳入的也是這兩個參數的值,所以我們構造的payload,應該是往password傳我們構造好的字符,而在username傳入的為計算好的\0\0\0個數,
按照流程來,先寫一個鏈子,某些參數先隨便寫
";s:8:"password";O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:5:"/flag";}}}
選中部分就是我們需要重新傳參的地方,我們傳進password,再次序列化看看效果
我所選擇的地方就是需要被吞噬的部分,然后才能使得后邊的代碼全部逃逸,長度為23,計算后發現需要8個\0\0\0,但8個這樣的符號會吞噬24個字符,因此我們可以在s:70:""雙引號里面可以隨意補一個字符讓它吞噬。
因此我們最終在password里面傳參的應該是:
i";s:8:"password";O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:5:"/flag";}}}
而在username中傳的就是8個\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(字符增加)
掃描目錄發現www.zip,下載后開始,審計源碼
我們根據他的網頁一步步看源碼,首先他要我們登陸,我們先注冊一個賬號進行登陸
看到上圖的界面,這時我們看到update.php,都是一些對參數的白名單按要求寫即可,圖片也隨便傳一個符合大小的即可,但是注意nickname是我們要操作的地方,稍后講解,然后有個序列化數組的過程
$user->update_profile($username, serialize($profile));
在上傳成功后,他會到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這個數組序列化后的值,讀取的是鍵名為photo里面的文件名,而flag在config.php也就是說我們需要構造的就是數組中$profile['photo']='config.php'
那么怎樣才能讓他讀這個config.php呢
我們看到class.php,看到父類mysql中:
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); }
發現序列化的值他會傳遞給user中的方法update_profile,接著update_profile又將這個值傳遞給了父類方法filter,顯而易見,就是一種過濾,防止sql注入,但是可以發現,他是以替換的方式給返回值,在過濾的字符串中,只有where變成hacker,由5個字符變成6個,所以是字符增加的逃逸方式,接著開始構造
選擇部分是需要傳入的,在phone和email都是白名單,傳入的格式受限制,只有nickname是黑名單,所以我們要繞過這個黑名單,bp抓包
他使用的是strlen()所以這里有個方式,這個函數如果參數是字符串,那么數出來的就是字符串個數,如果是數組,那么數出來的就是數組元素的個數
將nickname改成nickname[]然后傳參
傳的參數我們需要構造,需要計算,上面分析了我們需要構造的字符串";}s:5:"photo";s:10:"config.php";},但是他是字符增多,我們就需要知道需要逃逸的字符有多少個,計算了一下為34個,(這里因為改為了數組,而數組的序列化格式結束后是一個大括號,所以我們在一開始的”;后面增加了一個花括號)所以需要34個where幫助我們逃逸這部分代碼,上傳成功,根據這個修改放包即可
返回網頁點擊
之后可以看到一張顯示不出來的圖片,因為使用base64編碼了,右擊查看圖片信息
以上就是php中怎么反序列化字符TAOYI,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。