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
- 같은 wifi 접속
- wifi ! 눌러서 아래 프록시 구성 누르기
- 진단 pc 아이피와 프록시 잡을 포트 넣기
- 진단pc아이피:포트로 접속
- 인증서 다운받기
- 설정 - 일반 - 프로파일 - 포트스위거 인증서 설치
- 설정 - 일반 - 정보 - 인증서 신뢰 설정 - 신뢰하기
Frida
- Frida에서 frida_16.3.0_iphoneos-arm64.deb 다운
-
/var/root에 해당 파일 넣기 -
dpkg -i frida_16.3.0_iphoneos-arm64.deb로 설치 - 이제 알아서 켜짐
Debug
진단 대상 설치
Sideloadly
Appinst
- cydia → http://cydia.akemi.ai 소스 추가 (or https://)
- appinst 설치
- appsync 설치
- ipa 파일을 ftp로 옮긴 후
- appinst 앱이름.ipa
3uTools
ReProvision Reborn
https://havoc.apprepo 등록 및 설치
.ipa 파일 길게 누른 뒤 ReProvision 앱 선택 후 설치
정적 분석
바이너리 추출
ssh, 3utools 등으로 아래 경로에 들어가서 바이너리 가져오기
/var/containers/Bundle/Application/[APP_ID]/[APP_NAME].app/
암호화
- 트윅 깔아서 추출
- 앱 실행 시 앱이 메모리에 복호화 되어 로드되었을 때, 이를 덤프하여 바이너리 추출
- 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
동적 분석
Deeplink
딥링크는 각종 인증을 우회할 수 있음.
# 딥링크 테스트용 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
# 딥링크 퍼징
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 <appName,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. |