iOS Pentesting

Tip

AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE) Azure 해킹 배우기 및 연습하기: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기

iOS 기본

iOS Basics

테스트 환경

이 페이지에서는 iOS simulator, emulatorsjailbreaking에 대한 정보를 확인할 수 있습니다:

iOS Testing Environment

초기 분석

기본 iOS 테스트 작업

테스트 중에는 여러 작업이 제안됩니다 (기기 연결, 파일 읽기/쓰기/업로드/다운로드, 도구 사용 등…). 따라서 이러한 작업을 수행하는 방법을 모른다면, 먼저 해당 페이지를 읽으세요:

iOS Basic Testing Operations

Tip

다음 단계들을 진행하려면 앱이 기기에 설치되어 있어야 하며 애플리케이션의 IPA 파일을 이미 확보하고 있어야 합니다.
Read the Basic iOS Testing Operations page to learn how to do this.

기본 정적 분석

흥미로운 iOS - IPA 파일 디컴파일러들:

IPA 파일에 대해 자동 정적 분석을 수행하려면 도구 MobSF를 사용하는 것이 권장됩니다.

바이너리에 적용된 보호 기능 식별:

  • PIE (Position Independent Executable): 활성화되면 애플리케이션이 실행될 때마다 무작위 메모리 주소에 로드되어 초기 메모리 주소 예측이 어려워집니다.
otool -hv <app-binary> | grep PIE   # It should include the PIE flag
  • Stack Canaries: 스택 무결성을 검증하기 위해 함수 호출 전에 ‘canary’ 값을 스택에 두고 함수 종료 시 다시 검증합니다.
otool -I -v <app-binary> | grep stack_chk   # It should include the symbols: stack_chk_guard and stack_chk_fail
  • ARC (Automatic Reference Counting): 일반적인 메모리 손상 취약점을 방지하기 위해 사용됩니다.
otool -I -v <app-binary> | grep objc_release   # It should include the _objc_release symbol
  • Encrypted Binary: 바이너리는 암호화되어 있어야 합니다.
otool -arch all -Vl <app-binary> | grep -A5 LC_ENCRYPT   # The cryptid should be 1

민감하거나 안전하지 않은 함수 식별

  • Weak Hashing Algorithms
# On the iOS device
otool -Iv <app> | grep -w "_CC_MD5"
otool -Iv <app> | grep -w "_CC_SHA1"

# On linux
grep -iER "_CC_MD5"
grep -iER "_CC_SHA1"
  • Insecure Random Functions
# On the iOS device
otool -Iv <app> | grep -w "_random"
otool -Iv <app> | grep -w "_srand"
otool -Iv <app> | grep -w "_rand"

# On linux
grep -iER "_random"
grep -iER "_srand"
grep -iER "_rand"
  • Insecure ‘Malloc’ Function
# On the iOS device
otool -Iv <app> | grep -w "_malloc"

# On linux
grep -iER "_malloc"
  • Insecure and Vulnerable Functions
# On the iOS device
otool -Iv <app> | grep -w "_gets"
otool -Iv <app> | grep -w "_memcpy"
otool -Iv <app> | grep -w "_strncpy"
otool -Iv <app> | grep -w "_strlen"
otool -Iv <app> | grep -w "_vsnprintf"
otool -Iv <app> | grep -w "_sscanf"
otool -Iv <app> | grep -w "_strtok"
otool -Iv <app> | grep -w "_alloca"
otool -Iv <app> | grep -w "_sprintf"
otool -Iv <app> | grep -w "_printf"
otool -Iv <app> | grep -w "_vsprintf"

# On linux
grep -R "_gets"
grep -iER "_memcpy"
grep -iER "_strncpy"
grep -iER "_strlen"
grep -iER "_vsnprintf"
grep -iER "_sscanf"
grep -iER "_strtok"
grep -iER "_alloca"
grep -iER "_sprintf"
grep -iER "_printf"
grep -iER "_vsprintf"

일반적인 Jailbreak 감지 방법

  • 파일 시스템 검사: /Applications/Cydia.app 또는 /Library/MobileSubstrate/MobileSubstrate.dylib 같은 일반적인 jailbreak 파일 및 디렉터리의 존재를 확인합니다.
  • 샌드박스 위반: 비탈옥 기기에서는 차단되어야 하는 파일 시스템의 제한 구역에 접근을 시도합니다.
  • API 검사: fork()로 자식 프로세스를 생성하거나 system()으로 /bin/sh의 존재를 확인하는 등 금지된 호출의 사용 가능성을 확인합니다.
  • 프로세스 검사: Cydia, Substrate, ssh 등 알려진 jailbreak 관련 프로세스의 존재를 모니터링합니다.
  • 커널 익스플로잇: 일반적으로 jailbreak에 사용되는 커널 익스플로잇의 존재를 확인합니다.
  • 환경 변수: DYLD_INSERT_LIBRARIES 같은 jailbreak 징후가 있는 환경 변수를 검사합니다.
  • 로딩된 라이브러리 검사: 앱 프로세스에 로드된 라이브러리를 확인합니다.
  • 스킴 확인: canOpenURL(URL(string: "cydia://")) 같은 스킴을 확인합니다.

일반적인 Anti-Debugging 탐지 방법

  • 디버거 존재 여부 확인: sysctl 또는 다른 방법을 사용해 디버거가 연결되어 있는지 확인합니다.
  • Anti-Debugging API: ptrace 또는 SIGSTOP 같은 anti-debugging API 호출(예: ptrace(PT_DENY_ATTACH, 0, 0, 0))을 찾습니다.
  • 타이밍 검사: 특정 작업에 걸리는 시간을 측정하고 디버깅 가능성을 나타내는 불일치를 찾습니다.
  • 메모리 검사: 알려진 디버거 아티팩트나 수정사항을 찾기 위해 메모리를 검사합니다.
  • 환경 변수: 디버깅 세션을 나타낼 수 있는 환경 변수를 확인합니다.
  • Mach 포트: 디버거가 mach exception ports를 사용하는지 감지합니다.

Anti-Debugging & Anti-Tamper 기술 (계층적 검사)

실제 앱은 종종 pre-exec, on-attach 및 지속적인 검사를 계층화합니다. 찾아볼 일반적인 패턴(그리고 테스트 중 이를 무력화하는 방법):

  • Private API side-channel fingerprinting: private launch API(e.g., SBSLaunchApplicationWithIdentifierAndURLAndLaunchOptions)가 리턴 코드/로그를 기반으로 설치된 번들 ID(com.opa334.TrollStore, org.coolstar.SileoStore, com.tigisoftware.Filza 등)를 탐지하는 데 악용됩니다. 해당 호출을 후크하고 인수/반환 값을 정리하여 깨끗한 기기를 에뮬레이트합니다.
  • 코드 서명 상태를 통한 자기 증명(self-attestation): csops()CS_OPS_ENTITLEMENTS_BLOB로 권한을 읽어 예상치 못한 값이 나오면 종료합니다. 리소스의 CRC32/MD5, 인증서 검증, LC_ENCRYPTION_INFO_64 같은 Mach-O 메타데이터와 짝을 이루어 재서명 또는 패치 여부를 감지합니다. 이러한 루틴을 계측하고 분석 중에는 “예상된” 결과를 강제합니다.
  • 붙는 즉시 종료(kill-on-attach): ptrace(PT_DENY_ATTACH)abort()/exit()의 조합으로 디버깅 시 종료합니다. 종료 경로를 무력화하거나 ptrace를 후크하여 거부를 강제하지 않고 성공하도록 우회합니다.
  • 크래시 포렌식 방해: 크래시 전에 CPU 레지스터를 덮어써 백트레이스를 파괴합니다. 크래시 로그에 의존하기보다 탐지 경로 초기에 브레이크포인트/후크를 사용하는 것이 좋습니다.
  • Jetsam 기반 종료: 의도적으로 메모리 압력을 가해 jetsam을 트리거하면 정상적인 크래시 로그가 남지 않습니다. 탐지 로직 주변의 큰 할당을 찾아 제한하거나 단축시켜 로그를 유지합니다.
  • 지연 집행을 수반한 지속적 검사: 하트비트 타이머가 검사를 재실행하고 나중에 조치를 취합니다. 타이머/dispatch source를 추적하고 지연된 종료 경로를 우회하여 프로세스를 유지합니다.

