CodeMash 2018 CTF write-up
01 Do you like my Style?
Every one is talking about styles. Sometimes its about what you wear and sometimes its about what you do.
All I know is that the flag you want definitely has Style.
The Web Developer Tools (available for Firefox and Chrome) easily reveal the flag:

The flag:
cm18-te94-1tuJ-ddx9-3dQO
02 Hobo Robo
Hobo Robo prepared a paper chase for you.
A click on the provided link redirects to the C-3PO Wikipedia site. Downloading the site with wget sheds some more light into the dark.
wget https://codemash.hacking-lab.com/codemash/bots/bots.html
<html> <head> <title>Bots</title> <script type="text/javascript"> eval(String.fromCharCode(105, 102, 32, 40, 33, 40, 110, 97, 118, 105, 103, 97, 116, 111, 114, 46, 117, 115, 101, 114, 65, 103, 101, 110, 116, 32, 61, 61, 61, 32, 39, 72, 111, 98, 111, 82, 111, 98, 111, 39, 41, 41, 32, 123, 32, 108, 111, 99, 97, 116, 105, 111, 110, 46, 114, 101, 112, 108, 97, 99, 101, 40, 39, 104, 116, 116, 112, 58, 47, 47, 101, 110, 46, 119, 105, 107, 105, 112, 101, 100, 105, 97, 46, 111, 114, 103, 47, 119, 105, 107, 105, 47, 67, 45, 51, 80, 79, 39, 41, 59, 125)) </script> </head> <body style="background: white; border: 20px solid white;"> <div style="widht: 100%; height: 100%; background: url('./robotbg.jpg') no-repeat center center fixed; -webkit-background-size: contain; -moz-background-size: contain; -o-background-size: contain; background-size: contain;"> </div> </body> </html>
At a first glance, the string looks promising. The following Python code can be used to decode it:
data = [105, 102, 32, 40, 33, 40, 110, 97, 118, 105, 103, 97, 116, 111, 114, 46, 117, 115, 101, 114, 65, 103, 101, 110, 116, 32, 61, 61, 61, 32, 39, 72, 111, 98, 111, 82, 111, 98, 111, 39, 41, 41, 32, 123, 32, 108, 111, 99, 97, 116, 105, 111, 110, 46, 114, 101, 112, 108, 97, 99, 101, 40, 39, 104, 116, 116, 112, 58, 47, 47, 101, 110, 46, 119, 105, 107, 105, 112, 101, 100, 105, 97, 46, 111, 114, 103, 47, 119, 105, 107, 105, 47, 67, 45, 51, 80,79, 39, 41, 59, 125] buf = "" for i in data: buf += chr(i) print buf
python2 sol.py if (!(navigator.userAgent === 'HoboRobo')) { location.replace('http://en.wikipedia.org/wiki/C-3PO');}
We already know about the redirection, so let's take a look the mentioned URL (url('./robotbg.jpg')):
wget https://codemash.hacking-lab.com/codemash/bots/robotbg.jp

The words
bama waboki pisal fatatu fomu wosebi seju sowu seju - bamas mufe wafub fomu mowewe
are written in the Robot Interaction Language (ROILA) and decode to:
you must make word of addition two and two - this be name of page
wget https://codemash.hacking-lab.com/codemash/bots/four.html
The file content leads to the next URL and the next image:
<html> <head> <title>Bots</title> <meta name="description" content="Robots talk in ROILA language: eman egap eht esrever tsum"> <meta name="keywords" content="secret, page, robots, fun, hacky easter, blrt, five, beep"> <script type="text/javascript"> eval(String.fromCharCode(105, 102, 32, 40, 33, 40, 110, 97, 118, 105, 103, 97, 116, 111, 114, 46, 117, 115, 101, 114, 65, 103, 101, 110, 116, 32, 61, 61, 61, 32, 39, 72, 111, 98, 111, 82, 111, 98, 111, 39, 41, 41, 32, 123, 32, 108, 111, 99, 97, 116, 105, 111, 110, 46, 114, 101, 112, 108, 97, 99, 101, 40, 39, 104, 116, 116, 112, 58, 47, 47, 101, 110, 46, 119, 105, 107, 105, 112, 101, 100, 105, 97, 46, 111, 114, 103, 47, 119, 105, 107, 105, 47, 67, 45, 51, 80, 79, 39, 41, 59, 125)) </script> </head> <body style="background: white; border: 20px solid white;"> <div style="widht: 100%; height: 100%; background: url('./robotbg2.jpg') no-repeat center center fixed; -webkit-background-size: contain; -moz-background-size: contain; -o-background-size: contain; background-size: contain;"> </div> </body> </html>
wget https://codemash.hacking-lab.com/codemash/bots/robotbg2.jpg

