Skip to content

MASTG-DEMO-0018: Uses of Insecure Encryption Algorithms in CommonCrypto with r2

Content in BETA

This content is in beta and still under active development, so it is subject to change any time (e.g. structure, IDs, content, URLs, etc.).

Send Feedback

Sample

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import SwiftUI
import CommonCrypto

struct MastgTest {
    static func mastgTest(completion: @escaping (String) -> Void) {
        let key = "0123456789abcdef01234567" // 24-byte key for 3DES
        let data = "This is a sample text".data(using: .utf8)!

        // Create a buffer for encrypted data
        var encryptedBytes = [UInt8](repeating: 0, count: data.count + kCCBlockSize3DES)
        var numBytesEncrypted: size_t = 0

        let cryptStatus = data.withUnsafeBytes { dataBytes in
            key.withCString { keyBytes in
                CCCrypt(
                    CCOperation(kCCEncrypt),              // Encrypt
                    CCAlgorithm(kCCAlgorithm3DES),        // 3DES Algorithm
                    CCOptions(kCCOptionPKCS7Padding),     // PKCS7 Padding
                    keyBytes, kCCKeySize3DES,             // Key and key length
                    nil,                                  // Initialization Vector (optional)
                    dataBytes.baseAddress, data.count,    // Input data
                    &encryptedBytes, encryptedBytes.count, // Output data
                    &numBytesEncrypted                    // Number of bytes encrypted
                )
            }
        }

        if cryptStatus == kCCSuccess {
            let encryptedData = Data(bytes: encryptedBytes, count: numBytesEncrypted)
            let encryptedHex = encryptedData.map { String(format: "%02hhx", $0) }.joined()
            let value = "Original:\n\n \(String(data: data, encoding: .utf8)!)\n\nEncrypted (Hex):\n \(encryptedHex)"
            completion(value)
        } else {
            completion("Encryption failed with status: \(cryptStatus)")
        }
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
            ;-- section.0.__TEXT.__text:
            ; XREFS: 0x1000000d0  STRN 0x100004040  STRN 0x100004168  CALL 0x100004774  CALL 0x1000047d8  CALL 0x100005330  
 276: sym.func.100004000 (int64_t arg1, int64_t arg2, int64_t arg4, int64_t arg5, int64_t arg6, int64_t arg7, int64_t arg_80h);
           0x100004000      sub sp, sp, 0x80                          ; [00] -r-x section size 13240 named 0.__TEXT.__text
           0x100004004      str x28, [var_20h]
           ; CODE XREF from str.0123456789abcdef01234567 @ +0x14(x)
           0x100004008      stp x27, x26, [var_30h]
           0x10000400c      stp x25, x24, [var_40h]
           0x100004010      stp x23, x22, [var_50h]
           0x100004014      stp x20, x19, [var_60h]
           0x100004018      stp x29, x30, [var_70h]
           0x10000401c      add x29, sp, 0x70
           0x100004020      mov x20, x8
           0x100004024      lsr x8, x4, 0x3e                          ; arg5
           0x100004028      mov x19, x21
           0x10000402c      mov x24, x6                               ; arg7
           0x100004030      mov x26, x5                               ; arg6
           0x100004034      mov x22, x1                               ; arg2
           0x100004038      mov x23, x0                               ; arg1
           0x10000403c      mov x25, 0
           0x100004040      adrp x9, sym.func.100004000               ; 0x100004000
           0x100004044      add x9, x9, 0x114                         ; 0x100004114
                                                                      ; sym.func.100004114
           ;-- switch:
           0x100004048      adr x10, switch.0x100004048               ; switch table (4 cases) at 0x100004114
           0x10000404c      ldrsw x11, [x9, x8, lsl 2]                ; 0x100004114
                                                                      ; sym.func.100004114
           0x100004050      add x10, x10, x11                         ; 0x100004058
                                                                      ; case.0x100004048.0
           0x100004054      br x10
           ;-- case 0:                                                ; from 0x100004048
           ; DATA XREFS from sym.func.100004000 @ 0x100004048(x), 0x100004050(w)
           0x100004058      ubfx x25, x4, 0x30, 8                     ; arg5
       ┌─< 0x10000405c      b case.0x100004048.3
          ;-- case 1:                                                ; from 0x100004048
          ; CODE XREF from sym.func.100004000 @ 0x100004048(x)
          0x100004060      lsr x8, x3, 0x20                          ; arg4
          0x100004064      subs w8, w8, w3
      ┌──< 0x100004068      b.vs 0x10000410c
      ││   0x10000406c      sxtw x25, w8
     ┌───< 0x100004070      b case.0x100004048.3
     │││   ;-- case 2:                                                ; from 0x100004048
     │││   ; CODE XREF from sym.func.100004000 @ 0x100004048(x)
     │││   0x100004074      ldp x9, x8, [x3, 0x10]                    ; arg4
     │││   0x100004078      subs x25, x8, x9
    ┌────< 0x10000407c      b.vs 0x100004110
    ││││   ;-- case 3:                                                ; from 0x100004048
    ││││   ; CODE XREFS from sym.func.100004000 @ 0x100004048(x), 0x10000405c(x), 0x100004070(x)
    │└─└─> 0x100004080      ldr x28, [x26]
         0x100004084      ldr x27, [x28, 0x10]
         0x100004088      mov x0, x28
         0x10000408c      bl sym.imp.swift_isUniquelyReferenced_nonNull_native
         0x100004090      str x28, [x26]
     │┌─< 0x100004094      tbnz w0, 0, 0x1000040b0
     ││   0x100004098      mov w0, 0
     ││   0x10000409c      mov x1, x27                               ; signed int64_t arg2
     ││   0x1000040a0      mov w2, 0
     ││   0x1000040a4      mov x3, x28                               ; int64_t arg_10h
     ││   0x1000040a8      bl sym.func.1000045dc
     ││   0x1000040ac      mov x28, x0
     ││   ; CODE XREF from sym.func.100004000 @ 0x100004094(x)
     │└─> 0x1000040b0      str x28, [x26]
         0x1000040b4      add x8, x28, 0x20
         0x1000040b8      stp x27, x24, [var_8h]
         0x1000040bc      str x8, [sp]
         0x1000040c0      mov w0, 0
         0x1000040c4      mov w1, 2
         0x1000040c8      mov w2, 1
         0x1000040cc      mov x3, x23
         0x1000040d0      mov w4, 0x18
         0x1000040d4      mov x5, 0
         0x1000040d8      mov x6, x22
         0x1000040dc      mov x7, x25
         0x1000040e0      bl sym.imp.CCCrypt                        ; CCCryptorStatus CCCrypt(CCOperation op, CCAlgorithm alg, int32_t options, const void *key, uint32_t keyLength, const void *iv, const void *dataIn, uint32_t dataInLength, void *dataOut, uint32_t dataOutAvailable, uint32_t *dataOutMoved)
         0x1000040e4      str w0, [x20]
         0x1000040e8      mov x21, x19
         0x1000040ec      ldp x29, x30, [var_70h]
         0x1000040f0      ldp x20, x19, [var_60h]
         0x1000040f4      ldp x23, x22, [var_50h]
         0x1000040f8      ldp x25, x24, [var_40h]
         0x1000040fc      ldp x27, x26, [var_30h]
         0x100004100      ldr x28, [var_20h]
         0x100004104      add sp, sp, 0x80
         0x100004108      ret
         ; CODE XREF from sym.func.100004000 @ 0x100004068(x)
     └──> 0x10000410c      brk 1
          ; CODE XREF from sym.func.100004000 @ 0x10000407c(x)
    └────> 0x100004110      brk 1
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// ✨ Decompiled using OpenAI's ChatGPT o1-review model ✨

import CommonCrypto

func encryptSampleText() -> String? {
    // Step 1: Define the key and input text
    let keyString = "0123456789abcdef01234567"
    let inputText = "This is a sample text"

    // Step 2: Convert key and input text to Data
    guard let keyData = keyString.data(using: .utf8),
          let inputData = inputText.data(using: .utf8) else {
        print("Failed to convert key or input text to data")
        return nil
    }

    // Step 3: Set up the output buffer
    let bufferSize = inputData.count + kCCBlockSize3DES
    var buffer = Data(count: bufferSize)

    // Step 4: Perform encryption
    var numBytesEncrypted = 0
    let cryptStatus = keyData.withUnsafeBytes { keyBytes in
        inputData.withUnsafeBytes { dataInBytes in
            buffer.withUnsafeMutableBytes { bufferBytes in
                CCCrypt(
                    CCOperation(kCCEncrypt),                // Operation
                    CCAlgorithm(kCCAlgorithm3DES),          // Algorithm
                    CCOptions(kCCOptionPKCS7Padding),       // Options
                    keyBytes.baseAddress,                   // Key pointer
                    kCCKeySize3DES,                         // Key size
                    nil,                                    // IV (nil for ECB mode)
                    dataInBytes.baseAddress,                // Data In
                    inputData.count,                        // Data In Length
                    bufferBytes.baseAddress,                // Data Out
                    bufferSize,                             // Data Out Available
                    &numBytesEncrypted                      // Data Out Moved
                )
            }
        }
    }

    // Step 5: Check the result and return encrypted data
    if cryptStatus == kCCSuccess {
        buffer.count = numBytesEncrypted
        // Convert encrypted data to base64 string for display
        let encryptedString = buffer.base64EncodedString()
        return encryptedString
    } else {
        print("Encryption failed with status: \(cryptStatus)")
        return nil
    }
}

// Usage
if let encryptedText = encryptSampleText() {
    print("Encrypted Text (Base64): \(encryptedText)")
} else {
    print("Encryption failed")
}

Steps

  1. Unzip the app package and locate the main binary file ( Exploring the App Package), which in this case is ./Payload/MASTestApp.app/MASTestApp.
  2. Open the app binary with radare2 for iOS with the -i option to run this script.
cccrypt.r2
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
e asm.bytes=false
e scr.color=false
e asm.var=false

?e Uses of the CCCrypt function:
afl~CCCrypt

?e

?e xrefs to CCCrypt:
axt @ 0x1000076c4

?e

?e Use of CCCrypt:

# Seek to the function where CCCrypt is called (Replace with the address found from axt output)
pd-- 9 @ 0x1000040e0

pdf @ sym.func.100004000 > function.asm
run.sh
1
2
#!/bin/bash
r2 -q -i cccrypt.r2 -A MASTestApp > output.asm

Observation

The output contains the disassembled code of the function using CCCrypt.

output.txt
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
0x1000076c4    1     12 sym.imp.CCCrypt
fcn.1000040b8 0x1000040e0 [CALL:--x] bl sym.imp.CCCrypt
 84: fcn.1000040b8 (int64_t arg_20h, int64_t arg_30h, int64_t arg_40h, int64_t arg_50h, int64_t arg_60h, int64_t arg_70h, int64_t arg_80h);
           ; arg int64_t arg_20h @ sp+0x20
           ; arg int64_t arg_30h @ sp+0x30
           ; arg int64_t arg_40h @ sp+0x40
           ; arg int64_t arg_50h @ sp+0x50
           ; arg int64_t arg_60h @ sp+0x60
           ; arg int64_t arg_70h @ sp+0x70
           ; arg int64_t arg_80h @ sp+0x100
           0x1000040b8      fbe300a9       stp x27, x24, [sp, 8]
           0x1000040bc      e80300f9       str x8, [sp]
           0x1000040c0      00008052       mov w0, 0
           0x1000040c4      41008052       mov w1, 2
           0x1000040c8      22008052       mov w2, 1
           0x1000040cc      e30317aa       mov x3, x23
           0x1000040d0      04038052       mov w4, 0x18
           0x1000040d4      050080d2       mov x5, 0
           0x1000040d8      e60316aa       mov x6, x22
           0x1000040dc      e70319aa       mov x7, x25
           0x1000040e0      790d0094       bl sym.imp.CCCrypt
           0x1000040e4      800200b9       str w0, [x20]
           0x1000040e8      f50313aa       mov x21, x19
           0x1000040ec      fd7b47a9       ldp x29, x30, [sp, 0x70]
           0x1000040f0      f44f46a9       ldp x20, x19, [sp, 0x60]
           0x1000040f4      f75b45a9       ldp x23, x22, [sp, 0x50]
           0x1000040f8      f96344a9       ldp x25, x24, [sp, 0x40]
           0x1000040fc      fb6b43a9       ldp x27, x26, [sp, 0x30]
           0x100004100      fc1340f9       ldr x28, [sp, 0x20]
           0x100004104      ff030291       add sp, sp, 0x80
           0x100004108      c0035fd6       ret

Evaluation

Inspect the disassembled code to identify the use of insecure algorithms.

In CommonCryptor.h you can find the definition of the CCCrypt function:

CCCryptorStatus CCCrypt(
    CCOperation op,         /* kCCEncrypt, etc. */
    CCAlgorithm alg,        /* kCCAlgorithmAES128, etc. */
    CCOptions options,      /* kCCOptionPKCS7Padding, etc. */
    const void *key,
    size_t keyLength,
    const void *iv,         /* optional initialization vector */
    const void *dataIn,     /* optional per op and alg */
    size_t dataInLength,
    void *dataOut,          /* data RETURNED here */
    size_t dataOutAvailable,
    size_t *dataOutMoved);

There you will also find the alg and the op:

/*!
    @enum        CCAlgorithm
    @abstract    Encryption algorithms implemented by this module.

    @constant    kCCAlgorithmAES128    Advanced Encryption Standard, 128-bit block
    @constant    kCCAlgorithmDES        Data Encryption Standard
    @constant    kCCAlgorithm3DES    Triple-DES, three key, EDE configuration
    @constant    kCCAlgorithmCAST    CAST
    @constant    kCCAlgorithmRC4        RC4 stream cipher
*/
enum {
    kCCAlgorithmAES128 = 0,
    kCCAlgorithmDES,        
    kCCAlgorithm3DES,        
    kCCAlgorithmCAST,        
    kCCAlgorithmRC4,
    kCCAlgorithmRC2        
};
typedef uint32_t CCAlgorithm;

/*!
    @enum        CCOperation
    @abstract    Operations that an CCCryptor can perform.

    @constant    kCCEncrypt    Symmetric encryption.
    @constant    kCCDecrypt    Symmetric decryption.
*/
enum {
    kCCEncrypt = 0,    
    kCCDecrypt,        
};

With this information we can now inspect the disassembled code and we'll see that the 3DES algorithm (kCCAlgorithm3DES) can be found by its numeric value 2 in the second argument of the CCCrypt function (w1). The CCCrypt function is called with a padding option of PKCS7, no initialization vector, and a key of 24 bytes:

evaluation.txt
1
2
3
4
5
6
7
8
9
           0x1000040c0      00008052       mov w0, 0           -> kCCEncrypt (0 for encryption)
           0x1000040c4      41008052       mov w1, 2           -> kCCAlgorithm3DES (2 for 3DES)
           0x1000040c8      22008052       mov w2, 1           -> kCCOptionPKCS7Padding (1 for PKCS7 padding)
           0x1000040cc      e30317aa       mov x3, x23         -> key (pointer to the encryption key)
           0x1000040d0      04038052       mov w4, 0x18        -> keyLength (0x18 or 24 bytes for 3DES)
           0x1000040d4      050080d2       mov x5, 0           -> iv (0 or NULL, implying no initialization vector)
           0x1000040d8      e60316aa       mov x6, x22         -> dataIn (pointer to the input data to be encrypted)
           0x1000040dc      e70319aa       mov x7, x25         -> dataOut (pointer to the output buffer where encrypted data will be stored)
           0x1000040e0      790d0094       bl sym.imp.CCCrypt  -> Call to CCCrypt function

The test fails because the 3DES encryption algorithm was found in the code.

Note: Using artificial intelligence we're able to decompile the disassembled code and review it. The output is a human-readable version of the assembly code. The AI decompiled code may not be perfect and might contain errors but, in this case, it clearly shows the use of CCCrypt and the associated algorithm.