軟體漏洞分析技巧分享
作者:riusksk
0x00 背景
在日常分析軟體漏洞時,經常需要耗費比較長的分析時間,少則幾小時,多則數天,甚至更久。因此,經常總結一些分析技巧是非常有必要的,針對不同的漏洞型別採取不同的分析思路和技巧,可以有效地提高分析速度。對於一些被曝出來的熱門0day,網上一般都會有分析文章,但一般都是“結論性分析”,也就是直接帖漏洞程式碼,指出哪裡出錯,而非“思路性分析”。如果你經常分析漏洞的話,會發現佔用你分析時間的往往不是分析漏洞程式碼,而是定位漏洞程式碼。所以說,除錯分析漏洞有時就是看下斷點下得準不,再加上一些胡猜亂想來推測,最後才是分析漏洞程式碼了,如果熟悉彙編指令,這個就不是問題了。
下面是筆者就以往分析過的若干例項漏洞,總結出的一些小技巧。不過,技巧甚多,篇幅有限,此處僅列舉一些比較個人常用的方法,也歡迎各位分享自己的一些分析技巧,大家共同學習探討。
0x01 技巧
技巧一:快速定位JS程式碼呼叫的IE類成員函式
CVE-2011-0027 Microsoft Data Access元件整數溢位漏洞是Pwn2Own 2010駭客大賽中被用來攻破IE8瀏覽器的漏洞,其中關鍵的漏洞觸發程式碼如下:
#!js
localxmlid1 = document.getElementById('xmlid1').recordset; // 獲取xml元素xmlid1的recordset,即資料庫表的記錄集
localxmlid1.CacheSize = 0x40000358; // 設定能夠被儲存的記錄條數,此值最終造成整數溢位
現在我們就介紹一種快速定位上述兩行程式碼將對應呼叫的IE類成員函式,首先用IDA載入漏洞檔案msado15.dll,並允許載入微軟符號表,然後選中“Function name”一欄,按“Alt + T”快捷鍵彈出搜尋框,輸入搜尋關鍵字“cachesize”:
透過按“Ctrl+T”可繼續搜尋下一個,最後找到兩個相關函式:
根據函式名猜測下面的函式可能就是用於設定和獲取cachesize屬性值的:
#!cpp
CRecordset::put*CacheSize(long *)
CRocordset::get*CacheSize(long)
我們對CRecordset::put_CacheSize
下斷點驗證下前面的猜測,用Windbg附加IE程式執行後開啟poc.html,確實斷在put_CacheSize,透過檢視引數可以看到poc.html中設定的CacheSize值0x40000358,說明CRecordset::put_CacheSize
確實是設定CacheSize值的函式:
#!bash
0:005> g
Breakpoint 1 hit
eax=40000358 ebx=04bdcfd8 ecx=6e61d340 edx=00000000 esi=01fbf144 edi=00000000
eip=6e6ac957 esp=01fbeb58 ebp=01fbf040 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
msado15!CRecordset::put_CacheSize:
6e6ac957 8bff mov edi,edi
0:005> dd esp
01fbeb58 6e62f3ec 04bdcfd8 40000358 00000000
01fbeb68 01fbf074 04bdcfd8 11000011 00000000
01fbeb78 03570ae8 004ad070 00538a30 00000088
01fbeb88 00470000 00000002 03570760 01fbec84
01fbeb98 76fc3193 00470138 76fc316f 764736b8
01fbeba8 00000000 00470000 03570768 00518a78
01fbebb8 004767b8 004768e4 00559258 00476db8
01fbebc8 76f8d74d 0051d968 00472a98 01fbedc0
說明 localxmlid1.CacheSize = 0x40000358 這行設定CacheSize的JS程式碼對應呼叫的就是CRecordset::put_CacheSize 函式。採用同類方法也很容易確認 document.getElementById('xmlid1').recordset 呼叫的是CGenericElement::get_recordset函式,各位朋友可以自己動手試下。
技巧二:透過頁堆快速定位堆漏洞程式碼
頁堆是windows 2000 引入的除錯支援功能,簡稱DPH(Debug Page Heap),啟用該機制後,堆管理器會在堆塊後增加專門用於檢測溢位的柵欄頁,當資料溢位觸及柵欄頁便會立刻觸發異常,此時往往就是觸發漏洞的最及時的位置,它不僅適用於堆溢位,對於其它型別的堆漏洞也是適用的。
以CVE-2013-0077 微軟DirectShow堆溢位漏洞為例,透過以下命令開啟頁堆(gflags):
#!bash
gflags.exe –i player.exe +hpa
開啟頁堆hpa後,重新附加執行後,在複製資料到堆邊界時斷下:
#!bash
(4b8.358): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=000000c3 ebx=003fac98 ecx=00000003 edx=000000f7 esi=001bbdd4 edi=003fb000
eip=7d0706d0 esp=02a5f650 ebp=02a5f658 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=0038 gs=0000 efl=00010202
quartz!ParseSequenceHeader+0x114:
7d0706d0 f3a5 rep movs dword ptr es:[edi],dword ptr [esi]
上面就是斷在複製資料導致溢位的指令,透過分析其所在函式往往很容易定位漏洞程式碼。如果不開啟頁堆,直接以預設形式除錯的話,你會發現是斷在以下指令:
#!bash
(4c8.6bc): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=41414141 ebx=003f0000 ecx=41414141 edx=03128e40 esi=03128e38 edi=00000012
eip=7c930efe esp=0465f998 ebp=0465fbb8 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246
ntdll!RtlAllocateHeap+0x653:
7c930efe 8b39 mov edi,dword ptr [ecx] ds:0023:41414141=????????
這已經是堆溢位後導致的記憶體讀取異常了,不再是觸發漏洞時最原始的場景了。因此開啟頁堆後,會更方便你去定位漏洞程式碼。
技巧三:基於堆分配記錄定位整數溢位漏洞程式碼
以CVE-2011-0027 Microsoft Data Access元件整數溢位漏洞為例,這個漏洞就是在Pwn2Own 2010駭客大賽中,荷蘭駭客Peter Vreugdenhil利用它來攻破Win7上的IE8瀏覽器的。
下面是開啟poc.html後的異常情況:
#!bash
(7b8.278): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0000036b ebx=0000035b ecx=00000000 edx=00000001 esi=088c8000 edi=00000000
eip=6887746f esp=044dee84 ebp=044dee88 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010202
mshtml!CImpIRowset::HRowNumber2HROWQuiet+0x23:
6887746f 8906 mov dword ptr [esi],eax ds:0023:088c8000=????????
0:005> !heap -p -a 088c8000
address 088c8000 found in
_DPH_HEAP_ROOT @ 8821000
in busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize - VirtAddr VirtSize)
88227ec: 88c7298 d64 - 88c7000 2000
72eb8e89 verifier!AVrfDebugPageHeapAllocate+0x00000229
77034ea6 ntdll!RtlDebugAllocateHeap+0x00000030
76ff7d96 ntdll!RtlpAllocateHeap+0x000000c4
76fc34ca ntdll!RtlAllocateHeap+0x0000023a
730d975d MSDART!MpHeapAlloc+0x00000029
6e5406e7 msado15!CRecordGroup::AllocateHRowRange+0x00000085
6e540650 msado15!CRecordset::PrepareForFetch+0x000000e2
6e5d44ae msado15!CRecordset::MoveAbsolute+0x000003e3
6e5680a5 msado15!CRecordset::_MoveFirst+0x0000007d
6e5d7957 msado15!CRecordset::MoveFirst+0x00000221
6e54fde6 msado15!CRecordset::Invoke+0x00001560
71dcdb38 jscript!IDispatchInvoke2+0x000000f0
71dcda8c jscript!IDispatchInvoke+0x0000006a
71dcd9ff jscript!InvokeDispatch+0x000000a9
71dcdb8a jscript!VAR::InvokeByName+0x00000093
71dcd8c8 jscript!VAR::InvokeDispName+0x0000007d
71dcd96f jscript!VAR::InvokeByDispID+0x000000ce
71dce3e7 jscript!CScriptRuntime::Run+0x00002b80
71dc5c9d jscript!ScrFncObj::CallWithFrameOnStack+0x000000ce
71dc5bfb jscript!ScrFncObj::Call+0x0000008d
71dc5e11 jscript!CSession::Execute+0x0000015f
71dbf3ee jscript!NameTbl::InvokeDef+0x000001b5
71dbea2e jscript!NameTbl::InvokeEx+0x0000012c
71db96de jscript!NameTbl::Invoke+0x00000070
685aaa7b mshtml!CWindow::ExecuteTimeoutScript+0x00000087
685aab66 mshtml!CWindow::FireTimeOut+0x000000b6
685d6af7 mshtml!CStackPtrAry<unsigned long,12>::GetStackSize+0x000000b6
685d1e57 mshtml!GlobalWndProc+0x00000183
770e86ef USER32!InternalCallWinProc+0x00000023
770e8876 USER32!UserCallWinProcCheckWow+0x0000014b
770e89b5 USER32!DispatchMessageWorker+0x0000035e
770e8e9c USER32!DispatchMessageW+0x0000000f
根據上面異常的資訊,可以知道程式是在向地址為0x88c7298,大小0xd64的堆塊寫入資料時造成堆溢位了。再根據!heap命令中返回的棧回溯資訊可以知道,被溢位的堆塊是在CRecordset::MoveFirst
函式中呼叫MpHeapAlloc函式分配的,呼叫該分配函式的上層函式是CRecordGroup::AllocateHRowRange
,對此函式下斷,跟進後:
#!bash
Breakpoint 0 hit
eax=00000001 ebx=40000358 ecx=76fc316f edx=096f2d34 esi=09759d70 edi=40000358
eip=69da06be esp=0446eeec ebp=0446eef8 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
msado15!CRecordGroup::AllocateHRowRange+0x5e:
69da06be 85ff test edi,edi
0:005> p
eax=00000001 ebx=40000358 ecx=76fc316f edx=096f2d34 esi=09759d70 edi=40000358
eip=69da06c0 esp=0446eeec ebp=0446eef8 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
msado15!CRecordGroup::AllocateHRowRange+0x60:
69da06c0 0f8e34380200 jle msado15!CRecordGroup::AllocateHRowRange+0x62 (69dc3efa) [br=0]
0:005> p
eax=00000001 ebx=40000358 ecx=76fc316f edx=096f2d34 esi=09759d70 edi=40000358
eip=69da06c6 esp=0446eeec ebp=0446eef8 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
msado15!CRecordGroup::AllocateHRowRange+0x64:
69da06c6 8bc7 mov eax,edi // eax = edi = 0x40000358,即CacheSize
0:005> p
eax=40000358 ebx=40000358 ecx=76fc316f edx=096f2d34 esi=09759d70 edi=40000358
eip=69da06c8 esp=0446eeec ebp=0446eef8 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
msado15!CRecordGroup::AllocateHRowRange+0x66:
69da06c8 8b3dfc10d969 mov edi,dword ptr [msado15!_imp__MpHeapAlloc (69d910fc)] ds:0023:69d910fc={MSDART!MpHeapAlloc (72de9730)}
0:005> p
eax=40000358 ebx=40000358 ecx=76fc316f edx=096f2d34 esi=09759d70 edi=72de9730
eip=69da06ce esp=0446eeec ebp=0446eef8 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
msado15!CRecordGroup::AllocateHRowRange+0x6c:
69da06ce 89460c mov dword ptr [esi+0Ch],eax ds:0023:09759d7c=00000000
0:005> p
eax=40000358 ebx=40000358 ecx=76fc316f edx=096f2d34 esi=09759d70 edi=72de9730
eip=69da06d1 esp=0446eeec ebp=0446eef8 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
msado15!CRecordGroup::AllocateHRowRange+0x6f:
69da06d1 8b0d10f0e569 mov ecx,dword ptr [msado15!g_hHeapHandle (69e5f010)] ds:0023:69e5f010=096f0000
0:005> p
eax=40000358 ebx=40000358 ecx=096f0000 edx=096f2d34 esi=09759d70 edi=72de9730
eip=69da06d7 esp=0446eeec ebp=0446eef8 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
msado15!CRecordGroup::AllocateHRowRange+0x75:
69da06d7 8d048504000000 lea eax,[eax*4+4] // 分配的堆塊大小= 0x40000358*4+4 = 0xd64,這裡eax的最大值是0xFFFFFFFF,經eax*4+4後變成0x100000D64 > 0xFFFFFFFF造成整數溢位,結果溢位後等於0xD64。如果我們把CacheSize設定成0x3FFFFFFF,那麼後面分配的堆塊就是0了。
0:005> p
eax=00000d64 ebx=40000358 ecx=096f0000 edx=096f2d34 esi=09759d70 edi=72de9730
eip=69da06de esp=0446eeec ebp=0446eef8 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
msado15!CRecordGroup::AllocateHRowRange+0x7c:
69da06de 50 push eax // 堆塊大小=0xd64
0:005> p
eax=00000d64 ebx=40000358 ecx=096f0000 edx=096f2d34 esi=09759d70 edi=72de9730
eip=69da06df esp=0446eee8 ebp=0446eef8 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
msado15!CRecordGroup::AllocateHRowRange+0x7d:
69da06df 680000a000 push 0A00000h
0:005> p
eax=00000d64 ebx=40000358 ecx=096f0000 edx=096f2d34 esi=09759d70 edi=72de9730
eip=69da06e4 esp=0446eee4 ebp=0446eef8 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
msado15!CRecordGroup::AllocateHRowRange+0x82:
69da06e4 51 push ecx
0:005> p
eax=00000d64 ebx=40000358 ecx=096f0000 edx=096f2d34 esi=09759d70 edi=72de9730
eip=69da06e5 esp=0446eee0 ebp=0446eef8 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
msado15!CRecordGroup::AllocateHRowRange+0x83:
69da06e5 ffd7 call edi {MSDART!MpHeapAlloc (72de9730)}
該漏洞主要是由於對CacheSize整數值未作有效判斷,導致經CacheSize4+4造成整數溢位,當以CacheSize4+4結果作為分配堆塊的大小時,由於分配過小堆塊就會造成堆溢位。
技巧四:基於條件記錄斷點的漏洞分析技巧
以CVE-2012-0774 Adobe Reader TrueType字型整數溢位漏洞為例,該漏洞主要是在解析TTF字型中的虛擬指令導致的溢位,為了確定導致溢位的是哪條虛擬指令,就可以透過設定條件記錄斷點來實現。 開啟poc觸發崩潰後,透過棧回溯定位漏洞函式,此處標記為VulFunction,它就是用於索引虛擬指令處理函式的。
透過快捷鍵Shift + F4對VulFunction下條件記錄斷點,記錄每次呼叫VulFunction時對應的虛擬指令索引號
設定好後重新載入AcroRd32.exe執行(不要關閉偵錯程式否則前面設定的斷點可能失效),開啟poc.pdf執行後檢視日誌視窗:
導致漏洞的虛擬指令索引號為0x26,透過蘋果官方提供的指令集https://developer.apple.com/fonts/ttrefman/RM05/Chap5.html,可以查到位元組碼0x26對應的虛擬指令為MINDEX,正是該條指令導致的溢位。
技巧五:基於JS日誌的漏洞分析技巧
有時在除錯IE漏洞時,我們需要樣本中的JS程式碼的執行先後情況,透過新增一些數學函式,比如math.atan2、math.asin等,還可以使用其它函式,然後透過對jscript!Js::Math::Atan
(或者其它函式)下斷點來輸出log,以方便分析者觀察執行情況。比如poc中有如下js,其中的math.atan2是我們新增的:
#!cpp
Math.atan2(0xbabe, "[*] Creating object button...");
var obj = document.createElement("button");
Math.atan2(0xbabe, "[*] Assigning data to title...");
obj.title = data.substring(0,0x40000-0x58);
Math.atan2(0xbabe, "[*] Let's AppendChild");
div_container.appendChild(obj);
透過設定以下斷點:
#!bash
bu jscript!JsAtan2 ".printf \"%mu\", poi(poi(poi(esp+14)+8)+8);.echo;g"
執行後在windbg上的輸出視窗就會看到:
#!bash
[*] Creating object button...
[*] Assigning data to title...
[*] Let's AppendChild
技巧六:透過虛擬機器快照來固定堆地址
最早聽到這個方法,是instruder在QQ群裡面提到的。由於在分析漏洞時,尤其是堆漏洞時,每個重新載入執行時,分配的堆地址都是固定,無論是分析還是寫文件,都不太利用於我們分析和描述。因此我們可以先將程式除錯已經完成堆分配的某個地址,然後將其儲存為虛擬機器快照,等我們需要再重新開始除錯時,可透過恢復先前儲存的快照來重新除錯,那麼此時的堆地址跟之前分析的地址都是固定的。
技巧七:監控堆分配釋放動作來分析UAF、double free漏洞
以之前分配libpng某個double free漏洞的分析為例,主要是透過對釋放函式free進行監控並輸出記錄來分析的。程式崩潰時的棧回溯如下:
重新用windbg載入,透過監控堆塊的釋放過程,可以發現它在對其中某堆塊進行了雙重釋放,先下斷:
#!bash
bu 3440D279 ".if(1){.echo EnterVulnFunc;gc}"
bu 6e264b6c ".if(1){.echo Free heap block; dd esp l4;gc}"
輸出結果:
#!bash
EnterVulnFunc
Free heap block
0011bc5c 3441e2a2 138f0020 3b906313 10027b64
Free heap block
0011bc5c 3441dc6c 138f0020 3b906313 10027b64
(1508.e84): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=138f0018 ebx=138f0020 ecx=6e287a7e edx=10028a70 esi=008a0000 edi=00000000
eip=77691f88 esp=0011bbe8 ebp=0011bbf8 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246
ntdll!RtlFreeHeap+0x3a:
77691f88 80780705 cmp byte ptr [eax+7],5 ds:0023:138f001f=??
可以看到它對同一個堆塊進行多次釋放。在IE漏洞分析中最常見的還是UAF,如果想快速定造成UAF的IE物件,就需要對物件的分配的釋放進行釋放,這個可透過偵錯程式指令碼來實現,或者像之前h4ckmp同學基於Windbg介面寫的EXE工具,可以快速分析出導致UAF漏洞的物件,如下圖所示。
技巧八:基於汙點追蹤的分析方法
汙點追蹤理論挺好的,但成品往往不容樂觀。汙點追蹤猶如“七傷拳”一般,“先傷己,再傷人”,開發汙點追蹤工具,不僅費時費力,而且開發完成後,執行比較大的工具往往需要執行很長時間,比如IE、Adobe等軟體,有時甚至需要整整一天的時間,這種一般是在除錯分析不方便的時候才使用的,主要針對檔案格式漏洞。另外,你也可利用pin等動態插樁框架開發出動態分析工具,針對特定函式掛鉤,比如堆分配與釋放函式,也可以用它來實現快速分析。
相關文章
- 中介軟體漏洞2024-10-04
- 軟體測試培訓分享:軟體測試崗位面試技巧有哪些?2021-11-17面試
- 桌面整理軟體分享!2023-05-19
- 經驗分享 | 網路安全漏洞分析者之路2020-12-17
- 軟體工程估算的技巧 - shubhro2022-05-12軟體工程
- 金蝶KIS軟體操作小技巧2020-12-25
- 分享快樂,用好用的軟體,分享五款win10軟體2023-03-30Win10
- 筆記|軟體除錯的技巧2019-07-07筆記除錯
- Intel軟體被曝漏洞:直接放棄2018-04-08Intel
- 淺談中介軟體漏洞與防護2018-06-23
- 中介軟體漏洞攻防學習總結2024-04-09
- Web中介軟體常見漏洞總結2020-07-09Web
- 常見中介軟體漏洞復現(上)2022-03-18
- 勒索軟體利用 Windows 驅動程式漏洞關閉防病毒軟體2020-02-09Windows
- SOLIDWORKS軟體引數化建模配合技巧2021-11-29Solid
- 軟體工程師前景分析2019-10-19軟體工程工程師
- 軟體測試-需求分析2020-06-20
- 軟體需求與分析 業務建模分析2024-10-30
- PHPUnit 加速技巧分享2019-01-21PHP
- Python小技巧分享2023-10-30Python
- go技巧分享(二)2021-09-09Go
- 做資料分析,軟體工具少不了,好用的資料分析軟體工具2021-12-29
- 【分享】—如何學習軟體測試2020-10-12
- 分享Windows/Mac電腦軟體大全2022-02-17WindowsMac
- 分享5款Mac免費軟體2023-01-11Mac
- 有趣軟體分享之第一彈2021-01-03
- 軟體測試培訓分享:軟體測試和軟體開發學哪個好呢2021-11-12
- linux常用服務軟體搭建及使用技巧2019-05-14Linux
- java培訓分享:java軟體開發可以用哪些軟體?2021-12-09Java
- 【漏洞分析】KaoyaSwap 安全事件分析2022-08-28事件
- 足彩軟體app哪個好 足球分析軟體app排名2022-11-14APP
- 利用勒索軟體Locky的漏洞來免疫系統2020-08-19
- 戴爾預裝軟體曝出安全漏洞2019-06-25
- CWE公佈最新25個危險軟體漏洞列表 記憶體損壞漏洞居首位2021-07-26記憶體
- 資料分析相關軟體2018-11-01
- 軟體測試需求分析方法2018-07-17
- linux ddos惡意軟體分析2020-08-19Linux
- 惡意軟體Linux/Mumblehard分析2020-08-19Linux