iOS 이해

진단 환경 구축

SSH

# Download
scp root@192.168.1.10:~/somefile.txt ./
scp -r root@192.168.1.10:~/somedir ./

# Upload
scp somefile.txt root@192.168.1.10:~/
scp -r somedir root@192.168.1.10:~/

SSH over USB

와이파이 환경에서 SSH로 접근 불가능할때 차선

in Windows

iproxy 설치 releases 들어가서 win-x64 다운 libimobiledevice.1.2.1-r1122-win-x64\iproxy.exe 2222 22 실행

in Linux
apt-get install libimobiledevice*
apt-get install libgcrypt20-doc gnutls-doc gnutls-bin usbmuxd

git clone https://github.com/rcg4u/iphonessh
cd iphonessh/python-client
chmod +x *

python tcprelay.py -t 22:2222

ssh -p 2222 root@localhost

Passwordless SSH

ssh-keygen -t rsa
scp id_rsa.pub root@IPHONIP:~/
ssh root@IPHONEIP
mkdir .ssh
cat id_rsa.pub >> .ssh/authorized_keys
chmod 644 .ssh/authorized_keys
chmod 700 .ssh

# Edit /etc/ssh/sshd_config
vi /etc/ssh/sshd_config

// - uncomment this - // 
#RSAAuthentication yes
#PubkeyAuthentication yes
#AuthorizedKeysFile .ssh/authorized_keys

// -       to       - // 
RSAAuthentication yes
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys

# Restart sshd
launchctl stop com.openssh.sshd
launchctl start com.openssh.sshd

Tweak

Proxy

  1. 같은 wifi 접속
  2. wifi ! 눌러서 아래 프록시 구성 누르기
  3. 진단 pc 아이피와 프록시 잡을 포트 넣기
  4. 진단pc아이피:포트로 접속
  5. 인증서 다운받기
  6. 설정 - 일반 - 프로파일 - 포트스위거 인증서 설치
  7. 설정 - 일반 - 정보 - 인증서 신뢰 설정 - 신뢰하기

Frida

  1. Frida에서 frida_16.3.0_iphoneos-arm64.deb 다운
  2. /var/root에 해당 파일 넣기
  3. dpkg -i frida_16.3.0_iphoneos-arm64.deb 로 설치
  4. 이제 알아서 켜짐

Debug

진단 대상 설치

Sideloadly

Sideloadly 설치

Appinst

  1. cydia → http://cydia.akemi.ai 소스 추가 (or https://)
  2. appinst 설치
  3. appsync 설치
  4. ipa 파일을 ftp로 옮긴 후
  5. appinst 앱이름.ipa

3uTools

ReProvision Reborn

https://havoc.app repo 등록 및 설치

.ipa 파일 길게 누른 뒤 ReProvision 앱 선택 후 설치

정적 분석

바이너리 추출

ssh, 3utools 등으로 아래 경로에 들어가서 바이너리 가져오기 /var/containers/Bundle/Application/[APP_ID]/[APP_NAME].app/

암호화

  1. 트윅 깔아서 추출
  2. 앱 실행 시 앱이 메모리에 복호화 되어 로드되었을 때, 이를 덤프하여 바이너리 추출
  3. hackcatml 블로그 참조

frida-ios-dump

SSH가 안될경우 SSH over USB 로 설정 후 python dump.py com.someapp.dev 하면 됨
그래도 안될경우 Passwordless SSH 참고

git clone https://github.com/AloneMonkey/frida-ios-dump
cd frida-ios-dump
pip3 install -r requirements.txt

python3 dump.py -o decrypted.ipa -P alpine -p 22 -H 192.168.1.10 com.someapp.dev

Clutch

kill -9 error 발생 시 Clutch 바이너리 ASLR 제거

Clutch 설치 릴리즈에 들어가서 바이너리 다운로드 안되면 Clutch 블로그 참조하여 내 바이너리 사용

scp Clutch-2.0.4 root@ip:~/
chmod +x Clutch-2.0.4

Clutch [OPTIONS]
-b --binary-dump     Only dump binary files from specified bundleID
-d --dump            Dump specified bundleID into .ipa file
-i --print-installed Print installed application
--clean              Clean /var/tmp/clutch directory
--version            Display version and exit
-? --help            Display this help and exit

