Linux GLibC Stack Canary Values(转载)

I was recently been asked by a friend how the Linux’s stack canary values work. After performing a quick online research I wasn’t able to find anything useful to give him. So, here is my writing on how GNU C Library stack canary values work. :)
WARNING: There is no exploitation information in this post. Just the GLIBC’s stack canary value’s functionality.

Let’s have a look at this dummy C code…

1
2
3
4
5
6
7
8
9
xorl:~$ cat ahoy.c
#include <stdio.h>
 
int main( void )
{
         return printf ( "Ahoy!\n" );
}
 
xorl:~$

We’ll compile it using the -fstack-protector-all to force GCC into using the Stack Smashing Protection features it has.

1
2
3
4
5
xorl:~$ gcc -fstack-protector-all ahoy.c -o ahoy -ggdb2
xorl:~$ . /ahoy
Ahoy!
xorl:~$ gdb -q . /ahoy
(gdb)

Now it’s time to understand the code. So, here we are…

1
2
3
4
5
6
7
8
(gdb) disas main
0x080483f4 <main+0>:    lea    0x4(%esp),%ecx
0x080483f8 <main+4>:    and    $0xfffffff0,%esp
0x080483fb <main+7>:    pushl  -0x4(%ecx)
0x080483fe <main+10>:   push   %ebp
0x080483ff <main+11>:   mov    %esp,%ebp
0x08048401 <main+13>:   push   %ecx
0x08048402 <main+14>:   sub    $0x14,%esp

Since this post is mostly written for people that are just getting started with system’s internals, I’ll explain everything. The address of the stack pointer at an offset of 0×4 is loaded to ECX, then stack pointer is aligned and the original stack pointer (ECX-4) is pushed into the stack. Then, the well known function prologue takes place. The current base pointer (indicating a new stack frame) is pushed onto the stack and value of the stack pointer is placed in the base pointer. Next, the current value of ECX (containing the original stack pointer’s value) is also pushed on the stack and stack pointer is decremented by 0×14 in order to make sufficient space for the function.

1
2
3
0x08048405 <main+17>:   mov    %gs:0x14,%eax
0x0804840b <main+23>:   mov    %eax,-0x8(%ebp)
0x0804840e <main+26>:   xor    %eax,%eax

This is the actual stack canary code in the binary. It will obtain the canary’s value from ‘%gs:0×14′ and store it in EAX register. It will then place it on the stack just after the stored, previously constructed stack frame. Then EAX is zeroed out since the canary value has no reason in being left there.

1
2
0x08048410 <main+28>:   movl   $0x8048500,(%esp)
0x08048417 <main+35>:   call   0x8048320 < printf @plt>

This is the actual program that places the contents of the 0×8048500 to the stack and invokes printf(3) from the available PLT (the Procedure Linkage Table) entry. Obviously…

1
2
3
(gdb) x /s 0x8048500
0x8048500:   "Ahoy!\n"
(gdb)

After the return of printf(3) library routine, SSP code performs the canary value check…

1
2
3
4
0x0804841c <main+40>:   mov    -0x8(%ebp),%edx
0x0804841f <main+43>:   xor    %gs:0x14,%edx
0x08048426 <main+50>:   je     0x804842d <main+57>
0x08048428 <main+52>:   call   0x8048330 <__stack_chk_fail@plt>

It retrieves the canary value from the stack and stores it in EDX register. Then compares it against ‘%gs:0×14′ which is the actual canary value. If they are equal it will use the ‘je’ (Jump if Equal) instruction to continue with the execution at the 0x804842d address. Otherwise, it will call the __stack_chk_fail() library routine through the PLT.
In the first case, the execution will continue from this point:

1
2
3
4
5
0x0804842d <main+57>:   add    $0x14,%esp
0x08048430 <main+60>:   pop    %ecx
0x08048431 <main+61>:   pop    %ebp
0x08048432 <main+62>:   lea    -0x4(%ecx),%esp
0x08048435 <main+65>:   ret 

The first instruction frees the allocated stack space and the next ones are a common function epilogue procedure. It restores the stored stack and frame pointer by popping them from the stack, updating the stack pointer’s value with the original one stored in ECX and calling ‘ret’ to complete the execution.
This is pretty much what’s going on with the SSP. Now, let’s have a look at the __stack_chk_fail() routine which is implemented in the GNU C Library and you can find it at debug/stack_chk_fail.c like this:

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
#include <stdlib.h>
 
 
extern char **__libc_argv attribute_hidden;
 
void
__attribute__ (( noreturn ))
__stack_chk_fail ( void )
{
   __fortify_fail ( "stack smashing detected" );
}

The attribute “noreturn” is used in functions that cannot return such as this one. So, the code is a simple wrapper around __fortify_fail(). This routine is available at debug/fortify_fail.c and here is its code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
#include <stdlib.h>
 
 
extern char **__libc_argv attribute_hidden;
 
void
__attribute__ (( noreturn ))
__fortify_fail (msg)
      const char *msg;
{
   /* The loop is added only to keep gcc happy.  */
   while (1)
     __libc_message (2, "*** %s ***: %s terminated\n" ,
                     msg, __libc_argv[0] ?: "<unknown>" );
}
libc_hidden_def (__fortify_fail)