기본 동적 분석

MobSF가 수행하는 동적 분석을 확인하세요. 다양한 뷰를 탐색하고 상호작용해야 하지만, 여러 클래스를 후킹하면서 여러 작업을 수행하며 작업이 완료되면 보고서를 준비합니다.

설치된 앱 나열

설치된 앱의 bundle identifier를 확인하려면 frida-ps -Uai 명령을 사용하세요:

$ frida-ps -Uai
PID  Name                 Identifier
----  -------------------  -----------------------------------------
6847  Calendar             com.apple.mobilecal
6815  Mail                 com.apple.mobilemail
-  App Store            com.apple.AppStore
-  Apple Store          com.apple.store.Jolly
-  Calculator           com.apple.calculator
-  Camera               com.apple.camera
-  iGoat-Swift          OWASP.iGoat-Swift

기본 Enumeration & Hooking

애플리케이션의 구성요소를 enumerate하는 방법과 objection으로 메소드와 클래스를 쉽게 hook하는 방법을 배우세요:

iOS Hooking With Objection

IPA 구조

IPA file의 구조는 본질적으로 zipped package의 구조와 같습니다. 확장자를 .zip로 변경하면 내용을 확인하기 위해 decompress할 수 있습니다. 이 구조 내에서 Bundle은 설치 준비가 된 완전한 패키지된 애플리케이션을 나타냅니다. 내부에는 애플리케이션의 리소스를 포함하는 <NAME>.app 디렉토리가 있습니다.

  • Info.plist: 이 파일은 애플리케이션의 특정 구성 세부 정보를 담고 있습니다.
  • _CodeSignature/: 이 디렉토리는 서명을 포함한 plist 파일을 포함하며, 번들 내 모든 파일의 무결성을 보장합니다.
  • Assets.car: 아이콘과 같은 에셋 파일을 저장하는 압축 아카이브입니다.
  • Frameworks/: 이 폴더에는 애플리케이션의 네이티브 라이브러리가 들어 있으며, .dylib 또는 .framework 파일 형태일 수 있습니다.
  • PlugIns/: 여기에는 .appex 파일로 알려진 애플리케이션의 확장 기능이 포함될 수 있으나 항상 존재하는 것은 아닙니다. * Core Data: 오프라인 사용을 위한 영속 데이터 저장, 임시 데이터 캐시, 단일 장치에서의 undo 기능 추가 등에 사용됩니다. 단일 iCloud 계정의 여러 장치 간에 데이터를 동기화하려면 Core Data가 자동으로 스키마를 CloudKit 컨테이너에 미러링합니다.
  • PkgInfo: PkgInfo 파일은 애플리케이션 또는 번들의 type 및 creator 코드를 지정하는 대체 방법입니다.
  • en.lproj, fr.proj, Base.lproj: 특정 언어에 대한 리소스를 포함하는 언어 팩이며, 지원되지 않는 언어가 있을 경우의 기본 리소스를 제공합니다.
  • 보안(Security): _CodeSignature/ 디렉토리는 디지털 서명을 통해 번들된 모든 파일의 무결성을 검증함으로써 앱 보안에서 중요한 역할을 합니다.
  • 에셋 관리(Asset Management): Assets.car 파일은 그래픽 에셋을 효율적으로 관리하기 위해 압축을 사용하여 애플리케이션 성능 최적화와 전체 크기 감소에 기여합니다.
  • Frameworks 및 PlugIns: 이 디렉토리들은 iOS 애플리케이션의 모듈성을 강조하며, 재사용 가능한 코드 라이브러리(Frameworks/)를 포함하고 앱 기능을 확장(PlugIns/)할 수 있게 합니다.
  • 현지화(Localization): 구조는 여러 언어를 지원하도록 설계되어 특정 언어 팩의 리소스를 포함함으로써 글로벌 배포를 용이하게 합니다.

Info.plist

Info.plist는 iOS 애플리케이션의 핵심으로서 key-value 쌍 형태의 주요 구성 데이터를 캡슐화합니다. 이 파일은 애플리케이션뿐만 아니라 번들된 앱 확장과 프레임워크에도 필수적입니다. XML 또는 바이너리 형식으로 구성되며, 앱 권한부터 보안 구성에 이르기까지 중요한 정보를 담고 있습니다. 사용 가능한 키에 대한 자세한 내용은 Apple Developer Documentation을 참조하세요.

이 파일을 더 다루기 쉬운 형식으로 작업하려는 경우, macOS(버전 10.2 이상에 기본 제공)의 plutil 또는 Linux의 plistutil을 사용하여 XML로 쉽게 변환할 수 있습니다. 변환 명령은 다음과 같습니다:

  • For macOS:
$ plutil -convert xml1 Info.plist
  • Linux용:
$ apt install libplist-utils
$ plistutil -i Info.plist -o Info_xml.plist

Info.plist 파일이 노출할 수 있는 수많은 정보 중 특히 눈에 띄는 항목으로는 앱 권한 문자열 (UsageDescription), 커스텀 URL 스킴 (CFBundleURLTypes), 그리고 App Transport Security 구성 (NSAppTransportSecurity)이 있습니다. 이러한 항목들은 UTExportedTypeDeclarations / UTImportedTypeDeclarations 같은 내보내기/가져오기 커스텀 문서 타입과 함께 파일을 검사하거나 간단한 grep 명령을 사용하여 쉽게 찾을 수 있습니다:

$ grep -i <keyword> Info.plist

데이터 경로

iOS 환경에서 디렉터리는 시스템 애플리케이션사용자 설치 애플리케이션에 대해 별도로 지정됩니다. 시스템 애플리케이션은 /Applications 디렉터리에 위치하고, 사용자 설치 앱은 /var/mobile/containers/Data/Application/ 아래에 배치됩니다. 이러한 애플리케이션에는 128-bit UUID로 알려진 고유 식별자가 할당되므로 디렉터리 이름이 무작위여서 앱 폴더를 수동으로 찾기 어렵습니다.

Warning

iOS에서 애플리케이션은 sandboxed 되어야 하므로, 각 앱은 앱의 **CFBundleIdentifier**를 폴더 이름으로 하는 $HOME/Library/Containers 내의 폴더도 가집니다.

그러나 두 폴더(데이터 폴더와 컨테이너 폴더) 모두에는 .com.apple.mobile_container_manager.metadata.plist 파일이 있으며, 이 파일의 MCMetadataIdentifier 키가 두 폴더를 연결합니다.)

사용자 설치 앱의 설치 디렉터리 탐색을 용이하게 하기 위해, objection toolenv라는 유용한 명령을 제공합니다. 이 명령은 해당 앱의 상세 디렉터리 정보를 보여줍니다. 아래는 이 명령을 사용하는 예시입니다:

OWASP.iGoat-Swift on (iPhone: 11.1.2) [usb] # env

