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-…

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s