LineCTF 2022에 출제된 리버싱 문제이다.
아쉽게도 이번 CTF에서는 이거 한문제 풀었다.
대부분의 안드로이드 리버싱 문제가 그렇지만 폰으로 앱을 다운받아서 켜서 버튼을 누르면 종료되서 뭔가 문제가 있다 싶었다.
jadx 같은 안드로이드 앱 디컴파일러로 분석하면 액티비티 한개를 가지고 있고, 플래그 검사는 native 코드에서 하는 것을 알 수 있다.
그래서 그 네이티브 라이브러리를 IDA로 까보면 아래와 같은 플래그 검사 루틴이 존재했다.
strlen
으로 구한 문자열 길이로 반복문이 돌아가는 것을 통해서 1바이트 씩 플래그 검사를 한다고 추측할 수 있었고 저 코드를 직접 재구현주기로 했다.
핸드폰에서 앱이 안돌아 갔기 때문에 해당 코드를 정상적인 방법으로 실행이 불가능했다... 만약 가능했으면 frida
를 통해서 조금 더 쉽게 풀 수 있었을 것이다.
다행인 것은 termux
라는 안드로이드 앱을 통해서 핸드폰에 리눅스 쉘 환경을 구축할 수 있었고, arm64 명령어 실행이 네이티브로 가능했기 때문에 해당 library 를 dlopen
해서 플래그 검증과 관련된 메모리 값들은 심볼로 가지고 올 수 있어서 노가다를 좀 덜해도 됐다.
#include <stdio.h>
#include <dlfcn.h>
#include <stdlib.h>
int main()
{
void* handle = dlopen("/data/data/com.termux/files/home/rolling/libnative-lib.so",RTLD_NOW );
if(!handle)
{
puts("dlopen error");
exit(0);
}
printf("pid : %d\n", getpid());
puts("dlopen success");
printf("handle addr : %p\n", handle);
int len = 0;
unsigned int* (*meatbox)(char*) = dlsym(handle, "_Z7meatboxPc");
unsigned int* (*soulbox)(char*) = dlsym(handle, "_Z7soulboxPc");
unsigned int* (*godbox)(char*) = dlsym(handle, "_Z6godboxPc");
char* asc_box = (size_t)meatbox + (0x30e0);
printf("meatbox addr : %p\n",meatbox);
printf("soulbox addr : %p\n",soulbox);
printf("godbox addr : %p\n",godbox);
printf("asc_box addr : %p\n",asc_box);
char flag[0x100] = {0};
char* ch = flag;
int idx = 0;
int while_flag = 1;
while(while_flag)
{
//brute force
for(int i = 0x20; i<=0x7f; i++)
{
*ch = i;
unsigned int* res_meatbox = meatbox(ch);
unsigned int* res_soulbox = soulbox(ch);
unsigned int* res_godbox = godbox(ch);
//printf("%p\n", asc_box+4*idx);
if(*((unsigned int*)(asc_box+4*idx)) == *res_meatbox)
{
if(*((unsigned int*)&asc_box[4*(idx+1)]) == *res_soulbox)
{
if(*((unsigned int*)&asc_box[4*(idx+2)]) == *res_godbox)
{
printf("%c",i);
if(i == '}')
while_flag = 0;
break;
}
}
}
}
ch++;
idx += 3;
}
}
위 코드를 arm64 환경에서 컴파일 하고 돌리게 되면 플래그가 나온다.