Science and technology

Identify safety properties on Linux utilizing checksec

Compiling supply code produces a binary. During compilation, you’ll be able to present flags to the compiler to allow or disable sure properties on the binary. Some of those properties are related to safety.

Checksec is a nifty little software (and shell script) that, amongst different features, identifies the safety properties that had been constructed right into a binary when it was compiled. A compiler may allow a few of these properties by default, and also you may need to supply particular flags to allow others.

This article explains the way to use checksec to establish the safety properties on a binary, together with:

  1. The underlying instructions checksec makes use of to seek out data on the safety properties
  2. How to allow safety properties utilizing the GNU Compiler Collection (GCC) when compiling a pattern binary

Install checksec

To set up checksec on Fedora and different RPM-based techniques, use:

$ sudo dnf set up checksec

For Debian-based distros, use the equal apt command.

The shell script

Checksec is a single-file shell script, albeit a reasonably massive one. An benefit is you could learn via the script shortly and perceive all of the system instructions operating to seek out details about binaries or executables:

$ file /usr/bin/checksec
/usr/bin/checksec: Bourne-Again shell script, ASCII textual content executable, with very lengthy traces

$ wc -l /usr/bin/checksec
2111 /usr/bin/checksec

Take checksec for a drive with a binary you most likely run every day: the ever present ls command. The command’s format is checksec --file= adopted by absolutely the path of the ls binary:

$ checksec --file=/usr/bin/ls
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      Symbols         FORTIFY Fortified       Fortifiable     FILE
Full RELRO      Canary discovered      NX enabled    PIE enabled     No RPATH   No RUNPATH   No Symbols        Yes   5       17              /usr/bin/ls

When you run this in a terminal, you see color-coding that reveals what is sweet and what most likely is not. I say “probably” as a result of even when one thing is in pink, it does not essentially imply issues are horrible—it would simply imply the distro distributors made some tradeoffs when compiling the binaries.

The first line offers numerous safety properties which can be normally accessible for binaries, like RELRO, STACK CANARY, NX, and so forth (I clarify intimately beneath). The second line reveals the standing of those properties for the given binary (ls, on this case). For instance, NX enabled means some property is enabled for this binary.

A pattern binary

For this tutorial, I am going to use the next “hello world” program because the pattern binary.

#embody <stdio.h>

int primary()

 

Note that I didn’t present gcc with any further flags throughout compilation:

$ gcc howdy.c -o howdy
 
$ file howdy
howdy: ELF 64-bit LSB executable, x86-64, model 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=014b8966ba43e3ae47fab5acae051e208ec9074c, for GNU/Linux three.2.zero, not stripped

$ ./howdy
Hello World

Run the binary via checksec. Some of the properties are totally different than with the ls command above (in your display, these could also be displayed in pink):

$ checksec --file=./howdy
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      Symbols         FORTIFY Fortified       Fortifiable     FILE
Partial RELRO   No canary discovered   NX enabled    No PIE          No RPATH   No RUNPATH   85) Symbols       No    zero       zero./howdy
$

Changing the output format

Checksec permits numerous output codecs, which you’ll specify with --output. I am going to select the JSON format and pipe the output to the jq utility for fairly printing.

To observe alongside, ensure you have jq installed as a result of this tutorial makes use of this output format to shortly grep for particular properties from the output and report sure or no on every:

$ checksec --file=./howdy --output=json | jq

Walking via the safety properties

The binary above consists of a number of safety properties. I am going to evaluate that binary towards the ls binary above to look at what’s enabled and clarify how checksec discovered this data.

1. Symbols

I am going to begin with the straightforward one first. During compilation, sure symbols are included within the binary, principally for debugging. These symbols are required if you end up creating software program and require a number of cycles for debugging and fixing issues.

These symbols are normally stripped (eliminated) from the ultimate binary earlier than it is launched for common use. This doesn’t have an effect on the binary’s execution in any method; it’ll run simply as it could with the symbols. Stripping is usually carried out to save lots of area, because the binary is considerably lighter as soon as the symbols have been stripped. In closed-source or proprietary software program, symbols usually are eliminated as a result of having these symbols in a binary makes it considerably straightforward to deduce the software program’s interior workings.

According to checksec, symbols are current on this binary, but they weren’t within the ls binary. You can even discover this data by operating the file command on this system—you see not stripped within the output in direction of the top:

$ checksec --file=/bin/ls --output=json | jq | grep symbols
    "symbols": "no",

$ checksec --file=./howdy --output=json | jq | grep symbols
    "symbols": "yes",

$ file howdy
howdy: ELF 64-bit LSB executable, x86-64, model 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=014b8966ba43e3ae47fab5acae051e208ec9074c, for GNU/Linux three.2.zero, not stripped

How did checksec discover this data? Well, it offers a useful --debug possibility to indicate which features ran. Therefore, operating the next command ought to present you which of them features ran throughout the shell script:

$ checksec --debug --file=./howdy

