$30 off During Our Annual Pro Sale. View Details »

I just hacked your app - TakeOff 2016

I just hacked your app - TakeOff 2016

Android security is nowhere near where it should be. I have been able to hack and get sensitive information from a few different apps and I’m just an amateur hacker at best.

In this session we will explore a number of ways an Android app can be exploited and most importantly methods that we can use to avoid these attacks.

Marcos Placona

October 20, 2016
Tweet

More Decks by Marcos Placona

Other Decks in Programming

Transcript

  1. I just hacked your app

  2. None
  3. PWNED

  4. Marcos Placona @marcos_placona placona.co.uk marcos@twilio.com

  5. NOT

  6. None
  7. None
  8. None
  9. None
  10. Kuba Gretzki http://bit.ly/hack4beer

  11. loyalty \ˈlȯi(-ə)l-tē\

  12. loyalty + =

  13. loyalty HTTP Proxy

  14. POST /users/461845f5d03e6c052a43afbc/points Accept: application/json Accept-Language: en-gb X-App-Version: 1.28.0 User-Agent: Dalvik/2.1.0

    (Linux; U; Android 6.0.1;) ... Content-Type: application/json; charset=UTF-8 Content-Length: 375 Host: api.eatapp.com Connection: Keep-Alive Accept-Encoding: gzip { "authentication_token":"boKUp9vBHNAJp7XbWZCK", "latitude":..., "longitude":..., "point":{ "isDoneByGesture":false, "main_beacon":{ "major":38995, "minor":12702, "uuid":"2C75E74B-41B7-49E3-BD26-CE86B2F569F8" }, "place_id":"450", "promoted_products_ids":[ {"id":"647035946536601578040000"}, {"id":"647035946536601578040000"}, {"id":"647035946536601578050000"} ] } }
  15. POST /users/461845f5d03e6c052a43afbc/points Accept: application/json Accept-Language: en-gb X-App-Version: 1.28.0 User-Agent: Dalvik/2.1.0

    (Linux; U; Android 6.0.1;) ... Content-Type: application/json; charset=UTF-8 Content-Length: 375 Host: api.eatapp.com Connection: Keep-Alive Accept-Encoding: gzip { "authentication_token":"boKUp9vBHNAJp7XbWZCK", "latitude":..., "longitude":..., "point":{ "isDoneByGesture":false, "main_beacon":{ "major":38995, "minor":12702, "uuid":"2C75E74B-41B7-49E3-BD26-CE86B2F569F8" }, "place_id":"450", "promoted_products_ids":[ {"id":"647035946536601578040000"}, {"id":"647035946536601578040000"}, {"id":"647035946536601578050000"} ] } }
  16. POST /users/461845f5d03e6c052a43afbc/points Accept: application/json Accept-Language: en-gb X-App-Version: 1.28.0 User-Agent: Dalvik/2.1.0

    (Linux; U; Android 6.0.1;) ... Content-Type: application/json; charset=UTF-8 Content-Length: 375 Host: api.eatapp.com Connection: Keep-Alive Accept-Encoding: gzip { "authentication_token":"boKUp9vBHNAJp7XbWZCK", "latitude":..., "longitude":..., "point":{ "isDoneByGesture":false, "main_beacon":{ "major":38995, "minor":12702, "uuid":"2C75E74B-41B7-49E3-BD26-CE86B2F569F8" }, "place_id":"450", "promoted_products_ids":[ {"id":"647035946536601578040000"}, {"id":"647035946536601578040000"}, {"id":"647035946536601578050000"} ] } }
  17. None
  18. None
  19. stop!

  20. • Encrypt all the values • Utilise security features when

    they exist • Certificate pinning • DO NOT TRUST THE DEVICE
  21. // Sending side byte[] data = text.getBytes("UTF-8"); String base64 =

    Base64.encodeToString(data, Base64.DEFAULT); // Receiving side byte[] data = Base64.decode(base64, Base64.DEFAULT); String text = new String(data, "UTF-8"); Encrypt all the values
  22. Your keys will end up in GitHub

  23. • Encrypt all the values • Utilise security features when

    they exist • Certificate pinning • DO NOT TRUST THE DEVICE
  24. Utilise security features when they exist

  25. • Encrypt all the values • Utilise security features when

    they exist • Certificate pinning • DO NOT TRUST THE DEVICE
  26. public class KeyPinStore { private static KeyPinStore instance = null;

    private SSLContext sslContext = SSLContext.getInstance("TLS"); public static synchronized KeyPinStore getInstance() throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException, KeyManagementException{ if (instance == null){ instance = new KeyPinStore(); } return instance; } private KeyPinStore() throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException, KeyManagementException{ // Load CAs from an InputStream // (could be from a resource or ByteArrayInputStream or ...) CertificateFactory cf = CertificateFactory.getInstance("X.509"); // randomCA.crt should be in the Assets directory InputStream caInput = new BufferedInputStream(MainActivity.context.getAssets().open("randomCA.crt")); Certificate ca; try { ca = cf.generateCertificate(caInput); System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN()); } finally { caInput.close(); } // Create a KeyStore containing our trusted CAs String keyStoreType = KeyStore.getDefaultType(); KeyStore keyStore = KeyStore.getInstance(keyStoreType); keyStore.load(null, null); keyStore.setCertificateEntry("ca", ca); // Create a TrustManager that trusts the CAs in our KeyStore String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm); tmf.init(keyStore); // Create an SSLContext that uses our TrustManager // SSLContext context = SSLContext.getInstance("TLS"); sslContext.init(null, tmf.getTrustManagers(), null); } public SSLContext getContext(){ return sslContext; } } Certificate pinning https://www.owasp.org/index.php/Certificate_and_Public_Key_Pinning#Android
  27. • Encrypt all the values • Utilise security features when

    they exist • Certificate pinning • DO NOT TRUST THE DEVICE
  28. Someone will decompile your app

  29. And when they do…

  30. –Every Developer “But I need magic strings”

  31. Options • Make sure you encrypt them • Protect them

    with tools like DexGuard and ProGuard • Get them off a server http://bit.ly/SafeKey
  32. start!

  33. Tampering detection // myPackageName should decode at runtime to "com.yourpackagename"

    // google should decode at runtime to "com.android.vending"; // amazon should decode at runtime to "com.amazon.venezia"; public boolean isHacked(Context context, String myPackageName, String google, String amazon) { //Crooks renamed your app? if (context.getPackageName().compareTo(myPackageName != 0) return true; // BOOM! //Rogues relocated your app? String installer = context.getPackageManager().getInstallerPackageName(myPackageName); if (installer == null) return true; // BOOM! if (installer.compareTo(google) != 0 && installer.compareTo(amazon) != 0) return true; // BOOM! return false; } http://bit.ly/isHacked
  34. Tampering detection // myPackageName should decode at runtime to "com.yourpackagename"

    // google should decode at runtime to "com.android.vending"; // amazon should decode at runtime to "com.amazon.venezia"; public boolean isHacked(Context context, String myPackageName, String google, String amazon) { //Crooks renamed your app? if (context.getPackageName().compareTo(myPackageName != 0) return true; // BOOM! //Rogues relocated your app? String installer = context.getPackageManager().getInstallerPackageName(myPackageName); if (installer == null) return true; // BOOM! if (installer.compareTo(google) != 0 && installer.compareTo(amazon) != 0) return true; // BOOM! return false; } http://bit.ly/isHacked
  35. • Check your app’s signature • Check for rooted device

    • Check for emulator • Check if the app is debuggable
  36. private static final int VALID = 0; private static final

    int INVALID = 1; private static final String APP_SIGNATURE = "1038C0E34658923C4192E61B16846"; public static int checkAppSignature(Context context) { try { PackageInfo packageInfo = context.getPackageManager() .getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES); for (Signature signature : packageInfo.signatures) { byte[] signatureBytes = signature.toByteArray(); MessageDigest md = MessageDigest.getInstance("SHA"); md.update(signature.toByteArray()); //compare signatures if (SIGNATURE.equals(APP_SIGNATURE)){ return VALID; }; } } catch (Exception e) { //assumes an issue in checking signature., but we let the caller decide on what to do. } return INVALID; } Check your app’s signature http://bit.ly/AndroidTampering
  37. • Check your app’s signature • Check for rooted device

    • Check for emulator • Check if the app is debuggable
  38. private static boolean canExecuteCommand(String command) { try { int exitValue

    = Runtime.getRuntime().exec(command).waitFor(); if (exitValue != 0) return false; else return true; } catch (Exception e) { return false; } } Check for rooted device
  39. • Check your app’s signature • Check for rooted device

    • Check for emulator • Check if the app is debuggable
  40. Build.FINGERPRINT.startsWith("generic") Check for emulator

  41. • Check your app’s signature • Check for rooted device

    • Check for emulator • Check if the app is debuggable
  42. public static boolean isDebuggable(Context context){ return (context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) !=

    0; } Check if the app is debuggable
  43. Debuggable app

  44. None
  45. None
  46. Marcos Placona @marcos_placona placona.co.uk marcos@twilio.com Thanks