Digital forensics

Gentoo Hardening: Part 3: Using Checksec

Dejan Lukan
November 4, 2013 by
Dejan Lukan

Checksec

The checksec.sh file is a Bash script used to verify which PaX security features are enabled. The latest version can be downloaded with the wget command:

Learn Digital Forensics

Learn Digital Forensics

Build your skills with hands-on forensics training for computers, mobile devices, networks and more.

[python]

# wget http://www.trapkit.de/tools/checksec.sh

# chmod +x checksec.s

# ./checksec.sh --version

checksec v1.5, Tobias Klein, www.trapkit.de, November 2011

[/python]

Let's take a look at how checksec.sh does what it does. Let's first run it without any arguments, which will print its help page as shown below.

[python]

# ./checksec.sh

Usage: checksec [OPTION]

Options:

--file <executable-file>

--dir <directory> [-v]

--proc --proc-all

--proc-libs --kernel

--fortify-file <executable-file>

--fortify-proc --version

--help

For more information, see:

http://www.trapkit.de/tools/checksec.html

[/python]

The checksec.sh script can check whether ELF executables are set, and it processes support for the following security mitigations:

  • RELRO
  • Stack Canary
  • NoeXecute (NX)
  • Position Independent Code (PIE)
  • Address Space Layout Randomization (ASLR)
  • Fortify Source

The --file can be used to check which security mitigations are enabled for a file, whereas the --dir checks all files in current directory. The --proc attribute checks certain process, the --proc-all attribute checks all currently running processes and --proc-libs checks process libraries.

Let's see how the /bin/bash program was compiled:

[python]

# ./checksec.sh --file /bin/bash

RELRO STACK CANARY NX PIE RPATH RUNPATH FILE

Partial RELRO No canary found NX enabled No PIE No RPATH No RUNPATH /bin/bash

[/python]

Let's see how the checksec.sh script checks for RELRO support. In the graphic below, we can see that it's using the readelf command to check whether one of the file's segment headers is GNU_RELRO. When the RELRO is enabled, it just needs to determine whether full or partial RELRO is supported.

To check for stack canary, the -s option in readelf is used to check whether one of the entries is in __stack_chk_fail. When the stack canary is enabled, an indication appears. Otherwise it's not.

NoeXecute is checked by using the -l option of readelf, where the RWE string needs to be present in the same line as the GNU_STACK segment header string, if NX is disabled.

PIE support is determined by using the -h option passed to readelf, which prints the ELF header. If the ELF header type contains string 'EXEC' PIE, it's disabled. But if it contains 'DYN' PIE, it's enabled.

The rpath and run path are determined by checking whether one of the dynamic sections is named rpath or runpath, as shown below.

There are also --fortify-file and --fortify-proc arguments that check whether the binary file or process has been compiled with FORTIFY_SOURCE support. FORTIFY_SOURCE is a security mitigation that can detect and prevent certain buffer overflows. When a function uses a known buffer size and we pass an argument to it, it can detect that we passed an overly long argument, because it knows the size of the buffer it operates with.

That works when the compiler can determine the size of the buffer and implement checks in dangerous functions, like strcpy that copies only the maximum amount of bytes into the buffer without overflowing it.

Let's use --fortify-proc with PID 3323 (of the oowriter program) to check which functions support FORTIFY_SOURCE. The output can be seen below.

[python]

# ./checksec.sh --fortify-proc 3323

* Process name (PID) : bash (3323)

* FORTIFY_SOURCE support available (libc) : Yes

* Binary compiled with FORTIFY_SOURCE support: Yes

------ EXECUTABLE-FILE ------- . -------- LIBC --------

FORTIFY-able library functions | Checked function names

-------------------------------------------------------

gethostname | __gethostname_chk

printf_chk | __printf_chk

longjmp_chk | __longjmp_chk

wctomb | __wctomb_chk

readlink | __readlink_chk

fdelt_chk | __fdelt_chk

read | __read_chk

