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.

755c8de62a6b84adb347797f374670c9?s=128

Marcos Placona

October 20, 2016
Tweet

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