본문으로 바로가기

가. UnCrackable 정의 및 설치

1. UnCrackable 란?

- MAS Crackmes의 UnCrackable Apps는 모바일 리버스 엔지니어링을 실습할 수 있는 앱으로, OWASP MSTG(Mobile Security Testing Guide)로 활용할 수 있다.

- 쉽게말해 런타임 조작 또는 탈옥탐지 우회 등과 같은 안드로이드 모의해킹 실습을 할 수 있는 앱이다.

 

2. UnCrackable 설치 방법

- 아래의 링크에 접속하여 "UnCrackable-Level2.apk" 파일을 다운로드한다.

https://mas.owasp.org/crackmes/

 

MAS Crackmes - OWASP Mobile Application Security

MAS Crackmes Welcome to the MAS Crackmes aka. UnCrackable Apps, a collection of mobile reverse engineering challenges. These challenges are used as examples throughout the OWASP MASTG. Of course, you can also solve them for fun.

mas.owasp.org

 

- adb를 이용하거나 본인이 알고 있는 다양한 방법으로 모바일 디바이스에 다운로드한 APK파일을 설치한다.

//명령어 형식
adb install [options] [filepath]

//명령어 예시
adb install -r UnCrackable-Level2.apk

 

나. Frida Hooking을 통한 문제 풀이

1. 루팅탐지 우회

- 앱 설치 후 실행하면 "Root detected!"라는 오류 메시지가 출력되며 앱이 강제종료된다.

 

- 앱 실행 시 루팅탐지 메시지가 출력했으므로, 앱 실행 시 먼저 호출되는 이벤트 함수 onCreate()의 내용을 JADX를 통해 확인한다.

※ JADX다운로드 및 소스코드 확인방법은 아래의 게시글을 참고하길 바란다.

[AOS App 취약점 진단 · 모의해킹] - [AOS 취약점 진단] 05강 - 하드코딩된 중요정보 확인(실습 1)

 

[AOS 취약점 진단] 05강 - 하드코딩된 중요정보 확인(실습 1)

가. 하드코딩된 중요정보 노출 취약점이란? 1. 하드코딩된 중요정보 - 하드코딩이란 소스코드 내에 데이터를 직접 입력해서 저장하는 경우를 말한다. - 중요정보(관리자 계정, 암호화키 등)가 소

hagsig.tistory.com

 

 MainActivity 클래스의 onCreate() 내용을 보니 init()함수를 호출하는 코드가 보인다. init 함수의 선언부에 native가 적힌 것을보아 외부 라이브러리 파일에서 함수를 호출하는 것 같다.

② Level1과 동일한 로직으로 루팅과 디버깅을 탐지하는것을 알 수 있다.

③ 스레드로 0.1초마다 앱이 디버깅 툴과 연결되었지를 확인하고 에러메시지와 출력하는 코드가 추가된 것을 확인할 수 있다.

 

※ Level 1 소스코드 설명 및 풀이방법은 아래의 게시글을 참고하길 바란다.

[AOS App 취약점 진단 · 모의해킹] - Android UnCrackable Level 1 문제풀이

 

Android UnCrackable Level 1 문제풀이

가. UnCrackable 정의 및 설치 1. UnCrackable 란? - MAS Crackmes의 UnCrackable Apps는 모바일 리버스 엔지니어링을 실습할 수 있는 앱으로, OWASP MSTG(Mobile Security Testing Guide)로 활용할 수 있다. - 쉽게 말해 런타임

hagsig.tistory.com

 

- MainActivity 클래스의 상단부분에 라이브러리 파일 foo를 로드하는 부분이 있는것을 알 수 있다.

 

- foo 라이브러리에서 init() 함수가 무슨 일을 하는지 알아내기 위해 UnCrackable-Level2.apk 파일을 디컴파일 한다.

 

※ APK 파일 디컴파일 방법은 아래의 게시글을 참고하길 바란다.

[AOS App 취약점 진단 · 모의해킹] - 안드로이드 APK 파일 디컴파일/리패키징 방법

 

안드로이드 APK 파일 디컴파일/리패키징 방법

1. 디컴파일/리패키징 이란? 1.1. 컴파일(compile) - 소스코드(source code)에서 기계가 인식할 수 있는 바이너리(binary) 파일로 변환하는 과정을 말한다. - 안드로이드는 컴파일 이후 패키징(packaging)과 코

hagsig.tistory.com

 

- 디컴파일 폴더에서 libfoo.so 파일을 찾아 기드라(Ghidra) 또는 아이다(IDA)를 이용하여 읽어드린다.