confstr | __confstr_chk

getcwd | __getcwd_chk

fprintf_chk | __fprintf_chk

mbstowcs | __mbstowcs_chk

memmove_chk | __memmove_chk

memmove | __memmove_chk

vsnprintf_chk | __vsnprintf_chk

fgets | __fgets_chk

strncpy | __strncpy_chk

strncpy_chk | __strncpy_chk

mbsrtowcs | __mbsrtowcs_chk

getgroups | __getgroups_chk

snprintf_chk | __snprintf_chk

memset | __memset_chk

memcpy_chk | __memcpy_chk

memcpy | __memcpy_chk

mbsnrtowcs | __mbsnrtowcs_chk

vfprintf_chk | __vfprintf_chk

wcrtomb | __wcrtomb_chk

strcpy | __strcpy_chk

strcpy_chk | __strcpy_chk

sprintf_chk | __sprintf_chk

wcsrtombs | __wcsrtombs_chk

strcat | __strcat_chk

asprintf_chk | __asprintf_chk

SUMMARY:

* Number of checked functions in libc : 76

* Total number of library functions in the executable: 1684

* Number of FORTIFY-able functions in the executable : 32

* Number of checked functions in the executable : 13

* Number of unchecked functions in the executable : 19

[/python]

One of the interesting arguments is --kernel, which is used to check the kernel configuration options. When this option is used, the checksec.sh script will check for the .config kernel files in this order: /proc/config.gz, /boot/config-<version> and /usr/src/linux/.config, where the <version> is the kernel version.

It first checks whether the /proc/config.gz file exists, which contains the current version of the .config configuration file. If that file doesn't exist, it then checks in the /boot/ directory for the configuration file of currently running kernel version. After that, it checks the /usr/src/linux/ directory, which should point to the current kernel.

The result of running checksec.sh with the --kernel option can be seen below, where the /usr/src/linux/.config was found and examined. Note that this was run on the gentoo-sources kernel and not on a hardened kernel.

[python]

# ./checksec.sh --kernel

* Kernel protection information:

Description - List the status of kernel protection mechanisms. Rather than

inspect kernel mechanisms that may aid in the prevention of exploitation of

userspace processes, this option lists the status of kernel configuration

options that harden the kernel itself against attack.

Kernel config: /usr/src/linux/.config

Warning: The config on disk may not represent running kernel config!

GCC stack protector support: Enabled

Strict user copy checks: Disabled

Enforce read-only kernel data: Enabled

Restrict /dev/mem access: Disabled

Restrict /dev/kmem access: Disabled

* grsecurity / PaX: No GRKERNSEC

The grsecurity / PaX patchset is available here:
http://grsecurity.net/

* Kernel Heap Hardening: No KERNHEAP

The KERNHEAP hardening patchset is available here:

https://www.subreption.com/kernheap/

[/python]

Most of the hardened options are disabled, since we're running the normal kernel and not the hardened one. If Grsecurity and PaX is enabled in the kernel, all its options are also checked if they're set. That isn't visible in the output above, because PaX is disabled. The checksec.sh script checks for the following configuration variables being checked in the .config:

[python]

# cat checksec.sh | grep "CONFIG_" | sed 's/.*(CONFIG_[^=]*).*/1/g'

CONFIG_CC_STACKPROTECTOR

CONFIG_DEBUG_STRICT_USER_COPY_CHECKS

CONFIG_DEBUG_RODATA

CONFIG_STRICT_DEVMEM

CONFIG_DEVKMEM

CONFIG_GRKERNSEC

CONFIG_GRKERNSEC_HIGH

CONFIG_GRKERNSEC_MEDIUM

CONFIG_GRKERNSEC_LOW

CONFIG_PAX_KERNEXEC

CONFIG_PAX_MEMORY_UDEREF

CONFIG_PAX_REFCOUNT

CONFIG_PAX_USERCOPY

CONFIG_GRKERNSEC_KMEM

CONFIG_GRKERNSEC_IO

