Wordpress 3.8.2補丁分析 HMAC timing attack

wyzsk發表於2020-08-19
作者: insight-labs · 2014/04/13 10:24

author: [email protected]

0x00 背景


在github翻來覆去看了半天,官方版的diff只在php裡改動了一個位置:

#!diff
-  if ( $hmac != $hash ) {  
+  if ( hash_hmac( 'md5', $hmac, $key ) !== hash_hmac( 'md5', $hash, $key ) ) {  

WP的開發人員也只是含糊的說這個版本修復了一個可以偽造cookie的漏洞。苦思半天翻來覆去看程式碼之後,甚至順帶挖出了個0day,才發現原來自己又想多了,這個洞的原理其實很簡單,那就是邊通道攻擊。

0x01 細節


邊通道攻擊我就不具體解釋了,這個地方補的是一個關於HMAC的邊通道攻擊,利用時間差來判斷HMAC。

HMAC是一種加密後的hash,比如WP裡用的是HMAC-MD5,外觀和md5一樣,也是長度位32的16進位制字串,但是經過一個key加密,用來防止重放攻擊等。

透過時間差攻擊(Timing attack),可以獲取完整的HMAC從而偽造cookie。

首先我們來看下wordpress登入後的cookie長什麼樣,拿烏雲drops為例:

wordpress_logged_in_7065d11a793a3ec8482214fcc4f0a55b=insight-labs%7C1397480887%7Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx  

cookie名字wordpress_logged_in_後面的那個hash看似很神秘,其實是:

#!php
if ( !defined( 'COOKIEHASH' ) ) {  
$siteurl = get_site_option( 'siteurl' );  
if ( $siteurl )  
define( 'COOKIEHASH', md5( $siteurl ) );  

也就是說其實就是wp網站的url,比如drops的是'http://drops.wooyun.org',md5 一下你就知道了。

但是cookie的內容就沒那麼簡單了,%7c是鍵盤上的豎線 | 。

wp的cookie用豎線作為分隔符,前面是使用者名稱,中間是cookie過期時間,後面是32位的hmac。

hmac的來源比較複雜,但是如果我們能得到這個hmac,我們就可以登入任意已知使用者了(WP並沒有在本地記錄登入session,全靠cookie)。

下面來看下HMAC timing attack的最基本原理,字串對比。

即使沒有看到php的原始碼,從理性角度分析,任何程式語言的字串對比應該都是這樣實現的:

如果兩個字串第一個字元不相同,那麼後面的即使相同也沒有意義了,所以返回 False。

如果第一個字元相同,那麼到第二個字元,如果第二個字元也相同,判斷第三個字元...直到最後一個字元,如果其中有一個字元不一樣,那麼就終止後續判斷,返回 False,如果全部一樣,就返回 True。

看到這裡大家應該都懂了,那就是判斷

'abcdef'=='zxcvbn'

'abcdef'=='abcdeg'

用的時間短。

0x02 攻擊


構造POC:

下面我有一個32位的md5 hash:

f2835bb2a6ab584fc5cf268bb384c598  

有個簡單的php程式

#!php
<?php  
$hash='f2835bb2a6ab584fc5cf268bb384c598'  

if($_GET['hmac']!=$hash){  
exit('Go away...')  
}
echo 'You are admin!'  
?>  

然後我提交:

00000000000000000000000000000000

並且記錄從提交到伺服器返回結果所需要的時間(需要精確到微秒,起碼也得是毫秒級)
之後提交

10000000000000000000000000000000  
..  
20000000000000000000000000000000  
..  
30000000000000000000000000000000  
.  
.  
.  
f0000000000000000000000000000000 

從0-f,然後對比一下他們所需要的時間:

0 0.005450913  
1 0.005829198  
2 0.004905407  
3 0.005286876  
4 0.005597611  
5 0.004814430  
6 0.004969118  
7 0.005335884  
8 0.004433182  
9 0.004440246  
a 0.004860263  
b 0.004561121  
c 0.004463188  
d 0.004406799  
e 0.004978907  
f 0.004887240 

等等……這不是差不多麼,而且明明第一位是f,但是7的時間最長,這尼瑪不是……

enter image description here

但是統計學告訴我們,任何微小的差異在重複多次後都會放大,所以這次我們把每個請求輪流跑500遍,然後都記錄下來,注意,要輪流跑,如果先把0跑500遍再把1跑500遍,第二次對比相同內容的時候伺服器返回都飛快,似乎有某種快取機制。

把第一位的0-f輪流跑了500次之後,把記錄的時間差做成圖:

enter image description here

可以看出,多次重試增加了誤差的統計顯著性。

從圖裡可以看出,大部分情況下0-e的反應速度都在0.005毫秒以內,但是f卻大於這個時間。

知道了第一位之後我們可以用同樣的方法去計算第二位,這次把已知的第一位放進去:

f0000000000000000000000000000000  
f1000000000000000000000000000000  
f2000000000000000000000000000000  
.  
.  
.  
ff000000000000000000000000000000  

相同過程就不再闡述了,不過大家可以試試看,計算所有響應時間的方式是用平均值好還是標準差好。

透過這種攻擊方式,我們只需要16*500*32=256000次請求即可獲取完整的HMAC cookie。

這下就能理解官方補丁的用意了,這樣雖然沒有去掉時間差(時間差總是存在的,除非用恆定時間對比演算法),但是因為多加了一層hmac,攻擊者的payload被隨機化了,無法透過時間差來猜測具體字元是哪一個。

不過在網際網路環境下,這個洞還是有點雞肋,但是如果是在同一個機房或者虛擬主機同站的話效果應該還是不錯的。

WP的這個洞是補上了,我相信大家會舉一反三的,畢竟用 == 或者!=的地方太多了。

PS: 小編欠飯次數+=1

本文章來源於烏雲知識庫,此映象為了方便大家學習研究,文章版權歸烏雲知識庫!

相關文章