디렉토리 탐색

사용자의 중요 정보 저장 및 설정 파일이 쉽게 변조가 되는지를 보는 것 (SSH, FTP로 탐색)

  • APP 디렉토리에서 info.plist 파일 분석
  • APP 디렉토리에서 중요 정보가 저장될 수 있는 파일들을 확인

정보 노출

🔥 App 사용 중 수정되는 파일들 App 사용 중 수정되는 파일들의 데이터를 확인하여 중요 정보가 단말기에 남는 취약점의 누락을 방지할 수 있음 find / -type f -mmin -5 (5분 이내 수정된 파일들 출력하는 명령어)

Filemon 으로 파일 트래킹

wget http://www.newosxbook.com/tools/filemon.tgz && tar zxvf filemon.tgz && chmod +x filemon
./filemon -c -f com.someapp.dev

소스코드 내 중요 정보 노출

App 바이너리 내에 존재하는 문자열에서 중요 정보가 노출되는 경우

  • 개발용 서버 IP 및 포트
  • 관리자 계정
  • 관리자 페이지 URL
  • DB 서버 IP 및 포트
진단 방법 사용 도구
App 바이너리 내 String 추출 후 중요 정보 존재 여부 확인 Strings.exe
Strings
IDA 등 정적 분석 Tool의 String View 확인 IDA
Ghidra
Hooper

rabin2를 이용한 수동 탐색 및 scraper 사용하여 자동 탐색
```shell rabin2 -zzzqq somefile | grep -Pi ‘[^\w\d\n]+(?:basic|bearer)\ .+’ rabin2 -zzzqq somefile | grep -Pi ‘(?:access|account|admin|basic|bearer|card|conf|cred|customer|email|history|id|info|jwt|key|kyc|log|otp|pass|pin|priv|refresh|salt|secret|seed|setting|sign|token|transaction|transfer|user)[\w\d]*(?:"\ *:|\ *=).+’ rabin2 -zzzqq somefile | grep -Pi ‘[^\w\d\n]+(?:bug|comment|fix|issue|note|problem|to(?:_|\ |)do|work)[^\w\d\n]+.+’

rabin2 -zzzqq somefile grep -Po ‘\w+:\/\/[\w-.\@:\/\?=\%\&#]+’ sort -uf tee urls.txt        
rabin2 -zzzqq somefile grep -Po ‘(?:\b25[0-5] \b2[0-4][0-9] \b[01]?[0-9][0-9]?)(?:.(?:25[0-5] 2[0-4][0-9] [01]?[0-9][0-9]?)){3}’ sort -uf tee ips.txt
rabin2 -zzzqq somefile sort -uf > strings.txt    
grep -Po ‘(?:[a-zA-Z0-9+\/]{4})*(?:[a-zA-Z0-9+\/]{4} [a-zA-Z0-9+\/]{3}= [a-zA-Z0-9+\/]{2}==)’ strings.txt sort -uf > base64.txt
for string in $(cat base64.txt); do res=$(echo “${string}” base64 -d 2>/dev/null grep -PI ‘[\s\S]+’); if [[ ! -z $res ]]; then echo -n “${string}\n${res}\n\n”; fi; done tee base64_decoded.txt

apt-get -y install radare2 pip3 install file-scraper file-scraper -dir Payload -o results.html -e default


#### 단말기 내 중요 정보 노출
##### plist
- Document 디렉토리
- Library/Preference 디렉토리
- NSUserDefaults - 간단한 사용자 정보를 저장 (/var/mobile/Containers/Data/Application/[AppID]/Library/Preferences)

##### keychain
- [Keychain-Dumper](https://github.com/ptoomey3/Keychain-Dumper)
- Objection 사용

|키체인 보호 속성|의미|
|---|---|
|kSecAttrAccessibleAfterFirstUnlock|이 키는 부팅 이후에 사용자가 처음으로 패스코드를 해제할 때까지는 접근 불가, 그 후에는 언제나 접근 가능|
|==kSecAttrAccessibleAlways==|이 키는 기기가 부팅된 후에는 언제든 접근 가능|
|kSecAttrAccessibleAlwaysThisDeviceOnly|이키는 ==kSecAttrAccessibleAlways==와 동일하지만 다른 iOS 기기로 전달할 수 없음.|
|kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly|이 키는 kSecAttrAccessibleAfterFirstUnlock와 동일하지만 다른 iOS 기기로 전달할 수 없음.|
|kSecAttrAccessibleWhenUnlocked \(\*\*default\*\*)|이 키는 기기가 잠금 해제돼 있을 때만 접근 가능|
|kSecAttrAccessibleWhenUnlockedThisDeviceOnly|이키는 kSecAttrAccessibleWhenUnlocked와 동일하지만 다른 iOS 기기로 전달할 수 없음.|
|kSecAttrAccessibleWhenPasscodeSetThis-DeviceOnly|이 키는 사용자가 패스코드를 설정해 둔 상태에서 패스코드를 해제한 경우에만 접근 가능하면, 패스코드 설정을 하지 않는 경우에는 키가 삭제됨.|

##### Library 디렉토리 (sqlite 등)
App이 사용하는 DB파일들이 저장되며 중요 정보들을 이 파일에 저장하는 경우가 존재함
1. CoreData Framework
    해당 프레임워크를 사용하여 데이터를 관리하는 경우 Library/Application Support 디렉토리에 sqlite 파일로 저장되어 관리됨.
    
2. Realm 등 DB
    Realm등 다양한 DB가 App에서 사용될 수 있으며 이 경우 Document 디렉토리에 저장되는 경우가 많음.

3. [Property-lister](https://github.com/ivan-sincek/property-lister) 이

#### 메모리 내 중요 정보 노출
fridump

#### 네트워크 내 중요 정보 노출
tcpdump

#### 백그라운드 화면 정보 노출

#### 디버그 로그 내 중요 정보 노출

### Repackaging
```shell
mkdir Payload
cp -r someapp.app Payload
zip -r repackaged.ipa Payload
rm -rf Payload

