ดาวน์โหลด PDF: 2022_C00C00R00CON_jailbreak.pdf
โดยไม่ต้องเจลเบรค
ด้วย ptrace (lldb / frida) → แอปต้องการสิทธิ์ get-task-allow
โดยการฉีดรหัส frida → จำเป็นต้องทำการบรรจุแอปใหม่
ทั้งสองกรณีคุณจะต้องลาออกจากใบสมัคร
แต่มีผลข้างเคียงมากมาย:
รหัสทีมที่แตกต่างกัน
ไฟล์ได้รับการแก้ไข
ด้วยการแหกคุก
กลไกการตรวจจับการเจลเบรกถูกเพิ่มเข้าไปในการป้องกันทางวิศวกรรมย้อนกลับ เพื่อทำให้การรันแอปบนอุปกรณ์ที่ถูกเจลเบรคทำได้ยากขึ้น ซึ่งจะบล็อกเครื่องมือและเทคนิคบางอย่างที่วิศวกรย้อนกลับชอบใช้ เช่นเดียวกับการป้องกันประเภทอื่นๆ ส่วนใหญ่ การตรวจจับการเจลเบรคไม่ได้มีประสิทธิภาพมากนักในตัวเอง แต่การกระจายการตรวจสอบทั่วทั้งซอร์สโค้ดของแอปสามารถปรับปรุงประสิทธิภาพของแผนการต่อต้านการปลอมแปลงโดยรวมได้
ความเสี่ยงด้านความปลอดภัยขององค์กรเกิดจากการเจลเบรกเมื่อเผชิญกับเครื่องมือที่สามารถช่วยให้ผู้ใช้หลบเลี่ยงวิธีการตรวจจับการเจลเบรกทั่วไปได้อย่างง่ายดาย ผู้ใช้สามารถดาวน์โหลดการปรับแต่งแอพใดๆ เช่น Spotify++ ได้โดยตรงจาก Cydia จาก App Store ของบุคคลที่สาม แอปพลิเคชันจำนวนมากมีการตรวจจับการแหกคุก เช่นเดียวกับแอปสื่อและบริการทางการเงินจำนวนมากที่ต้องการจำกัดการละเมิดลิขสิทธิ์เนื้อหาและการประนีประนอมบัญชีตามลำดับ น่าเสียดายที่การตรวจจับการเจลเบรกเหล่านี้อาศัยการผสมผสานระหว่างการทดสอบที่ค่อนข้างตรงไปตรงมาและหลีกเลี่ยงได้ เช่น:
ข้อจำกัดพื้นฐานของการทดสอบการตรวจจับเหล่านี้และการทดสอบที่เทียบเคียงได้คือในฐานะการทดสอบฝั่งไคลเอ็นต์ สามารถเข้าถึง ทำวิศวกรรมย้อนกลับ และหลบเลี่ยงโดยผู้โจมตีได้ นอกจากนี้ แอพที่ทำการทดสอบการตรวจจับการเจลเบรกเหล่านี้ (เช่น แอพ MDM) จะต้องผ่านกระบวนการตรวจสอบแอพของ Apple โดยจำกัดขอบเขตของข้อมูลที่สามารถรวบรวมเพื่อวิเคราะห์สถานะการเจลเบรกของอุปกรณ์ มาดูกันอย่างรวดเร็ว:
ตรวจสอบไฟล์และไดเร็กทอรีที่มักเกี่ยวข้องกับการเจลเบรค เช่น:
//suspicious system and app paths to check
private static var suspicousAppandSystemPaths : [ String ] {
return [
" /usr/sbin/frida-server " ,
" /etc/apt/sources.list.d/electra.list " ,
" /etc/apt/sources.list.d/sileo.sources " ,
" /.bootstrapped_electra " ,
" /usr/lib/libjailbreak.dylib " ,
" /jb/lzma " ,
" /.cydia_no_stash " ,
" /.installed_unc0ver " ,
" /jb/offsets.plist " ,
" /usr/share/jailbreak/injectme.plist " ,
" /etc/apt/undecimus/undecimus.list " ,
" /var/lib/dpkg/info/mobilesubstrate.md5sums " ,
" /Library/MobileSubstrate/MobileSubstrate.dylib " ,
" /jb/jailbreakd.plist " ,
" /jb/amfid_payload.dylib " ,
" /jb/libjailbreak.dylib " ,
" /usr/libexec/cydia/firmware.sh " ,
" /var/lib/cydia " ,
" /etc/apt " ,
" /private/var/lib/apt " ,
" /private/var/Users/ " ,
" /var/log/apt " ,
" /Applications/Cydia.app " ,
" /private/var/stash " ,
" /private/var/lib/apt/ " ,
" /private/var/lib/cydia " ,
" /private/var/cache/apt/ " ,
" /private/var/log/syslog " ,
" /private/var/tmp/cydia.log " ,
" /Applications/Icy.app " ,
" /Applications/MxTube.app " ,
" /Applications/RockApp.app " ,
" /Applications/blackra1n.app " ,
" /Applications/SBSettings.app " ,
" /Applications/FakeCarrier.app " ,
" /Applications/WinterBoard.app " ,
" /Applications/IntelliScreen.app " ,
" /private/var/mobile/Library/SBSettings/Themes " ,
" /Library/MobileSubstrate/CydiaSubstrate.dylib " ,
" /System/Library/LaunchDaemons/com.ikey.bbot.plist " ,
" /Library/MobileSubstrate/DynamicLibraries/Veency.plist " ,
" /Library/MobileSubstrate/DynamicLibraries/LiveClock.plist " ,
" /System/Library/LaunchDaemons/com.saurik.Cydia.Startup.plist " ,
" /Applications/Cydia.app " ,
" /Applications/blackra1n.app " ,
" /Applications/FakeCarrier.app " ,
" /Applications/Icy.app " ,
" /Applications/IntelliScreen.app " ,
" /Applications/MxTube.app " ,
" /Applications/RockApp.app " ,
" /Applications/SBSettings.app " ,
" /Applications/WinterBoard.app "
]
}
ส่วนใหญ่แล้วสิ่งเหล่านี้จะถูกตรวจสอบโดยใช้
-(BOOL)fileExistsAtPath:(NSString*)
วิธีการเส้นทางใน NSFileManager หรือ FileManager.default.fileExists(atPath: path)
อย่างไรก็ตาม ยังมีแอปพลิเคชันที่ใช้ฟังก์ชัน C ระดับล่าง เช่น fopen(), stat() หรือ access()
อีกวิธีหนึ่งในการตรวจสอบกลไกการเจลเบรคคือพยายามเขียนไปยังตำแหน่งที่อยู่นอกแซนด์บ็อกซ์ของแอปพลิเคชัน คุณสามารถทำได้โดยให้แอปพลิเคชันพยายามสร้างไฟล์ใน เช่น ไดเร็กทอรี /private
หากสร้างไฟล์สำเร็จ แสดงว่าอุปกรณ์ถูกเจลเบรคแล้ว
วิธีการนี้ประกอบด้วยการตรวจสอบสิทธิ์ของไฟล์และไดเร็กทอรีที่ระบุในระบบ มีไดเร็กทอรีจำนวนมากที่มีสิทธิ์ในการเขียนบนอุปกรณ์ที่ถูกเจลเบรคมากกว่าไดเร็กทอรีที่ไม่ถูกบุกรุก ตัวอย่างหนึ่งของสิ่งนี้คือพาร์ติชันรูท ซึ่งแต่เดิมมีสิทธิ์ในการอ่านเท่านั้น หากพบว่ามีสิทธิ์ในการอ่านและเขียน แสดงว่าอุปกรณ์นั้นเจลเบรคแล้ว มีหลายวิธีในการดำเนินการตรวจสอบเหล่านี้ เช่น การใช้ฟังก์ชัน NSFileManager และ C เช่น statfs()
สวิฟท์:
do {
let pathToFileInRestrictedDirectory = " /private/jailbreak.txt "
try " This is a test. " . write ( toFile : pathToFileInRestrictedDirectory , atomically : true , encoding : String . Encoding . utf8 )
try FileManager . default . removeItem ( atPath : pathToFileInRestrictedDirectory )
// Device is jailbroken
} catch {
// Device is not jailbroken
}
วัตถุประสงค์-C:
NSError *error;
NSString *stringToBeWritten = @" This is a test. " ;
[stringToBeWritten writeToFile: @" /private/jailbreak.txt " atomically: YES
encoding: NSUTF8StringEncoding error: &error];
if (error == nil ) {
// Device is jailbroken
return YES ;
} else {
// Device is not jailbroken
[[ NSFileManager defaultManager ] removeItemAtPath: @" /private/jailbreak.txt " error: nil ];
}
มีหลายวิธีในการดำเนินการตรวจสอบเหล่านี้ เช่น การใช้ฟังก์ชัน NSFileManager และฟังก์ชัน C เช่น statfs(), open(), utimes(), stat(), pathconf(), stat64(), fopen()
คุณสามารถตรวจสอบตัวจัดการโปรโตคอลได้โดยพยายามเปิด Cydia URL App Store ของ Cydia ซึ่งแทบทุกเครื่องมือเจลเบรกจะติดตั้งตามค่าเริ่มต้น โดยจะติดตั้งตัวจัดการโปรโตคอล cydia://
สวิฟท์:
if let url = URL ( string : " cydia://package/com.example.package " ) , UIApplication . shared . canOpenURL ( url ) {
// Device is jailbroken
}
วัตถุประสงค์-C:
if ([[UIApplication sharedApplication ] canOpenURL: [ NSURL URLWithString: @" cydia://package/com.example.package " ]]){
// Device is jailbroken
}
การตรวจสอบง่ายๆ นี้สามารถเข้าถึงได้ วิศวกรรมย้อนกลับ และหลบเลี่ยงโดยผู้โจมตี
ptrace ( PT_DENY_ATTACH );
void try_kill () {
const int pid = getpid ();
int ret = kill (pid, 0 );
}
ตามหน้า man ของ kill ( man 2 kill
) สัญญาณ 0
ใช้เพื่อตรวจสอบว่า pid
ที่กำหนดในพารามิเตอร์แรกมีอยู่จริง
[…] อย่างไรก็ตาม ค่า 0 จะทำให้มีการตรวจสอบข้อผิดพลาด (โดยไม่มีการส่งสัญญาณ) สามารถใช้เพื่อตรวจสอบความถูกต้องของ pid
การดำเนินการ kill นี้ตามมาด้วยการตรวจสอบ PTRACE
:
inline bool ptrace_detect () {
int32_t opt[ 4 ] = {
CTL_KERN,
KERN_PROC,
KERN_PROC_PID,
getpid (),
};
kinfo_proc info;
sysctl (opt, 4 , &info, sizeof (kinfo_proc), nullptr , 0 );
return info. kp_proc . p_flag & P_TRACED;
}
getppid() == 1
getfsstat64(), statvfs()
fcntl(F_ADDSIGS)
csops(CS_OPS_MARKKILL)
เราสามารถตรวจสอบความสมบูรณ์ของลายเซ็นไบนารี่ของเราได้ การตรวจสอบนี้เริ่มต้นด้วยการเปิดไบนารีของแอปหลักจากดิสก์ ค้นหาจนถึงลำดับ kSecCodeMagicEmbeddedSignature 0xfade0cc0
อ่านการให้สิทธิ์และคำนวณผลรวมตรวจสอบ
ส่วนการลงนามโค้ดมีโครงสร้าง superlob ซึ่งตัวมันเองก็มี blobs อื่นๆ ในประเภทที่แตกต่างกัน โครงสร้างของทั้งสองถูกกำหนดไว้ในโค้ด Apple โอเพ่นซอร์สเพิ่มเติม:
/*
* Structure of an embedded-signature SuperBlob
*/
typedef struct __BlobIndex {
uint32_t type; /* type of entry */
uint32_t offset; /* offset of entry */
} CS_BlobIndex;
typedef struct __SuperBlob {
uint32_t magic; /* magic number */
uint32_t length; /* total length of SuperBlob */
uint32_t count; /* number of index entries following */
CS_BlobIndex index[]; /* (count) entries */
/* followed by Blobs in no particular order as indicated by offsets in index */
} CS_SuperBlob;
Super Blob นั้นประกอบไปด้วยเวทย์มนตร์บางอย่าง ความยาวของส่วนการเซ็นโค้ดทั้งหมด จำนวน Blob และอาร์เรย์ของดัชนีสำหรับ Blob เหล่านั้น เวทมนตร์หยดที่ระบุที่แตกต่างกันถูกกำหนดไว้ในไฟล์นี้:
/*
* Magic numbers used by Code Signing
*/
enum {
kSecCodeMagicRequirement = 0xfade0c00 , /* single requirement */
kSecCodeMagicRequirementSet = 0xfade0c01 , /* requirement set */
kSecCodeMagicCodeDirectory = 0xfade0c02 , /* CodeDirectory */
kSecCodeMagicEmbeddedSignature = 0xfade0cc0 , /* single-architecture embedded signature */
kSecCodeMagicDetachedSignature = 0xfade0cc1 , /* detached multi-architecture signature */
kSecCodeMagicEntitlement = 0xfade7171 , /* entitlement blob */
};
ตัวอย่างเช่น ไบนารี iOS ps
มีเนื้อหาสี่หยดตามหลัง superlob เริ่มต้น:
เหล่านี้คือตามลำดับ:
0xfade0c02
) ซึ่งคุณสามารถมองเห็นได้เนื่องจากมีชื่อไบนารี่ที่ผ่านการรับรอง com.apple.ps
อยู่ในนั้น0xfade0c01
)0xfade7171
)0xfade0b01
)พยายามหยุดแอปหลังจากหมดเวลาครั้งใหญ่ และอย่าออกจากจุดตรวจจับการเจลเบรกโดยตรง
fork()
กระบวนการฟอร์ก Sandboxd ไม่ได้ปฏิเสธแอปพลิเคชันที่สามารถใช้ fork(), popen() หรือฟังก์ชัน C อื่นๆ เพื่อสร้างกระบวนการย่อยบนอุปกรณ์ที่ถูกเจลเบรค อย่างไรก็ตาม Sandboxd ปฏิเสธอย่างชัดเจนถึงกระบวนการฟอร์กบนอุปกรณ์ที่ไม่ได้เจลเบรค ดังนั้นด้วยการตรวจสอบ pid ที่ส่งคืนบน fork() แอปพลิเคชันจึงสามารถบอกได้ว่าอุปกรณ์ถูกบุกรุกหรือไม่ หากการ fork สำเร็จ แอปสามารถอนุมานได้ว่ากำลังทำงานบนอุปกรณ์ที่เจลเบรคแล้ว
system()
การเรียกฟังก์ชัน system() ด้วยอาร์กิวเมนต์ NULL บนอุปกรณ์ที่ไม่ได้เจลเบรคจะส่งคืนค่า 0 การทำเช่นเดียวกันบนอุปกรณ์ที่เจลเบรคแล้วจะส่งคืนค่า 1 เนื่องจากฟังก์ชันจะตรวจสอบว่ามี /bin/sh อยู่หรือไม่ และมีอยู่เฉพาะใน อุปกรณ์ที่เจลเบรคแล้ว
การเชื่อมต่อแบบวนกลับ SSH เนื่องจากอุปกรณ์เจลเบรคส่วนใหญ่ที่ติดตั้ง OpenSSH แอปพลิเคชั่นบางตัวจึงพยายามเชื่อมต่อกับ 127.0.0.1 บนพอร์ต 22 หากการเชื่อมต่อสำเร็จ แสดงว่าติดตั้งและรัน OpenSSH แล้ว ซึ่งพิสูจน์ว่าอุปกรณ์เจลเบรคแล้ว
ตรวจสอบลูปแบ็คสำหรับพอร์ตที่เปิด 22 (OpenSSH) และ 44 (checkra1n)
วิธีการตรวจจับนี้เริ่มต้นด้วยการเรียกใช้ฟังก์ชัน เช่น _dyld_image_count()
และ _dyld_get_image_name()
เพื่อดูว่า dylibs ใดบ้างที่โหลดอยู่ในปัจจุบัน วิธีนี้เป็นเรื่องยากมากที่จะแพตช์แบบไดนามิกเนื่องจากแพตช์นั้นเป็นส่วนหนึ่งของ dylibs
/usr/lib/substitute-inserter.dylib
เป็นต้น
สามารถใช้ dlopen / การสแกนหน่วยความจำ / โครงสร้างภายใน dyld เป็นต้น
private static func checkDYLD ( ) -> Bool {
let suspiciousLibraries = [
" FridaGadget " ,
" frida " ,
" cynject " ,
" libcycript "
]
for libraryIndex in 0 ..< _dyld_image_count ( ) {
guard let loadedLibrary = String ( validatingUTF8 : _dyld_get_image_name ( libraryIndex ) ) else { continue }
for suspiciousLibrary in suspiciousLibraries {
if loadedLibrary . lowercased ( ) . contains ( suspiciousLibrary . lowercased ( ) ) {
return true
}
}
}
return false
}
ตรวจสอบความสมบูรณ์ของรหัส
พยายามตรวจจับฟรีด้า
private static func isFridaRunning ( ) -> Bool {
func swapBytesIfNeeded ( port : in_port_t ) -> in_port_t {
let littleEndian = Int ( OSHostByteOrder ( ) ) == OSLittleEndian
return littleEndian ? _OSSwapInt16 ( port ) : port
}
var serverAddress = sockaddr_in ( )
serverAddress . sin_family = sa_family_t ( AF_INET )
serverAddress . sin_addr . s_addr = inet_addr ( " 127.0.0.1 " )
serverAddress . sin_port = swapBytesIfNeeded ( port : in_port_t ( 27042 ) )
let sock = socket ( AF_INET , SOCK_STREAM , 0 )
let result = withUnsafePointer ( to : & serverAddress ) {
$0 . withMemoryRebound ( to : sockaddr . self , capacity : 1 ) {
connect ( sock , $0 , socklen_t ( MemoryLayout < sockaddr_in > . stride ) )
}
}
if result != - 1 {
return true
}
return false
}
โดยทั่วไป ยิ่งการตรวจจับการเจลเบรคมีความซับซ้อนมากขึ้นเท่าใด การตรวจจับและบายพาสก็จะยิ่งยากขึ้นเท่านั้น ข้อผิดพลาดที่พบบ่อยที่สุดเมื่อใช้งานการตรวจจับการเจลเบรกมักจะอยู่ที่การใช้งานนั้นเอง
เรามักจะเจอแอปที่มีการตรวจจับการเจลเบรกที่ยอดเยี่ยม แต่การใช้งานนั้นอยู่ในฟังก์ชันเดียวที่ส่งคืนค่าจริงหรือเท็จ ขึ้นอยู่กับว่าอุปกรณ์นั้นเจลเบรกหรือไม่ ในกรณีเหล่านี้ เราจะข้ามการตรวจจับการแหกคุกโดยใช้ Cycript หรือเครื่องมือที่คล้ายกันเพื่อกลับค่าที่ส่งคืนจากฟังก์ชันการตรวจจับ
ในทางปฏิบัติ การตรวจจับการเจลเบรคที่ดีที่สุดได้รวมเอาเทคนิคต่างๆ เข้าด้วยกันและรวมเข้ากับฟังก์ชันอื่นๆ เพื่อไม่ให้ข้ามไปได้ง่ายๆ
https://github.com/mehrankmlf/SecurityKit
https://www.synacktiv.com/sites/default/files/2021-10/2021_sthack_jailbreak.pdf
https://www.romainthomas.fr/post/21-07-pokemongo-anti-frida-jailbreak-bypass/
https://aeonlucid.com/Snapchat-detection-on-iOS/
https://github.com/AeonLucid/SnapHide
https://redmaple.tech/blogs/macho-files/