Name               Path
-----------------  -------------------------------------------------------------------------------------------
BundlePath         /var/containers/Bundle/Application/3ADAF47D-A734-49FA-B274-FBCA66589E67/iGoat-Swift.app
CachesDirectory    /var/mobile/Containers/Data/Application/8C8E7EB0-BC9B-435B-8EF8-8F5560EB0693/Library/Caches
DocumentDirectory  /var/mobile/Containers/Data/Application/8C8E7EB0-BC9B-435B-8EF8-8F5560EB0693/Documents
LibraryDirectory   /var/mobile/Containers/Data/Application/8C8E7EB0-BC9B-435B-8EF8-8F5560EB0693/Library

또는 앱 이름을 /private/var/containers 내에서 find 명령을 사용하여 검색할 수 있습니다:

find /private/var/containers -name "Progname*"

pslsof와 같은 명령은 각각 앱의 프로세스를 식별하고 열린 파일을 나열하는 데 사용되어 애플리케이션의 활성 디렉터리 경로에 대한 통찰을 제공합니다:

ps -ef | grep -i <app-name>
lsof -p <pid> | grep -i "/containers" | head -n 1

번들 디렉터리:

  • AppName.app
  • 이것은 IPA에서 본 Application Bundle로, 필수 애플리케이션 데이터, 정적 콘텐츠 및 애플리케이션의 컴파일된 바이너리를 포함합니다.
  • 이 디렉터리는 사용자에게 표시되지만, 사용자는 여기에 쓸 수 없습니다.
  • 이 디렉터리의 내용은 백업되지 않습니다.
  • 이 폴더의 내용은 코드 서명 검증에 사용됩니다.

데이터 디렉터리:

  • Documents/
  • 사용자가 생성한 모든 데이터를 포함합니다. 애플리케이션 최종 사용자가 이 데이터의 생성을 시작합니다.
  • 사용자에게 표시되며 사용자가 여기에 쓸 수 있습니다.
  • 이 디렉터리의 내용은 백업됩니다.
  • 앱은 NSURLIsExcludedFromBackupKey를 설정하여 경로를 백업 대상에서 제외할 수 있습니다.
  • Library/
  • 모든 사용자별이 아닌 파일을 포함하며, 예를 들어 caches, preferences, cookies 및 property list (plist) 구성 파일이 있습니다.
  • iOS 앱은 일반적으로 Application SupportCaches 하위 디렉터리를 사용하지만, 앱은 사용자 정의 하위 디렉터리를 생성할 수 있습니다.
  • Library/Caches/
  • 반영구적인 캐시 파일을 포함합니다.
  • 사용자에게 보이지 않으며 사용자는 여기에 쓸 수 없습니다.
  • 이 디렉터리의 내용은 백업되지 않습니다.
  • 앱이 실행 중이 아니거나 저장 공간이 부족하면 OS가 이 디렉터리의 파일을 자동으로 삭제할 수 있습니다.
  • Library/Application Support/
  • 앱 실행에 필요한 영구적인 파일을 포함합니다.
  • 사용자에게 보이지 않으며 사용자는 여기에 쓸 수 없습니다.
  • 이 디렉터리의 내용은 백업 됩니다.
  • 앱은 NSURLIsExcludedFromBackupKey를 설정하여 경로를 백업 대상에서 제외할 수 있습니다.
  • Library/Preferences/
  • 애플리케이션을 재시작한 후에도 지속될 수 있는 속성을 저장하는 데 사용됩니다.
  • 정보는 암호화되지 않은 채 애플리케이션 샌드박스 내부의 [BUNDLE_ID].plist라는 plist 파일에 저장됩니다.
  • NSUserDefaults로 저장된 모든 키/값 쌍은 이 파일에서 확인할 수 있습니다.
  • tmp/
  • 앱 실행 간에 유지될 필요가 없는 임시 파일을 작성하려면 이 디렉터리를 사용하세요.
  • 비영구적인 캐시 파일을 포함합니다.
  • 사용자에게 보이지 않습니다.
  • 이 디렉터리의 내용은 백업되지 않습니다.
  • 앱이 실행 중이 아니거나 저장 공간이 부족하면 OS가 이 디렉터리의 파일을 자동으로 삭제할 수 있습니다.

Let’s take a closer look at iGoat-Swift’s Application Bundle (.app) directory inside the Bundle directory (/var/containers/Bundle/Application/3ADAF47D-A734-49FA-B274-FBCA66589E67/iGoat-Swift.app):

OWASP.iGoat-Swift on (iPhone: 11.1.2) [usb] # ls
NSFileType      Perms  NSFileProtection    ...  Name
------------  -------  ------------------  ...  --------------------------------------
Regular           420  None                ...  rutger.html
Regular           420  None                ...  mansi.html
Regular           420  None                ...  splash.html
Regular           420  None                ...  about.html

Regular           420  None                ...  LICENSE.txt
Regular           420  None                ...  Sentinel.txt
Regular           420  None                ...  README.txt

Binary Reversing

<application-name>.app 폴더 안에 <application-name>이라는 바이너리 파일이 있습니다. 이 파일이 실제로 실행되는 파일입니다. 도구 **otool**로 바이너리를 기본적으로 검사할 수 있습니다:

otool -Vh DVIA-v2 #Check some compilation attributes
magic  cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
MH_MAGIC_64    ARM64        ALL  0x00     EXECUTE    65       7112   NOUNDEFS DYLDLINK TWOLEVEL WEAK_DEFINES BINDS_TO_WEAK PIE

otool -L DVIA-v2 #Get third party libraries
DVIA-v2:
/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 400.9.1)
/usr/lib/libsqlite3.dylib (compatibility version 9.0.0, current version 274.6.0)
/usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.11)
@rpath/Bolts.framework/Bolts (compatibility version 1.0.0, current version 1.0.0)
[...]

앱이 암호화되었는지 확인

다음에 대한 출력이 있는지 확인:

otool -l <app-binary> | grep -A 4 LC_ENCRYPTION_INFO

Disassembling the binary

text 섹션을 Disassemble 하세요:

otool -tV DVIA-v2
DVIA-v2:
(__TEXT,__text) section
+[DDLog initialize]:
0000000100004ab8    sub    sp, sp, #0x60
0000000100004abc    stp    x29, x30, [sp, #0x50]   ; Latency: 6
0000000100004ac0    add    x29, sp, #0x50
0000000100004ac4    sub    x8, x29, #0x10
0000000100004ac8    mov    x9, #0x0
0000000100004acc    adrp    x10, 1098 ; 0x10044e000
0000000100004ad0    add    x10, x10, #0x268

샘플 애플리케이션의 Objective-C segment을 출력하려면 다음을 사용할 수 있습니다:

otool -oV DVIA-v2
DVIA-v2:
Contents of (__DATA,__objc_classlist) section
00000001003dd5b8 0x1004423d0 _OBJC_CLASS_$_DDLog
isa        0x1004423a8 _OBJC_METACLASS_$_DDLog
superclass 0x0 _OBJC_CLASS_$_NSObject
cache      0x0 __objc_empty_cache
vtable     0x0
data       0x1003de748
flags          0x80
instanceStart  8

더 간결한 Objective-C 코드를 얻기 위해 class-dump를 사용할 수 있습니다:

class-dump some-app
//
//     Generated by class-dump 3.5 (64 bit).
//
//     class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard.
//

#pragma mark Named Structures

struct CGPoint {
double _field1;
double _field2;
};

struct CGRect {
struct CGPoint _field1;
struct CGSize _field2;
};

struct CGSize {
double _field1;
double _field2;
};

However, the best options to disassemble the binary are: Hopper and IDA.

데이터 저장

To learn about how iOS stores data in the device read this page:

iOS Basics

Warning

다음의 정보 저장 위치는 애플리케이션을 설치 직후, 애플리케이션의 모든 기능을 검증한 후, 심지어 한 사용자에서 로그아웃한 후 다른 사용자로 로그인한 경우에도 확인해야 합니다.
목표는 애플리케이션(비밀번호, 토큰)의 보호되지 않은 민감한 정보와 현재 사용자 및 이전에 로그인했던 사용자의 정보를 찾는 것입니다.

Plist

plist 파일은 구조화된 XML 파일로 키-값 쌍을 포함합니다. 이는 영구적인 데이터를 저장하는 방법으로, 때때로 이러한 파일에서 민감한 정보를 발견할 수 있습니다. 앱을 설치한 직후와 집중적으로 사용한 후에 새 데이터가 기록되었는지 확인하는 것이 권장됩니다.

The most common way to persist data in plist files is through the usage of NSUserDefaults. This plist file is saved inside the app sandbox in Library/Preferences/<appBundleID>.plist

The NSUserDefaults class provides a programmatic interface for interacting with the default system. The default system allows an application to customize its behaviour according to user preferences. Data saved by NSUserDefaults can be viewed in the application bundle. This class stores data in a plist file, but it’s meant to be used with small amounts of data.

This data cannot be longer accessed directly via a trusted computer, but can be accessed performing a 백업.

You can 덤프 the information saved using NSUserDefaults using objection’s ios nsuserdefaults get

To find all the plist of used by the application you can access to /private/var/mobile/Containers/Data/Application/{APPID} and run:

find ./ -name "*.plist"

파일을 XML or binary (bplist) 형식에서 XML로 변환하려면 운영 체제에 따라 다양한 방법이 있습니다:

macOS 사용자: plutil 명령을 사용하세요. 이는 macOS (10.2+)에 내장된 도구로 이 목적을 위해 설계되었습니다:

$ plutil -convert xml1 Info.plist

Linux 사용자용: 먼저 libplist-utils를 설치한 후 plistutil을 사용해 파일을 변환하세요:

$ apt install libplist-utils
$ plistutil -i Info.plist -o Info_xml.plist

Within an Objection Session: 모바일 애플리케이션을 분석할 때, 특정 명령으로 plist 파일을 직접 변환할 수 있습니다:

ios plist cat /private/var/mobile/Containers/Data/Application/<Application-UUID>/Library/Preferences/com.some.package.app.plist

Core Data

Core Data은 애플리케이션의 객체 모델 계층을 관리하기 위한 프레임워크입니다. Core Data can use SQLite as its persistent store, 하지만 프레임워크 자체는 데이터베이스가 아닙니다.
CoreData는 기본적으로 데이터를 암호화하지 않습니다. 그러나 CoreData에 추가 암호화 레이어를 적용할 수 있습니다. 자세한 내용은 GitHub Repo를 참조하세요.

You can find the SQLite Core Data information of an application in the path /private/var/mobile/Containers/Data/Application/{APPID}/Library/Application Support

만약 SQLite를 열어 민감한 정보에 접근할 수 있다면, 이는 잘못된 구성(mis-configuration)을 발견한 것입니다.

-(void)storeDetails {
AppDelegate * appDelegate = (AppDelegate *)(UIApplication.sharedApplication.delegate);

NSManagedObjectContext *context =[appDelegate managedObjectContext];

User *user = [self fetchUser];
if (user) {
return;
}
user = [NSEntityDescription insertNewObjectForEntityForName:@"User"
inManagedObjectContext:context];
user.email = CoreDataEmail;
user.password = CoreDataPassword;
NSError *error;
if (![context save:&error]) {
NSLog(@"Error in saving data: %@", [error localizedDescription]);

}else{
NSLog(@"data stored in core data");
}
}

YapDatabase

YapDatabase은 SQLite 위에 구축된 key/value 스토어입니다.
Yap databases가 sqlite databases이므로 앞 섹션에서 제시한 명령어를 사용해 찾을 수 있습니다.

기타 SQLite 데이터베이스

애플리케이션이 자체 sqlite 데이터베이스를 생성하는 것은 흔합니다. 그 안에 민감한 데이터저장하고 암호화하지 않은 채 둘 수 있습니다. 따라서 애플리케이션 디렉터리 내의 모든 데이터베이스를 확인하는 것이 항상 중요합니다. 따라서 데이터가 저장된 애플리케이션 디렉터리로 이동하세요 (/private/var/mobile/Containers/Data/Application/{APPID})

find ./ -name "*.sqlite" -or -name "*.db"

Firebase Real-Time Databases

개발자는 Firebase Real-Time Databases를 통해 데이터를 저장하고 동기화할 수 있으며, 이는 NoSQL 클라우드 호스팅 데이터베이스 내에서 이루어집니다. 데이터는 JSON 형식으로 저장되며, 연결된 모든 클라이언트에 실시간으로 동기화됩니다.

You can find how to check for misconfigured Firebase databases here:

Firebase Database

Realm 데이터베이스

Realm Objective-C and Realm Swift는 Apple에서 제공하지 않는 강력한 데이터 저장 대안을 제공합니다. 기본적으로 데이터를 암호화하지 않고 저장하며, 특정 구성으로 암호화를 사용할 수 있습니다.

The databases are located at: /private/var/mobile/Containers/Data/Application/{APPID}. To explore these files, one can utilize commands like:

iPhone:/private/var/mobile/Containers/Data/Application/A079DF84-726C-4AEA-A194-805B97B3684A/Documents root# ls
default.realm  default.realm.lock  default.realm.management/  default.realm.note|

$ find ./ -name "*.realm*"

이러한 데이터베이스 파일을 보려면 Realm Studio 도구를 권장합니다.

Realm 데이터베이스에서 암호화를 구현하려면, 다음 코드 스니펫을 사용할 수 있습니다:

// Open the encrypted Realm file where getKey() is a method to obtain a key from the Keychain or a server
let config = Realm.Configuration(encryptionKey: getKey())
do {
let realm = try Realm(configuration: config)
// Use the Realm as normal
} catch let error as NSError {
// If the encryption key is wrong, `error` will say that it's an invalid database
fatalError("Error opening realm: \(error)")
}

Couchbase Lite 데이터베이스

Couchbase Lite경량임베디드 데이터베이스 엔진으로 문서 지향(NoSQL) 방식을 따릅니다. iOSmacOS에 네이티브로 설계되어 데이터를 원활하게 동기화할 수 있습니다.

디바이스에서 잠재적인 Couchbase 데이터베이스를 식별하려면 다음 디렉터리를 확인해야 합니다:

ls /private/var/mobile/Containers/Data/Application/{APPID}/Library/Application Support/

쿠키

iOS는 앱의 쿠키를 각 앱 폴더 내의 **Library/Cookies/cookies.binarycookies**에 저장합니다. 그러나 개발자들은 때때로 해당 cookie 파일은 백업에서 접근 가능하기 때문에 이를 keychain에 저장하기로 결정하기도 합니다.

쿠키 파일을 검사하려면 this python script 또는 objection의 ios cookies get.
objection을 사용하면 이 파일들을 JSON 형식으로 변환하여 데이터를 검사할 수도 있습니다.

...itudehacks.DVIAswiftv2.develop on (iPhone: 13.2.3) [usb] # ios cookies get --json
[
{
"domain": "highaltitudehacks.com",
"expiresDate": "2051-09-15 07:46:43 +0000",
"isHTTPOnly": "false",
"isSecure": "false",
"name": "username",
"path": "/",
"value": "admin123",
"version": "0"
}
]

Cache

기본적으로 NSURLSession은 HTTP requests and responses in the Cache.db 데이터와 같은 정보를 Cache.db 데이터베이스에 저장합니다. 이 데이터베이스는 토큰, 사용자명 또는 기타 민감한 정보가 캐시되어 있는 경우 민감한 데이터를 포함할 수 있습니다. 캐시된 정보를 찾으려면 앱의 데이터 디렉터리 (/var/mobile/Containers/Data/Application/<UUID>)를 열고 /Library/Caches/<Bundle Identifier>로 이동하세요. 또한 WebKit cache is also being stored in the Cache.db 파일에도 저장됩니다. Objectionsqlite connect Cache.db 명령으로 데이터베이스를 열고 상호작용할 수 있습니다, 이는 n정상적인 SQLite 데이터베이스이기 때문입니다.

요청 또는 응답에 민감한 정보가 포함될 수 있으므로, 이 데이터를 캐싱하지 않도록 권장됩니다. 아래 목록은 이를 달성하는 다양한 방법을 보여줍니다:

  1. 로그아웃 후 캐시된 응답을 제거하는 것이 권장됩니다. 이는 Apple이 제공하는 removeAllCachedResponses 메서드로 수행할 수 있습니다. 다음과 같이 호출할 수 있습니다:

URLCache.shared.removeAllCachedResponses()

이 메서드는 Cache.db 파일에 있는 모든 캐시된 요청과 응답을 제거합니다.

  1. cookies의 이점을 사용할 필요가 없다면 URLSession의 .ephemeral 구성 속성을 사용하는 것이 권장됩니다. 이는 쿠키와 캐시 저장을 비활성화합니다.

Apple documentation:

An ephemeral session configuration object is similar to a default session configuration (see default), except that the corresponding session object doesn’t store caches, credential stores, or any session-related data to disk. Instead, session-related data is stored in RAM. The only time an ephemeral session writes data to disk is when you tell it to write the contents of a URL to a file.

  1. Cache는 Cache Policy를 .notAllowed로 설정하여 비활성화할 수도 있습니다. 이렇게 하면 메모리나 디스크 등 어떤 방식으로도 캐시 저장이 비활성화됩니다.

Snapshots

홈 버튼을 누를 때마다 iOS는 애플리케이션 전환을 더 부드럽게 하기 위해 현재 화면의 스냅샷을 찍습니다. 그러나 현재 화면에 민감한 데이터가 존재하면, 이는 이미지저장되며(이는 재부팅넘어 지속됩니다). 이 스냅샷들은 홈 화면을 더블탭하여 앱 간 전환 시 접근할 수 있는 스냅샷들입니다.

iPhone이 탈옥되지 않은 경우, 이러한 스크린샷을 보기 위해서는 attackeraccess to the device unblocked 상태여야 합니다. 기본적으로 마지막 스냅샷은 애플리케이션 샌드박스의 Library/Caches/Snapshots/ 또는 Library/SplashBoard/Snapshots 폴더에 저장됩니다 (the trusted computers can’ t access the filesystem from iOX 7.0).

이러한 동작을 방지하는 한 가지 방법은 ApplicationDidEnterBackground() 함수에서 스냅샷을 찍기 전에 빈 화면을 표시하거나 민감한 데이터를 제거하는 것입니다.

다음은 기본 스크린샷을 설정하는 샘플 수정 방법입니다.

Swift:

private var backgroundImage: UIImageView?

func applicationDidEnterBackground(_ application: UIApplication) {
let myBanner = UIImageView(image: #imageLiteral(resourceName: "overlayImage"))
myBanner.frame = UIScreen.main.bounds
backgroundImage = myBanner
window?.addSubview(myBanner)
}

func applicationWillEnterForeground(_ application: UIApplication) {
backgroundImage?.removeFromSuperview()
}

Objective-C:

@property (UIImageView *)backgroundImage;

- (void)applicationDidEnterBackground:(UIApplication *)application {
UIImageView *myBanner = [[UIImageView alloc] initWithImage:@"overlayImage.png"];
self.backgroundImage = myBanner;
self.backgroundImage.bounds = UIScreen.mainScreen.bounds;
[self.window addSubview:myBanner];
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
[self.backgroundImage removeFromSuperview];
}

애플리케이션이 백그라운드로 전환될 때마다 overlayImage.png를 배경 이미지로 설정합니다. 이는 overlayImage.png가 항상 현재 뷰를 덮어쓰기 때문에 민감한 데이터 leaks를 방지합니다.

Keychain

iOS keychain에 접근하고 관리하기 위해 탈옥된 디바이스에 적합한 Keychain-Dumper와 같은 도구를 사용할 수 있습니다. 또한 Objection는 유사한 목적을 위해 ios keychain dump 명령을 제공합니다.

자격 증명 저장

NSURLCredential 클래스는 NSUserDefaults나 다른 래퍼를 우회하여 민감한 정보를 keychain에 직접 저장하는 데 이상적입니다. 로그인 후 자격 증명을 저장하려면 다음 Swift 코드를 사용합니다:

NSURLCredential *credential;
credential = [NSURLCredential credentialWithUser:username password:password persistence:NSURLCredentialPersistencePermanent];
[[NSURLCredentialStorage sharedCredentialStorage] setCredential:credential forProtectionSpace:self.loginProtectionSpace];

이 저장된 자격 증명을 추출하기 위해 Objection의 명령 ios nsurlcredentialstorage dump이 사용된다.

사용자 지정 키보드 및 키보드 캐시

iOS 8.0 이후, 사용자는 사용자 지정 키보드 확장을 설치할 수 있으며 이는 설정 > 일반 > 키보드 > 키보드에서 관리할 수 있다. 이러한 키보드는 기능을 확장하지만 키 입력 로그 수집과 외부 서버로 데이터 전송의 위험을 수반하며, 네트워크 접근이 필요한 키보드에 대해 사용자에게 알림이 제공된다. 앱은 민감한 정보 입력에 대해 사용자 지정 키보드의 사용을 제한할 수 있고 제한해야 한다.

보안 권장 사항:

  • 보안을 강화하기 위해 타사 키보드를 비활성화하는 것이 권장된다.
  • 기본 iOS 키보드의 자동수정 및 자동 제안 기능은 Library/Keyboard/{locale}-dynamic-text.dat 또는 /private/var/mobile/Library/Keyboard/dynamic-text.dat에 민감한 정보를 저장할 수 있다는 점을 유의하라. 이러한 캐시 파일은 정기적으로 민감한 데이터가 있는지 확인해야 한다. 캐시된 데이터를 지우려면 설정 > 일반 > 재설정 > 키보드 사전 재설정을 통해 키보드 사전을 재설정하는 것이 권장된다.
  • 네트워크 트래픽을 가로채면 사용자 지정 키보드가 원격으로 키 입력을 전송하는지 확인할 수 있다.

텍스트 필드 캐싱 방지

The UITextInputTraits protocol은 자동수정과 보안 텍스트 입력을 관리하는 속성을 제공하며, 이는 민감한 정보의 캐싱을 방지하는 데 필수적이다. 예를 들어 자동수정을 비활성화하고 보안 텍스트 입력을 활성화하려면 다음을 사용할 수 있다:

textObject.autocorrectionType = UITextAutocorrectionTypeNo;
textObject.secureTextEntry = YES;

또한 개발자는 텍스트 필드, 특히 비밀번호 및 PIN과 같은 민감한 정보를 입력하는 필드에서 캐싱을 비활성화하도록 해야 합니다. 이를 위해 autocorrectionTypeUITextAutocorrectionTypeNo로, secureTextEntryYES로 설정하십시오.

UITextField *textField = [[UITextField alloc] initWithFrame:frame];
textField.autocorrectionType = UITextAutocorrectionTypeNo;

Logs

코드 디버깅은 종종 logging을 사용합니다. 이는 logs may contain sensitive information 같은 위험을 동반합니다. 이전에는 iOS 6 및 그 이전 버전에서 logs가 모든 앱에서 접근 가능하여 sensitive data leakage의 위험이 있었습니다. 이제 애플리케이션은 자신들의 logs만 접근할 수 있도록 제한됩니다.

이러한 제한에도 불구하고, 잠금이 해제된 기기에 물리적으로 접근할 수 있는 attacker with physical access는 기기를 컴퓨터에 연결하여 reading the logs로 이를 악용할 수 있습니다. 또한 앱을 삭제한 후에도 logs가 디스크에 남아 있다는 점을 유의해야 합니다.

위험을 완화하기 위해서는 앱의 모든 기능과 입력을 철저히 사용해보고, 우발적으로 민감한 정보가 로그로 남지 않는지 확인하는 것이 권장됩니다.

앱 소스 코드를 검토할 때는 predefined 및 custom logging 문장을 모두 찾아보세요. 내장 함수 키워드인 NSLog, NSAssert, NSCAssert, fprintf와 custom 구현에서의 Logging 또는 Logfile 언급을 확인하십시오.

Monitoring System Logs

앱은 민감할 수 있는 다양한 정보를 logs에 남깁니다. 이러한 logs를 모니터링하기 위해 다음과 같은 도구와 명령을 사용할 수 있습니다:

idevice_id --list   # To find the device ID
idevicesyslog -u <id> (| grep <app>)   # To capture the device logs

유용합니다. 또한, Xcode는 콘솔 로그를 수집할 수 있는 방법을 제공합니다:

  1. Xcode를 엽니다.
  2. iOS 기기를 연결합니다.
  3. Window -> Devices and Simulators로 이동합니다.
  4. 기기를 선택합니다.
  5. 조사 중인 문제를 재현합니다.
  6. Open Console 버튼을 사용해 새 창에서 로그를 확인합니다.

보다 고급 로깅을 위해 기기 셸에 연결하여 socat을 사용하면 실시간 로그 모니터링이 가능합니다:

iPhone:~ root# socat - UNIX-CONNECT:/var/run/lockdown/syslog.sock

로그 활동을 관찰하는 명령들이 뒤따르며, 이는 문제 진단이나 로그에서 잠재적 data leakage를 식별하는 데 매우 유용할 수 있습니다.

백업

자동 백업 기능은 iOS에 통합되어 있으며, iTunes(최대 macOS Catalina), Finder(macOS Catalina 이후), 또는 iCloud를 통해 기기 데이터 복사본을 생성할 수 있게 합니다. 이러한 백업은 Apple Pay 세부정보나 Touch ID 구성과 같은 매우 민감한 요소를 제외한 거의 모든 기기 데이터를 포함합니다.

보안 위험

백업에 설치된 앱 및 해당 데이터가 포함되면 잠재적 data leakage 문제와 백업 수정이 앱 기능을 변경할 수 있음이라는 위험이 발생합니다. 이러한 위험을 완화하기 위해 모든 앱의 디렉터리 또는 하위 디렉터리에 민감한 정보를 plaintext로 저장하지 않는 것을 권장합니다.

백업에서 파일 제외하기

Documents/Library/Application Support/의 파일은 기본적으로 백업됩니다. 개발자는 NSURL setResourceValue:forKey:error:NSURLIsExcludedFromBackupKey를 사용하여 특정 파일이나 디렉터리를 백업에서 제외할 수 있습니다. 이러한 관행은 민감한 데이터가 백업에 포함되는 것을 방지하는 데 중요합니다.

취약성 테스트

앱의 백업 보안을 평가하려면, 먼저 Finder를 사용하여 백업을 생성한 다음 Apple’s official documentation의 지침을 따라 위치를 찾으십시오. 백업을 분석하여 앱 동작에 영향을 줄 수 있도록 변경할 수 있는 민감한 데이터나 구성 요소를 찾아보십시오.

민감한 정보는 명령줄 도구나 iMazing과 같은 애플리케이션을 사용해 찾아낼 수 있습니다. 암호화된 백업의 경우, 백업 루트의 “Manifest.plist” 파일에서 “IsEncrypted” 키를 확인하여 암호화 여부를 확인할 수 있습니다.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
...
<key>Date</key>
<date>2021-03-12T17:43:33Z</date>
<key>IsEncrypted</key>
<true/>
...
</plist>

암호화된 백업을 다룰 때, DinoSec’s GitHub repo에 있는 Python 스크립트(예: backup_tool.py, backup_passwd.py)가 유용할 수 있으나 최신 iTunes/Finder 버전과의 호환성을 위해 수정이 필요할 수 있습니다. 암호로 보호된 백업 내 파일에 접근하기 위한 다른 옵션으로는 iOSbackup tool이 있습니다.

앱 동작 수정

백업 수정을 통해 앱 동작을 변경하는 예는 Bither bitcoin wallet app에서 볼 수 있습니다. 여기서 UI 잠금 PIN은 net.bither.plistpin_code 키에 저장되어 있습니다. plist에서 이 키를 삭제하고 백업을 복원하면 PIN 요구가 사라져 무제한 접근이 가능합니다.

민감한 데이터에 대한 메모리 테스트 요약

애플리케이션 메모리에 저장된 민감한 정보를 다룰 때, 이 데이터의 노출 시간을 최소화하는 것이 중요합니다. 메모리 내용을 조사하는 주요 방법은 두 가지로, 메모리 덤프 생성실시간 메모리 분석입니다. 두 방법 모두 덤프 과정이나 분석 중에 중요한 데이터를 놓칠 가능성 등 어려움이 있습니다.

메모리 덤프 획득 및 분석

탈옥된 기기와 비탈옥 기기 모두에서 objectionFridump 같은 도구는 앱 프로세스 메모리를 덤프할 수 있게 해줍니다. 덤프한 후에는 찾고자 하는 정보의 성격에 따라 다양한 도구로 이 데이터를 분석해야 합니다.

메모리 덤프에서 문자열을 추출하려면 strings 또는 rabin2 -zz 같은 명령을 사용할 수 있습니다:

# Extracting strings using strings command
$ strings memory > strings.txt

# Extracting strings using rabin2
$ rabin2 -ZZ memory > strings.txt

특정 데이터 유형이나 패턴을 검색하는 등 보다 상세한 분석을 위해, radare2는 광범위한 검색 기능을 제공합니다:

$ r2 <name_of_your_dump_file>
[0x00000000]> /?
...

런타임 메모리 분석

r2frida는 memory dump 없이도 앱의 메모리를 실시간으로 검사할 수 있는 강력한 대안을 제공합니다. 이 도구를 사용하면 실행 중인 애플리케이션의 메모리에서 직접 검색 명령을 실행할 수 있습니다:

$ r2 frida://usb//<name_of_your_app>
[0x00000000]> /\ <search_command>

취약한 암호화

부적절한 키 관리 프로세스

일부 개발자는 민감한 데이터를 로컬 저장소(local storage)에 저장하고 코드에 hardcoded/predictable in the code 된 키로 암호화합니다. 이렇게 해서는 안 됩니다. 리버싱을 통해 공격자가 기밀 정보를 추출할 수 있기 때문입니다.

안전하지 않거나 더 이상 사용되지 않는 알고리즘의 사용

개발자는 데이터를 authorisation checks, store 또는 send 하기 위해 deprecated algorithms을(를) 사용해서는 안 됩니다. 이런 알고리즘으로는 RC4, MD4, MD5, SHA1 등이 있습니다. 예를 들어 비밀번호를 저장하기 위해 hashes를 사용하는 경우, salt와 함께 brute-force에 resistanthashes를 사용해야 합니다.

체크

수행해야 할 주요 체크는 코드 내에서 hardcoded 비밀번호/시크릿이 있는지, 또는 그것들이 predictable한지, 그리고 코드가 어떤 종류의 weak cryptography 알고리즘을 사용하고 있는지를 확인하는 것입니다.

다음과 같이 objection을 사용하여 일부 crypto libraries를 자동으로 monitor할 수 있다는 점도 흥미롭습니다:

ios monitor crypt

iOS 암호화 API 및 라이브러리에 대한 자세한 정보https://mobile-security.gitbook.io/mobile-security-testing-guide/ios-testing-guide/0x06e-testing-cryptography 를 참고하세요.

로컬 인증

로컬 인증은 특히 암호화 방법을 통해 원격 엔드포인트의 접근을 보호할 때 중요한 역할을 합니다. 핵심은 적절히 구현되지 않으면 로컬 인증 메커니즘이 우회될 수 있다는 점입니다.

Apple의 Local Authentication frameworkkeychain 은 각각 사용자 인증 대화상자를 제공하고 비밀 데이터를 안전하게 처리할 수 있는 강력한 API를 제공합니다. Secure Enclave는 Touch ID의 지문을 보호하며, Face ID는 생체 데이터를 손상시키지 않는 얼굴 인식에 의존합니다.

Touch ID/Face ID를 통합하기 위해 개발자는 두 가지 API 선택권을 갖습니다:

  • LocalAuthentication.framework: 생체 데이터에 접근하지 않는 고수준의 사용자 인증을 위한 것.
  • Security.framework: 키체인 서비스에 대한 저수준 접근을 제공하며, 생체 인증으로 비밀 데이터를 보호하는 데 사용. 다양한 open-source wrappers 가 키체인 접근을 단순화합니다.

Caution

그러나 LocalAuthentication.frameworkSecurity.framework 둘 다 취약점이 존재합니다. 이들 API는 주로 인증 과정에서 데이터를 전송하지 않고 boolean 값만 반환하므로 우회될 가능성이 있습니다 (참고: Don’t touch me that way, by David Lindner et al).

로컬 인증 구현

사용자에게 인증을 요청하려면 개발자는 LAContext 클래스의 evaluatePolicy 메서드를 사용해야 하며, 다음 옵션 중 하나를 선택합니다:

  • deviceOwnerAuthentication: Touch ID 또는 디바이스 패스코드를 요청하며 둘 다 활성화되어 있지 않으면 실패합니다.
  • deviceOwnerAuthenticationWithBiometrics: 오직 Touch ID만 요청합니다.

성공적인 인증은 **evaluatePolicy**의 boolean 반환값으로 표시되며, 이는 잠재적인 보안 결함을 드러냅니다.

키체인을 이용한 로컬 인증

iOS 앱에서 로컬 인증을 구현하려면 키체인 API를 사용하여 인증 토큰과 같은 비밀 데이터를 안전하게 저장해야 합니다. 이 과정은 데이터가 디바이스 패스코드나 Touch ID 같은 생체 인증을 통해서만 사용자에게 접근 가능하도록 보장합니다.

키체인은 SecAccessControl 속성을 사용하여 항목을 설정할 수 있으며, 이 속성은 사용자가 Touch ID 또는 디바이스 패스코드로 성공적으로 인증할 때까지 항목에 대한 접근을 제한합니다. 이 기능은 보안을 강화하는 데 중요합니다.

아래에는 Swift와 Objective-C의 코드 예제가 있으며, 이러한 보안 기능을 활용해 문자열을 키체인에 저장하고 가져오는 방법을 보여줍니다. 예제는 특히 Touch ID 인증을 요구하도록 접근 제어를 설정하고, 디바이스 패스코드가 설정된 경우에만 해당 데이터를 생성된 동일한 디바이스에서만 접근 가능하도록 하는 방법을 보여줍니다.

// From https://github.com/mufambisi/owasp-mstg/blob/master/Document/0x06f-Testing-Local-Authentication.md

// 1. create AccessControl object that will represent authentication settings

var error: Unmanaged<CFError>?

guard let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault,
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
SecAccessControlCreateFlags.biometryCurrentSet,
&error) else {
// failed to create AccessControl object

return
}

// 2. define keychain services query. Pay attention that kSecAttrAccessControl is mutually exclusive with kSecAttrAccessible attribute

var query: [String: Any] = [:]

query[kSecClass as String] = kSecClassGenericPassword
query[kSecAttrLabel as String] = "com.me.myapp.password" as CFString
query[kSecAttrAccount as String] = "OWASP Account" as CFString
query[kSecValueData as String] = "test_strong_password".data(using: .utf8)! as CFData
query[kSecAttrAccessControl as String] = accessControl

// 3. save item

let status = SecItemAdd(query as CFDictionary, nil)

if status == noErr {
// successfully saved
} else {
// error while saving
}

이제 keychain에서 저장된 항목을 요청할 수 있습니다. Keychain services는 사용자에게 인증 대화상자를 표시하고, 적절한 지문이 제공되었는지 여부에 따라 data 또는 nil을 반환합니다.

// 1. define query
var query = [String: Any]()
query[kSecClass as String] = kSecClassGenericPassword
query[kSecReturnData as String] = kCFBooleanTrue
query[kSecAttrAccount as String] = "My Name" as CFString
query[kSecAttrLabel as String] = "com.me.myapp.password" as CFString
query[kSecUseOperationPrompt as String] = "Please, pass authorisation to enter this area" as CFString

// 2. get item
var queryResult: AnyObject?
let status = withUnsafeMutablePointer(to: &queryResult) {
SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0))
}