동적 분석

딥링크는 각종 인증을 우회할 수 있음.

# 딥링크 테스트용 HTML 템플릿 생성
mkdir ios_deeplinks

## multiple URL schemes
for scheme in $(cat url_schemes.txt); do for url in $(cat urls.txt | grep -Poi "${scheme}\:\/\/.+"); do if [[ ! -z $url ]]; then echo -n "<a href='${url}'>${url}</a>\n<br><br>\n" | tee -a "ios_deeplinks/${scheme}_deeplinks.html"; fi; done; done

## single URL scheme
scheme="somescheme"; for string in $(cat urls.txt | grep -Poi "${scheme}\:\/\/.+"); do echo -n "<a href='${string}'>${string}</a>\n<br><br>\n"; done | tee -a "ios_deeplinks/${scheme}_deeplinks.html"

python3 -m http.server 9000 --directory ios_deeplinks

ios deeplink fuzzing

# 딥링크 퍼징
frida -U -no-pause -l ios-deeplink-fuzzing.js -f com.someapp.dev
frida -U -no-pause --codeshare ivan-sincek/ios-deeplink-fuzzing -f com.someapp.dev

Objection

iOS 버전이 낮을 경우 Spawn은 안될 수 있음

pip install objection
objection --help

# Attach [Base]
objection -g <ap­pNa­me,pid> explore

## bypass
ios sslpinning disable -q
ios jailbreak disable -q

## keychain 
ios keychain dump -q

## clipboard
ios pasteboard monitor -q

## cookie
ios cookies get

## plist
ios plist cat Info.plist

## storage
ios nsurlcredentialstorage dump

## info get
ios nsuserdefaults get

## screenshot
ios ui screenshot 1.png

## Trace
ios hooking watch class <class_name>
ios hooking watch method "-[<class_name> <method_name>]" --dump-args --dump-return --dump-backtrace
ios hooking generate simple <class_name>
ios hooking set return_value "-[<class_name> <method_name>]" false

Basic

SSL Pinning Bypass