The image shows the word metae, let's take a second look into four.html and especially the meta data entry:
<meta name="description" content="Robots talk in ROILA language: eman egap eht esrever tsum">
python2 -c 'print "eman egap eht esrever tsum"[::-1]' must reverse the page name
wget https://codemash.hacking-lab.com/codemash/bots/ruof.html
wget https://codemash.hacking-lab.com/codemash/bots/robotbg3_1337807.jpg

03 - 1337 Riddler
1337 r1ddler h4s a puzzl3 f0r u 2 solve!
H3 1s l1st3n1ng 0n th3 BEST p0r7 on this s3rv3r!
The "BEST p0r7"? Let's don't asume, let's test and verify:
nmap -p- codemash.hacking-lab.com PORT STATE SERVICE 22/tcp open ssh 80/tcp open http 443/tcp open https 3544/tcp closed teredo 8357/tcp open unknown
Port 8357 seems promising, let's verify using netcat:
nc codemash.hacking-lab.com 8357 Make an educated guess, dude: 1 I need 20 digits, dude!
nc codemash.hacking-lab.com 8357 Make an educated guess, dude: 11111111111111111111 0<
nc codemash.hacking-lab.com 8357 Make an educated guess, dude: 91111111111111111111 0>
nc codemash.hacking-lab.com 8357 Make an educated guess, dude: 71111111111111111111 1<
The task seems to be to guess the 20 digit code. The return value gives the number of already correct digits and a hint if the actual guess is smaller or bigger than the correct value.
The following Python code does not endeavour to be the most clever or elegant solution. It just works in a feasible time.
from pwn import * context.log_level = 'error' code = "0" * 20 i = 0 while i < 20: for j in range(10): conn = remote('codemash.hacking-lab.com', 8357) ret = conn.recvline() assert('Make an educated guess, dude' in ret) tmp = list(code) tmp[i] = str(j) tmp = ''.join(tmp) conn.send(tmp + '\n') ret = conn.recvline().rstrip() ret = ret.replace('<', '').rstrip() ret = ret.replace('>', '').rstrip() try: ret = int(ret) except: print j, "\n", tmp ret = "" while not "cm" in ret: ret = conn.recvline().rstrip() print ret conn.close() sys.exit() conn.close() if ret > i: code = tmp print j, i = i + 1 break
python2 03.py 7 8 0 2 5 9 2 8 2 3 2 9 2 0 7 1 2 9 6 7 78025928232920712967 Congrats! Here's your flag: cm18-Glz3-yM2k-h9i9-wntS
04 - Super Eyesight
Can you see what others cannot?
Here is an image to prove your super eyesight.

Opening the image in Gimp and playing with the color threshold reveals the flag.

