
Thread Local Storage
Thread Local Storage (TLS)๋ ๋ช ์นญ ๊ทธ๋๋ก ์ค๋ ๋์ ์ ์ฅ ๊ณต๊ฐ์ ์๋ฏธํ๋ค.
ELF ๋ฐ์ด๋๋ฆฌ๋ฅผ ์ดํด๋ณด๋ฉด, ๊ฐ๊ฐ์ ๋ชฉ์ ์ ๊ฐ์ง ์น์ ์์ ๋ฐ์ดํฐ๋ฅผ ๊ด๋ฆฌํ๋ค. ์ฝ๋๋ฅผ ์คํํ๊ธฐ ์ํ .text, ์ด๊ธฐํ๋์ง ์์ ์ ์ญ ๋ณ์๋ฅผ ์ํ .data๋ฑ์ ์์๋ก ๋ค ์ ์๋ค.
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 ๋งคํฌ๋ก์ ์ธ์๋ก ์ ๋ฌํ๋ค.
SET_FS
๋ค์์ dl_allocate_tls_storage์์ ํ ๋นํ TLS ์์ญ์ FS๋ก ์ด๊ธฐํํ๋ 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 ์์คํ ์ฝ์ ARCH_SET_FS๋ ํ๋ก์ธ์ค์ FS ์ธ๊ทธ๋จผํธ ๋ ์ง์คํฐ๋ฅผ ์ด๊ธฐํํ๋ ์์ ์ ์ํํ๋ ๋ช ๋ น์ด์ด๋ค.
๋ฐ๋ผ์ FS ์ธ๊ทธ๋จผํธ ๋ ์ง์คํฐ๋ TLS ์์ญ์ ๊ฐ๋ฆฌํค๊ฒ ๋๋ค.
Master Canary
์คํ ๋ฒํผ๋ฅผ ์ฌ์ฉํ๋ ๋ชจ๋ ํจ์๋ ๊ฐ์ ์นด๋๋ฆฌ ๊ฐ์ ์ฌ์ฉํ๋ค.
> ์์ ํจ์์์ ๋ฉ๋ชจ๋ฆฌ ๋ฆญ์ผ๋ก ์นด๋๋ฆฌ๋ฅผ ์์๋ผ ์ ์๋ค๋ฉด,
๋ค๋ฅธ ํจ์์์ ๋ฐ์ํ๋ ์คํ ๋ฒํผ ์ค๋ฒํ๋ก์ฐ์์ ์นด๋๋ฆฌ๋ฅผ ๋ฎ์ด์ฐ๊ณ ์คํ ํ๋ฆ์ ์กฐ์ํ ์ ์๋ค.
* SSP ๋์ ์๋ฆฌ
๋ฒํผ๋ฅผ ์ฌ์ฉํ๋ ํจ์์ ํ๋กค๋ก๊ทธ์์ FS:0x28์ ์์นํ๋ ๊ฐ์ ๊ฐ์ ธ์ RBP ๋ฐ๋ก ์์ ์ฝ์ ํ๋ค.
FS ์ธ๊ทธ๋จผํธ ๋ ์ง์คํฐ๋ ์์ arch_prctl ์์คํ ์ฝ์ ํตํด _dl_allocate_tls_storage์์ ํ ๋นํ ์ฃผ์๋ก ๋ชจ๋ ํจ์๊ฐ ํด๋น ์ฃผ์์์ ๊ฐ์ ๊ฐ์ ธ์ค๊ธฐ ๋๋ฌธ์ ๊ฐ์ ์นด๋๋ฆฌ ๊ฐ์ ์ด์ฉํ๋ค.
-> TLS ์ฃผ์์ 0x28 ๋ฐ์ดํธ ๋งํผ ๋จ์ด์ง ์ฃผ์์ ์์นํ ๋๋คํ ๊ฐ์ ๋ง์คํฐ ์นด๋๋ฆฌ (Master Canary)๋ผ๊ณ ํ๋ค.
๋ค์์ ์ด์ ์ ํ ๋นํ TLS ์์ญ์ ๋๋คํ ์นด๋๋ฆฌ ๊ฐ์ ์ฝ์ ํ๋ security_init ํจ์์ด๋ค.
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 ํจ์๋ ์ปค๋์์ ์์ฑํ ๋๋ค ๊ฐ์ ๊ฐ์ง๋ ํฌ์ธํฐ์ธ _dl_random์ ์ธ์๋ก ์นด๋๋ฆฌ๋ก ์์ฑํ๋ค.
์นด๋๋ฆฌ ๊ฐ ์์ฑ
๋ค์์ security_init ํจ์์์ ์ฒ์์ผ๋ก ํธ์ถํ๋ _dl_setup_stack_chk_guard ํจ์์ด๋ค.
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)));
์ฝ๋๋ฅผ ์ดํด๋ณด๋ฉด, ๊ณต์ฉ์ฒด ๋ณ์์ธ ret์ ์ปค๋์์ ์์ฑํ ๋๋คํ ๊ฐ์ ๊ฐ๋ dl_random์ ๋ฐ์ดํฐ๋ฅผ ๋ณต์ฌํ๋ค.
์ดํ, ๋ฐ์ด๋๋ฆฌ์ ๋ฐ์ดํธ ์ค๋๋ง(Byte Ordering)์ ๋ฐ๋ผ AND ์ฐ์ฐ์ ์ํํ๋๋ฐ, ๋ฆฌํ ์๋์ธ์ ๊ฒฝ์ฐ ๋ณต์ฌํ ๊ฐ์ ์ฒซ ๋ฐ์ดํธ๋ฅผ NULL ๋ก ๋ณํํ๋ค.
์นด๋๋ฆฌ์ ์ฒซ ๋ฐ์ดํธ๊ฐ NULL ์ธ ์ด์ ๋ _dl_setup_stack_chk_guard ํจ์๋ฅผ ๋ถ์ํ๋ฉด ์ ์ ์๋ค.
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)));
์นด๋๋ฆฌ ๊ฐ ์ฝ์
_dl_setup_stack_chk_guard์์ ์นด๋๋ฆฌ ๊ฐ์ ์์ฑํ ๋ค์, ํด๋น ๊ฐ์ THREAD_SET_STACK_GUARD ๋งคํฌ๋ก์ ์ธ์๋ก ์ ๋ฌํด ํธ์ถํ๋ค.
/* Set the stack guard field in TCB head. */
#define THREAD_SET_STACK_GUARD(value) \
THREAD_SETMEM (THREAD_SELF, header.stack_guard, value)
๋ค์์ ํด๋น ๋งคํฌ๋ก์ ์ ์ธ๋ถ์ด๋ค.
THREAD_SETMEM ๋งคํฌ๋ก๋ฅผ ํตํด ๋ ๋ฒ์งธ ์ธ์์ธ header.stack_guard ์์น์ value๋ฅผ ์ฝ์ ํ๋ค.
# tcphead_t ๊ตฌ์กฐ์ฒด
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;
ํ ๋น๋ TLS ์์ญ์ ๋ค์๊ณผ ๊ฐ์ด tcbhead_t ๊ตฌ์กฐ์ฒด๋ก ๊ตฌ์ฑ๋์ด ์๋๋ฐ, stack_guard๋ ์คํ ์นด๋๋ฆฌ์ ๊ฐ์ ๊ฐ์ง๋ ๋งด๋ฒ ๋ณ์์ด๋ค.
๋ฐ๋ผ์ THREAD_SET_STACK_GUARD๋ TLS + 0x28 ์์น์ ์์ฑ๋ ์นด๋๋ฆฌ ๊ฐ์ ์ฝ์ ํ๋ ๋งคํฌ๋ก์ด๋ค.
๋๋ฒ๊น
์์ ์ฝ๋)
// Name: master_canary.c
// Compile: gcc -o master_canary master_canary.c -no-pie
#include <stdio.h>
#include <unistd.h>
int main()
{
char buf[256];
read(0, buf, 256);
}
gdb์์๋ FS ์ธ๊ทธ๋จผํธ ๋ ์ง์คํฐ์ ์ฃผ์๋ฅผ ํ์ธํ๋ ๋ช ๋ น์ด๊ฐ ์๋ค.
์ด๋ฅผ ํตํด TLS ์ฃผ์์ ์๋ ๋ง์คํฐ ์นด๋๋ฆฌ๋ฅผ ํ์ธํ ์ ์๋ค.
'DreamHack > SystemHacking' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[DreamHack System Hacking] Master Canary - (2) (0) | 2022.09.19 |
---|---|
[DreamHack System Hacking] Master Canary - (1) (0) | 2022.09.19 |
[System Hacking] 2์ฃผ์ฐจ dreamhack stage 12 - (5) (0) | 2022.05.01 |
[System Hacking] 2์ฃผ์ฐจ dreamhack stage 12 - (4) (0) | 2022.05.01 |
[System Hacking] 2์ฃผ์ฐจ dreamhack stage 12 - (3) (0) | 2022.04.30 |