// Variables
var SSL_VERIFY_NONE = 0;
var ssl_ctx_set_custom_verify;
var ssl_get_psk_identity;

/* Create SSL_CTX_set_custom_verify NativeFunction */
ssl_ctx_set_custom_verify = new NativeFunction(
    Module.findExportByName("libboringssl.dylib", "SSL_CTX_set_custom_verify"),
    'void', ['pointer', 'int', 'pointer']
);

/** Custom callback passed to SSL_CTX_set_custom_verify */
function custom_verify_callback_that_does_not_validate(ssl, out_alert) {
    return SSL_VERIFY_NONE;
}

/** Wrap callback in NativeCallback for frida */
var ssl_verify_result_t = new NativeCallback(function(ssl, out_alert) {
    custom_verify_callback_that_does_not_validate(ssl, out_alert);
}, 'int', ['pointer', 'pointer']);

/* Do the actual bypass */
function bypassSSL() {
    console.log("[+] Bypass successfully loaded ");

    Interceptor.replace(ssl_ctx_set_custom_verify, new NativeCallback(function(ssl, mode, callback) {
        //  |callback| performs the certificate verification. Replace this with our custom callback
        ssl_ctx_set_custom_verify(ssl, mode, ssl_verify_result_t);
    }, 'void', ['pointer', 'int', 'pointer']));
}

bypassSSL();

ObjC

Function Enum

// enumerate all ObjC classes
function enumAllClasses() {
    var allClasses = [];

    for (var aClass in ObjC.classes) {
        if (ObjC.classes.hasOwnProperty(aClass)) {
            allClasses.push(aClass);
        }
    }

    return allClasses;
}

// enumerate all methods declared in an ObjC class
function enumMethods(targetClass) {
    var ownMethods = ObjC.classes[targetClass].$ownMethods;

    return ownMethods;
}

// enumerate all methods declared in all ObjC classes
function enumAllMethods() {
    var allClasses = enumAllClasses();
    var allMethods = {};

    allClasses.forEach(function(aClass) {
        enumMethods(aClass).forEach(function(method) {
            if (!allMethods[aClass]) allMethods[aClass] = [];
            allMethods[aClass].push(method);
        });
    });

    return allMethods;
}

Swift

Function Enum

function all_function(){
    var module_list = Process.enumerateModules();
    for(var module in module_list){
        console.log(module_list[module].name)
        var select_module = Process.getModuleByName(module_list[module].name);
        var symbol_list = select_module.enumerateExports();

        var Find_FuctionName = "";
        for(var sym_idx in symbol_list){
            console.log("\t[$] : " + symbol_list[sym_idx].name);
        }
    }
}
var t 
function getsymbol(){
    var m = Process.getModuleByName("prm");
    var symbols = m.enumerateSymbols();

    symbols.forEach(function(s) {
        if (s.name.indexOf("$s10Foundation4DataVSgWOy") !== -1) {
            console.log(s.name, s.address);
            t = s.address
        }
    });
}

Attach

var target_className = "class_name"

var sTarget = "$s3...yAMctFZ";
var pTarget = Module.findExportByName(target_className, sTarget);
console.log("[*] Method Name : " + sTarget);
console.log("[*] Method Address : " + pTarget);

//Hooking
Interceptor.attach(pTarget,{
    // 함수가 실행되기 직전에 실행되는 부분
    onEnter: function(args){
        console.log("[*] Target Function Called");
        // console.log(args[1])
    },
    // 함수가 리턴되기 직전에 실행되는 부분
    onLeave: function(retVal){
        console.log("[*] Target Function Leave");
        console.log("[=] return Origin Value : " + retVal);
        // retVal.replace(1);
        // send("[=] return Modified Value : " + retVal);
    }
});

유용한 Website 와 Tools

URL Description
developer.apple.com/account Official iOS documentation, create code signing certificates, etc.
developer.apple.com/apple-pay/sandbox-testing Test debit/credit cards for Apple Pay.
streaak/keyhacks Validate various API keys.
zxing.org/w/decode.jspx Decode QR codes.
youtube.com/user/iDeviceMovies Useful videos about jailbreaking, etc.
ipsw.me/product/iPhone Firmwares for Apple devices.