Jailbreak Detection은 iOS 기기에서 제일브레이크의 존재를 감지하고 제한이 제거된 환경에서 애플리케이션 실행을 방지하는 메커니즘 집합입니다. 제일브레이크는 샌드박스 외부의 파일 시스템에 대한 액세스를 제공하여 수정된 라이브러리 설치 및 시스템 호출 가로채기를 가능하게 합니다. Apple Security Documentation (2024)에 따르면, 제일브레이크된 기기는 Secure Boot 보안 모델을 준수하지 않습니다. Jailbreak Detection은 파일 표시기 검사, 런타임 호출 분석 및 샌드박스 서명 무결성 검증을 결합합니다.
핵심 사항
Jailbreak Detection은 운영 체제 제한이 제거된 iOS 기기를 식별하는 프로세스입니다. 제일브레이크는 iOS 커널을 수정하고, 코드 서명을 비활성화하며, 전체 파일 시스템에 대한 액세스를 제공하고, 승인되지 않은 라이브러리를 로드할 수 있게 합니다. 이러한 기기에서 실행되는 애플리케이션의 경우 런타임 환경 무결성에 대한 보장이 없습니다: 모든 프로세스가 애플리케이션 메모리를 읽고, 시스템 신뢰 저장소에 자체 인증서를 설치하여 SSL/TLS 트래픽을 가로채고, Cydia Substrate 또는 Substitute를 통해 코드를 주입할 수 있습니다.
iOS의 금융 애플리케이션은 PCI DSS 표준 요구 사항에 따라 Jailbreak Detection을 구현해야 합니다 — 인증을 받으려면 애플리케이션이 손상된 기기에서 실행되지 않음을 증명해야 합니다. OWASP Mobile Security (2024)는 Jailbreak Detection의 부재를 M8 취약점으로 분류합니다. App Store 애플리케이션의 경우 Apple은 제일브레이크된 기기에서 기능 차단을 금지하지 않지만, 수정될 수 있는 클라이언트 코드에만 의존하지 않도록 클라이언트 측과 서버 측 검사를 결합할 것을 권장합니다.
샌드박스 모델로 인해 iOS의 Jailbreak Detection 아키텍처는 Android의 Root Detection보다 더 복잡합니다. Android에서는 애플리케이션이 /proc을 읽어 시스템을 분석할 수 있습니다. iOS 샌드박스는 대부분의 시스템 표시기에 대한 직접 액세스를 차단합니다. 개발자는 canAccessFile API를 통해 제한된 영역의 파일 가용성을 확인하거나 exit 코드 검증과 함께 fork()를 통해 하위 프로세스를 시작하는 등의 대체 기술을 사용해야 합니다. 현대적인 검사는 제일브레이크에서만 가능한 작업을 시도하고 결과를 분석하는 방식으로 구축됩니다.
가장 간단하고 역사적으로 첫 번째 접근 방식은 제일브레이크된 기기에만 설치되는 파일 및 애플리케이션의 존재를 확인하는 것입니다. 단순함에도 불구하고 파일 검사는 방어의 기본 계층으로 남아 있으며, 이를 우회하려면 사용자의 적극적인 조치가 필요합니다.
제일브레이크된 기기에는 Cydia, Sileo, Zebra 또는 Installer와 같은 애플리케이션이 존재합니다. 이들의 존재는 NSFileManager를 통해 확인됩니다: [[NSFileManager defaultManager] fileExistsAtPath:@"/Applications/Cydia.app"]. unc0ver, checkra1n, Taurine 및 Chimera의 패키지도 유사하게 확인됩니다. 이러한 검사는 NSFileManager 호출을 가로채는 HideJB 트윅을 통해 우회될 수 있습니다.
제일브레이크는 기본 iOS에서 사용할 수 없는 UNIX 유틸리티를 설치합니다: apt, dpkg, ssh, rsync, sftp, dd, readlink 등. /usr/bin/ssh, /bin/bash, /bin/sh 및 /usr/libexec/sftp-server의 존재가 확인됩니다. 이러한 파일 중 하나라도 성공적으로 감지되면 제일브레이크 가능성이 높습니다. iOS 13–17의 경우 /var/jb의 존재를 확인하는 것도 중요합니다 — unc0ver 및 Taurine의 부트스트랩 루트 디렉터리입니다.
MobileSubstrate (CydiaSubstrate.dylib) 및 Substitute는 프로세스에 코드를 주입하기 위한 라이브러리입니다. 이들의 존재는 RTLD_NOLOAD 플래그와 함께 dlopen()을 통해 확인됩니다. 라이브러리가 주소 공간에 로드된 경우 — 프로세스가 제일브레이크 환경에서 실행 중입니다. HideJB는 이미 프로세스에 로드된 라이브러리를 언로드할 수 없기 때문에 이는 더 신뢰할 수 있는 검사입니다.
- (BOOL)isJailbrokenByFiles {
NSArray *paths = @[
@"/Applications/Cydia.app",
@"/Applications/Sileo.app",
@"/Applications/Zebra.app",
@"/usr/bin/ssh",
@"/bin/bash",
@"/var/jb",
@"/etc/apt"
];
for (NSString *path in paths) {
if ([[NSFileManager defaultManager]
fileExistsAtPath:path]) {
return YES;
}
}
return NO;
}
- (BOOL)isSubstrateLoaded {
void *handle = dlopen(
"/Library/MobileSubstrate/MobileSubstrate.dylib",
RTLD_NOLOAD | RTLD_LAZY
);
if (handle) {
dlclose(handle);
return YES;
}
return NO;
}
동적 검사는 iOS 샌드박스에서 금지된 작업을 수행하고 결과를 분석합니다. 작업이 차단되지 않으면 — 기기가 제일브레이크되었을 가능성이 높습니다.
기본 iOS에서 fork() 호출은 errno = EPERM과 함께 -1을 반환합니다. 제일브레이크된 기기에서는 샌드박스 제한이 제거되었기 때문에 fork()가 성공할 수 있습니다. 이 검사는 신뢰할 수 있지만 일부 iOS 버전에서 오탐지를 일으킬 수 있습니다. fork()는 하위 프로세스를 시작할 수 있는지 확인하기 위해 posix_spawn()으로 대체할 수도 있습니다.
제한된 영역에서 파일 읽기 시도: /etc/master.passwd, /var/log/system.log, /private/var/cache. 기본 iOS에서 이러한 읽기는 오류를 반환합니다. 애플리케이션이 이러한 파일을 성공적으로 읽으면 — 샌드박스가 비활성화된 것입니다. 또한 /private/에 쓰기 기능이 확인됩니다 — 샌드박스에서는 모든 시스템 파티션이 일반 애플리케이션에 대해 읽기 전용으로 마운트됩니다.
제일브레이크는 dyld 공유 캐시를 포함한 시스템 라이브러리를 수정합니다. 시스템 프레임워크 또는 개별 심볼의 해시를 확인하면 수정을 발견할 수 있습니다. iOS 14+의 경우 sysctl kern.version을 읽어 커널 주소 공간에서 jit_region_create 심볼 또는 Fugu14/checkra1n 작동의 다른 징후 존재를 확인합니다.
- (BOOL)isJailbrokenByRuntime {
// fork() 확인
int pid = fork();
if (pid == 0) {
exit(0);
}
if (pid > 0) {
waitpid(pid, NULL, 0);
return YES;
}
// 시스템 파일 액세스 확인
FILE *f = fopen("/etc/master.passwd", "r");
if (f) {
fclose(f);
return YES;
}
// sysctl kern.version 확인
size_t size = 0;
sysctlbyname("kern.version", NULL, &size, NULL, 0);
if (size > 0) {
char *version = malloc(size);
sysctlbyname("kern.version", version, &size, NULL, 0);
NSString *str = [NSString stringWithUTF8String:version];
free(version);
if ([str containsString:"pwned"]) {
return YES;
}
}
return NO;
}
Swift 코드는 쉽게 디스어셈블되고 Substrate를 통해 우회됩니다. libobjc 및 C 시스템 함수에 대한 직접 호출이 있는 Objective-C 네이티브 구현은 검사를 우회에 대해 훨씬 더 저항력 있게 만듭니다.
libc의 stat() 호출은 Objective-C 수준에서 가로챌 수 없습니다. NSFileManager 메서드를 가로채는 HideJB 트윅은 stat()에 영향을 미치지 않습니다. stat()을 사용하는 네이티브 검사는 HideJB 모듈이 설치된 기기에서도 파일 표시기를 감지합니다. 파일용 stat()과 라이브러리용 RTLD_NOLOAD가 있는 dlopen()의 조합은 두 개의 겹치지 않는 탐지 채널을 제공합니다.
네이티브 함수 SecStaticCodeCheckValidity는 Apple 인증서에 대한 애플리케이션의 코드 서명을 검증합니다. 제일브레이크된 기기에서는 이 검사가 커널 패치를 통해 위조될 수 있습니다. 위조를 방지하려면 Swift Bridge를 통하지 않고 Security.framework에서 dlopen()을 통한 호출로 네이티브 코드에서 검사를 수행해야 합니다.
#import <sys/stat.h>
#import <dlfcn.h>
- (BOOL)nativeCheckForJailbreak {
// stat()이 NSFileManager 후크를 우회함
struct stat st;
if (stat("/Applications/Cydia.app", &st) == 0) {
return YES;
}
// 로드 없이 Substrate 확인을 위한 dlopen
void *substrate = dlopen(
"/Library/MobileSubstrate/MobileSubstrate.dylib",
RTLD_NOLOAD
);
if (substrate) {
dlclose(substrate);
return YES;
}
return NO;
}
강력한 보호를 구축하려면 우회 기술을 이해하는 것이 필수적입니다. 현대적인 우회 도구는 활발히 발전하고 있으며, 정적 검사 세트는 몇 달 안에 효과가 없어집니다.
HideJB는 NSFileManager, stat(), dlopen() 및 fork()에 대한 호출을 가로채고 반환 값을 대체하는 트윅입니다. HideJB는 Cydia Substrate 수준에서 작동하며 Objective-C 및 C 함수를 모두 가로챕니다. iOS 15–16용 HideJB (Shadow) 버전은 커널 수준 후크 방법론을 사용합니다. 대책: 후크 체인을 끊기 위해 IPC를 통해 결과를 전달하는 별도 프로세스에서 검사를 수행합니다.
Choicy는 특정 프로세스에 대해 Substrate를 비활성화할 수 있습니다. 사용자는 보호된 애플리케이션에 대한 주입을 비활성화하기만 하면 모든 라이브러리 검사가 false를 반환합니다. Liberty Lite는 인기 있는 보호 라이브러리의 대부분 검사를涵盖하는 포괄적인 우회 도구입니다. 대책: DeviceCheck 및 App Attest를 통한 서버 측 검증 — 서버는 기기에 제일브레이크된 기기에서 위조할 수 없는 유효한 Apple 인증서가 있는지 확인합니다.
Fugu14 및 KFD와 같은 커널 익스플로잇은 커널 공간에서 코드를 실행하여 애플리케이션이 보기 전에 시스템 호출을 가로챌 수 있습니다. 이 수준에서 stat() 및 fork() 검사는 효과가 없어집니다. 유일한 신뢰할 수 있는 대책은 기기가 Apple Attestation 절차를 통과했는지 확인하는 서버 측 증명입니다. 이 프로토콜은 Secure Enclave 내부의 암호화 키를 기반으로 하며, 커널 수준 익스플로잇으로도 읽을 수 없습니다.
효과적인 Jailbreak Detection을 구축하려면 제일브레이크 중에 어떤 iOS 보안 메커니즘이 비활성화되는지 이해해야 합니다.
iOS는 서명 검사 시퀀스를 통해 부팅됩니다: Boot ROM → iBoot → iOS Kernel. 제일브레이크가 bootrom 익스플로잇(checkra1n)을 사용하는 경우 전체 Secure Boot Chain이 손상됩니다 — 애플리케이션 수준의 검사는 무용지물입니다. 소프트웨어 전용 익스플로잇(unc0ver, Taurine, Fugu14)이 사용되는 경우 부팅 체인은 손상되지 않으며 App Attest와 같은 Apple 서비스는 신뢰할 수 있는 상태로 유지됩니다.
iOS 10부터 Apple은 KPP를 도입했습니다 — 200ms마다 커널 무결성을 다시 확인하는 하드웨어 보호 기능입니다. 모든 최신 제일브레이크(iOS 14–17)는 PAC 또는 APRR을 통한 KTRR 우회를 사용하지만 KPP는 수정된 sysctl 시스템 테이블 형태로 흔적을 남깁니다. pwned, prod 또는 비표준 버전의 xnu와 같은 문자열이 있는지 kern.version을 확인하면 커널 패치를 발견할 수 있습니다.
iOS 샌드박스는 entitlements를 사용하여 TrustedBSD 수준에서 작동합니다. 제일브레이크는 샌드박스 프로필을 allow-all로 대체합니다. 애플리케이션은 Documents 디렉터리 외부의 파일을 읽으려고 시도하여 샌드박스를 확인할 수 있습니다. 성공하면 — 샌드박스가 수정된 것입니다. 샌드박스 무결성은 커널 수준 익스플로잇 없이는 위조할 수 없는 몇 안 되는 표시기 중 하나입니다. 권한 검사가 가로채기 전에 커널에서 수행되기 때문입니다.
자주 묻는 질문
Android용 Root Detection은 su 바이너리 및 Magisk의 존재를 확인합니다. iOS용 Jailbreak Detection은 Cydia, Sileo, MobileSubstrate를 찾고, fork() 실행 능력을 확인하며 시스템 파일을 읽습니다. iOS 샌드박스 아키텍처는 Android보다 더 엄격하므로 iOS 검사는 시스템 표시기 읽기보다는 금지된 작업 수행 시도에 더 의존합니다.
네, iOS 16–17의 경우 Dopamine, palera1n 및 checkra1n 제일브레이크가 관련됩니다. Jailbreak Detection은 작동하지만 새로운 도구에 맞게 검사를 업데이트해야 합니다. iOS 17에서 Apple은 샌드박스를 강화했으며 XNU의 변경으로 인해 많은 오래된 검사(예: fork())가 신뢰할 수 없게 되었습니다.
가장 간단한 방법은 HideJB 또는 Shadow로, 라이브러리 수준에서 검사를 가로챕니다. 더 복잡한 보호의 경우 특정 애플리케이션에 대한 주입을 비활성화하기 위해 Frida 또는 Choicy가 사용됩니다. 서버 측 증명(App Attest)은 Secure Enclave 하드웨어 키를 대체하는 커널 수준 익스플로잇을 통해서만 우회할 수 있으며, 이는 실질적으로 불가능합니다.
제일브레이크된 기기의 모든 애플리케이션은 다음에 노출될 수 있습니다: 신뢰 저장소 수정을 통한 SSL 트래픽 가로채기, 파일 시스템 액세스를 통한 Keychain 읽기, 토큰 처리 메서드 가로채기를 통한 Substrate 코드 주입, 암호화 키 획득을 위한 메모리 덤프.
App Attest는 애플리케이션과 기기의 무결성을 확인하는 Apple 서비스입니다. 시작 시 애플리케이션은 Apple 서버로부터 증명 챌린지(attestation challenge)를 받아 Secure Enclave의 개인 키로 서명하고 자체 서버로 전송합니다. 기기가 제일브레이크된 경우 Secure Enclave는 증명 실패를 반환하여 보호된 기능에 대한 액세스를 차단합니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.