CONFIG_GRKERNSEC_MODHARDEN

CONFIG_GRKERNSEC_HIDESYM

CONFIG_KERNHEAP

CONFIG_KERNHEAP_FULLPOISON

[/python]

Let's take a look at the CONFIG_DEBUG_STRICT_USER_COPY_CHECKS option in more detail. If we enter the "make menuconfig" screen and press the "/" character to start searching for an entry, and input CONFIG_DEBUG_STRICT_USER_COPY_CHECKS, we'll be presented with the search results. They'd notify us that the option is available under "Strict user copy size checks" option under "Kernel hacking". If we go to that option and press the Help button, we'll be presented with the details about that option, as shown below.

The first thing that you should notice is the "Depends on" feature notifies us about the options that option depends upon. If we'd like to see that option under "Kernel hacking" we have to:

  • Enable ARCH_HAS_DEBUG_STRICT_USER_COPY_CHECKS
  • Enable DEBUG_KERNEL
  • Disable TRACE_BRANCH_PROFILING
  • Disable PAX_SIZE_OVERFLOW

Below, we listed the Grsecurity and PaX options that we already described in the previous section. But they're presented with their actual names as they appear in the kernel, so we can cross-reference the options.

  • CONFIG_GRKERNSEC: Grsecurity
  • CONFIG_PAX_KERNEXEC: Enforce non-executable kernel pages
  • CONFIG_PAX_MEMORY_UDEREF: Prevent invalid userland pointer dereference
  • CONFIG_PAX_REFCOUNT: Prevent various kernel object reference counter overflows
  • CONFIG_PAX_USERCOPY: Harden heap object copies between kernel and userland
  • CONFIG_GRKERNSEC_KMEM: Deny reading/writing to /dev/kmem, /dev/mem, and /dev/port
  • CONFIG_GRKERNSEC_IO: Disable privileged I/O
  • CONFIG_GRKERNSEC_MODHARDEN: Harden module auto-loading
  • CONFIG_GRKERNSEC_HIDESYM: Hide kernel symbols

Other security non PaX options are described in detail and are presented below:

  • CONFIG_CC_STACKPROTECTOR (Processor type and features -> Enable -fstack-protector buffer overflow detection): When enabled, this option passes -fstack-protector flag to gcc compiler, which puts a canary on the stack before the return address. This canary is validated when the function returns and if it is not the same, it was overwritten due to the stack overflow.
  • CONFIG_DEBUG_STRICT_USER_COPY_CHECKS (Kernel hacking -> Strict user copy size checks): When enabled, additional security checks that check the length of the argument passed to the copy functions are enabled to ensure that the argument is within bounds.
  • CONFIG_DEBUG_RODATA (Kernel hacking -> Write protect kernel read-only data structures): When enabled, it marks the kernel read-only data as write protected in page tables to prevent writes to that memory segment.
  • CONFIG_STRICT_DEVMEM (Kernel hacking → Filter access to /dev/mem): When enabled, the kernel only allows users as well as kernel programs to access certain memory, but not all memory, since that would be a big security misconfiguration. It does that through /dev/mem. The only visible memory is each processes' own memory, as well as the device-mapped memory.
  • CONFIG_DEVKMEM (Device Drivers → Character devices → /dev/kmem virtual device support): When enabled, the /dev/kmem virtual device will be enabled and can be used for debugging purposes. We usually don't want to enable this feature.

It's also a good idea to keep a table of all the options the "checksec.sh --kernel" checks when evaluating security features in the kernel. The table below presents all the options preseneted by the "checksec.sh --kernel" command, where the first column presents the exact option reported by checksec.sh script, the second column presents the kernel equivalent variable and the third column briefly describes the corresponding option so we can quickly find more information about the option.

Checksec option Kernel variable Description

GCC stack protector support CONFIG_CC_STACKPROTECTOR Puts a canary on the stack, which is validated when the function returns to prevent overflowing saved EIP address.

