Home [Hackthebox] SAW
Post
Cancel

[Hackthebox] SAW

예전에 풀었던 문제인데 재밌었던 문제여서 여기에 올려본다ㅋㅋ 모바일 문제가 많이 있으면 좋겠는데… 많이 없어서 아쉽다🥲

키워드: Mobile, Reversing

description

코드 분석


문제 apk파일을 에뮬에 설치하고 실행을 했을 때 아무런 반응이 없었다. 따라서 jadx를 이용하여 코드를 살펴보았다. (Android 10.0 이상이었기때문에 에뮬로 돌렸다.)
onCreate 함수의 31L을 보면 getExtras 해주는 부분이 있는데 어디에도 putExtras를 하는 모습은 보이지 않는다. 따라서 extras는 null로 유추가 되고 바로 꺼지는 이유를 알 수 있었다. 또한 34L을 보면 open:sesame의 key-value 형태로 전달해야함을 알 수 있다.
oncreate

putExtra 함수는 액티비티 간 값을 주고 받는데 주로 쓰인다. 액티비티 간에 이동하기 위해서는 Intent로 액티비티 이동을 하게된다. 이 때 이전 액티비티에서 어떤 값을 넘기고 싶을 때 쓰면된다.


아래의 후킹코드를 이용하여 앱이 정상적으로 작동하도록 한다.

1
2
3
4
5
6
7
8
function hookGetIntent(){
    var cls = Java.use('android.app.Activity');

    cls.getIntent.implementation = function(){
        
        return this.getIntent().putExtra('open','sesame');
    }
}


문제를 풀고나서 구글링을 통해 다음과 같이 am(Activity Manager) 명령어로도 데이터를 전달할 수 있는 것을 알았다.

1
am start -a android.intent.action.MAIN --es 'open' 'sesame' -n com.stego.saw/.MainActivity


아무튼 이렇게 앱이 정상적으로 실행이 된다.
showup

하지만 Click me… 를 눌러보면 앱이 갑자기 종료가 된다. logcat을 확인해보면 아래와 같이 permission denied가 뜬것을 확인할 수 있다. 해결 방법은 앱 정보에서 '다른 앱 위에 그리기' 권한을 허용하면 된다.
logcat

그러면 이렇게 값을 입력할 수 있는 다이얼로그가 뜬다.
dialog

다이얼로그를 띄우는 alert 함수를 보면 69L에서 사용자의 입력값을 this.answer에 저장하고 그 값을 a 함수의 인자값으로 전달하는 것을 알 수 있다.
alert

a 함수가 선언되어있는 곳으로 가보면 native 키워드가 있는 것으로 보아 라이브러리에서 로딩하는 함수이고, 라이브러리 이름이 default와 관련있는 것을 유추할 수 있다.
native

libdefault.so 분석


ida를 통해서 libdefault.so를 열어보면 심볼이 제거되어있지 않기 때문에 a 함수를 쉽게 찾을 수 있다. 그리고 이곳에서 _Z1aP7_JNIEnvP8_1 함수를 사용하는 것을 알 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
__int64 __fastcall a(__int64 a1, __int64 a2, __int64 a3, __int64 a4)
{
  __int64 v4; // x21
  __int64 v5; // x19
  __int64 v6; // x20
  const char *v7; // x22
  const char *v8; // x0

  v4 = a3;
  v5 = a4;
  v6 = a1;
  v7 = (const char *)(*(__int64 (**)(void))(*(_QWORD *)a1 + 1352LL))();
  v8 = (const char *)(*(__int64 (__fastcall **)(__int64, __int64, _QWORD))(*(_QWORD *)v6 + 1352LL))(v6, v5, 0LL);
  _Z1aP7_JNIEnvP8_1(v7, v8);
  (*(void (__fastcall **)(__int64, __int64, const char *))(*(_QWORD *)v6 + 1360LL))(v6, v4, v7);
  return (*(__int64 (__fastcall **)(__int64, const char *))(*(_QWORD *)v6 + 1336LL))(v6, v7);
}


_Z1aP7_JNIEnvP8_1 함수로 가보면 아래와 같이 인자로 전달된 값과 특정 바이트를 xor 연산을 하는 부분이 존재하고 저장된 바이트와 맞지 않으면 result 변수에 1을 저장하고 리턴 해버리는 분기가 존재한다.
z1

만약에 맞다면 fopen 함수를 통해서 어떤 파일을 열고 fput 함수를 통해서 어떤 내용을 쓴다.
z2

Get Flag


_Z1aP7_JNIEnvP8_1 함수에서 검사하고자 하는 문자열이 어떤 것인지 알기 위해서 xor 연산의 특성을 이용하여 다음과 같은 스크립트를 짤 수 있다.

1
2
3
4
5
6
7
8
9
10
11
info = lambda x: print(f'[+] {x}')

key_array = [0xa, 0xb, 0x18, 0xf, 0x5e, 0x31, 0xc, 0xf]
result_array = [0x6c, 0x67, 0x28, 0x6e, 0x2a, 0x58, 0x62, 0x68]
input = ''

for i in range(len(key_array)):
    tmp = chr(key_array[i] ^ result_array[i])
    input += tmp
    
info(f'Input ==> {input}')


위 코드를 실행하면 fl0ating 이라는 문자열이 나온다. 또한 어느 위치에 파일을 쓰는지 알기위해서 _Z1aP7_JNIEnvP8_1 함수를 후킹한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function libDefault() {
    var lib_addr=Module.findBaseAddress('libdefault.so');
    const OFFSET = 0xab4
    
    Interceptor.attach(ptr(lib_addr).add(OFFSET), {
        
        onEnter: function (args) { 
            console.log(`[+] Enter!!`)
            console.log(`[+] ${args[0].readUtf8String()}`);
            console.log(`[+] ${args[1].readUtf8String()}`);
        }, 
        onLeave: function (retval) {
    
            console.log(`[+] ${retval.toString()}`);
        } 
    }); 
}


fl0ating을 입력하면 아래와 같이 경로는 /data/user/0/com.stego.saw/임을 알 수 있다.
path

해당 경로에 들어가서 생성된 h 파일을 확인해보면 플레그를 얻을 수 있다.
flag

Referece


[Tip] ngrok & Spawn stty shell

[Hackthebox] Investigator

Comments powered by Disqus.