[SOLVED] CS6035 Projects / Binary Exploitation + EXTRA CREDITS Spring2026 100%

150.00 $

Category:
Click Category Button to View Your Next Assignment | Homework

You will receive the following solution file(s) instantly after successful payment:

zip file icon CS6035_binexp_exploit_scriptsEC-kvukaf.zip (113.9 KB)
Assignment Instructions Updated Recently? Submit Below and we will provide new Solution!
Submit New Instructions
🔒 Securely Powered by:
Secure Checkout
5/5 - (4 votes)

2026 SUMMER SOLUTION LINK: CLICK HERE 

BInary Exploit individual Flags

 

Projects / Binary Exploitation / Stage A – Setup/Validation — (1 flags)

  • Learning Objectives
  • Exercises
  • Step A.1
  • Step A.2
  • 01_basic_overflow_1 [0 pts]
  • Instructions
  • OPTIONAL: Try using e.py!
  • Step A.3

Welcome to the Binary Exploitation (BinExp) project for CS6035! We’re excited to have you with us for this effort.

Binary exploitation is a really interesting and challenging domain within cybersecurity. It rests at the intersection of many sub-disciplines, including reverse engineering, low-level programming, operating systems, code review, etc. You’ll be expected to draw upon a variety of subjects matter in approaching and working through the challenges of this project. Understandably, many students find the project challenging at some point (or many points), due to the need to perform additional research in those areas on top of working the problems themselves; as such, we encourage you to not delay in getting started with the project!

Each stage within this project presents a set of learning objectives and associated challenges. We’ve endeavored to present these challenges in a logical order in the form of “Stages”, starting here in Stage 00 as a guided introduction to the project through to Stage 04 where you’ll be crafting your own novel exploits to some unique challenges. Within each stage, there are a number of exercises affiliated with the related material ranging from “easier” content (prefixed as 01, such as ei_basic_overflow_i.) to more challenging tasks (difficulty 03). While we encourage students to proceed through the challenges in-order if you’ve never done anything like this before, you are welcome to approach this projects’ challenges in any order you’d like; in fact, if you’re stuck on an exercise it may be best to move along and return back later.

Stage A – SetupValidation — (1 flags) I CS 6035

Learning Objectives

The learning objectives for this section are:

  • Project setup and understanding the project architecture
  • High-level introduction/exposure to project materials
  • Validation of project infrastructure, including:
  • Environment setup
  • /proc/flag
  • Gradescope

Exercises

This section features 1 exercise:

  • 01_basic_overflow_1

Step A.1

Before diving in, let’s ensure that our project environment is appropriately configured.

1 This project utilizes the same virtual machine (VM) that is used for other projects within CS6035.

If you’ve already got it configured, great! If not, see the respective “Course VM Download Thread” post in Ed Discussion for instructions.

2 Please follow the instructions in Canvas (navigate to “Assignments” > “Binary Exploitation”) for
login credentials to the VM as well as the requisite commands for fetching the project files.

3 Navigate to /home/binexp open the user.txt file (this file will be empty initially) and enter your nine-digit GTID. If you do not know your GTID, you should be able to discover this through https://gtid.gatech.edu. Note: if you fail to set this, no responses you submit to Gradescope will be accepted as correct.

Below is a brief summary of the project’s contents:

  • json: This is the one (and only) file you will submit to Gradescope to have your work be evaluated. See the Submission tab along the left-hand side of this page for additional guidance concerning project submission guidelines.
  • project\*: This is the directory and subdirectories that make up the project. Each subdirectory reflects an individual challenge within the project and contains all of the files necessary for solving that particular challenge. We have included a project\tutorial\ subdirectory that has amplifying guidance material to help get you oriented to the projects’ techniques. The contents of project\tutorial\ are not mandatory or graded – you should not include any practice work results you perform there in your project_binexp.json file.
  • project\*\flag: For most of the challenges, this is the compiled binary you’re attempting to If it isn’t, the student instructions for that particular challenge will say so.
  • project\*\flag.c : This is the source code for the flag binary, above. This is intended to be a useful reference to aid in identifying and crafting your exploits of flag .
  • project\*\e.py : This is a python3 script with some templated skeleton exploit code. You are welcome to use/ignore this as you see fit.
  • project\*\e.py.bak: This is just a copy of the initial state of py in case you accidentally delete it or wish to revert back to start. Simply copy from this file to get a clean-slate e.py.

In all of the projects’ binaries, your goal is to have the binary read from /proc/flag! Most of the time, this is via a system() call like:

system(“cat /proc/flag”);

However, sometimes it’s not that simple – carefully analyze your source code within each challenge to figure out how the binary is meant to read from /proc/flag .

Step A.2

Otbasic_overflow_l [0 pts]

INSTRUCTIONS

