The story of the Linux kernel 3.x…
In 2005 everybody was exited about possibility of bypass ASLR on all Linux 2.6 kernels because of the new concept called VDSO (Virtual Dynamic Shared Object). More information about this story can be found at the following link:
http://www.trilithium.com/johan/2005/08/linux-gate/
In short, VDSO was mmap’ed by the kernel in the user space memory always at the same fixed address. Because of that well-known technique ret-to-libc (or as some ppl prefer ROP) was possible and effective to bypass existing security mitigation in the system.
… 6 years later Linus Torvalds announced the release of the new kernel version – 3.x! Now, guess what happened…
pi3-darkstar new # uname -r 3.2.12-gentoo pi3-darkstar new # cat /proc/sys/kernel/randomize_va_space 2 pi3-darkstar new # cat /proc/self/maps|tail -2 bfa81000-bfaa2000 rw-p 00000000 00:00 0 [stack] ffffe000-fffff000 r-xp 00000000 00:00 0 [vdso] pi3-darkstar new # cat /proc/self/maps|tail -2 bfd5e000-bfd7f000 rw-p 00000000 00:00 0 [stack] ffffe000-fffff000 r-xp 00000000 00:00 0 [vdso] pi3-darkstar new # ldd /bin/ls|head -1 linux-gate.so.1 => (0xffffe000) pi3-darkstar new # ldd /bin/ls|head -1 linux-gate.so.1 => (0xffffe000) pi3-darkstar new #
I’m not using
dd if=/proc/self/mem of=linux-gate.dso bs=4096 skip=1048574 count=1
because I’m lame 🙂
pi3-darkstar new # echo "main(){}">dupa.c pi3-darkstar new # gcc dupa.c -o dupa pi3-darkstar new # gdb -q ./dupa Reading symbols from /root/priv/projekty/pro-police/new/dupa...(no debugging symbols found)...done. (gdb) b main Breakpoint 1 at 0x80483b7 (gdb) r Starting program: /root/priv/projekty/pro-police/new/dupa Breakpoint 1, 0x080483b7 in main () (gdb) dump binary memory test_dump.bin 0xffffe000 0xfffff000 (gdb) quit A debugging session is active. Inferior 1 [process 20117] will be killed. Quit anyway? (y or n) y pi3-darkstar new # file test_dump.bin test_dump.bin: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, stripped pi3-darkstar new # objdump -T ./test_dump.bin ./test_dump.bin: file format elf32-i386 DYNAMIC SYMBOL TABLE: ffffe414 g DF .text 00000014 LINUX_2.5 __kernel_vsyscall 00000000 g DO *ABS* 00000000 LINUX_2.5 LINUX_2.5 ffffe40c g DF .text 00000008 LINUX_2.5 __kernel_rt_sigreturn ffffe400 g DF .text 00000009 LINUX_2.5 __kernel_sigreturn pi3-darkstar new # readelf -h ./test_dump.bin ELF Header: Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 Class: ELF32 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: DYN (Shared object file) Machine: Intel 80386 Version: 0x1 Entry point address: 0xffffe414 Start of program headers: 52 (bytes into file) Start of section headers: 1172 (bytes into file) Flags: 0x0 Size of this header: 52 (bytes) Size of program headers: 32 (bytes) Number of program headers: 4 Size of section headers: 40 (bytes) Number of section headers: 12 pi3-darkstar new # objdump -f ./test_dump.bin ./test_dump.bin: file format elf32-i386 architecture: i386, flags 0x00000150: HAS_SYMS, DYNAMIC, D_PAGED start address 0xffffe414 ^^^^^^^^^^^^^^^^^^^^^^^^ pi3-darkstar new # objdump -d --start-address=0xffffe414 ./test_dump.bin ./test_dump.bin: file format elf32-i386 Disassembly of section .text: ffffe414 <__kernel_vsyscall>: ffffe414: 51 push %ecx ffffe415: 52 push %edx ffffe416: 55 push %ebp ffffe417: 89 e5 mov %esp,%ebp ffffe419: 0f 34 sysenter ffffe41b: 90 nop ffffe41c: 90 nop ffffe41d: 90 nop ffffe41e: 90 nop ffffe41f: 90 nop ffffe420: 90 nop ffffe421: 90 nop ffffe422: cd 80 int $0x80 <--------------------- Nice oldschool pop-ret :) ----------------------> ffffe424: 5d pop %ebp ffffe425: 5a pop %edx ffffe426: 59 pop %ecx ffffe427: c3 ret pi3-darkstar new #
If you look at the process memory layout and analyse every bytes from this address range you can find some useful instruction not only that which I listed in this lame write-up.
Btw. I wonder why no-one point this out before…
Btw2. Go and write reliable exploit for kernel 3.x ;p
[UPDATE]
Because my write-up wasn’t so clear this section need to be done. Problem is not in kernel 3.x by itself but in the configuration. If COMPAT_COMPAT_VDSO option was used for kernel then problem appears. Whole problem was discussed based on OpenSuse 12.1 system which enables this option by default. Nicolas Surribas in Full Disclosure list pointed out that in his case problem does not exists! After reading opensuse kernel developers list I found a problem and gentle fix:
http://lists.opensuse.org/opensuse-kernel/2012-03/msg00056.html
What about 64 bits Fedora and Ubuntu? They have fixed address range for VSYSCALL which after discussion with bliss it became as known issue: https://lkml.org/lkml/2011/8/9/274 and I didn’t know about that – my fault.
Summarizing:
OpenSuse 12.1 by default has this problem but latest kernel update fix it.
All 64 bits distros has VSYSCALL mmaped at fixed address range but this is known issue.
Thanks for everyone who was involved in this issue 😉
Best regards,
Adam Zabrocki