Skip to content

MASTG-DEMO-0074: Uses of Insecure Random Number Generation with frida-trace

Download MASTG-DEMO-0074 IPA Open MASTG-DEMO-0074 Folder Build MASTG-DEMO-0074 IPA

Sample

This demo uses the same sample code as in Uses of Insecure Random Number Generation with r2. It demonstrates the use of insecure random number generation using various APIs.

../MASTG-DEMO-0073/MastgTest.swift
  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
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
import Foundation
import Security
import Darwin
import CommonCrypto

// Unsafe bindings to libc srand and rand
@_silgen_name("srand")
func c_srand(_ seed: UInt32)

@_silgen_name("rand")
func c_rand() -> Int32

struct MastgTest {

    // Insecure: libc rand seeded with time, predictable and not suitable for cryptography
    static func generateRandomTokenRand() -> String {
        var token = ""

        for _ in 0..<16 {
            let value = c_rand() % 256
            token += String(format: "%02x", value)
        }
        return token
    }

    // Cryptographically secure on Apple platforms
    // Swift random APIs use SystemRandomNumberGenerator backed by the system CSPRNG via arc4random_buf
    // Shown here as a secure source that is not a dedicated crypto token API
    static func generateRandomTokenSwiftRandom() -> String {
        var token = ""
        for _ in 0..<16 {
            let b = UInt8.random(in: 0...255)
            token += String(format: "%02x", b)
        }
        return token
    }

    // Cryptographically secure: direct read from /dev/random on Apple platforms
    // However, this is a low level interface and is discouraged in favor of SecRandomCopyBytes
    static func generateRandomTokenDevRandom() -> String {
        let count = 16

        let fd = open("/dev/random", O_RDONLY)
        if fd < 0 {
            return "Error opening /dev/random"
        }

        var buffer = [UInt8](repeating: 0, count: count)
        let readCount = read(fd, &buffer, count)
        close(fd)

        if readCount != count {
            return "Error reading /dev/random"
        }

        return buffer.map { String(format: "%02x", $0) }.joined()
    }

    // Cryptographically secure but discouraged as a direct token API in Swift code 
    // because uses legacy C style interfaces that are easier to misuse
    // On Apple platforms arc4random_uniform is strong, but SecRandomCopyBytes or CryptoKit are preferred
    static func generateRandomTokenArc4RandomUniform() -> String {
        var token = ""
        for _ in 0..<16 {
            let value = arc4random_uniform(256)
            token += String(format: "%02x", value)
        }
        return token
    }

    // Cryptographically secure but discouraged as a direct token API
    // On Apple platforms arc4random is strong, but it is not the recommended crypto API
    static func generateRandomTokenArc4Random() -> String {
        var token = ""
        for _ in 0..<16 {
            let value = arc4random() % 256
            token += String(format: "%02x", value)
        }
        return token
    }

    // Cryptographically secure: SystemRandomNumberGenerator uses the system CSPRNG
    // It is suitable for cryptographic use, and CryptoKit builds on it
    // Included here to contrast secure generators with insecure ones
    static func generateRandomTokenSystemRNG() -> String {
        var token = ""
        var rng = SystemRandomNumberGenerator()

        for _ in 0..<16 {
            let b = UInt8.random(in: 0...255, using: &rng)
            token += String(format: "%02x", b)
        }
        return token
    }

    // Insecure: drand48 uses a 48 bit linear congruential generator
    // Not thread safe and not suitable for cryptographic purposes
    static func generateRandomTokenDrand48() -> String {
        var token = ""
        for _ in 0..<16 {
            let value = Int(drand48() * 256.0) % 256
            token += String(format: "%02x", value)
        }
        return token
    }

    // Cryptographically secure: CCRandomGenerateBytes uses the system CSPRNG
    // Secure, but a lower level API that is generally discouraged in favor of SecRandomCopyBytes
    static func generateRandomTokenCC() -> String {
        var buffer = [UInt8](repeating: 0, count: 16)
        let status = CCRandomGenerateBytes(&buffer, buffer.count)

        if status != kCCSuccess {
            return "Error generating random bytes with CCRandomGenerateBytes"
        }

        return buffer.map { String(format: "%02x", $0) }.joined()
    }