Now, let’s take a look at our first introductory challenge to ease us into the exploit development process at a high-level: Ol_basic_overflow:l. We’re going to walk you through this one just to give you a sense of what’s to come.

In this task, we’re looking at a simple buffer overflow. A buffer overflow occurs when input exceeds the expected bounds it’s intended to write to, thereby spilling outside those bounds and overwriting other areas of memory. Generally speaking, this kind of incident leads the running process to crash. However, a crafty (and determined) malicious actor may be able to get the process to do something else altogether!

If we were to review the source code for our binary (flag.c), we could start by tracing the code execution flow starting at main() , which is the starting point for all C program code.

 

cat –/project/Ol_basic_overflow_l/flag.c

1 The main() function starts by initializing the variable make_me_not_zero to 0.

2 It then declares an int buffer of size 300.

3 There’s a printf() call, which would write some instructions to the user to stdout .

4 The process then blocks for user input with scanf() , writing the input to buffer.

5 There’s then a if-conditional check to see if make_me_not_zero is still 0. If it is, the process

terminates; if it isn’t, we arrive at our desired destination in the binary which reads our flag out for us.

Intuitively, we can start to build our attack chain in reverse:

  • We want to get to the function call that reads out our flag.
  • To get to the above, we need to have make_me_not_zero not be zero by the time the if-conditional
  • Since the program does not otherwise allow for us to set make_me_not_zero, we need to either overwrite it in memory or otherwise disrupt the control flow of the process.
  • Our only input to the process is along the scanf() call, so we’ll investigate what exploit opportunities exist around here.

Now many of you may not necessarily be professional exploit developers already (in fact, some of you may not have exercised secure coding practices in C more generally); understandably, the vulnerability may not immediately be apparent. But if we look into references for scanf(), we can see that it reads in from stdin with the “s” specifier standing for…

Any number of non-whitespace characters, stopping at the first whitespace character found…

“Any number of non-whitespace characters”?! But buffer only allocates for 300 int (300 * 4 bytes)! This adds affirmation to our above-described attack-chain that a buffer overflow may be possible.

OPTIONAL: TRY USING E.PY!

Let’s test our assumptions! Open e.py and take a minute to look it over. When you’re ready, uncomment the following line:

payload = b’A’ * 1209

…and then run it from the Ol_basic_overflowi directory with our dbg option:

 

python3 e.py dbg

Assuming you’re running this in the VM, you should see another terminal open running GDB with the pwndbg extension. Don’t worry too much about understanding what’s happening here for now; we have several exercises coming up in other stages that dive into all of this. In brief, you’ve launched a debugger and hooked it onto the flag process; that process has ran and is now paused at the start of the main() function. For now, enter “c” or “continue” and let the process resume running.

GDB will likely halt again, throwing a SIGSEGV segmentation fault. Examining the BACKTRACE log panel will show that our main() function successfully made the subsequent function calls necessary to read out the flag; there will also be error messaging informing you of your test’s success – though specifying you need to run your exploit outside of GDB. For us to do that, we’d want to re-run e.py without the ‘dbg’ option like so:

python3 e.py

Make sure you’ve uncommented the payload line, or it won’t work!

Again, don’t worry too much right now about understanding all of the information that GDB is showing you. We’ll go more in-depth with that in the section(s) to follow. For now, go ahead and close the GDB window (or type in “q” or “quit”).

Step A.3

Having developed our attack chain as a thought exercise and (optionally) affirmed our assumptions through GDB, we can now move on to exploiting the binary for our flag.

Using either e.py or the command line, run flag and pass at least 1209 characters to the program and receive your flag. Note if you run your exploit multiple times, you will get a different value each time. This is expected behavior and nothing to worry about.

Now enter that hash into your project_binexp.json file and submit it to Gradescope to confirm you’ve correctly walked-through the initial setup! Again, if you’re uncertain about the format for what

project_binexp.json should look like, see the Submission tab to the left.

 

Stage B – Intro to Assembly — (2 flags) I CS 6035

Projects / Binary Exploitation / Stage B – Intro to Assembly — (2 flags)

  • Overview
  • Learning Objectives
  • Exercises
  • Step B.1
  • Why are we interested in Assembly at all?
  • Why are we interested in CPU registers?
  • Step B.2: Exercise 1
  • Step B.3: Exercise 2

Overview

In this part of the project, we’re going to focus more narrowly on some of the foundational aspects that undergird binary exploitation more generally. We’ll look at Intel x86 Assembly, using our tools like GDB to evaluate runtime statuses, and look to solidify our comprehension with the project environment before launching into the more exploit-centric material to come.

Learning Objectives

The core learning objectives for this section are:

  • A baseline familiarity with Intel x86 Assembly.
  • Utilizing GDB.
  • C programming language comprehension. Exercises