05 - Bools for fools
Calculate this!
((not(a) and b) or c) xor d
wget https://codemash.hacking-lab.com/codemash/attachments/boolsforfools_fixed.zip unzip boolsforfools_fixed.zip ls -ls a.txt b.txt c.txt c.txt cat a.txt 0111011001000100111111111 0100011101001101101110100 ...
The .zip file reveals four ASCII files (a.txt, b.txt, c.txt and c.txt) containing binary numbers. It seems we have to operate on the content of those files:
# 05 - Bools for fools # Calculate this! # ((not(a) and b) or c) xor d # import sys def snot(s): ret = "" for i in s: if i == '1': ret += '0' elif i == '0': ret += '1' else: return False return ret def sand(s1, s2): ret = "" if len(s1) != len(s2): return False for i, j in zip(s1, s2): ret += str(int(i) & int(j)) return ret def sor(s1, s2): ret = "" if len(s1) != len(s2): return False for i, j in zip(s1, s2): ret += str(int(i) | int(j)) return ret def sxor(s1, s2): ret = "" if len(s1) != len(s2): return False for i, j in zip(s1, s2): ret += str(int(i) ^ int(j)) return ret def get_file_content(fname): try: with open(fname, 'r') as f: cont = f.readlines() except Exception as e: print e sys.exit() return cont # ((not(a) and b) or c) xor d a = get_file_content('a.txt') b = get_file_content('b.txt') c = get_file_content('c.txt') d = get_file_content('d.txt') not_a = [] for line in a: tmp = snot(line.rstrip()) + '\r\n' not_a.append(tmp) not_a_and_b = [] for i, j in zip(not_a, b): tmp = sand(i.rstrip(), j.rstrip()) + '\r\n' not_a_and_b.append(tmp) or_c = [] for i, j in zip(not_a_and_b, c): tmp = sor(i.rstrip(), j.rstrip()) + '\r\n' or_c.append(tmp) xor_d = [] for i, j in zip(or_c, d): tmp = sxor(i.rstrip(), j.rstrip()) + '\r\n' xor_d.append(tmp) for l in xor_d: print l,
python2 05.py 1111111000100110001111111 1000001011100011001000001 1011101010000010101011101 1011101001001010001011101 1011101011101100101011101 ...
And now it gets crazy. The output file looks very similiar to the previous ones. So what now? Let's count lines and characters per line:
python2 05.py | wc -l 25 python2 05.py | head -n 1 | wc -c 27
Since the Python script added '\r\n' to each line, the actual count is 25.
echo $(( 25*25 )) 625
Lucky you if you know that a Version 2 QR code contains 25 x 25 pixels and indeed, changing the last print statement of the Python script helps to spot a QR code.
-for l in xor_d: - print l, +for l in xor_d: + for c in l: + print c,
python2 05.py
Highlighting some of the 1 bits in red, clearly shows the QR code:

Adding the following code to the Python script above takes the content of xor_d and creates a file flag.png which finally contains the QR code (credits go to the author of an old writeup for a similiar challenge).
from PIL import Image from qrtools import QR binstring = "" for line in xor_d: binstring += line.rstrip() outimg = Image.new('RGB', (25, 25), "black") pixels_out = outimg.load() count = 0 for bit in binstring: i = count % 25 j = count / 25 if bit == '0': pixels_out[(i, j)] = (255, 255, 255) count += 1 outimgname = "flag.png" outimg = outimg.resize((250, 250)) outimg.save(outimgname, "png")
python2 05.py zbarimg flag.png QR-Code:cm18-eJb2-mfTz-pIMu-oKaV
06 - Witchcraft
I was just messing around with my magic wand trying out some new spells and all of a sudden the flag was gone.
All I was left with is the following:
363336643331333832643438363537373432326436383530
333137323264346337393738333932643635373035343465
Can you undo my spell and get the flag?
Abracadabra? Hocus-pocus? Sim salabim? Whizz whizz? Hex hex? Hex hex!
flag = "363336643331333832643438363537373432326436383530" flag += "333137323264346337393738333932643635373035343465" print flag.decode('hex').decode('hex')
python2 06.py cm18-HewB-hP1r-Lyx9-epTN
07 - Happy Eyes
Barbara really loves to chat with all friends.
To express joy they use the characters ^^ which represent eyes of a smiling face.
Can you find this happy flag?
Hint: Offset by 2
1xT-Gcm8FV-5cYN-iBc-syHW
The characters ^^ do not only represent eyes of a smiling face, but also a zigzag pattern. Lucky you if you already heard about the Rail Fence (ZigZag) Cipher.

