반응형

■문제풀이 요약

(1) frida 탐지 코드 패치

(2) ptrace 호출 NOPping

(3) frida를 통한 루팅 탐지 및 무결성 검증 우회

(4) IDA 동적 디버깅을 통한 암호값 획득

※ Frida 만을 사용한 풀이 --> https://hackcatml.tistory.com/105

 

■문제풀이 과정

frida spawning 할 경우 앱 종료

 

ghidra에서 frida 탐지 코드 확인.

 

frida 탐지코드는 "FUN_001030d0()" 함수에 정의되어 있는데, 호출관계를 역추적해보면, "_INIT_0()" 함수에서 호출하고 있음을 확인 가능.

따라서, "libfoo.so" 파일이 로드되면 별도의 쓰레드에서 프리다 탐지 함수가 돌고 있겠거니 생각할 수 있고, "frida"나 "xposed"가 /proc/self/maps 에서 발견되면 goodbye() 함수를 호출하고 종료.

 

frida 탐지 못하도록 "frida" 문자열 "frada"로 패치.

 

앱 실행 후 ps -ef 로 확인 시 프로세스 두개 생성되어 있음. --> ptrace(PTRACE_ATTACH, ...) 의심.

 

ptrace함수를 IDA에서 검색해보면 "sub_323C"에서 사용되어지고 있음

 

"sub_323C"함수는 "Java_sg_vantagepoint_uncrackable3_MainActivity_init" 에서 호출되고 있음.

 

ptrace 호출 Nopping

 

"frida" 문자열 패치 및 "ptrace" 호출 NOPping 후 앱 설치 및 실행하고 프로세스 확인.

알 수 없는 프로세스가 하나 실행되고 있으나, 정상적으로 frida attach 가능.(ptrace에 의해 부모 프로세스가 선점유되어 있는 경우 frida attach 불가)

 

※ ptrace 호출만 NOPping 패치하고, frida 탐지 코드는 패치하지 않은 경우

"Process crashed: Trace/BPT trap" 앱 crash 발생. 

 

※ frida 탐지만 패치하고, ptrace호출은 패치하지 않는 경우

이 경우 ptrace anti debugging은 frida spawning으로 부모 프로세스를 선점유하여 우회가 가능함.

그러나 이렇게 할 경우, IDA를 통한 so파일 동적디버깅은 불가.

 

이제 Rooting 및 위변조 탐지 로직을 우회해야 함.

 

MainActivity에서 루팅 및 위변조(무결성) 검사를 하고 있음.

 

frida 탐지 및 ptrace를 우회하기 위해 so파일을 변조한 결과 .so파일의 무결성이 깨졌으므로 verifyLibs() 함수를 우회해야함.

또한, 루팅된 단말기에서 앱을 실행하고 있으므로 RootDetection 클래스도 우회해야함.

Java.perform(function(){
    // 루팅 탐지 우회
    var sg_vantagepoint_util_RootDetection = Java.use("sg.vantagepoint.util.RootDetection");

    sg_vantagepoint_util_RootDetection.checkRoot1.overload().implementation = function(){
        console.log("[*] RootDetection hooked")
        return false;
    }

    // 무결성 검증 우회
    var sg_vantagepoint_uncrackable3_MainActivity = Java.use('sg.vantagepoint.uncrackable3.MainActivity');

    sg_vantagepoint_uncrackable3_MainActivity.verifyLibs.overload().implementation = function(){
        console.log("[*] verifyLibs() hooked");
    }
})

==> 실행 결과

 

이제 암호를 찾아야 함.

MainActivty의 verify메서드에서 입력한 암호가 맞는지 여부를 검사.

 

check_code를 따라 들어가면, native 함수 bar로 사용자가 입력한 암호문이 넘어가느 것 확인.

 

bar 함수에 대해서 분석한 결과는 다음과 같음.

 

결국, v7과 v9의 값을 알아볼 필요가 있으므로, IDA로 .so파일 동적 디버깅 수행 필요.

IDA에서 break 포인트 세팅하고, 단말기에서 "qwer1234" 입력하여 동적 디버깅 수행.

 

IDA로 while문까지 진행한 다음에 local변수들을 살펴보면, v7 변수에는 내가 입력한 "qwer1234" 들어가는 것 확인.

 

cf) IDA로 while문까지 진행하려면 if ( (*(*v5 + 0x558LL))(v5, v4) == 0x18 ) 를 통과해야 하는데, 이는 분기문에서 z플래그의 값을 0에서 1로 바꿔주면 됨.

 

qword_70FBD34038에는 "pizzapizzapizzapizzapizz" 총 24자의 문자열이 들어있는 것 확인.

 

v9에는 SP+8 위치로 이동해보면 문자열로 변환되지 않는 hex값("1D 08 11 13 0F 17 49 15 0D 00 03 19 5A 1D 13 15  08 0E 5A 00 17 08 13 14")이 들어있는 것 확인 가능.

 

즉, "pizzapizzapizzapizzapizz" 문자열과 hex값("1D 08 11 13 0F 17 49 15 0D 00 03 19 5A 1D 13 15  08 0E 5A 00 17 08 13 14")에 대하여 각 자리마다 xor 연산을 수행하고, 그 값이 사용자가 입력한 값과 일치하는지 여부를 비교하게 되고, 24자 모두 일치하게되면 결과값으로 1을 반환하는 로직임.

따라서, xor 연산을 해서 암호문을 찾으면 됨.

xor 계산기: http://xor.pw/

 

※ 참고

https://enovella.github.io/android/reverse/2017/05/20/android-owasp-crackmes-level-3.html

https://bbs.pediy.com/thread-255361.htm

 

※ 도움이 되셨다면 공감이나 댓글 부탁드려요.

반응형

+ Recent posts