Once again it’s a non-return function and it will use the __libc_message() to print a simple message like this:

1
*** stack smashing detected ***: ./bla terminated

Just for your information, __libc_message() is a POSIX compatible library routine available at sysdeps/posix/libc_fatal.c. I won’t describe it since it’s a common error message printing code.
So, we have talked about what happens after we have an executable application using GCC’s SSP but how about what happens before?
Let’s have a look at the Run-Time Dynamic Linker (RTLD) that is located at etc/rtld.c…

1
2
3
4
5
6
7
8
9
10
11
12
13
#ifndef THREAD_SET_STACK_GUARD
/* Only exported for architectures that don't store the stack guard canary
    in thread local area.  */
uintptr_t __stack_chk_guard attribute_relro;
#endif
 
/* Only exported for architectures that don't store the pointer guard
    value in thread local area.  */
uintptr_t __pointer_chk_guard_local
      attribute_relro attribute_hidden __attribute__ ((nocommon));
#ifndef THREAD_SET_POINTER_GUARD
strong_alias (__pointer_chk_guard_local, __pointer_chk_guard)
#endif

The GCC attribute ‘nocommon’ is used to directly allocate space for that variable and not store it in a ‘common’ storage area. A look at elf/stackguard-macros.h will reveal exactly how this variable is initialized…

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdint.h>
 
#ifdef __i386__
# define STACK_CHK_GUARD \
   ({ uintptr_t x; asm ( "movl %%gs:0x14, %0" : "=r" (x)); x; })
#elif defined __x86_64__
# define STACK_CHK_GUARD \
   ({ uintptr_t x; asm ( "movq %%fs:0x28, %0" : "=r" (x)); x; })
         ...
#else
extern uintptr_t __stack_chk_guard;
# define STACK_CHK_GUARD __stack_chk_guard
#endif

Now, if we move to the security features initialization code of the RTLD we can read the following…

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
static void
security_init ( void )
{
   /* Set up the stack checker's canary.  */
   uintptr_t stack_chk_guard = _dl_setup_stack_chk_guard ();
#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.  */
   if (GLRO(dl_pointer_guard))
     {
       // XXX If it is cheap, we should use a separate value.
       uintptr_t pointer_chk_guard = stack_chk_guard;
#ifndef HP_TIMING_NONAVAIL
       hp_timing_t now;
       HP_TIMING_NOW (now);
       pointer_chk_guard ^= now;
#endif
#ifdef THREAD_SET_POINTER_GUARD
       THREAD_SET_POINTER_GUARD (pointer_chk_guard);
#endif
       __pointer_chk_guard_local = pointer_chk_guard;
     }
}

As you can see, it uses _dl_setup_stack_chk_guard() to setup the stack canary value. Next, it will initialize the pointer check guard value with an XOR logical operation using either a high precision timer’s current value or the same stack canary value based on the compile time options. Since our main concern is the canary value, we should now have a look at _dl_setup_stack_chk_guard() which is located either at sysdeps/unix/sysv/linux/dl-osinfo.h or sysdeps/generic/dl-osinfo.h depending on the operating system.
The sysdeps/unix/sysv/linux/dl-osinfo.h is UNIX System V release dependent.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static inline uintptr_t __attribute__ ((always_inline))
_dl_setup_stack_chk_guard ( void )
{
   uintptr_t ret;
#ifdef ENABLE_STACKGUARD_RANDOMIZE
   int fd = __open ( "/dev/urandom" , O_RDONLY);
   if (fd >= 0)
     {
       ssize_t reslen = __read (fd, &ret, sizeof (ret));
       __close (fd);
       if (reslen == (ssize_t) sizeof (ret))
         return ret;
     }
#endif
   ret = 0;
   unsigned char *p = (unsigned char *) &ret;
   p[ sizeof (ret) - 1] = 255;
   p[ sizeof (ret) - 2] = '\n' ;
   return ret;
}

If StackGuard’s stack randomization is enabled, it will open ‘/dev/urandom’ and simply read an ‘uintptr_t’ that is stored in ‘ret’ variable. If __read() returned a value that is equal to size of ‘ret’ which means that the reading operation was successful it will simply return that value. If no stack randomization is enabled, it will set ‘p’ to the address of ‘ret’ variable and then change two Bytes of ret’s value to 0xff (255 in decimal) and 0xA (which is the newline character) and return that value. So, the canary value will always be ’0xff0a0000′ which is the so-called terminator canary value.
The generic implementation which resides at sysdeps/generic/dl-osinfo.h uses just this implementation…

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdint.h>
 
static inline uintptr_t __attribute__ ((always_inline))
_dl_setup_stack_chk_guard ( void )
{
   uintptr_t ret = 0;
   unsigned char *p = (unsigned char *) &ret;
   p[ sizeof (ret) - 1] = 255;
   p[ sizeof (ret) - 2] = '\n' ;
   p[0] = 0;
   return ret;
}

That will always provide a terminator canary value.
So, that was pretty much a quick journey around stack canary values of the GNU C Library. :)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值