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!

Wednesday, September 10, 2014

Address Space Layout Randomization in Linux 2.6+

Hi all, this post is intended as a primer for the Vortex wargames I will be hacking here in the coming weeks. If you want to follow along with the wargames, they can be found at www.overthewire.org. I figured this would be a good place to briefly review virtual memory and ASLR in the Linux kernel, since some of the vulnerabilities in Vortex will require us to use buffer overruns, and ASLR is one of the techniques used to prevent such attacks.

Let's start off with a claim that we will then verify below: Roughly all processes run in the same virtual memory space, and are given the illusion that they are the only program running on the computer.

What this means, quite literally, is that if you run a program and look at its start location in virtual memory, and then run another program and look at its location in virtual memory, they should be the same. Let's see if we can verify this by hand:

First, fire up a Linux terminal and type the following:
cat
but don't provide the cat program with any arguments (this should keep it running, so that we can go investigate its memory usage). Now, from a seperate terminal on the same computer, type:
ps -a | grep 'cat'

Read the man pages for more details on the commands ps and grep. This basically lists all of the processes running on your system, and then filters them for the word "cat". Now, you should have a result that looks similar to this:
28733 pts/3    00:00:00 cat

The first number, in this case 28733 (will likely be different when you run this), is the process id for the cat command that we ran a moment ago. Now, from the same terminal, go ahead and run:
cat /proc/28733/maps
Which will produce the following (note when you run the example, substitute the process id on your system in place of 28733):
00400000-0040b000 r-xp 00000000 08:03 7077907                            /bin/cat
0060a000-0060b000 r--p 0000a000 08:03 7077907                            /bin/cat
0060b000-0060c000 rw-p 0000b000 08:03 7077907                            /bin/cat
00e5d000-00e7e000 rw-p 00000000 00:00 0                                  [heap]
7f6d015f1000-7f6d018ba000 r--p 00000000 08:03 12067216                   /usr/lib/locale/locale-archive
7f6d018ba000-7f6d01a6f000 r-xp 00000000 08:03 28053024                   /lib/x86_64-linux-gnu/libc-2.15.so
7f6d01a6f000-7f6d01c6e000 ---p 001b5000 08:03 28053024                   /lib/x86_64-linux-gnu/libc-2.15.so
7f6d01c6e000-7f6d01c72000 r--p 001b4000 08:03 28053024                   /lib/x86_64-linux-gnu/libc-2.15.so
7f6d01c72000-7f6d01c74000 rw-p 001b8000 08:03 28053024                   /lib/x86_64-linux-gnu/libc-2.15.so
7f6d01c74000-7f6d01c79000 rw-p 00000000 00:00 0
7f6d01c79000-7f6d01c9b000 r-xp 00000000 08:03 28053004                   /lib/x86_64-linux-gnu/ld-2.15.so
7f6d01e85000-7f6d01e88000 rw-p 00000000 00:00 0
7f6d01e99000-7f6d01e9b000 rw-p 00000000 00:00 0
7f6d01e9b000-7f6d01e9c000 r--p 00022000 08:03 28053004                   /lib/x86_64-linux-gnu/ld-2.15.so
7f6d01e9c000-7f6d01e9e000 rw-p 00023000 08:03 28053004                   /lib/x86_64-linux-gnu/ld-2.15.so
7fffe41c9000-7fffe41ea000 rw-p 00000000 00:00 0                          [stack]
7fffe41ff000-7fffe4200000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]



That is a snapshot of the virtual memory in use by cat. A couple of things to note here are that memory starts at 0x00400000, and that the [stack] is located at 0x7fffe41c9000.
Great, let's do this entire process again, but for a different program (you can even do this while your instance of cat is still running):
vim
and then, from another terminal:
ps -a | grep 'vim'
cat /proc/29569/maps
which gives:
00400000-005d6000 r-xp 00000000 08:03 12059867                           /usr/bin/vim.basic
007d5000-007d6000 r--p 001d5000 08:03 12059867                           /usr/bin/vim.basic
007d6000-007ec000 rw-p 001d6000 08:03 12059867                           /usr/bin/vim.basic
007ec000-007f8000 rw-p 00000000 00:00 0
00972000-00a36000 rw-p 00000000 00:00 0                                  [heap]

