Date Category misc Tags iOS

My 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, lets try to unhidden the hidden label using cycript. Lets 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 views. For an 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.

    <UILabel: 0x156a3b10; frame = (0 40; 82 20.5); text = 'i am groot!'; hidden = YES; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x156a3bf0>>

We can unhidden it by running the following command

cy# [#0x16e8f840 setHidden:NO]

Photo

And we have our flag :), lets move onto level 2.

Crackme Level 2

The second app has a number of security implementations, to identify those I did some minor reverse engineering. Let's start off with class-dump to identify what header class are used to get a glimpse of the application internal.

$ 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 #import "NSObject.h" @interface AESCrypt : NSObject { }

        + (id)decrypt:(id)arg1 password:(id)arg2;
        + (id)encrypt:(id)arg1 password:(id)arg2;

        @end

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 the viewDidLoad since it is to load functions and checks before the view controller and a common place to implement security checks.

There are a few things that tingle ma spider sense. Our Objective here is to identify security implementation that can possibly be anti-debugging, anti-jailbreak, memory integrity, application integrity and so on. I have identified that anti-debugging, anti-jailbreak were implemented.

Anti-debugging

Ptrace System Call

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

protectAgainstDebugger functions

Photo

Photo

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 file to determine if the phone is jailbroken.

Photo

/Applications/Cydia.app
/Library/MobileSubstrate/MobileSubstrate.dylib
/bin/bash
/usr/sbin/sshd
/etc/apt

Remote debugging

When I try to submit the flag on the app, there will be a jailbreak alert which again verified our observation about jailbreak protection. Now how are we going to test if there is actually debugging protection? We are going to use lldb and there is a fantastic write up on how to set up debugging on ios. link link After setting the environment up, let's see what happen if we try to attach a debugger onto the process.

$debugserver *:6666 -x backboard "/var/mobile/Containers/Bundle/Application/C5DC5006-8D20-4C88-BE41-E44B570A49B0/UnCrackable Level 2.app/UnCrackable Level 2"
⇒  lldb
Process 2548 stopped
* thread #1, stop reason = signal SIGSTOP
    frame #0: 0x1febe000 dyld`_dyld_start
dyld`_dyld_start:
->  0x1febe000 <+0>:  mov    r8, sp
    0x1febe004 <+4>:  sub    sp, sp, #16
    0x1febe008 <+8>:  bic    sp, sp, #7
    0x1febe00c <+12>: ldr    r3, [pc, #0x70]           ; <+132>
(lldb) c
Process 2548 resuming
2017-04-20 21:24:53.456 UnCrackable Level 2[2548:131476] === SSL Kill Switch 2: Preference set to 0.
2017-04-20 21:24:53.457 UnCrackable Level 2[2548:131476] === SSL Kill Switch 2: Subtrate hook disabled.
2017-04-20 21:24:53.546 UnCrackable Level 2[2548:131476] Introspy - Profiling disabled for sg.vp.UnCrackable-2
Process 2548 exited with status = 45 (0x0000002d)

Aha!!! Exit status 45 and that may suggest that ptrace was used to prevent debugging which was identified earlier on our static analysis.

Bypass anti-debugging

Now we have identified 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.

    define    PT_TRACE_ME 0 /* child declares it’s being traced */
    define    PT_READ_I 1 /* read word in child’s I space */
    define    PT_READ_D 2 /* read word in child’s D space */
    define    PT_READ_U 3 /* read word in child’s user structure */
    define    PT_WRITE_I 4 /* write word in child’s I space */
    define    PT_WRITE_D 5 /* write word in child’s D space */
    define    PT_WRITE_U 6 /* write word in child’s user structure */
    define    PT_CONTINUE 7 /* continue the child */
    define    PT_KILL 8 /* kill the child process */
    define    PT_STEP 9 /* single step the child */
    define    PT_ATTACH 10 /* trace some running process */
    define    PT_DETACH 11 /* stop tracing a process */
    define    PT_SIGEXC 12 /* signals as exceptions for current_proc */
    define PT_THUPDATE 13 /* signal for thread# */
    define PT_ATTACHEXC 14 /* attach to running process with signal exception */
    define    PT_FORCEQUOTA 30 /* Enforce quota for root */
    define    PT_DENY_ATTACH 31
    define    PT_FIRSTMACH 32 /* for machine-specific requests *

We identified earlier that ptrace functions 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 tweaks to hook the functions.

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.

/.fseventsd
/.cydia_no_stash
/etc/clutch.conf
/usr/bin/codesign_allocate
/var/apt
/var/log/syslog
/var/stash
/var/run/syslog
/var/run/syslog.pid
/var/tmp/cydia.log
/var/tmp/pgloader
/evasi0n7
/evasi0n7-installed
/usr/lib/pangu_xpcd.dylib
/usr/lib/pangu_xpcd.ipod.dylib
/xuanyuansword
/tmp/cydia.log
/tmp/FlipSwitchCache
/tmp/.pange93loaded
/tmp/pgloader
/tmp/pgii

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 :)

Apr 20 21:15:12 VP UnCrackable Level 2[2438]: [hookaescrypt] Tweak.xm:2 DEBUG: +[<AESCrypt: 0x9f0c4> decrypt:VaoQcPb6CChCzVmU45BGEKcNMXAYXoZvegJHYO2zqJg= password:cb17927cc23e2757aadb8d8dc7594c32]
Apr 20 21:15:12 VP UnCrackable Level 2[2438]:  = shakennotstirred

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.

Reference

    https://blog.cydia.so/iosdev/2016-01-07/154.html
    http://www.iosre.com/t/7-2-0-ios/770/4
    http://www.blogfshare.com/ali-ios-game.html
    http://www.isoft365.com/rainyx.com/?p=358

Comments

comments powered by Disqus