importFoundationimportSecurityimportDarwinimportCommonCrypto//Unsafebindingstolibcsrandandrand@_silgen_name("srand")funcc_srand(_seed:UInt32)@_silgen_name("rand")funcc_rand()->Int32structMastgTest{//Insecure:libcrandseededwithtime,predictableandnotsuitableforcryptographystaticfuncgenerateRandomTokenRand()->String{vartoken=""for_in0..<16{letvalue=c_rand()%256token+=String(format:"%02x",value)}returntoken}//CryptographicallysecureonAppleplatforms//SwiftrandomAPIsuseSystemRandomNumberGeneratorbackedbythesystemCSPRNGviaarc4random_buf//ShownhereasasecuresourcethatisnotadedicatedcryptotokenAPIstaticfuncgenerateRandomTokenSwiftRandom()->String{vartoken=""for_in0..<16{letb=UInt8.random(in:0...255)token+=String(format:"%02x",b)}returntoken}//Cryptographicallysecure:directreadfrom/dev/randomonAppleplatforms//However,thisisalowlevelinterfaceandisdiscouragedinfavorofSecRandomCopyBytesstaticfuncgenerateRandomTokenDevRandom()->String{letcount=16letfd=open("/dev/random",O_RDONLY)iffd<0{return"Error opening /dev/random"}varbuffer=[UInt8](repeating:0,count:count)letreadCount=read(fd,&buffer,count)close(fd)ifreadCount!=count{return"Error reading /dev/random"}returnbuffer.map{String(format:"%02x",$0)}.joined()}//CryptographicallysecurebutdiscouragedasadirecttokenAPIinSwiftcode//becauseuseslegacyCstyleinterfacesthatareeasiertomisuse//OnAppleplatformsarc4random_uniformisstrong,butSecRandomCopyBytesorCryptoKitarepreferredstaticfuncgenerateRandomTokenArc4RandomUniform()->String{vartoken=""for_in0..<16{letvalue=arc4random_uniform(256)token+=String(format:"%02x",value)}returntoken}//CryptographicallysecurebutdiscouragedasadirecttokenAPI//OnAppleplatformsarc4randomisstrong,butitisnottherecommendedcryptoAPIstaticfuncgenerateRandomTokenArc4Random()->String{vartoken=""for_in0..<16{letvalue=arc4random()%256token+=String(format:"%02x",value)}returntoken}//Cryptographicallysecure:SystemRandomNumberGeneratorusesthesystemCSPRNG//Itissuitableforcryptographicuse,andCryptoKitbuildsonit//IncludedheretocontrastsecuregeneratorswithinsecureonesstaticfuncgenerateRandomTokenSystemRNG()->String{vartoken=""varrng=SystemRandomNumberGenerator()for_in0..<16{letb=UInt8.random(in:0...255,using:&rng)token+=String(format:"%02x",b)}returntoken}//Insecure:drand48usesa48bitlinearcongruentialgenerator//NotthreadsafeandnotsuitableforcryptographicpurposesstaticfuncgenerateRandomTokenDrand48()->String{vartoken=""for_in0..<16{letvalue=Int(drand48()*256.0)%256token+=String(format:"%02x",value)}returntoken}//Cryptographicallysecure:CCRandomGenerateBytesusesthesystemCSPRNG//Secure,butalowerlevelAPIthatisgenerallydiscouragedinfavorofSecRandomCopyBytesstaticfuncgenerateRandomTokenCC()->String{varbuffer=[UInt8](repeating:0,count:16)letstatus=CCRandomGenerateBytes(&buffer,buffer.count)ifstatus!=kCCSuccess{return"Error generating random bytes with CCRandomGenerateBytes"}returnbuffer.map{String(format:"%02x",$0)}.joined()}//Recommended:SecRandomCopyBytesisthehighlevel,ApplerecommendedAPIforsecurerandombytesstaticfuncgenerateRandomTokenSecRandom()->String{varrandomBytes=[UInt8](repeating:0,count:16)letstatus=SecRandomCopyBytes(kSecRandomDefault,randomBytes.count,&randomBytes)guardstatus==errSecSuccesselse{return"Error generating secure random bytes"}returnrandomBytes.map{String(format:"%02x",$0)}.joined()}staticfuncmastgTest(completion:@escaping(String)->Void){//Seedlibcrandwithcurrenttimeletnow=UInt32(time(nil))c_srand(now)//Exampleofseedingdrand48withtime,whichalsomakesitpredictableiftheseedisknown//srand48(time(nil))letvalue=""" 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)}}
Unzip the app package and locate the main binary file, as described in Exploring the App Package. For this demo the path is ./Payload/MASTestApp.app/MASTestApp.
Run radare2 for iOS on the binary and use the -i option to execute the script below.
1
r2-q-iinsecure_random.r2-AMASTestApp>output.json
1
axtj @@=`ii~+rand[1]`
This script:
Uses ii to list imported symbols.
Filters that list with ~+rand to keep only imports whose names contain rand, such as rand, srand, drand48, arc4random, and arc4random_uniform.
Uses [1] to select the address column from that output.
Uses axtj @@=... to run axt on each of those addresses and print cross references in JSON.
Note: the output also shows calls to secure sources such as SecRandomCopyBytes, CCRandomGenerateBytes, SystemRandomNumberGenerator, and the Swift FixedWidthInteger.random implementation. These are present in the sample for contrast, but they are not the reason the test fails. The evaluation only treats uses of insecure libc PRNGs as findings.
Now we disassemble the functions that call the insecure PRNGs to confirm their use in security-relevant contexts.
As an example, take "fcn_name": "sym.MASTestApp.MastgTest.generateRandomTokenDrand48_...yFZ_" from the evaluation output and disassemble or decompile it.
Reading the disassembly confirms that it uses the output of drand48 to generate a random token (we intentionally don't perform this step for you here, please try it yourself).
Next we look for cross references to see where it is being called from.
Here it is called from sym.MASTestApp.MastgTest.mastg.completion_...FZ_. We can disassemble that function to understand its purpose and keep iterating.
If we find that it is called from a security-relevant context, such as during authentication or cryptographic operations, we can conclude that the use of the insecure PRNG is a security issue.