if status == noErr {
let password = String(data: queryResult as! Data, encoding: .utf8)!
// successfully received password
} else {
// authorization not passed
}

탐지

앱에서 프레임워크 사용 여부는 앱 바이너리의 공유 동적 라이브러리 목록을 분석하여 확인할 수 있습니다. 이는 otool을 사용하여 수행할 수 있습니다:

$ otool -L <AppName>.app/<AppName>

앱에서 LocalAuthentication.framework가 사용되는 경우, 출력에는 다음 두 줄이 모두 포함됩니다(참고: LocalAuthentication.framework는 내부적으로 Security.framework를 사용합니다):

/System/Library/Frameworks/LocalAuthentication.framework/LocalAuthentication
/System/Library/Frameworks/Security.framework/Security

If Security.framework is used, only the second one will be shown.

Local Authentication Framework Bypass

Objection

Objection Biometrics Bypass를 통해, this GitHub page에 위치한 이 기법은 LocalAuthentication 메커니즘을 우회할 수 있습니다. 이 접근법의 핵심은 Frida를 활용해 evaluatePolicy 함수를 조작하여 실제 인증 성공 여부와 관계없이 항상 True를 반환하도록 하는 것입니다. 이는 특히 결함이 있는 생체 인식 인증 과정을 회피할 때 유용합니다.

