Uncrackable2 write-up
Uncrackable2 write-up
앱 출처: https://github.com/OWASP/owasp-mstg/tree/master/Crackmes
Uncrackable1과 마찬가지로 루팅된 디바이스에서 앱 실행 시 아래와 같은 에러메시지와 함께 앱이 종료된다.
Uncrackable1과 동일하게 v0.setCancelable(false);를 smali 코드를 이용하여 v0.setCancelable(true);로 변경한 후 recompile하고 재설치하여 우회한다.
verify 함수를 분석해보면 위에서 생성된 CodeCheck 클래스의 a 함수의 결과에 의해 Success 문이 출력된느 것을 확인할 수 있다.
CodeCheck 클래스의 a함수를 분석해보면 사용자가 입력한 문자열을 bar 함수로 전송하는 것을 확인할 수 있으나 아래의 bar 함수에는 코드가 존재하지 않는다. bar 함수 앞에 native가 붙은 것으로 보아 라이브러리에 있는 코드를 사용하는 것으로 확인된다. MainActivity를 재확인 해보면 System.loadLibrary("foo"); 구문을 통해 라이브러리를 불러오는 것을 확인할 수 있다.
해당 라리브러리 파일은 apk를 디컴파일한 폴더에 lib/[해당 platform]/libfoo.so로 존재한다.
라이브러리 파일을 IDA를 통해 연후 Exports 탭에 가보면 CodeCheck_bar 함수를 확인할 수 있다.
해당 함수를 분석해보면 문자열을 처리하는 코드들과 비교하는 코드들이 보인다. 좀 더 내려가다 보면 strncmp 함수를 이용하여 문자열을 비교하는 것을 확인할 수 있다.
해당 부분을 통해 Secret String이 23자리인 것으로 확인할 수 있고 strncmp 함수를 통해 사용자 입력값과 평문의 Secret String과 비교하는 것으로 추측할 수 있다.
다음은 strncmp의 인수를 출력하여 secret string을 확인하는 후킹 코드이다.
setImmediate(function() { Java.perform(function() { console.log("[*] Hooking calls to System.exit");
var strncmp = undefined; imports = Module.enumerateImportsSync("libfoo.so"); for(i = 0; i < imports.length; i++) { if(imports[i].name == "strncmp") { strncmp = imports[i].address; break; } } Interceptor.attach(strncmp, { onEnter: function (args) { if(args[2].toInt32() == 23 && Memory.readUtf8String(args[0],23) == "01234567890123456789012") { console.log("[*] Secret string at " + args[1] + ": " + Memory.readUtf8String(args[1],23)); } }, }); console.log("[*] Intercepting strncmp"); }); }); |
Module.enumerateImportsSync()를 이용하여 libfoo.so 라이브러리에 존재하는 함수를 불러오고 해당 함수명이 strncmp일 경우 해당 함수의 주소 값을 저장한다.
int strncmp (const char * str1, const char * str2, size_t num) ; |
strcmp를 보게되면 총 3개의 인자를 갖는 것을 알 수 있다. arg[0], arg[1]은 비교할 문자열이고 arg[2]은 비교할 문자열의 길이이다.
Memory.readUtf8String(args[0],23) |
JAVA 문자열은 NULL로 끝나지 않는다. Frida의 Memory.readUtf8String 메소드로 문자열 포인터의 메모리 위치에 엑세스할 때
길이를 제공하지 않으면 Frida는 문자열이 끝나는 곳을 알 수 없어 에러가 발생하게 된다. 따라서 두번째 인수로 읽을 문자의 양을 지정하면 에러가 해결이 된다.
Interceptor.attach(strncmp, { onEnter: function (args) { if(args[2].toInt32() == 23 && Memory.readUtf8String(args[0],23) == "01234567890123456789012") { console.log("[*] Secret string at " + args[1] + ": " + Memory.readUtf8String(args[1],23)); } }, }); |
arg[0], arg[1] 둘 중 어느 것이 Secret String인지는 알 수 없으나 한번씩 해보면 확인할 수 있다. "01234567890123456789012" 문자열은 특별한 의미를 가지고 있는 것이 아니고 23자리의 사용자 입력 값이다.
즉, 첫번째 인자가 사용자가 입력한 값과 동일하고 세번째 인자가 23인 strncmp 함수만을 후킹하기 위한 if문이다.
frida -U -f 옵션을 이용하여 uncrackable2 앱을 실행시킨 후 위의 코드를 입력하여 실행시킨다.
(python을 이용하여 후킹 코드를 작성하여도 되나 에러메시지가 지속적으로 발생하여 아래의 방법을 이용하였다.)
해당 코드를 실행시킨 후 코드의 IF 문을 만족할 수 있도록 "01234567890123456789012"를 입력하고 VERIFY 버튼을 눌러주자.
다시 시도해보라는 메시지가 출력됐지만 stncmp 문의 두번째 인자를 출력하는 후킹 코드로 인해 Secret string을 확인할 수 있다.
확인한 문자열을 입력한 결과 success 메시지가 출력되는 것을 확인할 수 있다.
'Pentest > [Android]' 카테고리의 다른 글
Activity 강제 호출 with ADB(Android Debug Bridge) (3) | 2020.04.21 |
---|---|
Magisk를 이용한 안드로이드 루팅 (4) | 2020.04.10 |
안드로이드(갤럭시) 순정 펌웨어 설치 (3) | 2020.04.10 |
Uncrackable1 write-up (0) | 2019.03.03 |
Android 7.0 Nougat Burp Suite Proxy 설정 1 (0) | 2019.02.28 |