Cracking OWASP MSTG iOS Crackme - The Uncrackable
iOSMy fellow colleague Bernhard developed two iOS Crackme and here is the writeup.
Uncrackable Level 1
From the main view, it hints that the flag can be found in the hidden label.
Well, if that is not a red herring like every crackme challenge, let’s try to unhide the hidden label using Cycript. Let’s print the view hierarchy and locate the address for the hidden label.
The command [[UIApp keyWindow] recursiveDescription].toString() returns the view hierarchy of keyWindow. The description of every subview and sub-subview of keyWindow will be shown, and the indentation space reflects the relationships of each view. For example, UILabel, UITextField, and UIButton are subviews of UIView.
VP:~ root# ps aux | grep Crack
root 730 0.0 0.0 535232 432 s000 R+ 5:04PM 0:00.01 grep Crack
mobile 728 0.0 0.8 586328 8368 ?? Ss 5:04PM 0:00.35 /var/mobile/Containers/Bundle/Application/A8BD91A9-3C81-4674-A790-AF8CDCA8A2F1/UnCrackable Level 1.app/UnCrackable Level 1
VP:~ root# ./cycript -p 728
cy# UIApp.keyWindow.recursiveDescription().toString()
`<UIWindow: 0x155b8420; frame = (0 0; 320 568); gestureRecognizers = <NSArray: 0x155a0080>; layer = <UIWindowLayer: 0x155acd40>>
| <UIView: 0x156a7300; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x156a74a0>>
| | <UILabel: 0x156a3b10; frame = (0 40; 82 20.5); text = 'i am groot!'; hidden = YES; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x156a3bf0>>
| | <UILabel: 0x156a22f0; frame = (0 110.5; 320 20.5); text = 'A Secret Is Found In The ...'; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x156a2550>>
| | <UITextField: 0x156a3e70; frame = (8 141; 304 30); text = ''; clipsToBounds = YES; opaque = NO; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x155c2ce0>; layer = <CALayer: 0x156a4140>>
| | | <_UITextFieldRoundedRectBackgroundViewNeue: 0x156a65e0; frame = (0 0; 304 30); opaque = NO; autoresize = W+H; userInteractionEnabled = NO; layer = <CALayer: 0x156a6800>>
| | <UIButton: 0x155ba6d0; frame = (8 191; 304 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x155ba980>>
| | | <UIButtonLabel: 0x155b2ac0; frame = (133 6; 38 18); text = 'Verify'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x155b3680>>
| | <_UILayoutGuide: 0x156a7500; frame = (0 0; 0 20); hidden = YES; layer = <CALayer: 0x156a7700>>
| | <_UILayoutGuide: 0x155c0a00; frame = (0 568; 0 0); hidden = YES; layer = <CALayer: 0x155c0fe0>>`
We can see that the UILabel 0x156a3b10 is hidden.
We can unhidden it by running the following command

And we have our flag! :) Let’s move onto level 2.
Crackme Level 2
The second app has a number of security implementations. To identify those, I performed some reverse engineering. Let’s start off with class-dump to identify what header classes are used to get a glimpse of the application internals.
$ class-dump -S -s -H uncrackable2_32 -o uncrackable2.dump
$ ~/tools/ios/hackme/uncrackable2.dump|
⇒ ls -laR
.:
total 64
drwxr-xr-x 17 tnayr staff 578 Apr 20 16:58 .
drwxr-xr-x 16 tnayr staff 544 Apr 20 16:58 ..
-rw-r--r-- 1 tnayr staff 288 Apr 20 16:58 AESCrypt.h
-rw-r--r-- 1 tnayr staff 929 Apr 20 16:58 AppDelegate.h
-rw-r--r-- 1 tnayr staff 362 Apr 20 16:58 CDStructures.h
-rw-r--r-- 1 tnayr staff 238 Apr 20 16:58 NSData-Base64Additions.h
-rw-r--r-- 1 tnayr staff 555 Apr 20 16:58 NSData-CommonCryptor.h
-rw-r--r-- 1 tnayr staff 328 Apr 20 16:58 NSData-CommonDigest.h
-rw-r--r-- 1 tnayr staff 297 Apr 20 16:58 NSData-CommonHMAC.h
-rw-r--r-- 1 tnayr staff 949 Apr 20 16:58 NSData-LowLevelCommonCryptor.h
-rw-r--r-- 1 tnayr staff 253 Apr 20 16:58 NSError-CommonCryptoErrorDomain.h
-rw-r--r-- 1 tnayr staff 880 Apr 20 16:58 NSObject-Protocol.h
-rw-r--r-- 1 tnayr staff 268 Apr 20 16:58 NSString-Base64Additions.h
-rw-r--r-- 1 tnayr staff 5237 Apr 20 16:58 UIApplicationDelegate-Protocol.h
-rw-r--r-- 1 tnayr staff 779 Apr 20 16:58 ViewController.h
-rw-r--r-- 1 tnayr staff 303 Apr 20 16:58 __ARCLiteIndexedSubscripting__-Protocol.h
-rw-r--r-- 1 tnayr staff 279 Apr 20 16:58 __ARCLiteKeyedSubscripting__-Protocol.h
AESCrypt.h:
After viewing AESCrypt.h, I suspect that the flag encrypts and decrypt with AESCrypt. but we should make no assumption and analyze it with a dissembler and see how do things work.
viewDidLoad
We will first look at viewDidLoad since it loads functions and checks before the view controller and is a common place to implement security checks.
There are a few things that tingle my spider sense. Our objective here is to identify security implementations that can possibly be anti-debugging, anti-jailbreak, memory integrity, application integrity, and so on. I have identified that anti-debugging and anti-jailbreak were implemented.
Anti-debugging
Ptrace System Call