이 bypass를 활성화하려면 다음 명령을 사용합니다:

...itudehacks.DVIAswiftv2.develop on (iPhone: 13.2.3) [usb] # ios ui biometrics_bypass
(agent) Registering job 3mhtws9x47q. Type: ios-biometrics-disable
...itudehacks.DVIAswiftv2.develop on (iPhone: 13.2.3) [usb] # (agent) [3mhtws9x47q] Localized Reason for auth requirement: Please authenticate yourself
(agent) [3mhtws9x47q] OS authentication response: false
(agent) [3mhtws9x47q] Marking OS response as True instead
(agent) [3mhtws9x47q] Biometrics bypass hook complete

이 명령은 Objection이 작업을 등록하여 evaluatePolicy 체크의 결과를 사실상 True로 변경하는 일련의 동작을 시작합니다.

Frida

다음은 DVIA-v2 application에서 **evaluatePolicy**의 사용 예시입니다:

+(void)authenticateWithTouchID {
LAContext *myContext = [[LAContext alloc] init];
NSError *authError = nil;
NSString *myLocalizedReasonString = @"Please authenticate yourself";

if ([myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&authError]) {
[myContext evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
localizedReason:myLocalizedReasonString
reply:^(BOOL success, NSError *error) {
if (success) {
dispatch_async(dispatch_get_main_queue(), ^{
[TouchIDAuthentication showAlert:@"Authentication Successful" withTitle:@"Success"];
});
} else {
dispatch_async(dispatch_get_main_queue(), ^{
[TouchIDAuthentication showAlert:@"Authentication Failed !" withTitle:@"Error"];
});
}
}];
} else {
dispatch_async(dispatch_get_main_queue(), ^{
[TouchIDAuthentication showAlert:@"Your device doesn't support Touch ID or you haven't configured Touch ID authentication on your device" withTitle:@"Error"];
});
}
}

