가. UnCrackable 정의 및 설치
1. UnCrackable 란?
- MAS Crackmes의 UnCrackable Apps는 모바일 리버스 엔지니어링을 실습할 수 있는 앱으로, OWASP MSTG(Mobile Security Testing Guide)로 활용할 수 있다.
- 쉽게 말해 런타임 조작 또는 탈옥탐지 우회 등과 같은 안드로이드 모의해킹 실습을 할 수 있는 앱이다.
2. UnCrackable 설치 방법
- 아래의 링크에 접속하여 "UnCrackable-Level1.apk" 파일을 다운로드한다.
https://mas.owasp.org/crackmes/
- adb를 이용하거나 본인이 알고 있는 다양한 방법으로 모바일 디바이스에 다운로드한 APK파일을 설치한다.
//명령어 형식
adb install [options] [filepath]
//명령어 예시
adb install -r UnCrackable-Level1.apk
나. Frida 후킹을 통한 문제 풀이
1. 루팅탐지 우회
- 앱 설치 후 실행하면 "Root detected!"라는 오류 메시지가 출력되며 앱이 강제종료된다.
- 앱 실행 시 가장먼저 호출되는 이벤트 함수 onCreate()의 내용을 보니 *루팅과 *디버깅을 탐지하는 것을 알 수 있다.
*루팅 탐지 로직 : 클래스 c의 a, b, c 함수 반환값 중에 TRUE가 존재할 경우 루팅된 디바이스라고 판단
*디버깅 탐지 로직 : 클래스 b의 a 함수 반환값이 TRUE일때 앱이 디버깅되고 있음을 판단
- 이후 MainActivity 클래스 a 함수에 결과문구를 안자값으로 전달하면서 호출하는 것을 알 수 있다.
- 클래스 c의 a, b, c 함수 소스를 보니 각기 다른방식으로 루팅 된 환경을 탐지하고 있고, 탐지가 되었을 때 TRUE를 반환하는 것을 알 수 있다.
*a 함수 내용 : 환경변수에 등록된 파일 중 su가 존재하는지 확인하여 루팅 여부를 판단
*b 함수 내용 : 테스트키가 등록되어져 있는지 확인하여 루팅 여부를 판단
*c 함수 내용 : 루팅된 환경에서만 설치되는 패키지 또는 파일들이 존재하는지 확인하여 루팅 여부를 판단
- 클래스 b의 a함수 소스를 보니 디버그모드일 경우 TRUE를 반환하는 것을 알 수 있다.
*a 함수 내용 : getapplicationinfo().flags의 값이 0이 아닐 경우 디버그 중인 것으로 판단
- 클래스 MainActivity의 a 함수를 보면 인자로 받은 문구를 에러 메시지의 제목으로 출력하면서 앱이 종료되도록 해놓은 것을 알 수 있다.
- 여기서 루팅탐지 우회를 할 수 있는 방법은 에러메시지의 버튼을 눌러도 앱이 종료되지 않도록 하거나, 모든 루팅 탐지 함수의 반환값을 FALSE로 바꾸는 방법 이렇게 크게 두가지로 나누어진다.
- 먼저 에러 메시지의 버튼클릭 시 exit()로 인해 앱이 종료되지 않도록 하는 방법을 알아보자.
- 아래의 후킹 코드를 메모장으로 작성한다음 .js 확장자로 저장한다.
//소스코드 예시 1 : onClick 함수를 재정의하여 exit()무력화
//MainActivity 클래스 내부에서 new DialogInterface.OnClickListener()를 통해 새로운 익명클래스가 생성되었으므로, $1으로 지정하여야 후킹이 가능하다.
setImmediate(function(){
Java.perform(function(){
var exitBypass = Java.use("sg.vantagepoint.uncrackable1.MainActivity$1");
exitBypass.onClick.implementation = function (arg1, arg2) {
console.log("[*] 루팅 탐지 후킹 성공!");
}
})
})
//소스코드 예시 2 : exit 함수를 재정의하여 exit() 무력화
setImmediate(function(){
Java.perform(function(){
var exitBypass = Java.use("java.lang.System");
exitBypass.exit.implementation = function () {
console.log("[*] 루팅 탐지 후킹 성공!");
}
})
})
- 위에서 코드를 저장한 파일을 프리다를 통해 실행하면 에러메시지의 버튼을 눌러도 앱이 종료되지 않는 것을 확인할 수 있다.
//명령어 형식
frida [options] [script path] [packagename/PID]
//명령어 예시
frida -U -l hagsig_hooking.js Uncrackable1
- 다음으로 루팅 탐지 함수의 반환값을 모두 FALSE로 변조하는 후킹 소스코드를 작성해 보자.
setImmediate(function(){
if (Java.available) {
Java.perform(function () {
try {
var c_class = Java.use("sg.vantagepoint.a.c");
c_class.a.implementation = function () {
console.log("[+] 루팅탐지 a 메소드 반환값 FALSE로 변조 완료");
return false;
}
c_class.b.implementation = function () {
console.log("[+] 루팅탐지 b 메소드 반환값 FALSE로 변조 완료");
return false;
};
c_class.c.implementation = function () {
console.log("[+] 루팅탐지 b 메소드 반환값 FALSE로 변조 완료");
return false;
};
var b_class = Java.use("sg.vantagepoint.a.b");
b_class.a.implementation = function (context) {
console.log("[+] 디버그 탐지 a 메소드 반환값 FALSE로 변조 완료");
return false;
};
}
catch (error) {
console.log("[-] 아래와 같은 오류가 발생함");
console.log(string(error.stack));
}
});
}
else {
console.log("[-] Java가 활성화되지 않음");
}
})
- 앱 실행과 동시에 프리다 후킹 파일이 실행되어야 하므로 -f 옵션을 사용한다.
*f 옵션을 사용할 때에는 패키지명을 사용하여야 함
//앱 실행과 동시에 프리다 코드를 사용하는 명령어 예시
frida -U -f owasp.mstg.uncrackable1 -l root-bypass.js
- 명령어 입력 시 앱이 실행과 동시에 후킹 파일이 동작하며 루팅탐지 기능을 우회한 것을 확인할 수 있다.
2. Secret String 입력 우회
- 루팅 탐지 우회 후 문자열을 입력하고 'VERIFY' 버튼을 입력하면, 에러 메시지가 출력되는 것을 확인할 수 있다.
- verify 버튼이 클릭되는 부분의 로직을 확인해 보니 입력한 문자열을 인자로 a클래스의 a함수를 호출하고, 반환값에 따라 성공/실패를 구분하는 것을 알 수 있다.
- a 클래스의 boolean 타입의 a 함수를 보면 인자로 입력받은 str과 bArr의 값이 같으면 TRUE를 반환하는 것을 알 수 있다.
*a 함수 내용 : b함수의 결과값과 "5UJiFctbmgbDoLXmpL12mkno8HT4Lv8dlat8FxR2GOc="를 디코딩한 결과를 a패키지의 a클래스로 인자값으로 보내고 그 반환값을 bArr에 저장한다.
*b 함수 내용 : 인자 값 "8d127684cbc37c17616d806cf50473cc"를 아스키코드로 변환하여 반환한다.
- a패키지의 a클래스는 인자로 받은 값을 암호화 키(key), AES로 암호화한 후 반환한다.
- 해당 문제를 푸는 방법은 여러가지가 있겠지만, if 결과를 true로 변경하거나 secretkey를 알아내는 방법이 있다.
//후킹 소스코드 예시 1 : if 조건문의 결과를 참으로 변경
setImmediate(function(){
Java.perform(function(){
var class_a = Java.use("sg.vantagepoint.uncrackable1.a");
class_a.a.implementation = function (arg){
return true;
}
})
})
//후킹 소스코드 예시 2 : SecretKey 값을 찾아내기
setImmediate(function(){
Java.perform(function(){
var class_a = Java.use("sg.vantagepoint.a.a");
class_a.a.implementation = function (arg1, arg2){
var findCode = this.a(arg1, arg2);
var secret = "";
for (var i=0; i<findCode.length; i++){
secret = secret + String.fromCharCode(findCode[i]);
}
console.log("[+] SecretKey : " + secret);
return findCode;
}
})
})
- 위에서 작성한 프리다 후킹 코드를 실행하면 아래와 같은 결과를 얻을 수 있다.
다. smali 변조를 통한 문제 풀이
1. 루팅탐지 우회
- MainActivity 클래스의 onCreate() 함수의 달빅 바이트 코드를 보면 조건문의 결과에 따라 탈옥과 디버깅 모드를 탐지하는 것을 알 수 있다.
- 맨 처음 루팅을 탐지하는 60번 라인의 점프(jmp) 부분을 루팅된 환경이 아니라고 판단될 시 실행되는 부분으로 변경한다.
- 위와 같이 변경하여 저장한 뒤에 리패키징한 후 설치하여 앱을 실행하면 루팅된 환경에서도 정상 동작하는 것을 볼 수 있다.
2. Secret String 입력 우회
- MainActivity 클래스의 verify() 함수의 달빅 바이트 코드를 보면 *135번 라인의 분기점에 의해 입력한 문자열이 정확한지 파악하는 것을 알 수 있다.
*if-eqz p1, :cond_2b → p1의 값이 0과 같을 경우 :cond_2b 지점으로 이동
- 135번 라인의 조건문을 반대로 변경한다.
- 위와 같이 변경하여 저장한 뒤에 리패키징한 후 설치하여 앱을 실행하면 루팅된 환경에서도 정상 동작하는 것을 볼 수 있다.
라. 참고 URL
- 루팅 탐지 우회 취약점 정의 및 대응(조치) 방법 설명
[AOS App 취약점 진단 · 모의해킹] - [AOS 취약점 진단] 16강 - 루팅 탐지 우회 취약점 점검
- 런타임 조작 취약점 및 smali, 달빅 바이트 코드 설명
[AOS App 취약점 진단 · 모의해킹] - [AOS 취약점 진단] 14강 - 런타임 조작 취약점 점검(Frida/ADB)
- APK파일 리패키징/인증서 씌우는 과정 설명
[AOS App 취약점 진단 · 모의해킹] - 안드로이드 APK 파일 디컴파일/리패키징 방법
- 프리다 서버/클라이언트 설치 방법 설명
[AOS App 취약점 진단 · 모의해킹] - 안드로이드 프리다 서버/클라이언트 설치방법
- 프리다 후킹 소스코드 설명
[iOS App 취약점 진단 · 모의해킹] - 프리다(Frida) 문법 및 후킹에 사용되는 함수 설명
'Mobile App 취약점 진단 · 모의해킹 > AOS App 취약점 진단 · 모의해킹' 카테고리의 다른 글
[AOS 취약점 진단] 05강 - 하드코딩된 중요정보 확인(실습 4) (0) | 2024.07.16 |
---|---|
Android UnCrackable Level 2 문제풀이 (0) | 2024.03.02 |
[AOS 취약점 진단] 16강 - 루팅 탐지 우회 취약점 점검 (0) | 2024.02.25 |
[AOS 취약점 진단] 15강 - 애플리케이션 패칭/앱 무결성 검증 취약점 (0) | 2024.02.19 |
[런타임 조작 실습] FridaLab 설치 및 문제풀이 과정 정리 (0) | 2024.02.19 |