This section features 2 exercises:

  • 01_bb_steps
  • 02_assemble_the_assembly

 

Step B.1

Why are we interested in Assembly at all?

When you compile a source code file (such as flag. c ), the compiler (like gcc ) translates the high-level human-readable code into machine code that the computer’s processor can execute. A number of operations are performed at compile time (such as optimizing and linking), which obfuscates the binary’s original source code. However, we can still use a disassembler (like objdump) to translate machine code back into lower-level assembly instructions.

In practice, exploit developers generally do not possess the original source code of the binaries they research. But they can utilize tools to pour over and examine the assembly instructions – which can be just as good (provided you know how to read/contextualize assembly). Understanding what these assembly instructions are doing – both individually and collectively – is a fundamental baseline for reverse engineering (and by extension, exploit development).

NOTE: there are also tools that can “de-compile” binaries; these take the translation a step further by attempting to recreate the source code from the disassembled instructions. However, this is often incomplete and – in some cases – inaccurate. We do not supply you with a decompiler tool in this project because we provide you with the original source code. We’ve also compiled all our binaries in gcc with the -g flag, which produces debugging info in the OS’ native format that GDB can use to rebuild the source.

Being able to read and comprehend assembly is often a labor-intensive process, especially if you’ve primarily been exposed to only higher-level languages before. We encourage you to lean into this challenge, however. Without fostering this aptitude, you’ll often be left in a position of brute­forcing/guesswork (being unsure what a process is doing or why your exploit is behaving a certain way).

If this is your first time seeing/engaging x86 Assembly be forewarned that you’ll need to be a quick study for this project. This section’s exercises are meant to help orient you more generally, but the sections to follow will require a firm understanding if you want to avoid getting lost.

In the table below, we’ve listed some of the common instructions you’ll encounter in the course of this project. At a high-level, assembly operations (e.g. mov, , xor, , ret , etc.) may have 0, 1, or 2 “arguments” to them depending on the particular operation – these arguments are referred to as “operands”. Depending on the instruction, the operand may be a value, something referential to the stack/heap, or a register. We encourage you to consult other reference material as needed to foster your comprehension.

Stage B – Intro to Assembly — (2 flags) I CS 6035

Instruction                                                                            Description

mov                                                                                                                      Moves the contents of one memory location into another (as specified by operands).

XORs the values of 2 locations in memory against one another, storing the result in the xor                                                                                                         primary operand.

“Load effective address”: computes an address of the source operand and stores it in the

lea

general register specified by the second.
Saves procedure linking information on the stack and branches to the called procedure; in

call

layman’s terms: it initiates a function call.

This is an unconditional jump, redirecting the control flow to elsewhere in the binary’s jmp               instruction set. Examples of conditional jumps may apppear as jne, jnz, etc., which make
the jump only if particular conditions are met (common at branches, such as if-else blocks).

Returns transfer of the program control to a return address on the top of the stack;

ret

commonly the last instruction performed by most disassembled functions.

Why are we interested in CPU registers?

Registers are part of a CPU’s architecture and are used to store data and perform operations. Assembly instructions make use of registers all the time (and by extension, the stack and heap -topics for another section). In the setup exercise (

basic_overflow_l ), you may have observed some of

the registers and their contents at runtime within the GDB debugger like so:

[ ILL.!.

“RAY, Ox60983alfcif5            r,. 4— endbr64

‘RBX Ox7ffc0bd365f8         Ox7ffc0b089f,1 4 1home/binexp/project binexp/experiment/projv2/01 basic overflow 1/flag’

*RCX Ox60983a1fedb0 ( do_global_dtors_aux_tini_array_entry)                                                      ■- endbr64

*ROX Ox7ffc0bd36608       Ox7ffc0bd3813b A- ‘SHELL=/bin/bash’

“ROI Oxl

‘RSI Ox7ffc0bd365f8       Ox7ffc0b6380f4 A- 1home/binexp/project_binexp/experiment/projv2/01_basic_overflow_1/flag’

R8 Ox0

*R9                                    ■— endbr64

*R10 Ox7tfc0bd361f0 ■— Ox600000

‘R11 Ox203

‘912 Oxl

Ri3 Ox0

‘“14 Ox60983alfedb0 ( do_global_dtors_aux_fini_array_entry)                                                     4— endbr64

115 0x7d1t59cde000 c_rtld_gLobal)          0.x7dir39cdt2eu       0x60983a1fb000 ■— 0x10102464c457f

‘RBI) Ox7ffc0bd364d0       Ox7ffc0bd36570 -* Ox7ffcObd365d 4- Ox0

*RSP Ox7ffc0bd364d0              Ox7ffc0bd36570       Ox7ffc0bd365d0 A- Ox0