Local Authentication의 bypass를 달성하기 위해 Frida 스크립트를 작성합니다. 이 스크립트는 evaluatePolicy 체크를 목표로 하여 콜백을 가로채 해당 콜백이 success=1을 반환하도록 합니다. 콜백의 동작을 변경함으로써 인증 검사는 사실상 우회됩니다.

아래 스크립트는 evaluatePolicy 메서드의 결과를 수정하도록 주입됩니다. 콜백의 결과를 항상 성공으로 표시하도록 변경합니다.

// from https://securitycafe.ro/2022/09/05/mobile-pentesting-101-bypassing-biometric-authentication/
if(ObjC.available) {
console.log("Injecting...");
var hook = ObjC.classes.LAContext["- evaluatePolicy:localizedReason:reply:"];
Interceptor.attach(hook.implementation, {
onEnter: function(args) {
var block = new ObjC.Block(args[4]);
const callback = block.implementation;
block.implementation = function (error, value)  {

console.log("Changing the result value to true")
const result = callback(1, null);
return result;
};
},
});
} else {
console.log("Objective-C Runtime is not available!");
}

Frida 스크립트를 inject하고 생체 인증을 bypass하기 위해 다음 명령이 사용됩니다:

frida -U -f com.highaltitudehacks.DVIAswiftv2 --no-pause -l fingerprint-bypass-ios.js