The flag:
cm18-FxVs-T5yc-YHNG-WicB
08 - Lock
The key for that lock got lost.
If you are good at lock picking you will find the flag.
A good tool to decompile Java code is jad (Java decompiler).
jad Lock.class
The decompiled code looks as follows:
package com.hackinglab.ctf; import java.io.PrintStream; class Lock { public Lock() { } public static void main(String args[]) { StringBuffer stringbuffer = new StringBuffer(); for(int i = 0; i < cipher.length(); i++) stringbuffer.append((char)((key.charAt(i % key.length()) - cipher.charAt(i)) + 54)); System.out.println(stringbuffer.toString()); } private static String key = "lockpickingisfun"; private static String cipher = "?8hiyKT5fw*W^J~art3t.47i"; }
For less experienced Java programmers, it might be easier to reimplement the main function's functionality in Python:
cipher = "?8hiyKT5fw*W^J~art3t.47i" key = "lockpickingisfun" codeword = "" for i in range(len(cipher)): codeword += chr(ord(key[i % len(key)]) - ord(cipher[i]) + 54) print codeword
python2 08.py cm18-TEl9-sHKR-C01f-xkb8
09 - Meow!
Can you lure the cat out of the hiding place?
pdfimages -all meow.pdf out
The first image (out-000.jpg) contains the flag.

Don't worry if you cannot read it, it's not your fault. The flag is actually:
cm18-cxJg-MB4M-Q9E3-ZYlC
with the second last letter being a lower case ell.
10 - Chest
Can you find the flag inside of the chest?
wget https://codemash.hacking-lab.com/codemash/attachments/chest
file chest chest: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=5042299eb1d095cd4db443de65640c7073668114, not stripped
The file to examine is a x86-64 executable. Before actually running it, let's check if the flag appears in plain text.
strings chest | grep "cm18-" cm18-jNiO-bUon-ylon-flag cm18-eIdK-bUoC-orna-ment ...
Unfortunately, the binary contains a lot of canditates, 998 to be more precise.
strings chest | grep "cm18-" | wc -l 998
Many of the canditates are very similar and differ only in a few letters. Let's try to sort them:
strings chest | grep "cm18-" | sort -u | less cm18-Ml2l-bUoC-vXXE-Fc8c cm18-bFaL-bUgo-lden-coin cm18-bFdG-bUgo-lden-coin cm18-bFgF-bUgo-lden-coin ...
The very first hit is unique within the chest and the searched flag.
cm18-Ml2l-bUoC-vXXE-Fc8c
11 - Bacon!
Get the bacon!
I'll skip this for now, be patient, I'll deliver an update, soon.
12 - On-site Challenge
The flag for this challenge can only be found onsite at the conference, in the location seen on top of the backside of the elephant, in this picture.
This challenge was on-site and I was, well, not on-sight.
13 - Alice
Follow the white rabbit.
The only usable information source on the website is an image of a white rabbit:

Let's search for the image URL ...

