Thursday, October 9, 2014

Buffer Overrun Vulnerabilities and Exploits, Part 1

Hello,

Since we will be delving deeper into the Vortex wargames in the near future, I figured this would be a good time to bring everyone up to speed on buffer overruns. So this will be the first posting in a multi-part series. In this first part, we will cover basic buffer overrun vulnerabilities, as well as simple exploits. Subsequent posts will cover how to execute the actual attacks, as well as return-to-libc theory, which comes into play when the stack is NX (not-executable).

I will cover everything you need to know to have a basic understanding of buffer overruns here. If you want to delve more deeply, here is some suggested reading:
http://www.phrack.com/issues/49/14.html#article
https://www.ethicalhacker.net/columns/heffner/smashing-the-modern-stack-for-fun-and-profit

To learn buffer overruns, and to play Vortex or any other wargame server, you must know how to use gdb to examine memory. I will show some tips/tricks here.

Okay, buffer overruns. What's wrong with this code snippet?

vulnerable1.c                                                                                                                                             

#include <stdio.h>
#include <string.h>
int main(){
  char buffer[80];
  strcpy(buffer,argv[1]);
  return 1;
}

buffer is a char array of size 80. We then use strcpy to take argv[1] (whatever input the user supplies at the command line) and copy it into the buffer variable. No bounds checking is performed here, so the user can write an arbitrary length of data into the buffer variable. This will generally lead to a seg fault, and will crash the program. Unless the attacker is clever...

Let's look at how the stack is laid out for this particular program:

Usually, the stack grows from higher memory (return address) to lower memory (buffer). The function stack layout is:
higher memory -------------------------------------------------------------------------------lower memory
[arguments to the function][return address][saved framepointer(%ebp)][local variables]

A new stack is generated every time we call a function. Further, the memory address of the next instruction to be executed by the cpu after we return from the called function is pushed onto the stack. So if we had code like:
int x = 1;
<call to function>
x += 1;
Then the address of the instruction x+= 1; would be pushed onto the stack when we call <call to function>. This is so that when we return from <call to function>, the cpu knows where to resume execution.
Now, if you were somehow able to gain control of that return address, then you could point it to any accessible address in memory. Basically, you control the program at that point. But, how do we gain control of the return address?
In this case, since no bounds checking is enforced on our input, it is trivial. If you want to follow along, compile vulnerable1.c with the following options:
gcc -ggdb -fno-stack-protector -mpreferred-stack-boundary=2 -o vulnerable1 vulnerable1.c

This will align the stack and turn of stack-smashing detection, necessary if you want these examples to work on a modern Linux system. You will also need to disable stack randomization (ASLR). Do this by running the following command:
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
This will not persist through a reboot.

Now, let's run it:
root@bt: ./vulnerable1 $(python -c 'print "A"*80+"B"*4+"C"*4')

We get a seg fault, predictably.
Let's fire up gdb:
root@bt: gdb ./vulnerable1
gdb: break main
gdb: run AAAA...ABBBBCCCC  (that's 80 A's, 4 B's, and 4 C's)
gdb: x/90xw $esp  (look at the top 90 words in hex of the stack ($esp))
gdb: s  (single step past the strcpy)
gdb: x/90xw $esp (look at the top of the stack again, should see our input)
gdb: c (continue)
seg fault, 0x43434343 in ?

Note, 0x41 is "A" in hex, 0x42 is "B" in hex, and 0x43 is "C" in hex, so our 4 "C"s overwrote the return address! The "B"s overwrote ebp, and the 80 "A"s filled in the buffer. Thus, we have successfully overwritten the return address with 0x43434343 by writing past the buffer (supplying 88 pieces of data instead of only 80 or less).

That's all for this round. We've seen that the entire goal in a buffer overrun attack is to gain control of the return address on the stack. Once we have that, we can take care to point it to something else (like, our own function that spawns a shell). We will see in the next posting just how to do that.

Wednesday, October 1, 2014

Vortex level 0

"If looks could kill they probably will, in games without frontiers - war without tears"

Wargame time. Vortex is the first target (probably regrettable, since that server is notoriously hard).
Here is the walkthrough for level0. Problem description can be found here:
http://overthewire.org/wargames/vortex/vortex0.html

I will try to cover all of the levels on this machine. I will post my complete answer, but I'm going to refrain from posting the actual passwords (just run the code if you really want them). Part of the fun of wargames is the agony that comes from trying to solve the damn things, so spoiler alert, use these resources sparingly. It's very easy to read the answer and cheat yourself out of the solution.
Big thanks to overthewire.org for continuing to host these games!

Some suggested reading for this level:
http://www.tutorialspoint.com/python/python_networking.htm
https://docs.python.org/2/library/struct.html
http://teaching.idallen.com/cst8214/08w/notes/bit_operations.txt

This level wants us to connect to a remote host on a specified port, and read four unsigned integers in host-byte order. We are told that the machine is little-endian format. Host-byte order is just a standard used to avoid confusion among little endian and big endian machines. We will read the four unsigned ints, add them together, send back the sum, and receive the solution. There's a catch though, the numbers we receive are quite large, and adding them together will almost always overflow past 32 bits. Thus, at each intermediate stage, we keep our answer to 32 bits by masking it with a 32-bit mask (0xffffffff)

Here's the code. I went with Python over C since I value my sanity;)

#!/usr/bin/python
from socket import *
from struct import *

host = "vortex.labs.overthewire.org" #Target host
port = 5842 #Target port
integer_sum = 0  #This will hold the sum of the received integers
s = socket(AF_INET, SOCK_STREAM) #create a socket connection
s.connect((host,port)) #connect to remote host
#We receive 4 unsigned ints, each 32 bit (4 byte) in Little Endian
for i in range(4): 
data = s.recv(4)
integer_sum += unpack("<I",data)[0]
integer_sum &= 0xffffffff
#unpack handles the network data in Little Endian format
s.send(pack("<I",(integer_sum)))
print s.recv(1024) #username:password
s.close()

We start by importing socket and struct (used for packing and unpacking network data).
We create variables for the host and the port, and then an int that will hold our sum.
we then create a connection-oriented socket and connect to the remote host and port.
The 4 loop will read call recv four times, each time reading in 4 bytes (32 bits) of data.
unpack("<I",data)[0] will take the network stream data in little-endian ( "<" ) format, read it as an unsigned int ("I") and store it in sum. We then consider only the first 32 bits of integer_sum by hitting it with the mask 0xffffffff (this could also be done only once at the end, before sending the sum to the host).
We then send it off (as a little endian unsigned int "<I"), receive the answer (or a "bzzzt, wrong" if you've made a mistake) and call it good. Level0 down, on to the next one!