Sensitive Functionality Exposure Through IPC

iOS Custom URI Handlers / Deeplinks / Custom Schemes

iOS Universal Links

UIActivity Sharing

iOS UIActivity Sharing

UIPasteboard

iOS UIPasteboard

App Extensions

iOS App Extensions

WebViews

iOS WebViews

Serialisation and Encoding

iOS Serialisation and Encoding

Network Communication

통신이 암호화되지 않은 상태로 이루어지고 있지 않은지 그리고 애플리케이션이 서버의 TLS certificate를 올바르게 검증하고 있는지 확인하는 것이 중요합니다.
이러한 문제를 확인하기 위해 Burp 같은 프록시를 사용할 수 있습니다:

iOS Burp Suite Configuration

Hostname check

TLS 인증서를 검증할 때 흔한 문제 중 하나는 인증서가 trusted CA에 의해 서명되었는지를 확인하지만, 인증서의 hostname이 접근하려는 호스트명과 일치하는지를 확인하지 않는 경우입니다.
이 문제를 Burp로 확인하려면 iPhone에서 Burp CA를 신뢰한 후, Burp로 다른 hostname에 대한 새 인증서를 생성하여 사용하면 됩니다. 애플리케이션이 여전히 동작하면 취약한 것입니다.

Certificate Pinning

애플리케이션이 SSL Pinning을 올바르게 사용하고 있다면, 애플리케이션은 예상된 인증서일 때만 동작합니다. 테스트 시에는 Burp가 자체 인증서를 제공하기 때문에 문제가 될 수 있습니다.
jailbroken 기기에서는 이 보호를 우회하기 위해 애플리케이션 SSL Kill Switch 또는 Burp Mobile Assistant를 설치할 수 있습니다.

또한 objection’s ios sslpinning disable을 사용할 수도 있습니다

Misc

  • **/System/Library**에는 시스템 애플리케이션에서 사용하는 프레임워크가 설치되어 있습니다
  • App Store에서 사용자가 설치한 애플리케이션은 **/User/Applications**에 위치합니다
  • 그리고 **/User/Library**는 사용자 레벨 애플리케이션이 저장한 데이터를 포함합니다
  • 애플리케이션 내에 저장된 노트를 읽으려면 **/User/Library/Notes/notes.sqlite**에 접근할 수 있습니다.
  • 설치된 애플리케이션의 폴더(/User/Applications/<APP ID>/) 안에는 다음과 같은 흥미로운 파일들이 있습니다:
  • iTunesArtwork: 앱에서 사용하는 아이콘
  • iTunesMetadata.plist: App Store에 사용된 앱 정보
  • /Library/*: 설정(preferences) 및 캐시를 포함합니다. **/Library/Cache/Snapshots/***에는 애플리케이션을 백그라운드로 보내기 전에 수행된 스냅샷을 찾을 수 있습니다.

Hot Patching/Enforced Updateing

개발자는 애플리케이션을 다시 App Store에 제출하고 승인될 때까지 기다리지 않고도 원격으로 앱의 모든 설치본을 즉시 패치할 수 있습니다.
이를 위해 일반적으로 JSPatch가 사용됩니다. 하지만 Siren이나 react-native-appstore-version-checker와 같은 다른 옵션도 있습니다.
이 메커니즘은 악의적인 서드파티 SDK에 의해 악용될 수 있으므로, 자동 업데이트에 어떤 방법이 사용되는지(있다면) 확인하고 테스트하는 것이 권장됩니다. 이를 위해 앱의 이전 버전을 다운로드해 시도해볼 수 있습니다.

Third Parties

서드파티 SDK(3rd party SDKs)의 큰 문제는 기능에 대한 세분화된 제어 부족입니다. 개발자는 SDK를 통합하여 잠재적 보안 취약점과 개인정보 문제를 포함한 모든 기능을 받아들이거나, 그 혜택을 완전히 포기하는 선택을 해야 합니다. 종종 개발자는 이러한 SDK 내부의 취약점을 스스로 패치할 수 없습니다. 더구나 SDK가 커뮤니티에서 신뢰를 얻으면 일부는 악성코드를 포함하게 될 수도 있습니다.

서드파티 SDK가 제공하는 서비스에는 사용자 행동 추적, 광고 표시, 사용자 경험 개선 등이 포함될 수 있습니다. 하지만 개발자는 이러한 라이브러리에서 실행되는 코드에 대해 완전히 알지 못할 수 있어 개인정보 및 보안 위험이 발생합니다. 서드파티 서비스와 공유하는 정보는 필요한 것에 한정하고 민감한 데이터가 노출되지 않도록 하는 것이 중요합니다.

서드파티 서비스의 구현은 보통 독립 라이브러리나 전체 SDK 두 가지 형태로 제공됩니다. 사용자 프라이버시를 보호하기 위해 이러한 서비스와 공유되는 데이터는 PII(Personally Identifiable Information)의 노출을 방지하기 위해 anonymized되어야 합니다.

애플리케이션이 사용하는 라이브러리를 식별하려면 otool 명령을 사용할 수 있습니다. 이 도구는 애플리케이션과 그 애플리케이션이 사용하는 각 공유 라이브러리에 대해 실행되어 추가 라이브러리를 발견해야 합니다.

otool -L <application_path>

흥미로운 취약점 및 사례 연구

Air Keyboard Remote Input Injection

Itunesstored Bookassetd Sandbox Escape

Zero Click Messaging Image Parser Chains

참고자료 및 추가 리소스

Tip

AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE) Azure 해킹 배우기 및 연습하기: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기