Project Goals
The goals of this project are:
- to exploit a remote memory corruption vulnerability [8% of project score]
- to inject shellcode and take control of a remote process [12% of project score]
Administrative Information
The project is an individual project. It is due
on Monday, May 18, 2020, 23:59:59 PST (no
deadline extensions; late flags will not be accepted).
Introduction
A buffer overflow occurs when a program or process tries to store
more data in a buffer (or some temporary data storage area) than
that buffer was intended to hold. This extra data, which has to go
somewhere, overflows adjacent memory regions, corrupting or
overwriting the valid data stored there. If the buffer is stored
on the stack, as it is the case for local variables in C, control
information such as function return addresses can be altered. This
allows the attacker to redirect the execution flow to arbitrary
memory addresses. By injecting machine code into the process
memory (e.g., as part of the data used to overflow the buffer, or
in environment variables), the attacker can redirect the execution
flow to this code and execute arbitrary machine instructions with
the privileges of the running process. Thus, it is imperative that
the length of the input data is checked before copied into buffers
of fixed lengths. Unfortunately, a number of popular C functions
(e.g., strcpy, strcat, sprintf, gets, or fgets) do not perform
such length checks, making many applications vulnerable to this
kind of attack.
Detailed Description
Your task is to exploit vulnerabilities in remote server
applications. Specifically, your objective is to create and then
inject appropriate exploit inputs into each target application
so that you force the program to return a flag. Like for
previous challenges, you will first go to our CTFd site and
launch your service instances. The site will give you the
address and destination port where your server is running and
listening for your connections.
For this challenge, we have prepared three vulnerable
services. The first two applications
(minecraft_hello and
minecraft) are easier to exploit, and if you
succeed with these two, you will receive full credit for this
project. The third application (lazy_panel) is
significantly more challenging. You will receive extra credit
(an additional 10% of your project score) if you manage to
exploit it. Although we don't expect all students to be able to
do so, we encourage everyone to at least give it a try. However,
to keep it challenging, we don't plan to provide a lot of
support for this part.
You can find the source code (for the first two applications)
and the binaries (for all three) from here. The
first two applications are compiled for 32-bit x86 on Linux. The
third application is compiled for 64-bit. The binaries running
on the server are the exact copies of the binaries you get.
Our service is launched by xinetd. Xinetd is a
wrapper that listens on a specific port and starts a
corresponding program. When it receives data from the network,
it will forward it to the (standard) input of the
program. Similarly, it will send data that it gets from the
program (written to the standard output) back over the
network. To talk to the services and come up with exploits, we
recommend pwntools.
We used gcc to build the binaries from the sources,
using different command line flags for the different
challenges. You can use checksec to understand what
security related features the binary is built with (or without).
Hints
Performing a successful remote buffer overflow can be tricky,
because it is important to get all the little details
right. This section lists a few things that you might consider.
The first two applications: minecraft_hello / minecraft
-
The "mine" piece of the service implements an information
leakage vulnerability. This allows you to read data from the
stack of the remote program. This will come in very handy for
you to understand what values are stored on the
stack. Moreover, it will also be crucial to help you bypass
certain security protections (such as stack canaries and
random stack locations, due to ASLR).
-
When you look at minecraft_hello, you will
see that we have added a convenient function called
win(). If you manage to craft an input that makes
the vulnerable function (after you have overflown the buffer)
return there, you might not need any shellcode at all! Also,
this "hello world" challenge does not use stack canaries.
-
For the second service (minecraft), the
win() function is no longer useful. Bummer! :) In
addition, we have enabled stack canaries. That is, the
compiler will insert a value before the return address that
protects from buffer overflows. This means that you need to
make sure that you include the canary value as part of your
buffer overflow so that the correct value remains at the right
position.
-
At this point, you will also need a shellcode. If you haven't
written a buffer overflow before, check out the standard
tutorial article by Aleph One. Everyone interested in
security should read this paper, it’s a really good
introduction and a classic.
-
There are powerful tools available that help you craft your
shellcode. For example, check out pwntools' shellcraft or
venom/metasploit
-
Make sure that your shell code works correctly. That is, write
a little test program that jumps to your shell code and make
sure that it does what you want it to do before proceeding.
-
While -no-pie makes sure that the code is always at the same
location, this is not true for the stack. Our system has ASLR
enabled. This means that, for each execution of a program, the
operating system will place the stack segment at a random
value. This makes it very difficult to predict where the stack
will be, and as a result, it makes it hard to know the address
of your buffer where you will put the shellcode. This is where
the mining part (the info leak vulnerability) comes in
handy. Find a value on the stack that points into the stack,
and use this as an anchor to understand where the stack of the
current instance (of the application that is running) is
located. Then, adjust the address that you jump to accordingly
to make it point to the buffer that you have overflown.
-
Also, think about using a nop sled. It makes exploitation
easier if you don't get the address of your shellcode
perfectly right.
-
Use the debugger (gdb) to see what is going on when
you try your exploit. The debugger is your best
friend. Getting all the addresses right can be
difficult. Consider powering up your good old gdb
with modern tools like gef. It
makes your debugging life much more enjoyable. Keep in mind,
however, that when running the program in gdb the
addresses of the process can be different. For an accurate
representation of the addresses, attach to a running process
instead of starting the process in gdb.
-
Since you cannot debug the vulnerable programs directly (as it
runs remotely), you might want to run a local version of the
vulnerable program and try to exploit it first.
-
Calling conventions are not always strictly
followed. Sometimes, your mental model may differ from the
actual code being executed. In addition to debugging, reverse
engineering gives you insight about how the program actually
executes in gory details. There are many powerful RE tools out
there but you may want to try Ghidra first (it's
free!). You may also want to check out IDA, Radare2 or Binary Ninja (they have a
freely accessible cloud version).
-
The flag is stored in the file /flag.
The third application (for extra credit): lazy_panel
-
lazy_panel requires you to first
crack/bypass/guess-your-way-through a poor password
check. Only then can you exploit a buffer overflow.
-
For this application, we have disabled stack
canaries. However, we have enabled data execution protection
(DEP). Thus, you can no longer just inject shellcode into the
buffer on the stack and jump there. Instead, you have to
launch your exploit using return-oriented programming
(ROP). Adopting rop'ing tools like angrop
will definitely make your life easier.
-
This time, you will have to reverse engineer the binary to
understand the program logic. Also remember that different
from the former applications, this application is compiled to
be 64-bit.
-
The flag is stored in the file /flag.
-
Well... no more hints. Good luck!