Polaris (Launched 1998)
- Username:
polaris
-
Click to reveal password:
tolearn
- Points: 5 for checkpoint, 5 for code, 5 for write-up
Relevant lectures: 5 - Mitigating Memory Safety Vulnerabilities
The Spica logs seem to be definitive proof of EvanBot's existence, but without further clues, you seem to have hit a dead end. Luckily, some time later, CSA assigns you to deorbit Polaris, a former Gobian spy satellite. As the space race became more competitive, newer Gobian Union satellites like Polaris introduced stack canaries to protect top-secret information from enemy spies. Although stack canaries were considered state-of-the-art defense at the time, we now know that they can be defeated. Hack into Polaris to see what intelligence it contains, and don't forget to deorbit it afterwards.
For this question, stack canaries are enabled. You need to make sure the value of the canary isn’t changed when the function returns, but you still need to overwrite the RIP. Can you find a way to get around this mitigation?
Note: This Project will use 4 random bytes as the canary, instead of 3 random bytes and 1 NULL byte. This is different from what is taught in lecture! For exams, you should still assume that the canary always has one NULL byte.
The vulnerable dehexify
program takes an input and converts it so that its hexadecimal escapes are decoded into the corresponding ASCII characters. Any non-hexadecimal escapes are outputted as-is. For example:
$ ./dehexify
\x41\x42 # outputs AB
XYZ # outputs XYZ
Note that we are not inputting the byte \x41
here. Instead, we are inputting a literal backslash and the literal characters x
, 4
, and 1
. Also note that you can decode multiple inputs within a single execution of a program.
For this question, you will write an interact
script. Instead of doing simple output, the interact
script has the ability to send and receive from the vulnerable program. This means that the output from the program can be used to affect your next input to the program.
The interact
API
The interact
script lets you send multiple inputs and read multiple outputs from a program. In particular, you have access to the following variables and functions:
-
SHELLCODE
: This variable contains the shellcode that you should execute. Rather than opening a shell, it prints theREADME
file, which contains the password. Note that this is different from the shellcode in the previous two questions. -
p.start()
: This function reads starts the vulnerable program. p.send(s)
: This function sends a byte strings
to the C program. You must include a newline'\n'
at the end of your input strings
(this is like pushing “Enter” when manually typing input).- Note that in Python, to send a literal backslash character, you must escape it as
\\
.
- Note that in Python, to send a literal backslash character, you must escape it as
-
p.recv(n)
: This function readsn
bytes from the C program’s output. This function will hang or stall out if less thann
bytes are received from the C program’s output. p.recvline()
: This function reads all bytes until a newline ('\n'
) from the C program’s output. The newline is included at the end of the returned string.
An example utilizing the interact
API
Let’s assume that our C program contains the following.
int main() {
char buf[8];
gets(buf);
printf("%s", buf);
return 0;
}
The interact
program that we write must align with the pre-existing C program (since we cannot modify the C program itself). For instance, every p.send(s)
that you send must align with a place within the program that accepts user input, like a gets
or fread
call. Likewise, every p.recv(n)
or p.recvline()
must align with a place within the program that sends output to the user, like a printf
call. This means that you do need to correctly order your function calls.
For example, the following interact
file would work and produce output.
p.start()
p.send("Evanbot would like 5 pancakes please!\n")
foo = p.recvline()
print(foo)
Notice that we send the string Evanbot would like 5 pancakes please!
, which would be then utilized as the input into the gets(buf)
on line 3. gets
will strip out the \n
and replace it with a \0
(null terminator), which will make the string well-formed.
Then, when p.recvline()
is called, it is receiving the output printed on line 4 of the C code. Since what’s printed is just what is in buf
, this means that foo
will be set equal to the string Evanbot would like 5 pancakes please!
, which is what will be printed out on line 4 of the Python code. Note: Since we are using p.send
and p.recv
to interact with the program, calling print
is a great way to debug!
While the previous code works, writing the following in our interact
file will not work.
p.start()
foo = p.recvline()
print(foo)
p.send("Evanbot would like 5 pancakes please!\n")
Take some time first to understand why, and check your understanding with the toggle below.
Click to reveal why the above code block doesn't work.
If we first callp.recvline()
, the program will hang since it cannot printf
anything from the system until after it has received input. However, the program will not receive input until after the p.recvline()
executes and the received value is print
ed.
This shows the importance of the ordering of your API calls! Please read the C code carefully to make sure you don't run into this problem.
Success State
When running ./exploit
, the exploit will run three times. As long as it passes at least once (so it prints out the next question’s username and password), you’ve completed the question. You will not have to call cat README
–the SHELLCODE
for this question does not open a dummy shell.
Tips
- You might want to save some C program output and input part of it back into the C program. No hex decoding or little-endian reversing is necessary to do this. For example:
foo = p.recv(12) # receive 12 bytes of output
bar = foo[4:8] # slice the second word of the output
p.send(bar + '\n') # send the second word back to the C program
- You can display bytes in a readable format as follows:
foo = p.recv(12)
print([hex(ord(c)) for c in foo])
- Keep in mind that the function does not return immediately after the buffer overflow takes place (it might help to look at what codes are executed next and think about what it does to the stack), so you will need to account for any extra behavior so that the stack is set up correctly when the function returns.
Deliverables
- A script
interact
- A write-up.