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

iOS Attack Vectors

iOS Attack Vectors

iOS Attack Vectors given at Swiftable 2022

Avatar for Itay Brenner

Itay Brenner

December 05, 2022
Tweet

More Decks by Itay Brenner

Other Decks in Programming

Transcript

  1. 2

  2. • The CIA Triad • Why protect at App level?

    • Attacks ◦ Insecure Data Storage ◦ Unvalidated User Input ◦ MiTM ◦ Reverse Engineering ◦ Third Party Libraries ◦ App Redistribution 3 Agenda
  3. The CIA Triad 4 • Confidentiality: it involves the effort

    to make sure data is kept secret or private and only people with proper privileges can access secure information. • Integrity: it involves making sure your data is trustworthy and free from tampering. • Availability: it involves making sure that people with access to specific information must be able to consume it when they need to. The CIA Triad
  4. Why protect at App level? 5 Common Scenarios: • Your

    app handles Financial Information. • Your app stores Medical Records. • Your server depends on input from the app. • Your app contains important algorithms. Why protect at App level?
  5. Storage on iOS: • UserDefaults • CoreData • Local Sqlite

    Databases • Keychain • Secure Enclave Insecure Storage 6 Insecure Storage
  6. Insecure Storage 8 let fileManager = FileManager.default let documentsURL =

    fileManager.urls(for: .documentDirectory, in: .userDomainMask) var file: URL = documentsURL.first?.appending(path: "MyFile.zip") do { var res = URLResourceValues() res.isExcludedFromBackup = true try file.setResourceValues(res) } catch { print(error) } Insecure Storage
  7. Unvalidated User Input 11 Unvalidated User Input GOOGLE.COM ✅ *.GOOGLE.COM

    ✅ GOGLE.COM ❌ GOOGLE.CO ❌ GOOGLE.ANOTHER.COM ❌
  8. Reverse Enginering 14 import TrustKit lazy var trustKit: TrustKit =

    { let trustKitConfig: [String: Any] = [ kTSKSwizzleNetworkDelegates: false, kTSKPinnedDomains: [ "apple.com": [ kTSKEnforcePinning: true, kTSKIncludeSubdomains: true, kTSKPublicKeyHashes: [ "b'Nxk+41F5z0xPS6FmPH2DRprGssYO67pC66w2c2Snp3s='", ], ] ]] return TrustKit(configuration: trustKitConfig) }() MiTM - Pinning SSL Certificates
  9. Reverse Enginering 15 // from previous slide lazy var trustKit:

    TrustKit = {....} func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { if !trustKit.pinningValidator.handle(challenge, completionHandler: completionHandler) { // TrustKit did't handle the request, maybe domain is not pinned? // Use default behaviour completionHandler(.performDefaultHandling, nil) } } MiTM - Pinning SSL Certificates
  10. Reverse Enginering 16 // from previous slide lazy var trustKit:

    TrustKit = {....} func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { #if DEBUG completionHandler(.performDefaultHandling, nil) return #endif if !trustKit.pinningValidator.handle(challenge, completionHandler: completionHandler) { // TrustKit did't handle the request, maybe domain is not pinned? // Use default behaviour completionHandler(.performDefaultHandling, nil) } } MiTM - Pinning SSL Certificates
  11. Reverse Enginering 18 class MyClass { func encrypt(_ s: String)

    -> [UInt8] { return xorEncryption(s) } private func xorEncryption(_ clearText: String) -> [UInt8] { if clearText.isEmpty { return [UInt8]() } var encrypted = [UInt8]() let text = [UInt8](clearText.utf8) let key = [UInt8](myPass().utf8) let length = key.count for t in text.enumerated() { encrypted.append(t.element ^ key[t.offset % length]) } return encrypted } private func myPass() -> String { return "MyPassword" } } Reverse Engineering
  12. Reverse Enginering 21 float _$s11InsecureApp7MyClassC7encryptySays5UInt8VGSSF(int arg0, int arg1) { intrinsic_movaps(var_10,

    0x0); InsecureApp.MyClass.xorEncryption(arg0, arg1); return 0x0; } Reverse Engineering
  13. int _$s11InsecureApp7MyClassC13xorEncryption33_3965DF93689E3AD135CB07048E32082FLLySays5UInt8VGSSF(int arg0, int arg1) { var_D8 = r13; var_C8

    = arg1; var_D0 = arg0; memset(&var_18, 0x0, 0x10); memset(&var_20, 0x0, 0x8); memset(&var_28, 0x0, 0x8); memset(&var_40, 0x0, 0x8); memset(&var_58, 0x0, 0x8); memset(&var_60, 0x0, 0x8); rax = memset(&var_78, 0x0, 0x18); memset(&var_B0, 0x0, 0x9); if ((Swift.String.isEmpty.getter(var_D0, var_C8) & 0x1) != 0x0) goto loc_1000062aa; loc_1000062a8: var_28 = Swift.Array.init(*type metadata for Swift.UInt8); var_38 = Swift.String.utf8.getter(var_D0, var_C8); rax = lazy protocol witness table accessor for type Swift.String.UTF8View and conformance Swift.String.UTF8View : Swift.Sequence in Swift(); var_118 = rax; rsi = *type metadata for Swift.UInt8; rdx = *type metadata for Swift.String.UTF8View; rax = Swift.Array.init<A>(&var_38, rsi, rdx, rax); var_108 = rax; var_128 = Swift.String.utf8.getter(InsecureApp.MyClass.myPass(), rdx); swift_bridgeObjectRelease(rdx); var_50 = var_128; … Reverse Enginering 22 Reverse Engineering
  14. Third Party Libraries 26 Third Party Libraries source 'https://github.com/itaybre/PrivateRepo' source

    'https://cdn.cocoapods.org/' platform :ios, '13.0' target 'InsecureApp' do # Comment the next line if you don't want to use dynamic frameworks use_frameworks! # Internal Pods (Private) pod 'MyCompanySDK', '~>1.0' # Pods for InsecureApp pod 'Firebase/Core', '~>7.4' pod 'Firebase/Analytics', '~>7.4' pod 'Firebase/Crashlytics', '~>7.4' end
  15. Third Party Libraries 27 Third Party Libraries - Vulnerable Podfile

    source 'https://cdn.cocoapods.org/' source 'https://github.com/itaybre/PrivateRepo' platform :ios, '13.0' target 'InsecureApp' do # Comment the next line if you don't want to use dynamic frameworks use_frameworks! # Internal Pods (Private) pod 'MyCompanySDK', '~>1.0' # Pods for InsecureApp pod 'Firebase/Core', '~>7.4' pod 'Firebase/Analytics', '~>7.4' pod 'Firebase/Crashlytics', '~>7.4' end
  16. Third Party Libraries 28 Third Party Libraries - Solution Best

    source 'https://github.com/itaybre/PrivateRepo' source 'https://github.com/itaybre/VendoredRepo' platform :ios, '13.0' target 'InsecureApp' do # Comment the next line if you don't want to use dynamic frameworks use_frameworks! # Internal Pods pod 'InternalSDK', '~>1.0' # Pods for InsecureApp pod 'Firebase/Core', '~>7.4' pod 'Firebase/Analytics', '~>7.4' pod 'Firebase/Crashlytics', '~>7.4' end
  17. App Attest 31 let service = DCAppAttestService.shared service.generateKey { keyId,

    error in guard error == nil else { /* Handle the error. */ } // Cache keyId for subsequent operations. } App Redistribution - App Attest
  18. Key Attestation 32 import CryptoKit let challenge = <# Data

    from your server. #> let hash = Data(SHA256.hash(data: challenge)) service.attestKey(keyId, clientDataHash: hash) { attestation, error in guard error == nil else { /* Handle error and return. */ } // Send the attestation object to your server for verification. } App Redistribution - Key Attestation
  19. Validations: • Validate the certificate comes from Apple Root Certificate.

    • Validate the certificate was generated using our challenge. • Validate the the certificate and the attestation object match. • Validate the attestation is for our app and for production environment. • Validate the attestation request is new and has not been used before. Key Attestation (Server Side) 33 App Redistribution - Key Attestation (Server Side)
  20. Requests Attestation 34 let challenge = <# A string from

    your server #> let request = [ "action": "getGameLevel", "levelId": "1234", "challenge": challenge ] guard let clientData = try? JSONEncoder().encode(request) else { return } let clientDataHash = Data(SHA256.hash(data: clientData)) service.generateAssertion(keyId, clientDataHash: clientDataHash) { assertion, error in guard error == nil else { /* Handle the error. */ } // Send the assertion and request to your server. } App Redistribution - Requests Attestation