Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Актуальные вопросы безопасности Android-приложений

Актуальные вопросы безопасности Android-приложений

Доклад Дмитрия Терешина (Tinkoff) для PDUG-секции на форуме PHDays 9.

More Decks by Positive Development User Group

Other Decks in Programming

Transcript

  1. Заголовок ptsecurity.com Pressing security issues of Android applications Tinkoff Терёшин

    Дмитрий
  2. Заголовок 2 • Bad design • Need to support old

    Android versions • Incorrect use of Android API Reasons
  3. Заголовок 3 • Auth sharing • SSL pinning • Token

    encryption • Android wear • Push notifications Agenda
  4. Заголовок Auth sharing 4

  5. Заголовок 5 • 2 Android applications (master, slave) • common

    API • API access token Objective: seamless transition between applications Objective
  6. Заголовок 6 Variant 1: DeepLinks slave://main?sessionId=686A885A4FB644053C584B9BE2A70C7D Problem 1: DeepLink Hijacking

  7. Заголовок 7 • AppLinks – based on your website HTTP

    URL that has been verified to belong to your website Problem: need Android 6.0+ • Intent URL – add application id intent://main/#Intent;scheme=slave;package=com.example.slave.clie nt.android;end” Problem: application id forgering Problem 1: How to resolve
  8. Заголовок 8 Mistake 1: Session Fixation

  9. Заголовок 9 Variant 2: Content Provider Problem 2: unprotected provider

  10. Заголовок 10 Protection Level <permission android:name="com.example.contentprovider.access" android:protectionLevel="signature"/> <application> <provider ...

    android:readPermission="com.example.contentprovider.access"> </application> Problem 3: Permission forgering
  11. Заголовок 11 Problem 4: Different Signatures String pkg = this.getCallingPackage();

    PackageInfo pkgInfo = pkgmgr.getPackageInfo(pkg, GET_SIGNATURES); Signatures[] signatures = pkgInfo.signatures; for (Signature sig: signatures) { if (sig.equals(TRUSTED_SIGNATURE)) { // trusted signature found, trust the application } }
  12. Заголовок 12 Variant 3: Content Provider with signature checks

  13. Заголовок 13 Problem 4: Fake id vulnerability How to resolve:

    up minSDKLevel to 19 (Android 4.4)
  14. Заголовок SSL pinning 14

  15. Заголовок 15 • Rogue CAs • Compromised certificates • Phished

    users Goal Note: added in Android 7.0+ (Network Security Configuration) Protection against certificate forgery:
  16. Заголовок 16 X509TrustManager and HostnameVerifier customtrustManager = new X509TrustManager() {

    @Override public void checkClientTrusted(final X509Certificate[] chain, final String authType) { } @Override public void checkServerTrusted(final X509Certificate[] chain, final String authType) {} @Override public X509Certificate[] getAcceptedIssuers() {} }; HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() { public boolean verify(String hostname, SSLSession session) {} });
  17. Заголовок 17 Mistake 1: custom trust manager private static class

    X509CertPinningTrustManager implements X509TrustManager { public void checkServerTrusted(X509Certificate[] chain, String authType) throwsCertificateException List pinsInfo = AppConfig.getPinningInfo(); boolean validCertFound = false; for (pinnedCert: pinsInfo) { byte[] serverCertFingerprint = digest(chain[pinnedCert.chainPosition]); if (java.util.Arrays.equals(serverCertFingerprint, pinnedCert.fingerprint)){ validCertFound = true; break; } } if (!validCertFound) { throw new CertificateException("Invalid X509Cert used");} }
  18. Заголовок 18 Mistake 1: custom trust manager CVE-2016-2402: OkHttp before

    2.7.4 and 3.x before 3.1.2 allows man-in- the-middle attackers to bypass certificate pinning
  19. Заголовок 19 Mistake 1: attack mitmproxy --set add_upstream_certs_to_client_chain=true --set ssl_insecure=true

  20. Заголовок 20 Mistake 2: custom hostname verifier HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {

    public boolean verify(String hostname, SSLSession session) { return hostname.equals("www.bank.com"); } }); Pin intermediate certificate &
  21. Заголовок 21 Mistake 2: attack https://github.com/ChrisMcMStone/Spinner

  22. Заголовок 22 • How to support low levels API •

    What exactly pin • How to deploy • Rotate certificate or public key • Pin failures Decisions
  23. Заголовок 23 create pin-server Good implementation use internal CA pinning

    for pin-server request pins from pin-server establish connection
  24. Заголовок Token encryption 24

  25. Заголовок 25 • Send pin-code & RefreshToken to server •

    Local auth by pin-code, send RefreshToken to server • Encrypt RefreshToken with pin-code Mistakes
  26. Заголовок 26 Objective RefreshToken: aec27f0f-b8a3-43cb-b076-e075a095abfe Cipher Suite: AES/CBC/PKCS5Padding PIN: 1234

    Note: avoid local validation of decryption result
  27. Заголовок 27 Problem 1: padding 01 02 02 03 03

    03 04 04 04 04 05 05 05 05 05 06 06 06 06 06 06 … … | 61 62 66 65 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C | For our token:
  28. Заголовок 28 Problem 2: predictable token format aec27f0f-b8a3-43cb-b076-e075a095abfe xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx

  29. Заголовок 29 Token Format public String createRefreshToken() { byte[] refreshToken

    = new byte[64]; final SecureRandom secureRandom = new SecureRandom(); secureRandom.nextBytes(refreshToken); return Base64.getUrlEncoder() .withoutPadding().encodeToString(refreshToken); }
  30. Заголовок 30 Encryption Algorithm private static final String ALGORITHM =

    "AES"; private static final String CIPHER_SUITE = "AES/CBC/NoPadding"; private static final int AES_KEY_SIZE = 16; public byte[] encryptToken(String token, String pin, byte[] iv, byte[] salt) { byte[] decodedToken = decodeToken(token); byte[] rawPin = pin.getBytes(); byte[] key = kdf.deriveKey(rawPin, salt, AES_KEY_SIZE); Cipher cipher = Cipher.getInstance(CIPHER_SUITE); cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, ALGORITHM), new IvParameterSpec(iv)); return cipher.doFinal(decodedToken); }
  31. Заголовок Android wear 31

  32. Заголовок 32 Communication between watch and phone Wearable.MessageApi.sendMessage(mGoogleApiClient, nodeId, COMMAND_APP_LAUNCH,

    new byte[3] ); <service android:name="WearableListenerService" android:exported="true"> <intent-filter> <action android:name="com.google.android.gms.wearable.MESSAGE_RECEIVED"/> <data android:scheme="wear" android:host="*" android:path="/COMMAND_APP_LAUNCH"/> </intent-filter> </service>
  33. Заголовок 33 Problem 1: Bluetooth HCI snoop log /etc/bluetooth/bt_stack.conf: all

    bluetooth data is written to /sdcard/btsnoop_hci.log (Android < 8)
  34. Заголовок 34 Problem 1: how to resolve try { Log.d("Bluetooth

    log", String.valueOf( Settings.Secure.getInt(getContentResolver(), "bluetooth_hci_log"))); } catch (Settings.SettingNotFoundException e) { e.printStackTrace(); }
  35. Заголовок 35 Mistake 1: hijacking in WearableListenerService public class MyWearableListenerService

    extends WearableListenerService { private static final String COMMAND_APP_LAUNCH = "/COMMAND_APP_LAUNCH"; @Override public void onMessageReceived(MessageEvent messageEvent) { if (messageEvent.getPath().equals(COMMAND_APP_LAUNCH)) { Intent intent = WearSplashActivity.newIntent(this); intent.setFlags( Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); } } }
  36. Заголовок 36 Mistake 1: How to resolve public class MyWearableListenerService

    extends WearableListenerService { private static final String COMMAND_APP_LAUNCH = "/COMMAND_APP_LAUNCH"; @Override public void onMessageReceived(MessageEvent messageEvent) { if (messageEvent.getPath().equals(COMMAND_APP_LAUNCH)) { Intent intent = WearSplashActivity.newIntent(this); intent.setFlags( Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); startActivity(intent); } } }
  37. Заголовок Push notifications 37

  38. Заголовок 38 Message receiving public class MyFirebaseMessagingService extends FirebaseMessagingService {

    @Override public void onMessageReceived(RemoteMessage remoteMessage) { sendNotification(remoteMessage.getNotification().getBody()); } private void sendNotification(String messageBody) { NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, channelId) .setSmallIcon(R.drawable.logo_22) .setColor(getResources().getColor(R.color.logo_green)) .setContentTitle(“Пополнение. Счет RUB…“) .setContentText(messageBody) .setAutoCancel(true) .setSound(defaultSoundUri); } }
  39. Заголовок 39 Problem 1: notification forgering How to resolve: сreate

    unique UI fingerprint for application instance
  40. Заголовок 40 • move to iOS • move to Fuchsia

    • move to the dark side • use Android API safely Conclusions
  41. Заголовок 41 Links & contacts dm.teryoshin@gmail.com @dtereshin

  42. Заголовок ptsecurity.com Спасибо! Спасибо!