บทนำ (Overviews)
การตรวจสอบการ “Jailbreak” ของเครื่อง “iOS” ก็มีหลายวิธีเช่น ตรวจสอบการติดตั้ง “Cydia” หรือตรวจสอบการมีอยู่ของไฟล์ “MobileSubstate.dylib” หรือการลองเขียน “File” ลงในโฟลเดอร์ “Application” เป็นต้น อย่างไรก็ตามเราก็สามารถข้ามการทำ “Jailbreak Detection” ได้โดยใช้กระบวนการ “Method Swizzling”
ขั้นตอน (Steps)
- เครื่อง “iOS” ต้องผ่านการ Jailbreak เสียก่อน (How to jailbreak iOS – “PP Jailbreak”)
- ติดตั้งโปรแกรม “DVIA” จากนั้นไปที่รายการ “Jailbreak Detection” และเลือก “Jailbreak Test 1”
- สำหรับโปแกรมทั่วไปที่ดาวน์โหลดมาจาก “App Store” ให้ทำการ “Decrypt” เสียก่อน (http://blog.itselectlab.com/?p=565) แล้วติดตั้งโปรแปรมที่ “Decrypt” ใหม่อีกครั้ง
- เมื่อทำการ “Decrypt” โปรแกรมเรียบร้อยเราจึงสามารถ “Reverse Engineer” โปรแกรมได้ (อ่าน “Class” หรือ “Method” ออก ตามบทความ http://blog.itselectlab.com/?p=350) แต่สำหรับโปรแกรม “DVIA” ไม่ได้มาจาก “App Store” ดังนั้นจึงข้ามขั้นตอนที่ (3) ไปได้ ดังนี้
- “Remote Access” ผ่าน “SSH”
login as: root root@192.168.0.15's password: iPad:~ root# ls -l total 112 drwxr-xr-x 3 root wheel 102 Oct 23 2014 Documents/ drwxr-xr-x 8 root wheel 272 Oct 9 2014 Library/ drwxr-xr-x 3 root wheel 102 Oct 24 2014 Media/ -rw-r--r-- 1 root wheel 82904 Oct 10 2014 dumpdecrypted.dylib -rwxrwxrwx 1 root wheel 25744 Oct 10 2014 keychain_dumper*
- ค้นหา “Directory” สำหรับโปรแกรม “DVIA”
iPad:~ root# cd /var/mobile/Applications/ iPad:/var/mobile/Applications root# ps aux | grep Damn mobile 783 0.0 13.3 517264 68372 ?? Ss 10:10AM 0:31.21 /var/mobile/Applications/BD0BBE00-2C62-4533-B7C4-D8B46D1EF4C5/DamnVulnerableIOSApp.app/DamnVulnerableIOSApp iPad:/var/mobile/Applications/BD0BBE00-2C62-4533-B7C4-D8B46D1EF4C5/DamnVulnerabl eIOSApp.app root# ls -l total 21128 -rw-r--r-- 1 mobile mobile 11553 May 24 04:16 120x120.png -rw-r--r-- 1 mobile mobile 13907 May 24 04:16 152x152.png -rw-r--r-- 1 mobile mobile 6525 May 24 04:16 57x57.png -rw-r--r-- 1 mobile mobile 375699 May 24 04:16 640_960_SplashScn.png -rw-r--r-- 1 mobile mobile 464522 May 24 04:16 640x1136_SplashScn.png -rw-r--r-- 1 mobile mobile 7893 May 24 04:16 72x72.png -rw-r--r-- 1 mobile mobile 8464 May 24 04:16 76x76.png -rw-r--r-- 1 mobile mobile 11292 May 24 04:16 AppIcon40x40\@2x.png -rw-r--r-- 1 mobile mobile 11553 May 24 04:16 AppIcon60x60\@2x.png drwxr-xr-x 3 mobile mobile 102 May 24 04:16 Base.lproj/ -rwxr-xr-x 1 mobile mobile 19509584 May 24 04:17 DamnVulnerableIOSApp* -rw-r--r-- 1 mobile mobile 1427 May 24 04:16 Info.plist -rwxr-xr-x 1 mobile mobile 1341 May 24 04:16 LICENSE.txt* -rw-r--r-- 1 mobile mobile 464522 May 24 04:16 LaunchImage-700-568h\@2x.png -rw-r--r-- 1 mobile mobile 375699 May 24 04:16 LaunchImage-700\@2x.png drwxr-xr-x 2 mobile mobile 68 May 24 04:16 META-INF/ drwxr-xr-x 2 mobile mobile 136 May 24 04:16 Model.momd/ -rw-r--r-- 1 mobile mobile 8 May 24 04:16 PkgInfo drwxr-xr-x 3 mobile mobile 102 May 24 04:16 PlugIns/ -rwxr-xr-x 1 mobile mobile 324 May 24 04:16 README.txt* drwxr-xr-x 2 mobile mobile 102 May 24 04:16 _CodeSignature/ -rw-r--r-- 1 mobile mobile 4030 May 24 04:16 card-bg.png -rw-r--r-- 1 mobile mobile 2006 May 24 04:16 card-bg\@2x.png -rw-r--r-- 1 mobile mobile 8081 May 24 04:16 embedded.mobileprovision drwxr-xr-x 2 mobile mobile 102 May 24 04:16 en.lproj/ -rw-r--r-- 1 mobile mobile 1150 May 24 04:16 google.co.uk.cer -rw-r--r-- 1 mobile mobile 2927 May 24 04:16 header-bg.png -rw-r--r-- 1 mobile mobile 3155 May 24 04:16 header-bg\@2x.png -rw-r--r-- 1 mobile mobile 4840 May 24 04:16 main-bg.png -rw-r--r-- 1 mobile mobile 5424 May 24 04:16 main-bg\@2x.png -rw-r--r-- 1 mobile mobile 3789 May 24 04:16 menu-icon.png -rw-r--r-- 1 mobile mobile 5006 May 24 04:16 menu-icon\@2x.png -rw-r--r-- 1 mobile mobile 3922 May 24 04:16 menuIcon.png -rw-r--r-- 1 mobile mobile 5388 May 24 04:16 menuIcon\@2x.png -rw-r--r-- 1 mobile mobile 2941 May 24 04:16 slider-active.png -rw-r--r-- 1 mobile mobile 3133 May 24 04:16 slider-active\@2x.png -rw-r--r-- 1 mobile mobile 74035 May 24 04:16 slider-bg.png -rw-r--r-- 1 mobile mobile 176841 May 24 04:16 slider-bg\@2x.png
- ใช้คำสั่ง “class-dump-z ชื่อโปรแกรม > ชื่อไฟล์ที่ต้องการบันทึก” เพื่อ “Reverse Engineering”
iPad:/var/mobile/Applications/BD0BBE00-2C62-4533-B7C4-D8B46D1EF4C5/DamnVulnerableIOSApp.app root# class-dump-z DamnVulnerableIOSApp > zTest.txt iPad:/var/mobile/Applications/BD0BBE00-2C62-4533-B7C4-D8B46D1EF4C5/DamnVulnerableIOSApp.app root# ls -l total 21764 -rw-r--r-- 1 root mobile 649319 Jun 29 14:12 zTest.txt
- ทดสอบเปิดอ่าน “Class” “Method” และ “Properties”
iPad:/var/mobile/Applications/BD0BBE00-2C62-4533-B7C4-D8B46D1EF4C5/DamnVulnerableIOSApp.app root# cat zTest.txt @interface RLMRealm : NSObject { BOOL _inWriteTransaction; unsigned _threadID; @private NSThread* _thread; NSMapTable* _notificationHandlers; unique_ptr<tightdb::Replication, std::__1::default_delete<tightdb::Replication> > _replication; unique_ptr<tightdb::SharedGroup, std::__1::default_delete<tightdb::SharedGroup> > _sharedGroup; unique_ptr<tightdb::Group, std::__1::default_delete<tightdb::Group> > _readGroup; Group* _group; BOOL _readOnly; BOOL _inMemory; BOOL _autorefresh; BOOL _dynamic; NSString* _path; RLMSchema* _schema; } @property(readonly, assign, nonatomic, getter=getOrCreateGroup) Group* group; @property(readonly, assign, nonatomic) BOOL dynamic; @property(readonly, assign, nonatomic) BOOL inWriteTransaction; @property(assign, nonatomic) BOOL autorefresh; @property(retain, nonatomic) RLMSchema* schema; @property(readonly, assign, nonatomic, getter=isReadOnly) BOOL readOnly; @property(readonly, assign, nonatomic) NSString* path; +(id)migrateRealmAtPath:(id)path key:(id)key; +(id)migrateRealmAtPath:(id)path encryptionKey:(id)key; +(id)migrateRealmAtPath:(id)path; +(unsigned)schemaVersionAtPath:(id)path encryptionKey:(id)key error:(id*)error; +(unsigned)schemaVersionAtPath:(id)path error:(id*)error; +(void)setSchemaVersion:(unsigned)version forRealmAtPath:(id)path withMigrationBlock:(id)migrationBlock; +(void)setDefaultRealmSchemaVersion:(unsigned)version withMigrationBlock:(id)migrationBlock; +(void)resetRealmState; +(void)setEncryptionKey:(id)key forRealmsAtPath:(id)path; +(id)realmWithPath:(id)path key:(id)key readOnly:(BOOL)only inMemory:(BOOL)memory dynamic:(BOOL)dynamic schema:(id)schema error:(id*)error; +(id)realmWithPath:(id)path encryptionKey:(id)key readOnly:(BOOL)only error:(id*)error; +(id)inMemoryRealmWithIdentifier:(id)identifier; +(id)realmWithPath:(id)path readOnly:(BOOL)only error:(id*)error; +(id)realmWithPath:(id)path; +(id)defaultRealm; +(id)writeablePathForFile:(id)file; +(void)setDefaultRealmPath:(id)path; +(id)defaultRealmPath; +(void)initialize; +(BOOL)isCoreDebug; -(id).cxx_construct; -(void).cxx_destruct; -(BOOL)writeCopyToPath:(id)path encryptionKey:(id)key error:(id*)error; -(BOOL)writeCopyToPath:(id)path error:(id*)error; -(BOOL)writeCopyToPath:(id)path key:(id)key error:(id*)error; -(id)createObject:(id)object withObject:(id)object2; -(id)objects:(id)objects withPredicate:(id)predicate; -(id)objects:(id)objects where:(id)where args:(void*)args; -(id)objects:(id)objects where:(id)where; -(id)allObjects:(id)objects; -(void)deleteAllObjects; -(void)deleteObjects:(id)objects; -(void)deleteObject:(id)object; -(void)addOrUpdateObjectsFromArray:(id)array; -(void)addOrUpdateObject:(id)object; -(void)addObjects:(id)objects; -(void)addObject:(id)object; -(BOOL)refresh; -(void)handleExternalCommit; -(void)dealloc; -(void)invalidate; -(void)cancelWriteTransaction; -(void)transactionWithBlock:(id)block; -(void)commitWriteTransaction; -(void)beginWriteTransaction; -(void)sendNotifications:(id)notifications; -(void)removeNotification:(id)notification; -(id)addNotificationBlock:(id)block; -(id)migrationBlock:(id)block; -(id)initWithPath:(id)path key:(id)key readOnly:(BOOL)only inMemory:(BOOL)memory dynamic:(BOOL)dynamic error:(id*)error; @end
- “Remote Access” ผ่าน “SSH”
- เมื่อได้ชื่อ “Class” เรียบร้อยแล้ว จากนั้นช่วงเวลานี้อาจจะต้องลองศึกษาจากชื่อ “Class” “Method” หรือ “Properties” ที่เกี่ยวข้องกับช่องโหว่ที่เป็นไปได้ว่าจะมี ในตอนนี้เรากำลังหาช่องโหว่เกี่ยวกับการ “Jailbreak” จึงลองค้นหาคำที่อาจที่เกี่ยวข้องดังกล่าว
@interface JailbreakDetectionVC : UIViewController { } -(BOOL)isJailbroken; -(void)jailbreakTest2Tapped:(id)tapped; -(void)jailbreakTest1Tapped:(id)tapped; -(void)readArticleTapped:(id)tapped; -(void)didReceiveMemoryWarning; -(void)viewDidLoad; -(id)initWithNibName:(id)nibName bundle:(id)bundle; @end
- จากนั้นเราลองใช้โปรแกรม “Cycript” เชื่อมต่อ process ของโปรแกรม “DVIA” ที่กำลัง “Run” อยู่ดังนี้ (http://blog.itselectlab.com/?p=95)
iPad:/var/mobile/Applications/BD0BBE00-2C62-4533-B7C4-D8B46D1EF4C5/DamnVulnerableIOSApp.app root# ps aux | grep Damn root 957 2.9 0.1 339588 436 s000 R+ 2:35PM 0:00.01 grep Damn mobile 783 0.0 14.7 588420 75472 ?? Ss 10:10AM 0:49.83 /var/mobile/Applications/BD0BBE00-2C62-4533-B7C4-D8B46D1EF4C5/DamnVulnerableIOSApp.app/DamnVulnerableIOSApp iPad:/var/mobile/Applications/BD0BBE00-2C62-4533-B7C4-D8B46D1EF4C5/DamnVulnerableIOSApp.app root# cycript -p 783 cy#
- จะได้ “command prompt” ของ “Cycript” จากนั้นเราลองศึกษารายละเอียดของ “Class” ในขั้นตอนที่ (5) ก็จะประกอบไปด้วย ชื่อ “Class” คือ “JailbreakDetectionVC” และประกอบไปด้วย “Method” ที่น่าสนใจคือ “-(BOOL)isJailbroken;” ซึ่งมีค่า Return หลังจากเรียกใช้ Method นี้เป็นค่า “Boolean” นั้นเอง ด้วยเหตุนี้เราจะต้องเข้าถึงหน้า “UIViewController” ของหน้า “JailbreakDectionVC” ให้ได้เสียก่อนดังนี้
cy# UIApp #"<UIApplication: 0x17d5e000>" cy# UIApp.keyWindow #"<UIWindow: 0x17d94f80; frame = (0 0; 320 480); autoresize = W+H; gestureRecognizers = <NSArray: 0x17d8d660>; layer = <UIWindowLayer: 0x17d8d5c0>>" cy# UIApp.keyWindow.rootViewController #"<ECSlidingViewController: 0x17d8e2b0>" cy# UIApp.keyWindow.rootViewController.topViewController #"<UINavigationController: 0x17f64a10>" cy# UIApp.keyWindow.rootViewController.topViewController.visibleViewController #"<JailbreakDetectionVC: 0x17dfae70>"
เพิ่มเติม: “UINavigationController” จัดการเรื่องแสดงผลแต่ใช้วิธีการจัดการที่เรียกว่า “Navigation Stack” โดยส่วนแสดงผล (view controllers) แต่เก็บอยู่ในรูปของ “Array” เพราะฉะนั้นจึงมีหลาย ๆ “view controllers” ใน “Navigation Stack” ได้
- rootViewController คือ “view controllers” แรกที่แสดงผลที่อยู่ใน “array”
- แต่ “view controller” สุดท้ายใน “Array” จะเป็นตัวแสดงผลอยู่ ณ ปัจจุบัน
- topViewController เป็น “Property” ของ “View controller” ที่อยู่บนสุดในส่วน “Navigation Stack”
- visibleViewController เป็น “Property” ของ View controller ที่ปรากฏอยู่ ณ ปัจจุบัน บน “navigation controller” ในทีนี้คือ “JailbreakDetectionVC”
- เมื่อทราบ Address ของ “UIViewController” ของหน้า “JailbreakDectionVC” เราสามารถ “Assign” ให้กับตัวแปรได้ดังนี้
cy# jail = #0x17dfae70 #"<JailbreakDetectionVC: 0x17dfae70>"
- ดังนั้นเมื่อ “Assign” ค่า “Address” ของ “UIViewController” ดังกล่าว ดังนั้นตัวแปร “jail” จึงเปรียบเสมือน “object” หนึ่งของ “class” ที่ชื่อ “JailbreakDetectionVC” และสามารถเรียกใช้ “method” ของมันได้ด้วย ดังตัวอย่าง
cy# [jail isJailbroken] 1
- เมื่อเรียกใช้ method ที่ชื่อ “isJailBroken” พบว่า ได้ค่า “Return” คือ “1” ดังนั้นจาก “Data type” ที่เป็น “Boolean” ในข้อ (5) เราก็สามารถเดาได้ว่า อีกค่าน่าจะเป็น “0” หรือ “NO” แต่การเขียน Return ค่าจะต้องผ่าน “function” ดังนี้
- ค้นหา “class method” โดยใช้คำสั่ง <ชื่อ class>->isa.messages[‘<ชื่อ method>’]
jail->isa.messages['isJailbroken'] 0x1660d1
- จากนั้นสร้าง “function” เพื่อ “assign” ค่า “return” ไปยัง “class method”
cy# jail->isa.messages['isJailbroken'] = function () {return 0;} function () {return 0;}
- ลองทดสอบเรียกค่า “isJailbroken” พบว่าจาก “1” เปลี่ยนเป็น “0” แล้ว
cy# [jail isJailbroken] 0
- ค้นหา “class method” โดยใช้คำสั่ง <ชื่อ class>->isa.messages[‘<ชื่อ method>’]
- ทดสอบลองคลิกที่ “Jailbreak Test1” อีกครั้งจะพบข้อความว่า “Device is Not Jailbreak”