The Silence of the Canaries

Post on 15-Apr-2017

411 views 0 download

transcript

The Silence of the CanariesGili Yankovitch, Nyx Software Security Solutions

Prerequisites

● A functioning brain

● A knowledge in the x86-x64 architectures

● Process loading

● Security attacks

● Operating system basics

Calling Convention

● foo() has something to tell bar()● Presenting, our stack● And the Assembly for the code

i = 42RetAddr = 0x080483b6

EBP

Locals

Thread Stack

Lower Addr (0x00..)

Higher Addr (0xFF..)

Buffer Overflow

● Spot the vulnerability

argc, argv...RetAddr

EBP

Locals

Thread Stack

Lower Addr (0x00..)

Higher Addr (0xFF..)

● What happens now?

RetAddrEBP

Locals

“In computer security and programming, a buffer overflow, or buffer overrun, is an anomaly where a program, while writing data to a buffer, overruns the buffer’s boundary and overwrites adjacent memory locations.”

- Wikipedia

Canaries

● A brief historical context

● Random value

○ Must be random for an attacker won’t be able to guess it.

● Stored before protected data

○ “Before” is relative to direction of overflow.

● Should be changed as much as possible

○ Heavy operation depending on the number of places the canaries are placed at.

Canaries● gcc implements with -fstack protector

○ -fstack-protector-strong

○ -fstack-protector-all

i = 42RetAddr = 0x080483b6

EBP

Locals

Thread Stack

Lower Addr (0x00..)

Higher Addr (0xFF..)

Canary

Canary == %gs:0x14?

What is %gs?

● Segment register

○ Once used to partition the memory

○ Memory accesses were SEGMENT:OFFSET

○ i.e. %cs:0x0040 or %ds:0x0040 results different memory regions.

● Now used for special data storage

● %gs segment register used differently across architectures

● Canary values are stored

○ %gs:20 for 32 bit

○ %gs:40 for kernel 64 bit

○ %fs:40 for user 64 bit

Random

● execve() loads binary

● Transfers Auxiliary Vector to usermode

○ binfmt_elf.c:load_elf_binary() -> create_elf_tables()● “Good” random numbers

ld.so init

● Every ELF process has an “interpreter”

● Its path is named in the ELF header

● ELF binary interpreter is the dynamic loader

readelf -a <elf_binary>

...

● Initializing internal members at startup

● The described ld.so is GlibC

○ Too much code complexity

○ Very widespread

Using the random

● During init phase (dl_main), calls security_init

● Initializes TLS (Thread Local Storage)

○ in x86_64 stored in %fs segment register

Offset

0

81624283240

Check the canaries

● And again, validating the canaries (now x86_64)

Kernel canaries

● Compiling with CONFIG_CC_STACKPROTECTOR

○ General -> Stack Protector buffer overflow detection

○ Exists for quite some time in Linux

○ Even 2.6.32.68 in kernel.org supports it.

● When rebuilding, needs a clean build

○ Adds snippets for every function prologue and epilogue

● Adds a performance overhead

○ Sorry Linus :(

● Fattens the Kernel image

● Uses gcc -fstack-protector[-strong]

Kernel canaries

● Let’s say there’s a stack based BOF vulnerability in a system call

● Kernel compiled with CC_STACKPROTECTOR

● However, canary value stored at %gs.

● Malicious program can read value and bypass kernel protection!

Kernel canaries

● We call a system call● From Intel x86_64 Instruction set

● %gs holds percpu kernel data structures.

○ So we have a different canary for the Kernel.

arch/x86/include/asm/stackprotector.h

● start_kernel() callsboot_init_stack_canary()

● Canary saved on task_struct

○ Initialization of init processkernel canary● More important, percpu write

arch/x86/include/asm/percpu.h● Lots of macros...

● Eventually it is something like:

○ movl %1, %%gs:%0

○ Using gcc inline assembly

Returning to canaries setup

● Let’s focus on the values we write.

64 bit

32 bit

32 Bit canary placement

● In x86 32 bit, Kernel uses %gs only for canaries. Setup GDT accordingly● Reading stored canary from boot_init_stack_canary● Reading GDT table● Picking the GDT entry for stack canaries● Writing to the specific GDT entry in its wierd encoding● Flushing the GDT to the register

Kernel canary per process

● Not enough a single canary for kernel

● A kernel canary per user process

○ During fork() in dup_task_struct()

● Randomizes a new canary for Kernel

You get a canary, and you get a canary, and...

● We want a different kernel canary forevery process

● Need to swap the %gs segmentregister in context switch

● Load per-process kernel canaryexplicitly after task switch

● Kernel canary must be set explicitlyso stack unwinding will succeed aftercontext swapped in __switch_to()

LAZY_GS

● The top comment at

○ arch/x86/include/asm/stackprotector.h

LAZY_GS

● Returning to context switch.

○ This is __switch_to in

○ arch/x86/kernel/process_32.c

○ 64 bit isn’t lazy and saves the segment

32 bit System Call

● When we call 32 bit syscall, save all the registers

LAZY_GS Macros

● We can see that if %gs is not lazykernel changes the segment registerupon syscall entry.

● But when it’s lazy, it does nothing?● Problem someone?● If this is true, then a hostile usermode

process can overflow canarieswith no apparent problemon x86 32 bit withCONFIG_X86_32_LAZY_GS!

Can it be?

● Remember this comment at stackprotector.h?

● It seems to be the only place it is done, when kernel is LAZY_GS.

Look closer

● It seems the kernel holds logic not only in code:● in arch/x86/Kconfig

● So actually we cannot have stack protection and LAZY_GS after all.● (Well, obviously!)

“Buffer overflows are the poster child of why problems aren't getting better. They were discovered in the 1960s and were first used to attack computers in the 1970s. The Morris worm in 1989 was a very public use of an overflow, which at the time knocked out 10 percent of the Internet--6000 computers. Here we are 40 years later, and buffer overflows are the most common security problem. And that's an easy problem to fix. If you are a software vendor, there is zero excuse for buffer overflows.”

- Bruce Schneier

End to the Overflows

Questions?