... and download the file.
wget https://codemash.hacking-lab.com/codemash/images/banner/challenge_13_2433.jpg
Then check the file for hidden data using binwalk:
binwalk challenge_13_2433.jpg DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 JPEG image data, JFIF standard 1.01 382 0x17E Copyright string: "Copyright (c) 1998 Hewlett-Packard Company" 155385 0x25EF9 Zip archive data, at least v2.0 to extract, compressed size: 167065, uncompressed size: 167258, name: forest.jpg 322518 0x4EBD6 Zip archive data, at least v2.0 to extract, compressed size: 139956, uncompressed size: 140213, name: meadow.jpg 462542 0x70ECE Zip archive data, at least v2.0 to extract, compressed size: 171261, uncompressed size: 171490, name: water.jpg 634109 0x9ACFD End of Zip archive
The image contains a .zip archive. binwalk can be used to extract it:
binwalk -e challenge_13_2433.jpg cd _challenge_13_2433.jpg.extracted
The file contains three images:
- forest.jpg
- meadow.jpg
- water.jpg
strings forest.jpg | head -n 3 Exif 2017:12:12 04:01:24 This image has a protected secret. strings meadow.jpg | head -n 3 Exif 2017:12:12 04:00:30 This meadow is all dried out. Check the water first. strings water.jpg | head -n 3 Exif 2017:12:12 03:58:52 steghide was here. With an empty password
Steghide?
pacman -Ss steghide community/steghide 0.5.1-8 Embeds a message in a file by replacing some of the least significant bits
Steghide is a program that can be used to hide data in various kinds of image- and audio-files. Let's install it...
su -c 'pacman -S steghide'
... and use it to extract the hidden data:
steghide extract -sf water.jpg Enter passphrase: wrote extracted data to "text.txt".
Let's finally view the content:
cat text.txt You search the whole place but you can't find anything. ... Now get out before you get flushed down
Ok, nothing here. Let's check the next file:
steghide extract -sf meadow.jpg Enter passphrase: the file "text.txt" does already exist. overwrite ? (y/n) y wrote extracted data to "text.txt".
And view its content:
cat text.txt So you think a mole can speak?! ... Lucky you, this one can! He's name is Fred and he tells you the passphrase: The-Mad-Hatter
Let's check the last file:
steghide extract -sf forest.jpg Enter passphrase: The-Mad-Hatter the file "text.txt" does already exist. overwrite ? (y/n) y wrote extracted data to "text.txt".
And finally:
cat text.txt Congratulations here is the flag! cm18-xZl2-eHC5-axW3-ZkZG
14 - Security Regulations
Due to some new privacy regulations this flag had to be shred. The classification will be secret or topsecret depending on the content.
____-____-____-____-____
Clicking the link leeds to a second website with the following URL:
https://codemash.hacking-lab.com/codemash/secret_challenge_shred1.html
Shred 1: Note: the other shreds are classified as <<topsecret>>!
cm18
Two simple changes to the URL lead to the second shred:
https://codemash.hacking-lab.com/codemash/topsecret_challenge_shred2.html
Shred 2
bWh0-VIkC
And one additional change to the third shred:
https://codemash.hacking-lab.com/codemash/topsecret_challenge_shred3.html
Shred 3
cMf4-72jY
The flag sums up to
cm18-bWh0-VIkC-cMf4-72jY
15 - P.A.L.M. Login
Folks at HOBO Authentication Systems implemented a new authentication system named P.A.L.M. Login
Prove that you can break it and find a pair of username and passcode to log on.

function checkEntries() { var u = document.getElementById('puser').value; var p = document.getElementById('ppass').value; var used = [0,0,0,0,0,0,0,0,0,0]; var ok = false; if (u === 'cavs') { if (p > 0 && p.length == 10) { ok = true; for (i = 1; i <= 10; i++) { var digit = p.charAt(i-1); var part = p.substring(0, i); if (used[digit] != 0 || part % i != 0) { ok = false } if (used[digit] == 0) { used[digit] = 1 } } } } if (ok) { document.location.href='palm_'+u+'_'+p+'.html' } else { alert('nope') } }
The username is "cavs" and the password is a 10 digit number with the following characteristics:
- Each digit appears only once (used[digit] != 0).
- The first digit is divisible by 1 (part % i != 0).
- The number build out of the first two digits is divisible by 2.
- The number build out of the first three digits is divisible by 3.
- The number build out of the first four digits is divisible by 4.
- ...
- The whole number is divisible by 10, so the last digit is 0.
A quick Google search with the search string The number build out of the first two digits is divisible by 2 is sufficiant:
3816547290
Providing cavs as username and 3816547290 as password reveals the flag:
cm18-zbIc-O4Zh-gmxl-r5J6