Android/Uncrackable

Uncrackable 1 - Secret String

앱 정보

이전 루팅 우회를 사용하여 애플리케이션을 실행하면 다음과 같이 문자열을 입력받는 부분과 VERIFY 버튼을 확인할 수 있다.

[그림 1] 앱 기본 구성(루팅 탐지 이후)

 

이때 문자열을 아무 값을 넣은 후 VERIFY 버튼을 눌러주면 다음과 같이 다시 시도하라는 메시지 창을 확인할 수 있다.

[그림 2] 문자열 입력 및 인증

 

앱 분석

verify()

public void verify(View view) {
        String str;
        String obj = ((EditText) findViewById(R.id.edit_text)).getText().toString();
        AlertDialog create = new AlertDialog.Builder(this).create();
        if (a.a(obj)) {
            create.setTitle("Success!");
            str = "This is the correct secret.";
        } else {
            create.setTitle("Nope...");
            str = "That's not it. Try again.";
        }
        create.setMessage(str);
        create.setButton(-3, "OK", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialogInterface, int i) {
                dialogInterface.dismiss();
            }
        });
        create.show();
    }

verify() 함수를 분석하면 다음과 같은 정보를 알 수 있다.

  • obj 변수에 입력값을 저장한 후 a 클래스의 a() 함수에 인자로 넘겨준다.
  • 호출한 a() 함수의 반환 값이 True라면 "Success"가 타이틀인 박스를 생성하고 False라면 "Nope..."이 타이틀인 박스를 생성한다.

 

a()

public class a {
    public static boolean a(String str) {
        byte[] bArr;
        byte[] bArr2 = new byte[0];
        try {
            bArr = sg.vantagepoint.a.a.a(b("8d127684cbc37c17616d806cf50473cc"), Base64.decode("5UJiFctbmgbDoLXmpL12mkno8HT4Lv8dlat8FxR2GOc=", 0));
        } catch (Exception e) {
            Log.d("CodeCheck", "AES error:" + e.getMessage());
            bArr = bArr2;
        }
        return str.equals(new String(bArr));
    }

    public static byte[] b(String str) {
        int length = str.length();
        byte[] bArr = new byte[(length / 2)];
        for (int i = 0; i < length; i += 2) {
            bArr[i / 2] = (byte) ((Character.digit(str.charAt(i), 16) << 4) + Character.digit(str.charAt(i + 1), 16));
        }
        return bArr;
    }
}

public class a { //sg.vantagepoint.a.a.a
    public static byte[] a(byte[] bArr, byte[] bArr2) {
        SecretKeySpec secretKeySpec = new SecretKeySpec(bArr, "AES/ECB/PKCS7Padding");
        Cipher instance = Cipher.getInstance("AES");
        instance.init(2, secretKeySpec);
        return instance.doFinal(bArr2);
    }
}

a() 함수를 분석하면 다음과 같은 정보를 알 수 있다.

  • sg.vantagepoint.a.a.a() 함수에 첫 번째 인자로는 "8d127684cbc37c17616d806cf50473cc"의 바이트 값을 b() 함수로 넘긴 후의 반환 값을, 두 번째 인자로는 "5UJiFctbmgbDoLXmpL12mkno8HT4Lv8dlat8FxR2GOc="를 디코딩한 값을 넘겨준다.
  • sg.vantagepoint.a.a.a() 함수에서는 다음과 같이 진행된다.
    • obj 변수에 입력값을 저장한 후 a 클래스의 a() 함수에 인자로 넘겨준다.
    • SecretKeySpec(byte[]key,String Algorithm) 함수를 통해 넘어온 인자와 암호 알고리즘을 사용하여 SecretKeySpec을 생성한다.
    • Cipher.getInstance(String Algorithm) 를 통해 해당 알고리즘의 인스턴스를 생성한다.
    • public void init(int keysize, SecureRandom random)를 통해 난수를 생성한다.
    • 마지막으로 doFinal(byte[] input)을 통해 마무리한 후 이를 반환해준다.
  • 마지막으로 사용자가 입력한 값과 암호화된 문자열을 비교하여 같다면 True 다르다면 False를 반환한다.

 

Exploit

후킹을 이용한 암호화 키 확인

Exploit Code

console.log("Script loaded successfully");
Java.perform(function x() {
    console.log("java perform function");
    var sys = Java.use('java.lang.System');
    sys.exit.implementation = function () {
        console.log("success");
    }

    var crypto = Java.use("sg.vantagepoint.a.a");
    crypto.a.implementation = function (a,b) {
        var retval = this.a(a,b);
        var passcode = "";
        for (var i = 0 ; i < retval.length ; i++) {
            passcode += String.fromCharCode(retval[i]);
        }
        console.log("key : " + passcode);
        return retval;
    }
});

스크립트를 확인해보면 다음과 같다.

  • exit() 함수를 후킹 하여 루팅 감지에 걸려도 종료되지 않도록 한다.
  • sg.vantagepoint.a.a.a() 함수를 후킹 한 후 넘어온 인자를 그대로 다시 함수로 넘겨 반환 값을 retval 변수에 저장한다.
  • 반복문을 통해 retval 변수에 저장된 값을 다시 문자열 형태로 저장한다.
  • 이후 해당 값을 출력하면 비밀키를 알아낼 수 있다.

스크립트 실행 후 아무 문자나 입력하여 VERIFY 버튼을 눌러주면 다음과 같이 비밀키가 출력되는 것을 확인할 수 있다.

[그림 3] 암호문 출력 모습

 

마지막으로 획득한 비밀키를 입력 후 VERIFY 버튼을 눌러주면 "Success!"라는 타이틀의 메시지 박스를 확인할 수 있다.

[그림 4] 암호문 입력 및 인증 성공

 

Reference

'Android > Uncrackable' 카테고리의 다른 글

Uncrackable 2  (0) 2021.11.08
Uncrackable 1 - Root Detection  (0) 2021.11.01