cgy12306

[OWASP-MSTG] Level 1 본문

Mobile/OWASP-MSTG

[OWASP-MSTG] Level 1

cgy12306 2022. 4. 17. 17:20

 

파일 다운로드 경로 : https://github.com/OWASP/owasp-mstg/tree/master/Crackmes/Android/Level_01

 

GitHub - OWASP/owasp-mstg: The Mobile Security Testing Guide (MSTG) is a comprehensive manual for mobile app security testing an

The Mobile Security Testing Guide (MSTG) is a comprehensive manual for mobile app security testing and reverse engineering. It describes the technical processes for verifying the controls listed in...

github.com

 

Nox player 에 드로그앤 드롭으로 UnCracktable-Level1.apk 파일을 옮겨 줍니다.

 

 

실행 시켜보면 다음과 같은 화면이 뜹니다.

 

 

맨 처음 녹스 플레이어를 설치했을 때 ROOT를 켰기 때문에 루팅이 탐지되어 정상적으로 실행이 되지 않습니다.

dex2jar를 통해 디컴파일을 하고 jd-gui로 분석해보겠습니다.

 

 

첫 화면의 루팅탐지를 담당하는 듯 합니다.

c클래스의 a(), b(), c() 메소드 중 하나라도 참이면 루팅 탐지에 걸립니다.

 

 

c 클래스의 코드입니다.

 

 

우선 a() 메소드를 살펴보겠습니다.

환경변수에서 su라는 파일이 존재하면 true를 반환합니다. su라는 파일이 존재하지 않으면 false를 반환합니다.

 

 

b() 메소드를 확인해보면 Build.TAGS의 값이 null이 아니어야 하고 test-keys라는 문자열을 포함하면 true를 반환합니다.

false를 반환하기 위해서는 Build.TAGS 값이 null이거나 test-keys를 포함하면 됩니다.

 

 

c() 메소드를 보면 위의 파일중 하나라도 포함하면 true를 반환하고, 그렇지 않을 경우 false를 반환합니다.

 

 

다시 MainActivity로 돌아와서 메인의 a 메소드를 확인해보겠습니다.

 

 

인자로 넘겨받은 스트링을 출력하고 프로그램을 종료합니다.

다시 MainActivity의 onCreate 두번째 분기문을 보겠습니다.

 

 

b클래스의 a 메소드를 호출합니다.

 

 

flag와 2를 and 연산하여 0이면 false, 1이면 true를 반환합니다.

 

해당 플래그는 디버깅을 할 수 있는지를 체크합니다.

 

 

즉, 디버깅 플래그가 set되어 있으면 MainActivity의 a() 메소드를 호출하여 exit() 함수를 호출하게 되어 프로그램이 강제종료 됩니다.

 

 

그러면 첫번째로 해야할 일이 frida를 이용해 exit() 함수를 후킹하여 종료가 되지 않도록 합니다.

import frida, sys

def on_message(message, data):
    print(message)

PACKAGE_NAME = "uncrackable1"

jscode = """
console.log("[+] Start Script");

Java.perform(function(){
    console.log("[+] Hooking System.exit");
    var exitClass = Java.use("java.lang.System");
    exitClass.exit.implementation = function(){
        console.log("[+] System.exit called");
    }
});
"""

process = frida.get_usb_device(1).attach(PACKAGE_NAME)
script = process.create_script(jscode)
script.on("message", on_message)
print("[+] Running Hook")
script.load()
sys.stdin.read()

 

후킹이 완료되면 String을 입력할 수 있게 됩니다.

이제 verify() 메소드를 분석해보겠습니다.

 

 

입력한 string 값으로 a클래스의 a() 메소드를 호출하여 반환값이 true면 성공, false 다시 시도하라는 내용입니다.

 

 

하드코딩된 base64를 디코딩하여 arrayOfByte 변수에 담습니다.

그리고 b() 메소드를 호출하는데 인자로 스트링을 넘겨줍니다.

b() 메소드의 반환값과 base64 디코딩된 값을 a클래스의 a() 메소드를 호출하여 반환값을 arrayOfByte에 다시 담습니다. 이후 arrayOfByte와 내가 입력한 string과 비교하여 일치여부에 따라 bool 값을 반환합니다.

 

