cannary是如何生成的?
canary是如何生成并且写入TLS中0x28的?
canary是rbp之前8byte大小的随机区域(最低位为0),从fs:0x28读取并且在函数返回前校验。
简而言之,在ELF加载到内存后,执行_start
函数时,最终会调用security_init()
函数来向tcbhead_t.stack_guard中写入canary的值。其中canary的值是内核已经生成好保存在_dl_random
中的。glibc仅仅是将其最低位置为0后就存入tcbhead_t.stack_guard中。具体细节如下:
canary的结构
canary本质上是tcbhead_t结构体中的stack_guard。
sysdeps/x86_64/nptl/tls.h line 53 中 tcbhead_t结构体定义如下:
typedef struct // glibc canary相关结构
{
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;
int gscope_flag;
uintptr_t sysinfo;
uintptr_t stack_guard; // fs:0x28 canary的值
uintptr_t pointer_guard;
unsigned long int vgetcpu_cache[2];
# ifndef __ASSUME_PRIVATE_FUTEX
int private_futex;
# else
int __glibc_reserved1;
# endif
int __glibc_unused1;
/* Reservation of some values for the TM ABI. */
void *__private_tm[4];
/* GCC split stack support. */
void *__private_ss;
long int __glibc_reserved2;
/* Must be kept even if it is no longer used by glibc since programs,
like AddressSanitizer, depend on the size of tcbhead_t. */
__128bits __glibc_unused2[8][4] __attribute__ ((aligned (32)));
void *__padding[8];
} tcbhead_t;
写入过程
调用链:_start->_dl_start->_dl_start_final->_dl_sysdep_start->security_init
pwndbg> bt
#0 security_init () at rtld.c:721
#1 dl_main (phdr=<optimized out>, phnum=<optimized out>, user_entry=<optimized out>, auxv=<optimized out>) at rtld.c:1727
#2 0x00007ffff7df0632 in _dl_sysdep_start (start_argptr=start_argptr@entry=0x7fffffffe650, dl_main=dl_main@entry=0x7ffff7dd91e0 <dl_main>) at ../elf/dl-sysdep.c:249
#3 0x00007ffff7dd8c2a in _dl_start_final (arg=0x7fffffffe650) at rtld.c:323
#4 _dl_start (arg=0x7fffffffe650) at rtld.c:429
#5 0x00007ffff7dd7c38 in _start () from /lib64/ld-linux-x86-64.so.2
#6 0x0000000000000001 in ?? ()
#7 0x00007fffffffe87e in ?? ()
#8 0x0000000000000000 in ?? ()
elf/rtld.c 中 security_init中获取kernel设置好的_dl_random,清零最后一位,调用THREAD_SET_STACK_GUARD存储。
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); // set canary
#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;
}
fbtl/sysdeps/x86_64/tls.h line 351 (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)
# define THREAD_COPY_STACK_GUARD(descr) \
((descr)->header.stack_guard \
= THREAD_GETMEM (THREAD_SELF, header.stack_guard))
:
fbtl/sysdeps/x86_64/tls.h line 253 : (THREAD_SETMEM的定义)
/* Same as THREAD_SETMEM, but the member offset can be non-constant. */
# define THREAD_SETMEM(descr, member, value) \
({ if (sizeof (descr->member) == 1) \
asm volatile ("movb %b0,%%fs:%P1" : \
: "iq" (value), \
"i" (offsetof (struct pthread, member))); \
else if (sizeof (descr->member) == 4) \
asm volatile ("movl %0,%%fs:%P1" : \
: IMM_MODE (value), \
"i" (offsetof (struct pthread, member))); \
else \
{ \
if (sizeof (descr->member) != 8) \
/* There should not be any value with a size other than 1, \
4 or 8. */ \
abort (); \
\
asm volatile ("movq %q0,%%fs:%P1" : \
: IMM_MODE ((uint64_t) cast_to_integer (value)), \
"i" (offsetof (struct pthread, member))); \
}})
THREAD_SETMEM宏的定义很复杂,具体调试起来就是一个写操作。源码调试时设置源码搜索路径:
directory /root/Pwn/glibc/glibc-2.23/elf/
directory /root/Pwn/glibc/glibc-2.23/fbtl/
break security_init
set canary时的汇编如下:
todo问题
一个进程的中的canary值是一样的吗?
_dl_random的值是每次读区都随机吗?
为什么进程重启,canary的值就改变了?
tcb是什么?如果有多个thread,每个thread里的canary值还一样吗?
参考
- canary分析 https://hardenedlinux.github.io/2016/11/27/canary.html
- ctf wiki https://ctf-wiki.github.io/ctf-wiki/pwn/linux/mitigation/canary/
- sakura sixstar2018 babystack writeup http://eternalsakura13.com/2018/04/24/starctf_babystack/
- https://github.com/balsn/ctf_writeup/tree/master/20180421-*ctf#babystack-sces60107
- 栈溢出中关于canary的总结 https://zhakul.top/archives/216