본문 바로가기

Wargame & CTF

Uncrackable 1

이번에 실습해볼 앱은 Uncrackable 1입니다.

해당 앱은 AOS 진단시 입문용으로 많이 사용되는 앱입니다.

[사용할 진단도구] 안드로이드 디바이스, adb, frida

해당 앱은 https://github.com/OWASP/owasp-mastg/tree/master/Crackmes/Android/Level_01 에서 다운로드 받으실 수 있습니다.

해당 apk 파일을 다운로드 받은 후 apk 파일이 있는 경로에서 adb install [앱이름] 을 통해서 다운로드 받아주면 됩니다.

현재 루팅된 환경에서 진행하므로 루팅 상태를 체크해봅니다.

이제 본격적으로 앱을 열어서 확인해봅니다. 열어서 확인해보면 앱이 루팅을 탐지하고 있는 것을 확인할 수 있습니다.

이제 앱의 apk파일을 JEB에 올려서 어떻게 작동하는지 확인해봅니다.

 

이제 앱을 정적 분석을 이용해서 찾아보겠습니다. 기본적으로 root에 관련된 함수이므로 이름이 root와 관련될 가능성이 높습니다.

하지만 가장 먼저 다이얼로그에 나타나는 Root detected라는 단어를 사용하는 부분을 그대로 찾겠습니다. Ctrl + F 를 눌러서 특정 문자열을 검색해줍니다.

 

이제 해당 부분을 클릭하여 분석에 들어갑니다. 클릭하면 해당 부분의 스마일리 코드를 확인할 수 있습니다.

해당 부분의 소스코드를 확인하기 위해서 해당 단어부분을 클릭하고 tab키를 눌러서 확인합니다. 그러면 아래와 같은 소스코드를 확인할 수 있습니다. 해당 앱의 경우 c.a() 또는 c.b() 또는 c.c() 에서 루팅을 감지하면 해당 문자열이 출력되는 구조입니다.

 

첫번째 방법 : 루팅 탐지를 우회하는 방법

본격적으로 어느 부분에서 루팅을 탐지하는지 확인했다면 세부적으로 분석한 후에 루팅탐지하지 않도록 우회해야 합니다.

sg.vantagepoint.uncrackable1.MainActivity

이제 이부분중 하나라도 참이 되지 않도록 작성해야 루팅탐지를 우회할 수 있습니다. 해당 코드는 sg.vantagepoint.a.c.a() sg.vantagepoint.a.c.b() sg.vantagepoint.a.c.c() 입니다.

sg.vantagepoint.a

이제 루팅 탐지 우회를 위한 js파일을 작성해보겠습니다.

abc.js

function hookA() {
    Java.perform(function() {
        var class_c = Java.use("sg.vantagepoint.a.c");
        class_c.a.implementation = function() {
            console.log('[*] Hooking a');
            return false;
        }
    });
}

function hookB() {
    Java.perform(function() {
        var class_c = Java.use("sg.vantagepoint.a.c");
        class_c.b.implementation = function() {
            console.log('[*] Hooking b');
            return false;
        }
    });
}

function hookC() {
    Java.perform(function() {
        var class_c = Java.use("sg.vantagepoint.a.c");
        class_c.c.implementation = function() {
            console.log('[*] Hooking c');
            return false;
        }
    });
}

// 필요한 함수를 호출하여 후킹 작업 수행
hookA();
hookB();
hookC();

이와 같은 형태로 작성하여 루팀 탐지 로직을 우회하였습니다.

 

해당 코드 작성 후  frida -U -f owasp.mstg.uncrackable1 -l "C:\Users\USER\OneDrive\바탕 화면\abc.js" 명령을 이용하여 후킹코드를 작동시킵니다. 작동시키면 아래와 같은 결과를 터미널에서 확인할 수 있고 앱이 실행되는 것을 확인할 수 있습니다.

위와 같은 방법으로 루팅탐지 로직을 우회하여 앱에 접근이 가능합니다.

 

두번째 방법 : exit()의 작동을 우회하여 앱이 꺼지지 않도록 함

해당 방법은 루팅자체는 탐지하지만 exit()를 조작하여 앱이 꺼지지 않도록 하는 방법입니다.

sg.vantagepoint.uncrackable1

해당 부분을 참조하면 앱 다이얼로그에서 OK 부분을 클릭했을 때 System.exit(0);를 호출하여 종료시키는 로직이 존재합니다. 따라서 이 부분을 후킹하여 동작하지 않도록 바꿔줍니다. js파일을 하나 생성하여 후킹하도록 하겠습니다.

exit.js

Java.perform(function() {
        var exitClass = Java.use("java.lang.System");
        exitClass.exit.implementation = function(args1) {
            console.log('[*] System.exit function hooking');
        }
    });

이 코드를 작성한 이후  frida -U -f owasp.mstg.uncrackable1 -l "C:\Users\USER\OneDrive\바탕 화면\exit.js"를 이용하여 작동시킵니다. 이제 확인해보면 아래와 같이 작동하는 것을 확인할 수 있습니다. 실행시켜도 루팅을 탐지하는 부분은 여전히 존재하는 것을 확인할 수 있습니다.

정상적으로 코드가 작동하여 앱이 종료되지 않고 실행되는 것을 확인할 수 있습니다. 

 

비밀번호 우회하기

이 앱은 위에서 살펴보았던것은 크게 두가지였습니다.

1. 루팅탐지 로직 우회하기