In this tutorial, I am on the lookout for the underlying instructions used to seek out this data. Since it is a shell script, you’ll be able to at all times make the most of Bash options. This command will output each command that ran from throughout the shell script:

$ bash -x /usr/bin/checksec --file=./howdy

If you scroll via the output, you must see an echo_message adopted by the safety property’s class. Here is what checksec studies about whether or not the binary comprises symbols:

+ readelf -W --symbols ./howdy
+ grep -q '.symtab'
+ echo_message '33[31m96) Symbolst33[m  ' Symbols, ' symbols="yes"' '"symbols":"yes",'

To simplify this, checksec makes use of the readelf utility to learn the binary and offers a particular --symbols flag that lists all symbols throughout the binary. Then it greps for a particular worth, .symtab, that gives a depend of entries (symbols) it finds. You can check out the next instructions on the check binary you compiled above:

$ readelf -W --symbols ./howdy
$ readelf -W --symbols ./howdy | grep -i symtab

How to strip symbols

You can strip symbols after compilation or throughout compilation.

  • Post compilation: After compilation, you should utilize the strip utility on the binary to take away the symbols. Confirm it labored utilizing the file command, which now reveals the output as stripped:

    $ gcc howdy.c -o howdy
    $
    $ file howdy
    howdy: ELF 64-bit LSB executable, x86-64, model 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=322037496cf6a2029dcdcf68649a4ebc63780138, for GNU/Linux three.2.zero, not stripped
    $
    $ strip howdy
    $
    $ file howdy
    howdy: ELF 64-bit LSB executable, x86-64, model 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=322037496cf6a2029dcdcf68649a4ebc63780138, for GNU/Linux three.2.zero, stripped
    $

How to strip symbols throughout compilation

Instead of stripping symbols manually after compilation, you’ll be able to ask the compiler to do it for you by offering the -s argument:

$ gcc -s howdy.c -o howdy
$
$ file howdy
howdy: ELF 64-bit LSB executable, x86-64, model 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=247de82a8ad84e7d8f20751ce79ea9e0cf4bd263, for GNU/Linux three.2.zero, stripped
$

After rerunning checksec, you’ll be able to see that symbols are proven as no:

$ checksec --file=./howdy --output=json | jq | grep symbols
    "symbols": "no",
$

2. Canary

Canaries are identified values which can be positioned between a buffer and management information on the stack to observe buffer overflows. When an utility executes, two sorts of reminiscence are assigned to it.  One of them is a stack, which is just an information construction with two operations: push, which places information onto the stack, and pop, which removes information from the stack in reverse order. Malicious enter may overflow or corrupt the stack with specifically crafted enter and trigger this system to crash:

$ checksec --file=/bin/ls --output=json | jq | grep canary
    "canary": "yes",
$
$ checksec --file=./howdy --output=json | jq | grep canary
    "canary": "no",
$

How does checksec discover out if the binary is enabled with a canary? Using the strategy above, you’ll be able to slim it down by operating the next command throughout the shell script:

$ readelf -W -s ./howdy | grep -E '__stack_chk_fail|__intel_security_cookie'

Enable canary

To shield towards these instances, the compiler offers the -stack-protector-all flag, which provides further code to the binary to examine for such buffer overflows:

$ gcc -fstack-protector-all howdy.c -o howdy

$ checksec --file=./howdy --output=json | jq | grep canary
    "canary": "yes",

Checksec reveals that the property is now enabled. You can even confirm this with:

$ readelf -W -s ./howdy | grep -E '__stack_chk_fail|__intel_security_cookie'
     2: 0000000000000000     zero FUNC    GLOBAL DEFAULT  UND __stack_chk_fail@GLIBC_2.four (three)
    83: 0000000000000000     zero FUNC    GLOBAL DEFAULT  UND __stack_chk_fail@@GLIBC_2.four
$

three. PIE

PIE stands for position-independent executable. As the title suggests, it is code that’s positioned someplace in reminiscence for execution no matter its absolute tackle:

$ checksec --file=/bin/ls --output=json | jq | grep pie
    "pie": "yes",

$ checksec --file=./howdy --output=json | jq | grep pie
    "pie": "no",

Often, PIE is enabled just for libraries and never for standalone command-line applications. In the output beneath, howdy is proven as LSB executable, whereas, the libc normal library (.so) file is marked LSB shared object:

$ file howdy
howdy: ELF 64-bit LSB executable, x86-64, model 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=014b8966ba43e3ae47fab5acae051e208ec9074c, for GNU/Linux three.2.zero, not stripped

$ file /lib64/libc-2.32.so
/lib64/libc-2.32.so: ELF 64-bit LSB shared object, x86-64, model 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=4a7fb374097fb927fb93d35ef98ba89262d0c4a4, for GNU/Linux three.2.zero, not stripped

Checksec tries to seek out this data with:

$ readelf -W -h ./howdy | grep EXEC
  Type:                              EXEC (Executable file)

If you attempt the identical command on a shared library as an alternative of EXEC, you will note a DYN:

$ readelf -W -h /lib64/libc-2.32.so | grep DYN
  Type:                              DYN (Shared object file)

