Skip to content

Latest commit

 

History

History
163 lines (112 loc) · 6.02 KB

File metadata and controls

163 lines (112 loc) · 6.02 KB

Linux Binary Protections

There are three classes of protection we will be discussing in this section.

  1. No eXecute Bit
  2. Address Space Layout Randomisation
  3. Stack Canaries

We will discuss this using visualisations of how a classic exploitation technique attempt is stopped by the properties of each of the protections.

Classic Exploitation Illustration

First let's visualise how the stack looks like before a buffer is read into:

Fig 1.1. Clean stack

For clarification, the value of the saved base pointer is 0xbfff0030 and the value of the return address is 0x080484f0 (an address within the binary). The numbers are reversed in the visualisation because x86 is a little endian architecture.

On a valid run of the program, the buffer is filled within its bounds. Here we have 15 As and a null byte written to the 16 length buffer.

Fig 1.2. Within the bounds

However, since the read allows for the program to read more than 16 bytes into the buffer, we can overflow it and overwrite the saved return pointer.

Fig 1.3. Overwriting the saved return pointer

When the function returns, the program will crash since the instruction pointer is set to 0x41414141, an invalid address.

To complete the technique, the attacker will fill the first part of the buffer with the shellcode, append the appropriate padding and overwrite the saved return pointer with the address of the buffer.

Fig 1.5. Overwrite the saved return pointer with buffer address

Now, when the function returns, the program will begin executing the shellcode contained in the buffer since the saved return pointer was overwritten by the buffer address (0xbfff0000). From this point onwards, the attacker has achieved arbitrary code execution.

Fig 1.6. Arbitrary code execution

ASLR, NX, and Stack Canaries

Now that we understand how the classic exploitation technique works, let us start introducing protections and observing how they prevent the technique from working.

No eXecute (NX)

Also known as Data Execution Prevention (DEP), this protection marks writable regions of memory as non-executable. This prevents the processor from executing in these marked regions of memory.

If we look at the memory map of a program compiled with NX protection, the stack and heap are typically marked non-executable.

In the following diagrams, we will be introducing a new indicator colour for the memory regions to denote 'writable and non-executable' mapped regions. Firstly, the stack before the read occurs looks like this:

Fig 2.1. Stack marked non-executable

When we perform the same attack, the buffer is overrun and the saved pointers are overwritten once again.

Fig 2.2. Attack performed

After the function returns, the program will set the instruction pointer to 0xbfff0000 and attempt to execute the instructions at that address. However, since the region of memory mapped at that address has no execution permissions, the program will crash.

Fig 2.3. Non-executable memory violation

Thus, the attacker's exploit is thwarted.

Address Space Layout Randomisation

This protection randomises the addresses of the memory regions where the shared libraries, stack, and heap are mapped at. The reason for this is to frustrate an attacker since they cannot predict with certainty where their payload is located at and the exploit will not work reliably.

On the first run of the program, the stack looks like this just before the read:

Fig 3.1. Initial run 1

If we terminate the program and run it again, the stack might look like this before the read:

Fig 3.2. Initial run 2

Notice how the stack addresses do not stay constant and now have their base values randomised. Now, the attacker attempts to re-use their payload from the classic technique.

Fig 3.3. Classic payload in ASLR

Notice that the saved return pointer is overwritten with a pointer into the stack at an unknown location where the data is unknown and non-user controlled. When the function returns, the program will begin executing unknown instructions at that address (0xbfff0000) and will most likely crash.

Fig 3.4. Executing in an unknown location

Thus, it is impossible for an attacker to be able to reliably trigger the exploit using the standard payload.

Stack Canaries

This protection places a randomised guard value after a stack frame's local variables and before the saved return address. When a function returns, this guard value is checked and if it differs from the value provided by a secure source, then the program is terminated.

In the following stack diagram, an additional stack canary is added right after the buffer. The valid value of this stack canary is 0x01efcdab.

Fig 4.1. Stack canary after buffer

Now, the attacker attempts their exploit with the standard payload again. The stack diagram looks like this after the read:

Fig 4.2. Stack canary corrupted

Notice that the stack canary has been overwritten and corrupted by the padding of 'A's (0x41). The value of the canary is now 0x41414141. Before the function returns, the canary is xored against the value of the 'master' canary. If the result is 0, implying equality, then the function is allowed to return. Otherwise, the program terminates itself. In this case, the program fails the check, prints a warning message, and exits.

Fig 4.3. Stack canary check fails

Thus, the attacker is not even able to redirect control flow and the exploit fails.