A stream number 34 was about implementing some basic stuff of a CHIP-8 virtual machine. The ending screen contains ofcoruse a mission!

MISSION 015               goo.gl/JKN1Zq             DIFFICULTY: █████░░░░░ [5╱10] 
One of our operatives managed to find an information leak vulnerability on an 
internal website of a hostile syndicate. The vulnerability itself is quite 
amusing, as it allows to leak any file on the FS in form of a bar chart. Our 
operative leaked a script responsible for authenticating a system operator. 
Your task is to analyze the chart and then the script, and attempt to recover 
system operator's password. 
  Chart: goo.gl/YbYYcS 
Good luck!

Well, I think that there was some CTF challenge with data leaked via bar charts. Unfortunately I don’t have a link. If someone knows what CTF task it was, please give me a hint ;) The image looks like this:


Getting started

Leaking a source code via a png file sound like a fun! But how to extract the data? First of all I’ve checked all metadata.


It’s just a simple png file. Nothing more in it.

Since the image presents a bar plot, some information might be passed by values represented by bars rocket science, applause intensifies. I’ve decided to write a script in Python (that was obvious) and check what these values represent. My first idea was to check the length of all red lines.

First attempt

So the script looks like:

from PIL import Image
import binascii

def main():
	plt = Image.open('mission15.png')
	szX, szY = plt.size
	print("Image is {} x {} pixels".format(szX, szY))
	opcodes = []
	for line in range(szX):
		lng = 0
		for col in range(szY):
			if plt.getpixel((line, col))==(255,0,0):
				lng += 1
		print("{} {}".format(lng, bytes([lng])))

	with open("rom.bin", 'wb') as f:
		for l in opcodes:

if __name__ == '__main__':

The output goes as follows:


Huh, some numbers! Let’s convert them to hex.

60 0x3c
63 0x3f
112 0x70
104 0x68
112 0x70
10 0xa
10 0xa
105 0x69
102 0x66
32 0x20
40 0x28

Well, I’m not an expert but my first idea was to save this values in binary file and then try to disassemble them in radare2 or IDA.

So I opened binary file rom.bin in radare2 and… This looks kinda sorta familiar…:


But there are some invalid instructions and this:.



Well, ALWAYS try to print binary file. This numbers are not opcodes in sam PE/ELF file. This is source code in php:


if (!isset($_GET['password']) || !is_string($_GET['password'])) {
  die("bad password");

$p = $_GET['password'];

if (strlen($p) !== 25) {
  die("bad password");

if (md5($p) !== 'e66c97b8837d0328f3e5522ebb058f85') {
  die("bad password");

// Split the password in five and check the pieces.
// We need to be sure!
$values = array(
  0 => 'e6d9fe6df8fd2a07ca6636729d4a615a',
  5 => '273e97dc41693b152c71715d099a1049',
  10 => 'bd014fafb6f235929c73a6e9d5f1e458',
  15 => 'ab892a96d92d434432d23429483c0a39',
  20 => 'b56a807858d5948a4e4604c117a62c2d'

for ($i = 0; $i < 25; $i += 5) {
  if (md5(substr($p, $i, 5)) !== $values[$i]) {
    die("bad password");


That being said, we have to “guess” a password which gives us answer “GW!”

There is a little problem. I do not speak PHP :(… But well, let’s analyse the code.

So $p is a name/an alias (I don’t know how it’s in PHP) for variable that will contain password, I think.

The pass should be 25 letters long. After performing this check it’s being hashed using cough secure cough (never use that in production or not validation) md5. Then there are 5 more hashes, they are md5 too you can be prove that by checking them in some scritps or just looking on line 28. Fortunately there is a comment here, to split password in 5 and check hashes with those five in an array named $values.

I did so. You can write some code and use for example a rockyou.txt dictionary to check for this words. There is also a posibility to bruteforce the hash char by char (it’s doable in short time because of length of only five) or… use some awesome online crackers like hashkiller.co.uk.


So the parts of the password recovered from hashes looks like: Pie charts are delicious!. And after hashing this string with the md5 from source code (that one calculated for full password), we get: e66c97b8837d0328f3e5522ebb058f85 this is a correct value from line 13. Bruteforcing 25 chars doesn’t sound like a fun, so it’s good to have 5 additional hashes of the password’s parts. ;)

Thanx for mission Gynvael!