Strict user copy checks CONFIG_DEBUG_STRICT_USER_COPY_CHECKS Additional checks are enabled on function arguments to ensure they are within bounds.

Enforce read-only kernel data CONFIG_DEBUG_RODATA Kernel data is marked as read-only to prevent overwriting important kernel structures.

Restrict /dev/mem access CONFIG_STRICT_DEVMEM Limit access to memory through /dev/mem device.

Restrict /dev/kmem access CONFIG_DEVKMEM Enable or disable the /dev/kmem device.

Non-executable kernel pages CONFIG_PAX_KERNEXEC Kernel enforces non-executable bit on kernel pages.

Prevent userspace pointer deref CONFIG_PAX_MEMORY_UDEREF Prevent kernel from directly using userland pointers.

Prevent kobject refcount overflow CONFIG_PAX_REFCOUNT Prevent overflowing various kinds of object reference counters.

Bounds check heap object copies CONFIG_PAX_USERCOPY Enforce the size of heap objects when they are copied between user and kernel land.

Disable writing to kmem/mem/port CONFIG_GRKERNSEC_KMEM Prevent changing the running kernel by accessing it through /dev/kmem, /dev/mem, /dev/port and /dev/cpu/*/msr devices.

Disable privileged I/O CONFIG_GRKERNSEC_IO Prevent changing the running kernel through privileged I/O operations.

Harden module auto-loading CONFIG_GRKERNSEC_MODHARDEN Only allow privileged users to auto-load modules.

Hide kernel symbols CONFIG_GRKERNSEC_HIDESYM Disable unprivileged users from getting their hands on kernel information.

We haven't yet mentioned KernHeap, disabled by the checksec.sh script. Linux kernel heap hardening can be implemented by a special patch called kernheap, which has its own web page at [7], but can no longer be freely downloaded from the internet. I wasn't able to get much information, but some people on IRC said that PaX patches include most of the stuff kernheap does.

I'm not sure what to conclude about kernheap, but I'm sure if it can no longer be integrated into the current kernel, it should be disabled in checksec.sh, because it only introduces confusion.

References:

Learn Digital Forensics

Learn Digital Forensics

Build your skills with hands-on forensics training for computers, mobile devices, networks and more.

[1] Hardened Gentoo http://www.gentoo.org/proj/en/hardened/.

[2] Security-Enhanced Linux http://en.wikipedia.org/wiki/Security-Enhanced_Linux.

[3] RSBAC http://en.wikipedia.org/wiki/RSBAC.

[4] Hardened/Toolchain https://wiki.gentoo.org/wiki/Hardened/Toolchain#RELRO.

[5] Hardened/PaX Quickstart https://wiki.gentoo.org/wiki/Project:Hardened/PaX_Quickstart.

[6] checksec.sh http://www.trapkit.de/tools/checksec.html.

[7] KERNHEAP http://subreption.com/products/kernheap/.

[8] Advanced Portage Features http://www.gentoo.org/doc/en/handbook/handbook-amd64.xml?part=3&chap=6.

[9] Elfix http://dev.gentoo.org/~blueness/elfix/.

[10] Avfs: An On-Access Anti-Virus File System http://www.fsl.cs.sunysb.edu/docs/avfs-security04/.

[11] Eicar Download, http://www.eicar.org/85-0-Download.html.

[12] Gentoo Security Handbook, http://www.gentoo.org/doc/en/security/security-handbook.xml.

Dejan Lukan
Dejan Lukan

Dejan Lukan is a security researcher for InfoSec Institute and penetration tester from Slovenia. He is very interested in finding new bugs in real world software products with source code analysis, fuzzing and reverse engineering. He also has a great passion for developing his own simple scripts for security related problems and learning about new hacking techniques. He knows a great deal about programming languages, as he can write in couple of dozen of them. His passion is also Antivirus bypassing techniques, malware research and operating systems, mainly Linux, Windows and BSD. He also has his own blog available here: http://www.proteansec.com/.