Enable PIE

To allow PIE on a check program, ship the next arguments to the compiler:

$ gcc -pie -fpie howdy.c -o howdy

You can confirm PIE is enabled utilizing checksec:

$ checksec --file=./howdy --output=json | jq | grep pie
    "pie": "yes",
$

It ought to present as a PIE executable with the kind modified from EXEC to DYN:

$ file howdy
howdy: ELF 64-bit LSB pie executable, x86-64, model 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=bb039adf2530d97e02f534a94f0f668cd540f940, for GNU/Linux three.2.zero, not stripped

$ readelf -W -h ./howdy | grep DYN
  Type:                              DYN (Shared object file)

four. NX

NX stands for “non-executable.” It’s usually enabled on the CPU degree, so an working system with NX enabled can mark sure areas of reminiscence as non-executable. Often, buffer-overflow exploits put code on the stack after which attempt to execute it. However, making this writable space non-executable can forestall such assaults. This property is enabled by default throughout common compilation utilizing gcc:

$ checksec --file=/bin/ls --output=json | jq | grep nx
    "nx": "yes",

$ checksec --file=./howdy --output=json | jq | grep nx
    "nx": "yes",

Checksec determines this data with the command beneath. RW in direction of the top means the stack is readable and writable; since there isn’t a E, it is not executable:

$ readelf -W -l ./howdy | grep GNU_STACK
  GNU_STACK      0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW  0x10

Disable NX for demo functions

It’s not advisable, however you’ll be able to disable NX when compiling a program through the use of the -z execstack argument:

$ gcc -z execstack howdy.c -o howdy

$ checksec --file=./howdy --output=json | jq | grep nx
    "nx": "no",

Upon compilation, the stack turns into executable (RWE), which permits malicious code to execute:

$ readelf -W -l ./howdy | grep GNU_STACK
  GNU_STACK      0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RWE 0x10

5. RELRO

RELRO stands for Relocation Read-Only. An Executable Linkable Format (ELF) binary makes use of a Global Offset Table (GOT) to resolve features dynamically. When enabled, this safety property makes the GOT throughout the binary read-only, which prevents some type of relocation assaults:

$ checksec --file=/bin/ls --output=json | jq | grep relro
    "relro": "full",

$ checksec --file=./howdy --output=json | jq | grep relro
    "relro": "partial",

Checksec finds this data through the use of the command beneath. Here, one of many RELRO properties is enabled; subsequently, the binary reveals “partial” when verifying by way of checksec:

$ readelf -W -l ./howdy | grep GNU_RELRO
  GNU_RELRO      0x002e10 0x0000000000403e10 0x0000000000403e10 0x0001f0 0x0001f0 R   0x1

$ readelf -W -d ./howdy | grep BIND_NOW

Enable full RELRO

To allow full RELRO, use the next command-line arguments when compiling with gcc:

$ gcc -Wl,-z,relro,-z,now howdy.c -o howdy

$ checksec --file=./howdy --output=json | jq | grep relro
    "relro": "full",

Now, the second property can also be enabled, making this system full RELRO:

$ readelf -W -l ./howdy | grep GNU_RELRO
  GNU_RELRO      0x002dd0 0x0000000000403dd0 0x0000000000403dd0 0x000230 0x000230 R   0x1

$ readelf -W -d ./howdy | grep BIND_NOW
 0x0000000000000018 (BIND_NOW)          

6. Fortify

Fortify is one other safety property, but it surely’s out of scope for this text. I’ll go away studying how checksec verifies fortify in binaries and the way it’s enabled with gcc as an train so that you can sort out.

$ checksec --file=/bin/ls --output=json | jq  | grep -i forti
    "fortify_source": "yes",
    "fortified": "5",
    "fortify-able": "17"

$ checksec --file=./howdy --output=json | jq  | grep -i forti
    "fortify_source": "no",
    "fortified": "0",
    "fortify-able": "0"

Other checksec options

The matter of safety is unending, and whereas it is not potential to cowl all the pieces right here, I do need to point out a number of extra options of the checksec command that make it a pleasure to work with.

Run towards a number of binaries

You do not have to supply every binary to checksec individually. Instead, you’ll be able to present a listing path the place a number of binaries reside, and checksec will confirm all of them for you in a single go:

$ checksec --dir=/usr/bin

Processes

In addition to binaries, checksec additionally works on applications throughout execution. The following command finds the safety properties of all operating applications in your system. You can use --proc-all if you would like it to examine all operating processes, or you’ll be able to choose a selected course of through the use of its title:

$ checksec --proc-all

$ checksec --proc=bash

Kernel properties

In addition to checksec’s userland purposes described on this article, it’s also possible to use it to examine the kernel properties constructed into your system:

$ checksec --kernel

Give it a attempt

Checksec is an efficient method to perceive what userland and kernel properties are enabled. Go via every safety property intimately, and attempt to perceive the explanations for enabling every function and the sorts of assaults it prevents.

Most Popular

To Top