It is using ptrace system call to prevent process from being traced.
protectAgainstDebugger functions


It is using sysctl call to detect whether if the process is being debugged.
Jailbreak protection
Now, onto jailbreak protection. It is obvious that it uses a list of files to determine if the phone is jailbroken.

Remote debugging
When I tried to submit the flag in the app, there was a jailbreak alert, which again verified our observations about jailbreak protection. Now, how are we going to test if there is actually debugging protection? We are going to use lldb. There is a fantastic write-up on how to set up debugging on iOS: Installing LLDB (2014 reference, may be outdated). After setting the environment up, let’s see what happens if we try to attach a debugger to the process.
Aha!!! Exit status 45, which suggests that ptrace was used to prevent debugging, as we identified earlier in our static analysis.
Bypass anti-debugging
Now we have identified that there is anti-debugging (ptrace and sysctl) and jailbreak detection (list of files and Cydia URI) from static analysis, and we will bypass them! :)
Ptrace
We can easily bypass ptrace method by modifying the request argument that is passed into the ptrace function. If ptrace is set to 31 - PT_DENY_ATTACH as shows as the list below will deny any traces made to the process.
We identified earlier that the ptrace function is set to 31, which is PT_DENY_ATTACH, and we can bypass it by replacing the request argument from 31 to -1. We will write a tweak to hook the function.
static int (*oldptrace)(int request, pid_t pid, caddr_t addr, int data);
static int newptrace(int request, pid_t pid, caddr_t addr, int data){
if (request == 31) {
request = -1;
}
printf("newptrace:%d\n\n",request);
return oldptrace(request,pid,addr,data);
}
MSHookFunction ((void *)MSFindSymbol(NULL,"_ptrace"), (void *)newptrace, (void **)&oldptrace);
Bypass Sysctl debugging protection
Note that unlike the ptrace technique this method doesn’t prevent a debugger from attaching to a process. Instead, it uses the sysctl function to retrieve information about the process and determine whether it is being debugged. Apple has an article in their Mac Technical Q&As with sample code that uses this method: Detecting the Debugger
int (*oldsysctl) (int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, size_t newlen);
int newsysctl (int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, size_t newlen)
{
printf("sysctl");
return 0;
};
MSHookFunction ((void *)MSFindSymbol(NULL,"_sysctl"), (void *)newsysctl, (void **)&oldsysctl);
Bypass jailbreak protection
I used xcon to bypass jailbreak protection and they have precompiled list of files that uses to detect jailbreak devices.
Bypass all security implementations
If you have combined both tweaks and have xcon running, you should be able to attach a debugger to the process and no longer see the jailbreak alert.
Hook AESCrypt decrypt functions
As we have mentioned earlier, the flag is encrypted and can be decrypted with AESCrypt. So let’s hook the function and have it log the cleartext password. P.S: I also found out that there is no memory integrity check, so you can hook the AESCrypt function without bypass any security implementation.
%hook AESCrypt
+ (id)decrypt:(id)arg1 password:(id)arg2 { %log; id r = %orig; NSLog(@" = %@", r); return r; }
%end
and we have our password from /var/log/syslog :)
Final Tweaks
%hook AESCrypt
+ (id)decrypt:(id)arg1 password:(id)arg2 { %log; id r = %orig; NSLog(@" = %@", r); return r; }
%end
static int (*oldptrace)(int request, pid_t pid, caddr_t addr, int data);
static int newptrace(int request, pid_t pid, caddr_t addr, int data){
if (request == 31) {
request = -1;
}
printf("newptrace:%d\n\n",request);
return oldptrace(request,pid,addr,data);
}
int (*orig_sysctl) (int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, size_t newlen);
int replaced_sysctl (int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, size_t newlen)
{
printf("sysctl");
return 0;
};
%ctor {
@autoreleasepool
{
printf("Tweak to bypass anti-debugging with ptrace and sysctl Start!\n\n");
MSHookFunction ((void *)MSFindSymbol(NULL,"_sysctl"), (void *)replaced_sysctl, (void **)&orig_sysctl);
MSHookFunction ((void *)MSFindSymbol(NULL,"_ptrace"), (void *)newptrace, (void **)&oldptrace);
}
}
I will try to write up on how to prevent the bypass method hopefully next week if i have some time.