    // Recommended: SecRandomCopyBytes is the high level, Apple recommended API for secure random bytes
    static func generateRandomTokenSecRandom() -> String {
        var randomBytes = [UInt8](repeating: 0, count: 16)
        let status = SecRandomCopyBytes(kSecRandomDefault, randomBytes.count, &randomBytes)

        guard status == errSecSuccess else {
            return "Error generating secure random bytes"
        }

        return randomBytes.map { String(format: "%02x", $0) }.joined()
    }

    static func mastgTest(completion: @escaping (String) -> Void) {
        // Seed libc rand with current time
        let now = UInt32(time(nil))
        c_srand(now)

        // Example of seeding drand48 with time, which also makes it predictable if the seed is known
        // srand48(time(nil))

        let value = """
        Using libc rand seeded with time
        Token: \(generateRandomTokenRand())

        Using Swift random API backed by SystemRandomNumberGenerator
        Token: \(generateRandomTokenSwiftRandom())

        Using /dev/random low level interface
        Token: \(generateRandomTokenDevRandom())

        Using arc4random_uniform as a direct token source
        Token: \(generateRandomTokenArc4RandomUniform())

        Using arc4random as a direct token source
        Token: \(generateRandomTokenArc4Random())

        Using SystemRandomNumberGenerator directly
        Token: \(generateRandomTokenSystemRNG())

        Using drand48 linear congruential generator
        Token: \(generateRandomTokenDrand48())

        Using CCRandomGenerateBytes lower level API
        Token: \(generateRandomTokenCC())

        Using SecRandomCopyBytes
        Token: \(generateRandomTokenSecRandom())
        """

        completion(value)
    }
}

Steps

  1. Install the app on a device ( Installing Apps)
  2. Make sure you have Frida for iOS installed on your machine and the frida-server running on the device
  3. Run run.sh to spawn your app with Frida
  4. Click the Start button
  5. Stop the script by pressing Ctrl+C
run.sh
1
frida-trace -n 'MASTestApp' -i "*FixedWidthInteger*random*" -i "*rand" -i "*rand48" -i "arc4random*" -i "*SystemRandomNumberGenerator*" -i "CCRandom*" -i "SecRandomCopyBytes" # -i "open"

Observation

output.txt
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
  2959 ms  srand(seed=0x691a0327)
  2959 ms  rand()
           ... x 16
  2959 ms  $ss17FixedWidthIntegerPsE6random2inxSNyxG_tFZ()
  2959 ms     | arc4random_buf(buf=0x16ef965a8, nbytes=0x8)
           ... x 16
  2959 ms  open(path="/dev/random", oflag=0x0, ...)
  2960 ms  arc4random_uniform(upper_bound=0x100)
           ... x 16
  2960 ms  arc4random()
           ... x 16
  2960 ms  $ss27SystemRandomNumberGeneratorVABycfC()
  2960 ms  $ss17FixedWidthIntegerPsE6random2in5usingxSNyxG_qd__ztSGRd__lFZ()
  2960 ms     | $ss17FixedWidthIntegerPsE7_random5usingxqd__z_tSGRd__lFZ()
  2960 ms     |    | arc4random_buf(buf=0x16ef964d8, nbytes=0x8)
           ... x 16
  2960 ms  drand48()
           ... x 16
  2960 ms  CCRandomGenerateBytes()
  2960 ms  SecRandomCopyBytes()
  2960 ms     | CCRandomCopyBytes()
  2960 ms     |    | CCRandomGenerateBytes()

This output contains both insecure and secure APIs. For this test case the interesting calls are:

  • rand and srand, which expose the insecure libc PRNG.
  • drand48, which also uses an insecure linear congruential generator.

Evaluation

The test fails because insecure PRNGs are used in a security relevant context. See the evaluation section in Uses of Insecure Random Number Generation with r2 for more details.

The same output also shows calls to secure sources such as SecRandomCopyBytes, CCRandomGenerateBytes, SystemRandomNumberGenerator, and the Swift standard library's FixedWidthInteger.random implementation. These are present in the sample for contrast, but they are not the reason the test fails.