*라이브러리 경로 : [DecompileHome Dir]\lib\[OS Type]\[File Name]

*OS Type : 모바일 디바이스에서 getprop ro.product.cpu.abi 명령어를 실행하여 자신에게 맞는 OS Type을 알아낸다

 

※ 기드라를 통한 분석 및 수정한 파일을 저장하는 방법은 아래의 게시글을 참고하길 바란다.

[iOS App 취약점 진단 · 모의해킹] - [iOS App 진단] 12강 - 탈옥 탐지 우회(실습3)

 

[iOS App 진단] 12강 - 탈옥 탐지 우회(실습3)

가. 탈옥(Jailbreak) 이란? - iOS의 샌드박스 제한을 풀어 서명되지 않은 코드를 모바일 디바이스에서 실행되게 하도록 하는 것을 말함. - 탈옥된 기기에서의 사용자는 최고 권한을 얻기 때문에 앱(App

hagsig.tistory.com

 

- libfoo.so 라이브러리의 init 함수를 보니 FUN_001008d0()이라는 함수를 호출하는 것을 알 수 있다.

 

- FUN_001008d0() 함수의 코드를 분석한 결과는 다음과 같다. 요약하자면 디버그 모드로 연결되어 있다면 프로그램을 종료하라는 내용이다.

※소스코드에 대한 해석이 필요한 경우 아래의 버튼을 클릭한다.

더보기

13 라인 : fork() 함수 호출을 통해 현재 프로세스를 복제하여 새로운 자식 프로세스를 생성한다.

* fork() 함수 : UNIX 계열 운영 체제에서 사용되는 시스템 함수로, 현재 실행 중인 프로세스와 동일한 복제된 프로세스를 생성함

 

14 라인 : 생성된 프로세스가 자식 프로세스인지 확인한다.

*fork()함수가 부모 프로세스에서 호출되었다면 자식 프로세스의 PID, 자식 프로세스에서 호출되었다면 0이 저장됨

 

15 라인 : getppid() 함수를 사용하여 부모 프로세스의 PID를 가져온다.

 

16 라인 : ptrace()를 통해 자식 프로세스를 부모 프로세스에 연결, 자식 프로세스가 부모 프로세스를 디버깅할 수 있도록 한다.

 

17 라인 : 부모 프로세스의 디버깅이 성공적으로 수행되었는지를 확인한다.

*lVar2에 저장된 변수가 0이면 디버깅에 성공한 것이고, -1이면 실패한 것

 

18 라인 : waitpid() 함수를 호출하여 부모 프로세스가 멈출 때까지 기다린다.

 

19 라인 : 디버깅된 프로세스를 계속 실행하도록 요청한다.

 

21 라인 : 자식프로세스가 종료되지 않았거나 중단되지 않았을때 0을 반환, 자식프로세스의 상태를 확인한다.

 

23 라인 : local_20 변수에대한 비트연산을 수행하고, 그 결과가  0x7f인지를 판변한다. 0x7f는 SIGSEGV 시그널을 나타내므로 local_20변수에 SIGSEGV  시그널이 포함되어 있는지를 확인하고 있다.

*SIGSEGV 시그널 : "segmentation fault"를 나타내며, 프로그램이 메모리 접근 오류를 발생시키거나, 올바르지 않은 메모리 영역에 접근하려고 시도할 때 발생합니다. 이러한 오류는 주로 포인터 오버플로우나 잘못된 메모리 참조로 인해 발생할 수 있습니다.

 

25 라인 : local_20 변수가 SIGSEGV 시그널을 포함하고 있다면 프로그램이 디버그 모드로 연결되었다 판단하고 프로그램을 종료시킨다.

 

34 라인 : 반면 fork 함수가 부모 프로세스에서 호출되어 부모 프로세스가 생성되었다면, 새로운 쓰레드를 생성하여 다른 작업을 수행한다.

 

- 디버깅 탐지 기능을 무력화 하기 위해 부모 프로세스와 연결하는 ptrace()부분 00100922를 NOP으로 *변경한다.

*변경 방법 : 변경지점 클릭 → Patch Instruction 클릭 CALL 0x00100810 을 지우고 NOP 으로 변경 후 엔터

 

- NOP으로 수정할 시 아래와 같이 변경 되며, Decompile 코드 또한 변경되는 것을 확인할 수 있다.

 

- 위와 같이 수정한 내용을 *저장하고 리패키징한 APK을 설치 및 실행한다.

*저장방법 : File → Export Program → Format을 ELF로 선택 → OK 

 

- 원본 앱은 디버깅 도구(Frida)로 연결이 불가능 하였지만, 수정한 APK 파일로 동작하는 앱은 정상적으로 연결이 가능한 것을 볼 수 있다.

 

- ② 후킹과 디버깅을 탐지하는 로직은 아래와 같은 후킹 소스로 우회할 수 있다. 자세한 내용은 Level 1에서 다루었으므로 생략하고 넘어가겠다.

setImmediate(function(){
    if (Java.available){
        Java.perform(function(){
            var b_class = Java.use("sg.vantagepoint.a.b");
            b_class.a.implementation = function () {
                return false;
            }
            b_class.b.implementation = function () {
                return false;
            }
            b_class.c.implementation = function () {
                return false;
            }
            var a_class = Java.use("sg.vantagepoint.a.a");
            a_class.a.implementation = function (context) {
                return false;
            }
        })
    }
})

 

- ③ 스레드에서 디버깅 연결여부를 주기적으로 체크하는 로직은 아래의 후킹 코드로 우회가 가능하다.

setImmediate(function(){
    Java.perform(function(){
        var bypass_debug = Java.use("android.os.Debug");
        bypass_debug.isDebuggerConnected.implementation = function(){
            return false;
        }
    })
})

 

- libfoo.so 파일을 수정한 앱 실행 후, 위에서 작성한 후킹 파일을 Frida로 실행하면 에러메시지가 출력되지 않는 것을 볼 수 있다.

//명령어 형식
frida [options] [script path] [packagename/PID]

//명령어 예시
//-f 옵션 사용 시 앱 실행과 동시에 프리다 코드를 실행할 수 있음
frida -U -f owasp.mstg.uncrackable1 -l hagsig_hooking.js

 

2. Secret String 입력 우회

- 루팅 탐지 우회 후 문자열을 입력하고 'VERIFY' 버튼을 탭하면, 에러 메시지가 출력되는 것을 볼 수 있다.

 

- verify 버튼이 클릭되는 부분의 내용을 확인해보니 입력한 문자열을 인자로 m클래스의 a함수를 호출하고, 반환값에 따라 성공/실패를 구분하는 것을 알 수 있다.

 

- m은 CodeCheck 클래스를 인스턴스로 생성한 변수이므로, CodeCheck 클래스를 확인할 필요가 있다.

 

- CodeCheck 클래스의 a함수를 보니 native로 선언된 bar 함수를 호출하고 있는것을 알 수 있다.

 

- libfoo.so 라이브러리의 bar 함수를 기드라를 통해 확인해 본다.

 

- bar() 함수의 코드를 분석한 결과는 다음과 같다. 요약하자면 23개의 문자를 입력하면 사전에 정의한 문자열과 입력값을 비교하여 같으면 1 다르면 0을 반환한다는 내용이다.

※소스코드에 대한 해석이 필요한 경우 아래의 버튼을 클릭한다.

더보기

3 라인 : param1, param2, param3 함수호출 시 총 세개의 인자를 받음.

 

18 라인 : 현재 스택 프레임의 위치를 기록하여 스택 보호 기법인 스택 캐너리(Stack Canary)를 구현 함.

*스택 캐너리 : 함수가 반환될 때마다 스택 프레임 끝에 저장한 값을 검사하며, 변경되면 오버플로우라고 판단


19 라인 : 전역변수 DAT_0010400c 의 값이 1인지 검사함. DAT_0010400c는 init()함수에서 1을 저장하므로, init()함수가 정상적으로 실행되었는지를 검사하기위해 만든 라인인듯하다.

25 라인 : param_1이 가리키는 위치에 저장된 값에 0x5c0을 더한 주소에 있는 함수를 호출한다. 사용자가 입력한 문자열이 위치한 주소가 __s1에 저장된다. 

 

26 라인 : param_1이 가리키는 위치에 저장된 값에 0x558을 더한 주소에 있는 함수를 호출한다. 사용자가 입력한 문자열의 길이가 iVar1에 저장된다. 


27 라인 : iVar1의 값이 0x17(23)인지 확인한다. 입력된 값이 23개의 문자인지 확인하고 있다.

28 라인 : 25번 라인에서 저장한 __s1의 주소에 저장된 문자열과 local_38의 주소에 저장된 문자열에서 0x17(23)바이트 만큼의 문자열을 비교한다. 비교한 결과가 같을경우 0을 반환하며, 다를경우 음수 또는 양수를 반환한다.

29 라인 : 두 문자열을 비교한 결과가 같을경우 1이 저장되고, 틀릴경우 0이 저장된다.

37 라인 : 스택 보호기법으로 오버플로우와 같은 공격을 탐지하는데 사용하는 코드이다. 스택 보호가 유지되고 있지 않을 경우 `in_FS_OFFSET + 0x28`에 저장된 값과 `local_20`에 저장된 값을 비교했을 때 다를 것이고, 이는 스택에 변경이 있었음을 나타낸다. 스택이 보호되고 있으면 uVar2를 반환하며, 스택이 보호되지 않고 있을경우 __stack_chk_fail() 함수를 호출한다.

 

38 라인 : 오버플로우 공격이 없을 경우, 두 문자열을 비교한 결과를 반환한다.

 

- 38번 라인의 bar 함수 리턴값이 무조건 1이 되도록하여 Secret String 비교검증 부분을 우회할 수 있다.

//문제풀이 후킹 소스코드 예시1
setImmediate(function(){
    Java.perform(function(){
        var exitBypass = Java.use("java.lang.System");
        exitBypass.exit.implementation = function () {
            console.log("\n[+] exit bypass");
        };

        Interceptor.attach(
            Module.findExportByName("libfoo.so", "Java_sg_vantagepoint_uncrackable2_CodeCheck_bar"), {
                onLeave: function(retl) {
                    console.log("[+] secret bypass");
                    retl.replace(1);
                }
            }
        );
    })
})

 

- 위에서 작성한 후킹 코드를 프리다를 통해 실행한뒤 임의의 문자를 입력하면 성공 메시지가 뜨는것을 볼 수 있다.

//명령어 형식
frida [options] [script path] [packagename/PID]

//명령어 예시
frida -U -l hagsig_hooking.js 6394

 

- 또다른 방법으로는 strncmp를 *후킹하여 인자로 들어온 값을 확인하여 Secret String을 알아내는 방법이 있다.

*libfoo.so 파일에서 사용하는 모든 strncmp를 후킹하므로 입력한 값을 비교하는 구문을 꼭 넣어주어야 한다.

setImmediate(function(){
    Java.perform(function(){
        var exitBypass = Java.use("java.lang.System");
        exitBypass.exit.implementation = function () {
            console.log("\n[+] exit bypass");
        };

        //후킹 소스코드 예시 1
        Interceptor.attach(Module.findExportByName("libfoo.so","strncmp"),{
            onEnter: function(args){
                var param1 = Memory.readUtf8String(args[0]);
                var param2 = Memory.readUtf8String(args[1]);

                if(param1.indexOf('12345678901234567890123')!== -1){
                    console.log("\n[+] 후킹 소스코드 예시 1");
                    console.log("[+] input string : " + param1);
                    console.log("[+] input string : " + param2);
                }
            },
            onLeave: function(){}
        })

        //후킹 소스코드 예시 2
        Interceptor.attach(Module.findExportByName("libfoo.so","strncmp"),{
            onEnter: function(args){
                var param1 = args[0].readUtf8String();
                var param2 = args[1].readUtf8String();

                if(param1 == '12345678901234567890123'){
                    console.log("\n[+] 후킹 소스코드 예시 2");
                    console.log("[+] input string : " + param1);
                    console.log("[+] input string : " + param2);
                }
            },
            onLeave: function(){}
        })
    })
})

 

- secret string 입력 칸에 임의의 문자 23개를 입력하고 버튼을 누르면 프리다에 secret string이 노출된다.

//명령어 형식
frida [options] [script path] [packagename/PID]

//명령어 예시
frida -U -l hagsig_hooking.js 6394

 

- 노출된 secret string "Thanks for all the fish"을 입력하면 성공하는 것을 볼 수 있다.

 

다. smali 변조를 통한 문제 풀이

1. 루팅탐지 우회

- MainActivity 클래스 onCreate() 함수의 달빅 바이트 코드를 보면 조건문의 결과에 따라 탈옥과 디버깅 모드를 탐지하는 것을 알 수 있다. 

 

※ 스말리(smali)파일 달빅 바이트 코드를 보는 방법은 아래의 게시글을 참고하길 바란다.

[AOS App 취약점 진단 · 모의해킹] - [AOS 취약점 진단] 15강 - 애플리케이션 패칭/앱 무결성 검증 취약점

 

[AOS 취약점 진단] 15강 - 애플리케이션 패칭/앱 무결성 검증 취약점

안드로이드·갤럭시·AOS 애플리케이션 취약점진단/모의해킹 무료 강의 학식(hagsig) 가. 취약점 정의 1. 애플리케이션 패칭 취약점이란? - 애플리케이션의 코드를 변경하여 비정상적으로 작동되도

hagsig.tistory.com

 

- 맨 처음 루팅을 탐지하는 87번 라인의 점프(jmp) 부분을 루팅된 환경이 아니라고 판단될 시 실행되는 부분으로 변경한다.

 

- 위와 같이 변경하여 저장한 뒤에 리패키징한 후 설치하여 앱을 실행하면 루팅된 환경에서도 정상 동작하는 것을 볼 수 있다.

- 디버깅을 이용한 런타임 조작 방식이 아니므로 스레드로 디버그 모드를 주기적으로 탐지하는 ②은 우회할 필요가 없다.

 

2. Secret String 입력 우회

- MainActivity 클래스의 verify() 함수의 달빅 바이트 코드를 보면 *194번 라인의 분기점에 의해 입력한 문자열이 정확한지 파악하는 것을 알 수 있다. 

*if-eqz p1, :cond_2d → p1의 값이 0과 같을 경우 :cond_2d 지점으로 이동

 

- 194번 라인의 조건문을 반대로 변경한다.

 

- 위와 같이 변경하여 저장한 뒤에 리패키징한 후 설치하여 앱을 실행하면 루팅된 환경에서도 정상 동작하는 것을 볼 수 있다.

 

라. 참고 URL

- 루팅 탐지 우회 취약점 정의 및 대응(조치) 방법 설명

[AOS App 취약점 진단 · 모의해킹] - [AOS 취약점 진단] 16강 - 루팅 탐지 우회 취약점 점검

 

[AOS 취약점 진단] 16강 - 루팅 탐지 우회 취약점 점검

안드로이드·갤럭시·AOS 애플리케이션 취약점진단/모의해킹 무료 강의 학식(hagsig) 가. 취약점 정의 - 안드로이드는 사용자가 최상위 권한(root)을 획득할 수 없도록 되어있으나, 루팅(rooting)을 통

hagsig.tistory.com

 

- 런타임 조작 취약점 및 smali, 달빅 바이트 코드 설명

[AOS App 취약점 진단 · 모의해킹] - [AOS 취약점 진단] 14강 - 런타임 조작 취약점 점검(Frida/ADB)

 

[AOS 취약점 진단] 14강 - 런타임 조작 취약점 점검(Frida/ADB)

안드로이드·갤럭시·AOS 애플리케이션 취약점진단/모의해킹 무료 강의 학식(hagsig) 가. 취약점 정의 - 애플리케이션 실행 중에 동작을 조작하여 정상적인 결과가 아닌 공격자가 원하는 결과가 되

hagsig.tistory.com

 

- APK파일 리패키징/인증서 씌우는 과정 설명

[AOS App 취약점 진단 · 모의해킹] - 안드로이드 APK 파일 디컴파일/리패키징 방법

 

안드로이드 APK 파일 디컴파일/리패키징 방법

1. 디컴파일/리패키징 이란? 1.1. 컴파일(compile) - 소스코드(source code)에서 기계가 인식할 수 있는 바이너리(binary) 파일로 변환하는 과정을 말한다. - 안드로이드는 컴파일 이후 패키징(packaging)과 코

hagsig.tistory.com

 

- 프리다 서버/클라이언트 설치 방법 설명

[AOS App 취약점 진단 · 모의해킹] - 안드로이드 프리다 서버/클라이언트 설치방법

 

안드로이드 프리다 서버/클라이언트 설치방법

Frida Server/Client 설치 방법 - Android(AOS) 가. 설치 프로그램 설명 1. 아나콘다(Anaconda) - 오픈소스 라이브러리를 모아둔 개발 플랫폼이다. - 아나콘다를 사용하면 파이썬을 버전별로 설치해서 사용할

hagsig.tistory.com

 

- 프리다 후킹 소스코드 설명

[iOS App 취약점 진단 · 모의해킹] - 프리다(Frida) 문법 및 후킹에 사용되는 함수 설명

 

프리다(Frida) 문법 및 후킹에 사용되는 함수 설명

가. 기초 문법 1. ObjC.available - 현재 프로세스에 Objective-C 런타임이 로드되었는지 여부를 지정하는 Boolean(True, False) - 해당 앱에서 FIRDA가 실행가능한 환경인지 체크 2. ObjC.classes - 현재 등록된 클래

hagsig.tistory.com