Gentoo Hardening: Part 3: Using Checksec

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:

# 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

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.

# ./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

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:

# ./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

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.

image0

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.

image1

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.

image2

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.

image3

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

image4

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.

# ./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

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.

# ./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/

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:

# 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

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.

image5

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:

[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.

Comments