2. 루팅탐지는 허용하되 exit() 메소드를 조작하여 앱의 종료 막기

 

첫번째 방법 : 비밀번호가 틀려도 맞게 하도록 우회하기

이렇게 두가지를 완료하였다면 이제는 앱의 어떤 코드를 작성해서 비밀번호를 알아내야합니다.  먼저 비밀번호가 틀렸을 때 앱에서 어떤 반응이 나타나는지 확인해 봅니다.

이제 이 키워드를 이용해서 앱의  VERIFY를 눌렀을 때 나오는 다른 메시지를 찾습니다.

위의 코드를 살펴보았을 때 성공했을 때는 Success! 라는 키워드가 노출된다는 것을 확인할 수 있습니다. 해당 키워드 출력은 a.a(s)가 참인지 거짓인지에 따라서 결과를 다르게 호출한다는 것도 확인할 수 있습니다.

 

본격적으로 a.a(s)를 살펴보겠습니다.

분석해 보면 a()는 boolean 형태의 함수이므로 true 혹은 false만 출력합니다. 이를 판단하는 기준은 s.equals() 이므로 equals() 함수의 리턴값을 기준으로 판단하고 있습니다. 따라서 해볼 수 있는 발상 중 하나는 equals() 함수를 항상 참으로 만들어서 호출하는 방법이 있습니다. 이번엔 이 코드에 대해서 작성해 보고자 합니다.

일단 equals()에 대해서 살펴보면 아래와 같은 함수임을 알 수 있습니다.

따라서 이 경로를 확인하고 후킹 코드를 작성해줍니다. 작성할 때 유의할 점은 기존의 루팅탐지 우회 코드에 따로 작성해서 추가합니다. 코드는 아래와 같이 작성합니다. 

uncrack.js

function hookSystemExit() {
    Java.perform(function() {
        var exitClass = Java.use("java.lang.System");
        exitClass.exit.implementation = function(args1) {
            console.log('[*] System.exit function hooking');
        }
    });
}

function equalsTrue() {
    Java.perform(function() {
        var equalsClass = Java.use("java.lang.String");
        equalsClass.equals.implementation = function(args2) {
            console.log(args2);
			return true;
        }
    });
}

// 함수를 호출하여 후킹 작업 수행
hookSystemExit();
equalsTrue();

이와 같은 코드를 작성하고 frida -U -f owasp.mstg.uncrackable1 -l "C:\Users\USER\OneDrive\바탕 화면\uncrack.js"를 실행시키면 아래와 같은 결과를 얻을 수 있습니다.

두번째 방법 : 비밀번호 알아내기

두번째 방법은 비밀번호를 직접 알아내는 방법입니다.

해당 방법을 이용하면 비밀번호를 직접 알아내서 해결할 수 있습니다.

참조해야 될 부분은 

이 부분에 대해서 확인하고 코드를 작성해야 합니다.

 

이 부분을 참조해서 코드를 작성하면 아래와 같습니다.

find.js

function hookSystemExit() {
    Java.perform(function() {
        var exitClass = Java.use("java.lang.System");
        exitClass.exit.implementation = function(args1) {
            console.log('[*] System.exit function hooking');
        }
    });
}

function hookDecryptMethod() {
    Java.perform(function() {
        var decryptClass = Java.use("sg.vantagepoint.a.a");
    
        decryptClass.a.implementation = function(args1, args2) {
            console.log('[*] Hooking Call Decrypt');
            var retVal = this.a(args1, args2);
        
            console.log("[*] Passcode: " + retVal);
            return retVal;
        }
    });
}

hookSystemExit();
hookDecryptMethod();

이와 같이 코드를 작성하고 프리다를 작동시켜서 결과를 확인해봅니다.

숫자 형태로 패스워드가 나타나는 것을 확인할 수 있습니다.

여기에 나오는 Passcode를 그대로 10진수 텍스트 변환기를 이용하여 확인해봅니다.

정답은 I want to believe 입니다.

이 정답이 맞는지 직접 확인해 봅니다.

정답이 맞는 것을 확인할 수 있습니다.

기왕 작성한 거 직접 정답을 출력하는 형태로 만들어 보려고 합니다.

해당 코드를 직접 보여주도록 js 파일을 작성합니다.

find.js

function hookSystemExit() {
    Java.perform(function() {
        var exitClass = Java.use("java.lang.System");
        exitClass.exit.implementation = function(args1) {
            console.log('[*] System.exit function hooking');
        }
    });
}

function hookDecryptMethod() {
    Java.perform(function() {
        var decryptClass = Java.use("sg.vantagepoint.a.a");
    
        decryptClass.a.implementation = function(args1, args2) {
            console.log('[*] Hooking Call Decrypt');
            var retVal = this.a(args1, args2);
        
            var passcode = String.fromCharCode.apply(null, retVal);
            console.log("[*] Passcode: " + passcode);
            return retVal;
        }
    });
}

hookSystemExit();
hookDecryptMethod();

이 코드를 작성해서 결과를 확인해보면

답이 I want to believe인 것을 확인할 수 있습니다.

이상입니다~

'Wargame & CTF' 카테고리의 다른 글

[Portswigger] Path traversal  (0) 2023.12.22
[Dreamhack] Addition calculator  (1) 2023.12.21
[Webhacking.kr] 1,3,14,15,17번  (1) 2023.12.21
[Dreamhack] baby-union  (0) 2023.12.20
[Dreamhack] baseb64  (0) 2023.12.20