...
...
7f6489602000-7f6489604000 rw-p 00023000 08:03 28053004                   /lib/x86_64-linux-gnu/ld-2.15.so
7fffbcbb0000-7fffbcbd1000 rw-p 00000000 00:00 0                          [stack]
7fffbcbff000-7fffbcc00000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]


To note, the vim program also starts at 0x00400000! So both processes, vim and cat, start at the same location in virtual memory, even when they are both running at the same time. This is in general true of all processes. Note that I am using a 64-bit version of Linux, so the start location may be different on your system, but it should be the same for all of your processes. If you have been paying close attention, you may have noticed that the [stack] has moved from cat to vim, as it is now at 0x7fffbcbb0000. Let's run another instance of cat and see if the [stack] is still at 0x7fffe41c9000.
When I check on a new instance of cat, I find that its virtual memory looks like:
00400000-0040b000 r-xp 00000000 08:03 7077907                            /bin/cat
0060a000-0060b000 r--p 0000a000 08:03 7077907                            /bin/cat
0060b000-0060c000 rw-p 0000b000 08:03 7077907                            /bin/cat
00d0e000-00d2f000 rw-p 00000000 00:00 0                                  [heap]
7ffe3b35c000-7ffe3b625000 r--p 00000000 08:03 12067216                   /usr/lib/locale/locale-archive
7ffe3b625000-7ffe3b7da000 r-xp 00000000 08:03 28053024                   /lib/x86_64-linux-gnu/libc-2.15.so
7ffe3b7da000-7ffe3b9d9000 ---p 001b5000 08:03 28053024                   /lib/x86_64-linux-gnu/libc-2.15.so
7ffe3b9d9000-7ffe3b9dd000 r--p 001b4000 08:03 28053024                   /lib/x86_64-linux-gnu/libc-2.15.so
7ffe3b9dd000-7ffe3b9df000 rw-p 001b8000 08:03 28053024                   /lib/x86_64-linux-gnu/libc-2.15.so
7ffe3b9df000-7ffe3b9e4000 rw-p 00000000 00:00 0
7ffe3b9e4000-7ffe3ba06000 r-xp 00000000 08:03 28053004                   /lib/x86_64-linux-gnu/ld-2.15.so
7ffe3bbf0000-7ffe3bbf3000 rw-p 00000000 00:00 0
7ffe3bc04000-7ffe3bc06000 rw-p 00000000 00:00 0
7ffe3bc06000-7ffe3bc07000 r--p 00022000 08:03 28053004                   /lib/x86_64-linux-gnu/ld-2.15.so
7ffe3bc07000-7ffe3bc09000 rw-p 00023000 08:03 28053004                   /lib/x86_64-linux-gnu/ld-2.15.so
7fff2c34c000-7fff2c36d000 rw-p 00000000 00:00 0                          [stack]
7fff2c3ff000-7fff2c400000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]


 Same start address, but the stack has moved to 0x7fff2c34c000! Why is this? Well, this is the hallmark of Address Space Layout Randomization (ASLR). Linux kernels 2.6 and above randomize parts of virtual memory so that the stack is not always in the same place. But why do this? Well, when you try to perform a buffer overrun attack, one of your goals is to take control of a vulnerable program's stack. From there, you can steal control of the return address, and point it to the location of your malicious shellcode. However, if memory layout is randomized, then the relative locations of the stack and your shellcode will not remain constant, which will make it more difficult to jump to your shellcode! Don't worry if that doesn't make too much sense right now, as I will cover it in much greater detail when I move through the Vortex wargames (I may even do a brief tutorial on buffer overruns before that). Just know that there are ways around ASLR (known as NOP sleds).

That's all for this round. It feels great to be back on this blog again after so much time. I will be covering systems level stuff for a little while, and then might branch into some rootkit discussions, as well as memory forensics, which has really been catching my eye lately. Keep hacking.
ITNOT.
-G3n3s1s