PHP 中的類型強制轉換和 C 中的非常像:在要轉換的變數之前加上用括弧括起來的目標類型。
允許的強制轉換有:
(int),(integer) - 轉換成整型
(bool),(boolean) - 轉換成布爾型
(float),(double),(real) - 轉換成浮點型
(string) - 轉換成字串
(array) - 轉換成數組
(object) - 轉換成對象
注意在括弧內允許有空格和定位字元
還可以用settype ( mixed var, string type )進行強制轉換.
遠端管理外掛程式是十分受WordPress網站管理員歡迎的工具,它們允許使用者同時對多個網站執行相同的操作,如,更新到最新的發行版或安裝外掛程式。然而,為了實現這些操作,用戶端外掛程式需要賦予遠端使用者很大的許可權。因此,確保管理伺服器和用戶端外掛程式之間的通訊安全且不能被攻擊者偽造就變得相當重要了。本文將談及幾款可用外掛程式,利用其弱點,攻擊者甚至可以完全危及到運行這些外掛程式的網站。
ManageWP, InfiniteWP, and CMS Commander
這三個服務有著相同的用戶端外掛程式基礎代碼(目測最初是ManageWp實現,然後另外兩個對其進行了調整),因而它們都有簽名繞過漏洞並且會導致遠程代碼執行。
管理伺服器註冊一個用戶端外掛程式的私密金鑰,用來計算每一條訊息的訊息認證碼,而不是要求使用者提供管理員憑證[MAC,我們平時看到它會將其當做硬體的MAC地址,這裡是Message Authentication Code ]。當一條訊息通過使用共用密鑰的訊息摘要演算法後就產生了訊息摘要。該MAC隨後附在訊息後面一起發送出去,接收方收到後用共用秘鑰對收到的訊息進行計算,產生MAC2,然後和MAC1進行比較。訊息摘要用於驗證訊息的真實性和完整性[學過密碼學的同學都應該知道],是一個確保通訊安全的好方法,但是這三項服務的用戶端外掛程式在實現上的缺陷導致了嚴重的漏洞。
一條由helper.class.php認證的傳入訊息如下所示:
// $signature is the MAC sent with the message // $data is part of the message if (md5($data . $this->get_random_signature()) == $signature) { // valid message }
使用非嚴格的等於意味著在比較前會發生類型“欺騙”[類型轉換]。md5()函數的輸出永遠都是字串,但是如果$signature變了是一個整數,那麼比較時發生的類型轉換就容易偽造一個匹配的MAC。例如,如果真實的MAC是以”0”開頭,或者非數字字元開頭,那麼0就能匹配上,如果是”1xxx”這樣的,那麼整數1就能匹配,一次類推。[這裡其實是php的一個特性,當然其他語言也會有,當一個字串和數字進行非嚴格等於的比較時,如果第一個字元是數字就會將其轉換成對應的整數進行比較,如果是非0-9的字元,就會將其當做0,php.net的說明:如果比較一個數字和字串或者比較涉及到數字內容的字串,則字串會被轉換為數值並且比較按照數值來進行]。
字串轉換為數值:
當一個字串被當作一個數值來取值,其結果和類型如下:
如果該字串沒有包含 '.','e' 或 'E' 並且其數字值在整型的範圍之內(由 PHP_INT_MAX 所定義),該字串將被當成integer 來取值。其它所有情況下都被作為 float 來取值。
該字串的開始部分決定了它的值。如果該字串以合法的數值開始,則使用該數值。否則其值為 0(零)。合法數值由可選的加號或減號,後面跟著一個或多個數字(可能有小數點),再跟著可選的指數部分。指數部分由 'e' 或 'E' 後面跟著一個或多個數字構成。
// $signature is the MAC sent with the message
// $data is part of the message
if (md5($data . $this->get_random_signature()) == $signature) {
// valid message }
遺憾的是,攻擊者可以提供一個整數作為簽名。init.php中,傳入的請求將會使用base64_decode()解碼,然後還原序列化其結果。Unserialize()的使用意味著可以控制輸入資料的類型,一個偽造的序列化訊息如下:
a:4:{s:9:"signature";i:0;s:2:"id";i:100000;s:6:"action";s:16:"execute_php_code";s:6:"params";a:2:{s:8:"username";s:5:"admin";s:4:"code";s:25:"exec('touch /tmp/owned');";}}
這條訊息使用整數0作為簽名,然後使用外掛程式提供的execute_php_code執行任意的PHP代碼。
$signature = 0; // $data is the action concatenated with the message ID $data = 'execute_php_code' . 100000; if (md5($data . $this->get_random_signature()) == $signature) { // valid message if the output of // md5() doesn't start with a digit }
這個偽造的例子可能沒法直接使用,首先,id的索引值需要比之前合法訊息的值更大[使用增加的訊息ID用於防止重放攻擊,今天既有請求偽造,又有重放,這讓我想到了CSRF,跨站請求偽造,下面是不是還有中間人攻擊呢),其次要有用於匹配簽名的整數,這兩點要求可以進行暴力破解來突破。
for i from 100,000 to 100,500: for j from 0 to 9: submit request with id i and signature j
上面的虛擬碼嘗試發送具有很大ID值得虛假訊息,並且對每個ID都進行十次單獨的數位指紋匹配[前面說到過,對於一個字串,只要一個數字就可以在比較時進行匹配,這裡從0-9是因為每一種情況都能遇到]。
這一缺陷可以通過使用全等運算子[===]和對傳入的指紋進行檢查來修複。這幾個外掛程式服務都通過使用嚴格的全等運算子進行了修複[php.net的說明:a===b,則a和b值相等,且類型也相等;a==b,在發生類型轉換後再判斷其值是否相等]。
另外還有一些其他的問題,但是他們還沒有採取行動。首先,這一做法是有弱點的[密鑰追加到$data,然後進行散列],應該用HMAC[Hash-based Message Authentication Code,以一個密鑰和一個訊息為輸入,產生一個訊息摘要作為輸出]。其次,僅用於操作的action和訊息ID被用於建立簽名。這意味著,一個活躍的網路攻擊者可以改變訊息中的參數而簽名依舊是有效[例如改變execute_php_code訊息執行任意代碼]。為了進行保護,MAC應該包含整條訊息。
[注意,基於MD5的訊息摘要是一種後退,可以的話這些外掛程式使用openssl_verify();***2014-04公布出來的Openssl 1.0.f heartbleed漏洞號稱世紀級漏洞***]
Worpit
Worpit是另一個遠端管理服務,但它使用從頭開始構建的用戶端外掛程式,它同樣有強制類型轉換漏洞,可以讓攻擊者以管理員權限登陸。
該外掛程式提了遠端管理員登陸的方法,使用僅Woprit傳遞系統可配置的臨時的token值。這款外掛程式會檢查請求中提供的token值是否和儲存在資料庫中的值匹配。
if ( $_GET['token'] != $oWpHelper->getTransient( 'worpit_login_token' ) ) { die( 'WorpitError: Invalid token' ); }
令牌是從一次使用的資料庫中刪除。這意味著大多數的時候都是在資料庫中沒有令牌。因此,在調用getTransient()方法可能返回false。非嚴格的比較是,這意味著任何“falsey價值,比如字串0,將被視為一個有效令牌。一個例子網址以管理員身份登入:
這個token一經使用就會從資料庫中刪除,這意味著,大多數時候資料庫中是沒有token的。因此,對getTransient()方法的調用很可能返回false。非嚴格的比較也用到了,這意味著任何相當於false的值,例如字串0會被當做一個有效token,以管理員身份登陸的例子:http://victim/?worpit_api=1&m=login&token=0
至此,攻擊者該網站就為攻擊者所控制了,他有許可權安裝惡意外掛程式或修改已有的外掛程式。
這裡的修複方案是使用!==並進行其他檢查及從資料庫進行檢索。
結論:
一定要記住檢查使用者輸入的是預期的類型並在安全性很重要的函數中使用進行嚴格比較,如檢查身分識別驗證令牌。