이제 b() 메소드를 보겠습니다.

 

 

파라미터 길이의 절반 값을 동적할당 합니다.

그리고 각 배열의 문자 두개씩 뽑아 첫번째 숫자(16진수)를 left shift 4 연산을 하고 두번째 숫자(16진수)와 더해 문자열을 구합니다.

이 반환값과 base64 디코딩된 값을 a class의 a() 메소드를 인자로 호출합니다.

 

 

SecretKeySpec은 암복호화하기 위해 사용되는 클래스입니다.

 

 

첫번째 파라미터 값을 key로 이용해 PKCS7Padding 모드로 패딩을 진행합니다.

init 메소드에 첫번째 인자로 2가 입력이 됩니다. init 메소드의 첫번째 인자의 의미는 암복호화할때 opmode를 의미합니다.

 

 

2는 DecryptMode이므로 두번째 인자를 AES 복호화를 진행합니다.

 

이제 분석이 끝났으니 올바른 string을 찾아야 합니다.

방법은 직접 string을 찾는 방법과 frida라는 간편한 툴을 이용해 후킹을 진행하여 string을 찾는 방법이 있습니다.

 

frida의 후킹을 이용하여 찾아보겠습니다.

import frida, sys

def on_message(message, data):
    print(message)

PACKAGE_NAME = "uncrackable1"

jscode = """
console.log("[+] Start Script");

Java.perform(function(){
    console.log("[+] Hooking exit()");
    var systemClass = Java.use("java.lang.System");
    systemClass.exit.implementation = function(){
        console.log("[+] exit() called");
    }
});
Java.perform(function(){
    console.log("[+] Hooking equals()");
    var stringClass = Java.use("java.lang.String");
    stringClass.equals.implementation = function(arg1){
        console.log("[+] equals() called");
        console.log("[+] String hooked : " + arg1);
        return true;
    }
});
"""

process = frida.get_usb_device(1).attach(PACKAGE_NAME)
script = process.create_script(jscode)
script.on("message", on_message)
print("[*] Running Hook")
script.load()
sys.stdin.read()

 

 

많은 값들이 뜨게 되는데 Cipher 이후에 호출된 equals()가 flag값이 됩니다.

 

 

sg.vantagepoint.a.a.a() 메소드를 후킹한 버전

import frida, sys

def on_message(message, data):
    print(message)

PACKAGE_NAME = "uncrackable1"

jscode = """
console.log("[+] Start Script");

Java.perform(function(){
    console.log("[+] Hooking exit()");
    var systemClass = Java.use("java.lang.System");
    systemClass.exit.implementation = function(){
        console.log("[+] exit() called");
    }
});

Java.perform(function(){
    console.log("[+] Hooking sg.vantagepoint.a.a.a()");
    var aClass = Java.use("sg.vantagepoint.a.a");
    aClass.a.implementation = function(arg1, arg2){
        var str = this.a(arg1, arg2);
        var flag = "";

        for(var i = 0; i < str.length; i++){
            flag += String.fromCharCode(str[i]);
        }
        console.log("[+] flag : " + flag);
        return str;
    }
});
"""

process = frida.get_usb_device(1).attach(PACKAGE_NAME)
script = process.create_script(jscode)
script.on("message", on_message)
print("[*] Running Hook")
script.load()
sys.stdin.read()

 

 

에러 해결

frida.ProcessNotFoundError: unable to find process with name 'owasp.mstg.uncrackable1’

후킹 코드중 PACKAGE_NAME 변수에 패키지 이름 전부를 넣었더니 실행이 되지 않았습니다.

그래서 해결 방법은 frida-ps -U 명령어로 실행중이 프로세스의 이름을 가져와 넣었더니 제대로 실행이 되었습니다.

 

 

참고 : https://mingzz1.github.io/wargame/owasp/2020/09/10/owasp_uncrackable1_write-up.html/

https://frida.re/docs/home/

https://developer.android.com/reference/android/content/pm/ApplicationInfo#FLAG_DEBUGGABLE

 

Comments