| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 |
- MALWARE
- 분석
- 릭 릭스비
- 악성코드
- 사이버안보
- 우크라이나
- frida
- RootByPass
- Malware Tool
- 부다페스트 협약
- 사이버안보 협약
- L:azarus
- 앱 취약점 분석
- 악성코드분석
- CISO 제도 분석
- 디지털 증거의 증거능력
- 보안사고 회고
- 러시아
- 오래된 지혜
- 악성코드 분석
- Uncrakable1
- 오픈소스 관리체계
- CAN Network
- 랜섬웨어
- 오픈체인
- 판례평석
- 독서
- 블랙바스타
- 국제 사이버 범죄
- VirtualAddress
- Today
- Total
봔하는 수달
[안드로이드] Uncrakable1 본문
안녕하세요, 이번 글에서는 OWASP MSTG에서 제공하는 안드로이드 보안 실습 앱 중 하나인 UnCrackable-Level1을 대상으로 Frida를 활용한 루팅 탐지 우회 및 클라이언트 사이드 인증 우회를 수행한 과정을 공유합니다.
- 앱 이름: UnCrackable-Level1
- 패키지명: owasp.mstg.uncrackable1
UnCrackable-Level1은 Build.TAGS, su 바이너리 존재 여부, SystemProperties, Runtime.exec() 등을 통해 루팅 여부를 탐지함.

루팅 탐지 영역을 확인하기 위해 소스코드를 확인.
루팅 탐지는 class c에서 a(), b(), c() 메소드들의 반환값의 조건문을 통해 탐지가 진행됨.

해당 메소드들을 확인하면 아래와 같은 로직이 존재.

| 탐지 로직 | 탐지 방식 |
![]() |
|
| 로직 우회 방법 | File.exists() 메소드에서 su 포함된 값이 호출될 때 리턴값을 false 변조하거나 해당 c.a() 메소드 리턴값을 false로 변조하여 우회. |
| c.b() |
|
![]() |
Build.TAGS 전역 str값에서 test-keys를 확인하는 방법으로 전역에서 해당 문자열을 변조하거나 해당 메소드의 리턴값을 변조하여 우회 가능 |
| c.c() |
|
![]() |
File.exists()를 후킹해서 특정 루트 관련 파일에 대해서만 거짓(false) 를 반환하거나 해당 메소드 리턴값을 변조하여 우회 가능 |
| 전역적 우회 방법 | 지역적 우회 방법 |
|
Java.perform(() => {
const File = Java.use("java.io.File");
File.exists.implementation = function () {
const absPath = this.getAbsolutePath();
const shouldBypass = absPath.includes("su") || absPath.toLowerCase().includes("superuser");
if (shouldBypass) {
console.log(`[Bypass] File.exists("${absPath}") → false`);
return false;
}
return this.exists();
};
// [선택] String.contains("test-keys") → false 변조해도됨
const Build = Java.use("android.os.Build");
console.log(Build.TAGS)
Build.TAGS.value = "relase-keys"
console.log(Build.TAGS)
});
|
Java.perform(() => {
const RootCheckClass = Java.use("sg.vantagepoint.a.c");
//1번 우회
RootCheckClass.a.implementation = function () {
console.log("a return check: ", this.a())
return false;
};
//2번 우회 리빌드가 아니기에 우회 불필요
RootCheckClass.b.implementation = function (){
console.log("b return check: ",this.b())
return false;
}
//3번 우회
RootCheckClass.c.implementation = function (){
console.log("c return check: ",this.c())
return false;
}
});
|

인증 우회
소스코드가 양이 적어 정적으로 분석이 가능하지만 실제 취약점 분석을 수행하면 어택 벡터를 식별하는데 굉장한 리소스가 소모됨.
이러한 과정을 줄이기 위해 로드된 클래스 내 메소드 후킹을 통해 실제 사용되는 함수를 식별하는 과정이 필요함.
윈도우와 다르게 안드로이드가 이러한 기능이 가능한 이유는 java의 가장 큰 특징인 Write Once, Run Anywhere (WORA) 슬로건의 산물 JVM 때문.
아래는 특정 패키지 내 클래스 리스트를 출력해주고 해당 클래스를 hooAllMethods에 선언해주면 하위 메소드를 모니터링하는 코드이며 이를 통해 클래스들을 수행해보면서 인증 부분을 식별 가능.
perform(() => {
const classes = Java.enumerateLoadedClassesSync();
classes.forEach(className => {
if (className.includes("sg.vantagepoint.a"))
console.log(className);
});
});
Java.perform(function () {
console.log("=====================================")
hookAllMethods("sg.vantagepoint.a.a");
function hookAllMethods(targetClass) {
var total_hooked = 0;
try {
var clazz = Java.use(targetClass);
var methods = clazz.class.getDeclaredMethods();
methods.forEach((element) => {
var method_name = element.getName();
console.log("Hooking " + targetClass + "." + method_name);
try {
var overloads = clazz[element.getName()].overloads;
// console.log(
// "Method: " + method_name,
// "Overloads: " + overloads.length
// );
total_hooked += overloads.length;
// console.log(total_hooked);
overloads.forEach((overload) => {
overload.implementation = function () {
console.log("called " + targetClass + "." + method_name);
console.log("Arguments: " + arguments);
// console.log("Vibrate called with duration: " + args[0]);
console.log("Argument count: " + arguments.length);
for (let i = 0; i < arguments.length; i++) {
console.log("Arg[" + i + "]: Type=" + typeof arguments[i] + ", Value=" + arguments[i]);
}
console.log("\n");
var result = this[element.getName()].apply(this, arguments);
console.log("Result: " + result);
return result;
};
});
} catch (error) {
console.log("Error: " + error);
}
});
console.log("Total hooked: " + total_hooked);
} catch (error) {
console.log("Error: " + error);
}
}
});
위 코드를 포함하여 verify를 수행하게되면 아래 결과 확인 가능

Result 결과는 HEX 코드로 text로 변환하면 인증 우회 가능


정적 분석 방법론으로 접근해보면 verify 함수를 식별하고 a.a(string)의 반환의 조건문으로 인증이 진행되는걸 확인 가능함

a.a(string str) 함수를 확인하면 반환값으로 str.eqauls로 클라이언트에서 인증을 처리하는걸 확인할 수 있음
* 인증은 모두 서버에서 이뤄져야함

이러한 로직은 간단하게 해당 함수 리턴값을 변조하여 인증 조건문에 true값을 전달해도 쉽게 우회됨.
Java.perform(function () {
const verify = Java.use("sg.vantagepoint.uncrackable1.a");
verify.a.implementation = function (str) {
console.log("args[0]: ",str)
console.log("ret", this.a(str))
console.log("ret mod")
return true //인증 우회
};
});

'정보보안 > 모바일' 카테고리의 다른 글
| [안드로이드] Uncrackable2 (2) | 2025.07.30 |
|---|---|
| [안드로이드]InsecureBankv2 루팅 탐지 우회 (0) | 2025.07.22 |