*RIP Ox60983alfcifd (mair-:c.. A— mov eax, 0

In the above screenshot, the various R* values (RAX, RBX, RCX, etc.) in red along the left-hand side denote the CPU registers. The values immediately adjacent to them reflect what is presently stored in them. You’ll see that sometimes the register can hold referential addresses which point to other locations in memory (see RAX, RBX, RDX, RSI, etc.) whereas others contain the value itself (e.g. RDI, R8, R11, etc.).

 

Stage B – Intro to Assembly — (2 flags) I CS 6035

You can always query the current value of a register in GDB. For example, let’s say we wanted to view the contents of RDI:

pwndbg> x $rdi

Throughout this course, you’re going to be working with 64-bit registers. Besides being different in size from 32-bit registers, there’s actually some important architectural differences that you’ll need to know as they relate to binary exploitation. More to-the-point, not all registers are used in the same way by the CPU. Function calls – for example – look at specific registers for things like function arguments. For now, we encourage you to perform independent research into RBP, RSP, and RIP as these will be very important in the sections/exercises to follow.

Step B.2: Exercise 1

Ol_bb_steps [5 pts]

Resources

  • See our buffer overflow tutorial video
  • https://www.cs.uaf.ed u/2017/fa 11/cs301/lecture/09_11 _reg isters.htm I
  • GDB cheatsheet

Challenge Instructions

This challenge is meant to be a soft introduction to using GDB; however, you are also welcome to calculate the values by hand in reading the source code (fiag.c ) if you so choose. We recommend using GDB if you have never done so before because of how extensively the remaining project exercises engage the tool.

We can begin by manually starting GDB and hooking it to the flag binary process like so:

cd —/project/Ol_bb_steps/ gdb flag

We have extended the default vanilla GDB tool with pwndbg in order to help with things like readability and utility. If you were instead to invoke the binary into GDB with e.py ( python3 e.py dbg ), you’d observe GDB open as a separate window (see FAQ for folks opting to SSH into the VM). Either

https://g ith u b.gatech .ed u/pag es/cs6035-tool s/cs6035-tools.g ith u b. io/P rojects/B i n Exp/Ol_i ntroto_asse m b ly. htm I                                                                                                       4/8

 

Stage B – Intro to Assembly — (2 flags) I CS 6035

way, the pwndbg prompt will wait for you to enter a command; let’s start by setting a breakpoint for the debugger to catch on:

pwndbg> b main

The above sets a breakpoint at the start of the main() function (Note: as a courtesy, all of your e . py files have this configured by default when you invoke the dbg option). Recall that all C-based programs start execution at main(), so we can reliably expect such a function to be present in all of our binaries for this project. Let’s now start the flag binary by running it:

pwndbg> r

Within the GDB interface window, you’ll likely see a flurry of text/blocks showing various things like stack traces, register printouts, code prints, and more. GDB will pause the process’ execution at the start of main() (where we set our breakpoint) and await for the next command.

 

Projects / Binary Exploitation / Stage C – Stack Smashing —  (3 flags)

  • Overview
  • Learning Objectives
  • Exercises
  • Step C.1:
  • What is the stack? Why do we care about it?
  • What’s the danger?
  • Step C.2: Exercise 1
  • Step C.3: Exercise 2
  • Step C.4: Exercise 3

Overview

Now we get into the meat-and-potatoes of the binary exploitation project!

Recall in Stage 0 what we did in the guided exercise of 01_basic_overflow_1 : we learned how C could be a memory-unsafe language. More to-the-point: we performed a buffer overflow, thereby overwriting a variable (which altered the code flow of the process). It turns out that this kind of vulnerability can extend to overwriting other areas of the execution stack as well. In this section, we’re going to have our first look at stack-based overflows and learn the building blocks that will enable us to tackle more challenging exploits.

Learning Objectives

The core learning objectives for this section are:

  • Understanding the stack and stack-smashing comprehension
  • Working with pwntools and basic exploit development
  • Foundational considerations for code flow redirection

 

Exercises

 

This section features 3 exercises:

  • 01_basic_overflow_2
  • 01 mismatch
  • 02_report

Step C.1:

What is the stack? Why do we care about it?

In computer science, the stack is a contiguous block of allocated memory. As functions get called, said function’s variables get memory allocated on the stack; as the function call is resolved, the memory for the variables are de-allocated and removed. Helping organize and control this process are the RBP and RSP registers, which store the base pointer and stack pointer values, respectively. These pointers help reference either end of the stack frame and are useful both for pushing/popping values on/off the top of the stack (RSP) or referencing local variables (RBP).

STACK

 

2/9/26, 12:08 AM                                                                                                                         Stage C – Stack Smashing — (3 flags) I CS 6035

For the purposes of binary exploitation (and by extension, this project), this is useful to us in a lot of different ways. We’ve already seen how overflowing the stack can allow us to overwrite local variables contained within that particular function’s stack frame; but the real utility from this comes from writing into other stack information.

Consider what was described above: when a function call is resolved, it executes a ret assembly instruction to return the execution flow back to wherever it was originally invoked from: that destination is preserved in the stack! Since we’re already overflowing other values in the stack, we can likewise overwrite the destination that the ret instruction goes to!

 

• ••
Previous stack frame
300
200
.:                                                                                                                                        ,

,

r                                                                                                                                                                                                r

 Return address

1>EBP register of s / / o                 fir

.4.                                               /             /

the previous stack frame

Buffer
Current stack frame

 

Stack start, greater addresses
End of the stack, smaller addresses
overflow

There is often a padding between the register and the buffer

 

What’s the danger?

Now all of the above can feel quite abstract – especially if you’ve only ever learned about buffer overflows (or similar memory-based attacks) in academic textbooks. But there’s actually substantial security risks in being able to hijack a process’ control flow at runtime.

In all of the exercises that follow, we merely direct you to exploit the binary into reading from /proc/flag. But we could – in theory – make these binaries do anything we wanted under the EUID of the process ( binuser ); that’s not particularly useful/threatening in our case (since binuser has similar privileges as the user you’re already logged in as, binexp), but imagine the risks that poses for a

 

2/9/26, 12:08 AM                                                                                                                           Stage C – Stack Smashing — (3 flags) I CS 6035

vulnerable process running under elevated privileges; if we were to exploit a process running as root (or Administrator, in Windows parlance), we could force the process to perform actions as root. This goes without even addressing the potential harms to what the software itself is responsible for (one

could only imagine the potential impacts that could happen to software responsible for payroll or critical infrastructure, for example).

And before you go writing buffer overflows out as yesterday’s news – there continue to be many reported to this day.

Again however, we’re not going to be going that far in this class; these exercises are merely meant to get us acquainted with this class of vulnerability and comfortable with exploiting it at a basic level.

Step C.2: Exercise 1

O1_basic_overflow_2 [5 pts]

Resources

  • See our buffer overflow tutorial video
  • py (see the Instructor’s Note in the code comments)

Challenge Instructions

In this task you will learn details about binaries compiled from C code (with gcc) in a Linux

environment, and how some basic things can be exploited such as process redirection or control flow hijacking. We strongly encourage students consult the intro video included in the resources section above to help orient you to the task more generally.

For this task you have an executable binary named flag which is vulnerable to a buffer overflow in one of its functions. We will be using a Python exploitation library called pwntools to automate some of the overflow techniques and get the binary to call a function it otherwise wouldn’t have. This function called call_me0 generates a key using your Gradescope User ID to get a valid flag that you will ultimately write to your project_binexp.json file for grading.

Now we will run the binary just to see what the program is doing:

 

2/9/26, 12:08 AM                                                                                  Stage C – Stack Smashing — (3 flags) I CS 6035

$ cd –/project/01_basic_overflow_2 $ ./flag

cs6035Mcs6035:-/project_ctf/60_intro$ cd ../01_basic_overflow_2/ cs60350cs6035:-/proiect_ctfp1_basic_overflow_,_$ ./flag

Feed Me A Stray String:

We see the binary is asking for a string. Input any text you want or just press enter and you’ll (likely) see that the program does nothing and just exits. That would align with our expectations from reading the source code (flag. ). If we look into the read() function, we can learn…

read() attempts to read up to count bytes from file descriptor fd into the buffer starting at *buf

Oh no! In this case, read() will write up to 1000 bytes into the buffer, but buffer is only sized for a

lesser amount. As we learned earlier, a buffer overflow occurs when too much data is fed into an unprotected (or poorly protected) data buffer; it would appear that flag is vulnerable to a buffer overflow.

DEVELOPING THE EXPLOIT

Open e . py with your preferred text editor (the VM comes with xed by default) and analyze the content and comments. Once you understand what they do, proceed to uncomment the code in Part 1 and fill out the cyclic() size. What size do you need to make payload in order to trigger the segmentation fault from the buffer overflow?

After this, run the exploit through GDB:

python3 /home/binexp/project/Ol_basic_overflow_2/e.py dbg

This will open up a GDB terminal with a breakpoint set at main(). Within that terminal, pass the “continue” or “c” command to resume the process execution.

                                                                  Stage D – ROP______________ (2 flags) I CS 6035

Search CS 6035

Projects / Binary Exploitation / Stage D – ROP——————— (2 flags)

  • Overview
  • Learning Objectives
  • Exercises
  • Step D.1:
  • So what are ROP gadgets?
  • So how are function calls made?
  • Step D.2: Exercise 1
  • Step D.3: Exercise 2

Overview

It’s been a few years since “Smashing the Stack For Fun And Profit” was originally published; since that time additional binary protections have been enacted to mitigate the dreaded buffer overflow. This includes things like:

  • Address Space Layout Randomization (ASLR), which randomly arranges the address space positions of key data areas of a process.
  • The No eXecute (NX) bit (otherwise known as Data Execution Prevention – DEP), which marks certain areas of the program as not executable (including the stack).
  • …and much, much more.

But this hasn’t stopped buffer overflows from being problematic. Return-Oriented Programming (ROP) is a technique that was developed to otherwise bypass these and other controls. At its heart, ROP makes use of snippets of code that already exists within the binary – so called “gadgets” – in order to manipulate the code flow.

Learning Objectives

The core learning objectives for this section are:

 

2/9/26, 12:08AM                                                                                                                         Stage D – ROP______________ (2 flags) I CS 6035

  • Understanding function calls within 32- and 64-bit systems
  • Working with ropper and understanding gadgets

Exercises

This section features 2 exercises:

  • 03_inspector_gadget
  • 03_ROPscotch

Step D.1:

So what are ROP gadgets?

At its heart, the “return” in “return oriented programming” is what defines every gadget out there. If you were to dump the assembly instructions from these binaries (e.g. using objdump ), you would find any number of instruction sequences that terminate with a ret instruction. By jumping into these instructions, we allow for some atomic, register-oriented actions to take place before the ret instruction hits, thereby returning the execution flow back to the stack (which we ideally control, given our stack-based overflow techniques).

As an exercise, try dumping the assembly from our first problem in Stage 00 and CTRL+F search through the resulting flag.asm file for instances of the ret instruction:

objdump -D —/binexp/01_basic_overflow_1/flag > flag.asm

Now – obviously – manually parsing through an objdump for a list of operations preceding ret is quite tedious; this is compounded by the fact that not all instruction sequences are necessarily useful to us. Fortunately, we have a tool available for us to quickly identify all number of gadgets on our behalf: ropper!

As a follow up, try using ropper on the same binary:

ropper –file —./binexp/01_basic_overflow_1/flag

Do the addresses match?

 

                                                                                                               Stage D – ROP______________ (2 flags) I CS 6035

In essence, we’re still performing jumps to areas in code – much like how we were in the stack smashing portion; only this time, we’re additionally leveraging these gadgets to do some setup and register manipulation in order to allow us to get some other malicious actions done.

So how are function calls made?

Thus far, you’ve seen at least one example for how functions are called through assembly instructions and registers: recall the call instruction, which we’ve looked to several times in the past several exercises.

Up until now however, you’ve probably not thought about the structure/setup that these function calls have had to observe. What happens – for example – when a function has an argument (or two+)? Here is one difference between 32- and 64-bit architectures that’s worth noting. In 32-bit architectures, these arguments are pulled from the stack; by contrast, in 64-bit architectures, these values are referenced from the registers.

So, for example, when foo(bar,baz) is invoked in a 32-bit system, we’d want a payload looking something like:

payload = cyclic(…)

payload += p32(foo)

payload += p32(pop_pop_ret_gadget)

 

2/9/26, 12:08AM                                                                                                                          Stage D – ROP______________ (2 flags) I CS 6035

payload += p32(bar)
payload += p32(baz)

In the above example, first the function call to food is crossed in the stack. When that function call is made, the very next value in the stack is considered the return address; in this case, we’ve overflowed it as being an arbitrary pop_pop_ret gadget (I say “arbitrary”, because it largely doesn’t matter which registers – save for reserved ones like EIP, ESP, and EBP – will hold the removed values in 32-bit systems). The next values in the stack fit the sequential order of arguments expected (first bar, then baz). In this case, we’ve used the particular gadget because of how it will remove bar and baz from the stack after execution by “popping” them; this sets us up for sequential function calls as needed (aka ROP chaining).

There’s a subtle difference when it comes to 64-bit systems like the VM the project is hosted on. In those kinds of systems, you want to lead with the ROP gadget first. This is because it’s necessary to stage the arguments for the function before it’s called. Moreover, we need to be quite selective about which ROP gadgets we reach for (vs. the more arbitrary choices in 32-bit systems); this is because functions will look to specific registers for their values (starting with RDI for the first argument). More generally, you’d want your payload looking like this:

 

acdr 2
:Icdr               
.c.:a!i:c:et?.
A

1
r

1

Cyclic buffer data
Oxaaaaaaab, etc

 

 

Assuming that the initial address you overwrite in your buffer overflow is a “pop” gadget, the code flow

1 (ret)urn to the pop gadget, popping the arguments off the stack and into the respective registers.

2 At the end of the gadget, it will (ret)urn again – this time to the address of the function in question.

https://github.gatech.edu/pages/cs6035-tools/cs6035-tools.githu b. io/Projects/Bin Exp/03_rop. htm I                                                                              4/8

 

                                                                 Stage D – ROP______________ (2 flags) I CS 6035

3 When the function ends, it will (ret)urn back to whatever’s next in the stack; if we’re chaining ROP calls, this would mean going back to step (1).

Step D.2: Exercise 1

03_inspector_gadget [15 pts]

Resources

In this task, we’re going to build on our understanding of ROP by having you dig into more complicated gadget chains. Additionally, we’ll also need to work with yet another kind of vulnerability: race conditions! As always, evaluate the source code and try to come up with a plan of approach before you start exploiting.

On ROP

In past examples, we’ve shown how function calls in 64-bit systems have relied on arguments being placed in their appropriate registers. This is trivial when we have gadgets on-hand that simply pop the value off of the stack and into the register. However, we’re not always so fortunate as to have such options available. Instead, there’s usually a multitude of more complex gadgets present that we need to string together in a chain in order to setup our function calls. For example, instead of…

pop rdi; ret;

We might need to utilize 2 or more gadgets to achieve the same effect like…

pop rbp; ret;

mov rdi, rbp; ret;

      Stage E – Final Flags________ (3 flags) I CS 6035

CS 6035

Search CS 6035

Projects / Binary Exploitation / Stage E – Final Flags ———-  (3 flags)

  • Step E.1
  • Exercises
  • Step E.2: Exercise 1
  • Step E.3: Exercise 2
  • Step E.4: Exercise 3

Step E.1

Welcome to the final section of the Binary Exploitation project! We reserve this section semester­over-semester for more advanced topics as well as binaries that we feel help extend student comprehension over the prior sections. Topically, the exercises do not necessarily relate to one-another and thematically should be approached as being distinct in their learning objectives.

Exercises

This section features 3 exercises:

  • 02_guess_who 02_trust_buster
  • 03_chirp

Step E.2: Exercise 1

02_guess_who [10 pts]

Resources

  • None

 

                                                                 Stage E – Final Flags________ (3 flags) I CS 6035

Challenge Instructions

This challenge is a little different. In this one, we’re cross examining what it means exactly to be “random”.

Randomness – or entropy – is a classic staple in secrecy and security. We find it in things like cryptographic keys, secure communication protocols, and password salting – to name a few. Randomness – or at least the appearance of being random – adds a layer of obfuscation and mitigation to malicious activity; if bad actors don’t understand or cannot effectively guess what the values are, then they are more readily barred from impacting the CIA triangle (confidentiality, integrity, availability).

So what’s the problem here?

PSEUDO RANDOM NUMBER GENERATORS (PRNGS)

With the exception of some select hardware RNG, most modern machines aren’t producing truly random values – just pseudo-random ones. Most software approaches to yielding random values just appear random, but are actually inherently deterministic (admittedly, that might not be transparent to us as end users). Many PRNGs make use of something called a “seed” value, which determines the trajectory of all random outputs the PRNG will ever make; using the same seed and PRNG will produce the same sequence of “random” outputs every single time. It’s not uncommon –for example – for folks to see a PRNG with a seed like the current datetime stamp (down to the millisecond) due to how difficult it can be for a bad actor to predict that in a running process. Simple seeds are – as you might imagine – problematic.

INSUFFICIENT ENTROPY

Even if thoughtfully designed, a PRNG may be made vulnerable in the way it’s presented. Take a coin-flip, for example. Now while we likely cannot know with certainty what value the coin will present itself at the end (shy of cheating), we do still have pretty good odds of guessing what it is all the same. Modern computers are – by design – powerfully adept at making many computations every microsecond, so any PRNG we look to adopt would need to be vastly more computationally difficult to estimate in order to prevent just brute-forcing the correct random value.

INCORRECTLY IMPLEMENTED

By-and-large, the most common vulnerabilities that emerge (as far as PRNGs are concerned) are failures in securely integrating them into one’s system design. Architectural design choices, inappropriate code, etc. can undercut (or even negate) the use of PRNGs entirely.

OUR SUGGESTIONS

This is not a buffer overflow exercise; that is not the intended approach to the problem. There are several different sources of randomness incorporated into this challenge, including /devurandom, rand() , and arc4random() . These all behave differently from one another, so we encourage you to

 

2/9/26, 12:09 AM                                                                               Stage E – Final Flags________ (3 flags) I CS 6035

research how they are being used and integrated into the source code. Try to identify which might be stronger than others, which are appropriately utilized, etc.

It’s important to note that python and C approach implementing randomness in subtly different ways (but consequential in terms of seeding). Reproducing random values in e.py can be pretty tricky; we suggest instead working with either toy code (e.g. https://programiz.com/c­programming/online-compiler/ or something resident on the VM with gcc ), importing custom C shared libraries (i.e. custom.so) into your python script through ctypes, and/or working through some of the random behavior natively vs. trying to recreate the random seeding results through python exclusively. Know that the numbers you are meant to guess change each time you run the binary; as such, you cannot simply denote what they might be using GDB – they are not hardcoded anywhere. However, the numbers can be reliably predicted, guessed, or otherwise discerned.

Step E.3: Exercise 2

02_trust_buster [10 pts]

Resources

  • See our buffer overflow tutorial video

Challenge Instructions

In this task, you’re initialized as a low-privileged app user. Your goal is to escalate your privileges in the binary towards becoming an admin and getting your flag. This exercise is less of a puzzle and more about recognizing how a variety of different software vulnerabilities can be chained together to get you to your complete exploit.

Here’s our recommendations:

  • You’ll want to understand where and how /pros/flag is called in the binary. What are the conditions you need to meet in order for this to happen?
  • Reading over the source code shows that there are 3 tiers of privilege within the app: USER, MODERATOR, and ADMIN. What different kinds of actions can these tiers perform from one another?
  • Don’t forget that GDB can be used to query the state of anything in memory. You may find it helpful to examine how a user is mapped out (e.g. x &users[0].role) or look at a broader swathe

 

                                                  Stage E – Final Flags________ (3 flags) I CS 6035

of user data (e.g. x/20x &users[0]). The binary has you loaded in as a default user with the lowest privileges from the start, so it may be worth examining that user first.

Step E.4: Exercise 3

03_chirp [15 pts]

Resources

  • Format String Specifiers

Challenge Instructions

There have been a number of defensive upgrades made to binary security over the years designed to protect against memory corruption vulnerabilities like the stack-based buffer overflow.

In this challenge, we’ll cross-examine a few in particular: stack canaries and PIE .

WHAT ARE STACK CANARIES?

When you’ve been performing basic buffer overflow exploits to hijack the control flow of a binary, this has typically been done by simply overwriting the ret address stored in the stack that the function’s instructor pointer would reference.

Stack canaries frustrate this process by doing a couple things:

1 At compile time, the source code is slightly modified around these ret operations; the compilers

inject some additional operations in order to protect/preserve the integrity of the address referenced by the ret operation.

2 The way that the address is protected is that a runtime-determined random value (aka the “canary”) is inserted in-between that address and the rest of the calling function’s frame. When the function terminates, that random value is checked by the binary to determine if it’s changed; if it has, then the binary presumes that an overflow has taken place and terminates the whole process.

There are a few rules/behaviors that stack canaries follow that are worth being mindful of.

  • In our 64-bit system, stack canaries are always sized 8 bytes.
  • Stack canaries are set when the program starts up and do not change after. The same stack canary is even passed along to any child processes it spawns (i.e. they do not create their own

 

2/9/26, 12:09 AM                                                                                Stage E – Final Flags________ (3 flags) I CS 6035

stack canaries). Stack canaries are only reset when the process restarts.

  • The relative position of stack canaries within the stack do not change run-over-run (though the absolute address may change due to things like ASLR); stack canaries will always be positioned between all other variables within a function and the return address.

WHAT IS PIE?

Position Independent Executables (PIE) is a hardening measure that binaries can likewise embrace at compile-time. In brief, PIE randomizes the address space of the binary’s assembly at runtime making it practically impossible to statically reference particular instructions in our exploits.

Below is a screenshot of the same binary objdump compiled with and without PIE:

 

(0:
4017d1:
4017d2:
4017d5:
4017da:

114-;00

00      000170 <main>:

17e3:, f3 Of le fa

17e7:              5

17e8:       48 89 e5

17eb:       b8 00 00 00 00

life:       e8 25 ff ff ff

17f5: eb f4 endbr64

push Ubp

mov     UspArbp

mov    $0x0,%eax

ca11   401707 <coalmine>

imp   4017d5 <main4.0x8>

endbr64

push Vbp

mov     Vspi%rbp

mov   $0x0, eax

call   171a <coalmine>

imp   17eb <maini-Ox8>

 

In the case of the PIE-compiled binary, instead of addresses the objdump dumps offsets. Like stack canaries, PIE has a few rules/behaviors that are worth being mindful of:

  • At runtime, a PIE base address is randomly set and all of the binary’s instructions are offset from that base address. Each individual instruction is not seeded with its own randomized value, but are instead offset from this base address.
  • The PIE base address typically ends in 000 due to memory pages being the units of randomization, sized at Ox1000 bytes. For example, it might look like: 0x5b930239a000.

MEMORY LEAKS & FORMAT STRINGS

On their face, both of these protections may seem insurmountable:

1 We don’t know the value of the canary at runtime, so overflowing it with anything other than itself will prevent our exploit.

  • CS6035_binexp_exploit_scriptsEC-kvukaf.zip