8891 - Santas leak (Hackvent 2015 Teaser)

Hacking Lab14870

This was a fun and interesting challenge to kick off Hackvent. It took me 4 days to solve the problem! I strongly suggest you attempt the problem yourself before reading by solution.

You can get the single image you need to do the challenge here (santa.png):

HL-8891-santas_leak_new.png

These are all the files/scripts I ended up with after the challenge (zipped):

HL-8891-mobeigi-Santa-Leak-Solutions.zip

The Challenge

8891 Santa's Leak

Santa's work has been leaked by a the infamous group "Chuchichästli 1337 ".
They were only able to grab the logo, but not to reveal the secret. 

Can you help them and find the HACKvent - Nugget (HV15-aaaa-bbbb-cccc-dddd-eeee)

Goal

In order to get full points, submit the HV-Nugget and a (short) description how you get it.

Hint

This is a multistage challenge, with different steps you have to solve. At the end you will get the final nugget which begins with "HV15-".

Solution

The solution to this challenge is pretty long and has multiple steps. I've explained what I have done in detail and I have also explained approaches I tried which did not work.

Initially, we have the Christmas ball png image which I will call santa.png.

Santa Leak New Christmas Ball Image

I take the original image and use a QR reader to read the message.

I get the string:

nyy lbh arrq vf urer

This looks like rot13 to me (vf is the same as is, I recognised this instantly).
I pass the string through an online rot13 encoder/decoder (link) to decode to get:

all you need is here

Essentially this tells me that the only file I was given (santa.png) contains the solution.

Now, the HV15 nugget is of form: HV15-aaaa-bbbb-cccc-dddd-eeee
The solution to this teaser will start start with HV15-  based on rules.
Thus, we need 29 characters in the final solution.
Keep this in mind as we progress.

Next I try to open the santa.png with Winzip on Windows (to search for any hidden files within).
Luckily I get a file, 2.wav!

I play the WAV file and hear DTMF tones (probably generated via Audacity).
I use an online tool (link) to turn the codes in numbers and get the following number:

106117115116321121111151151059810810132119105116104105110321151179910432973211497114

These look like ASCII codes! I try to space them out so they give me regular character [A-Za-z0-9].
I get (using space as a separator):

106 117 115 116 32 112 111 115 115 105 98 108 101 32 119 105 116 104 105 110 32 115 117 99 104 32 97 32 114 97 114

Converting this to ASCII I get:

just possible within such a rar

This hint doesn't tell me much right away.
I try various others things such as binwalk on linux to try and reveal hidden files within.
Binwalk finds the header for a YAFFS filesystem at 0x3AAD4.
I try to mount this filesystem using yaffs2utils (which supports YAFFS1) but the process fails. This is a dead end and the header was just a coincidence.

I then look at Windows tools and find the SFind tool by McAfee.
I find hidden streams only visible on an NTFS system and see that 2.wav contains a file, namely 3.txt.

D:\Desktop> SFind 2.wav
2.wav:3.txt Size: 490901

I extract the contents and feed it to notepad:

notepad 2.wav:3.txt

I notice that the entire contents of 3.txt are encoded in base64. I use the python script below to decode the base64 encoded contents.

#!/usr/bin/python

#
# Decode base64 file
#

import sys,string

content = []
f = sys.argv[1]
s = open(f,'r')

for i in s:
  content.append(i.strip('\r\n'))

s.close()
  
g = open('decoded','w+')
for i in content:
  g.write(i.decode('base64'))
g.close()

Inspecting the output with HxD editor, I realise that we have a PDF version 1.5 file!
I open this in a PDF viewer and the file is valid.

The PDF contains the Brainfuck code below:

+++++ +++[- >++++ ++++< ]>+++ +++++ .<+++ ++[-> +++++ <]>++ ++.<+ +++[-
>++++ <]>++ ++.<+ +++++ +++[- >---- ----- <]>-- ----- -.<++ +++++ +[->+
+++++ ++<]> +++++ +++++ ++.-- ----- ..<++ +[->+ ++<]> +++++ +.-.< +++++
+++[- >---- ----< ]>--- ----. <+++[ ->--- <]>-- -.<++ ++[-> ----< ]>---
.---. <++++ +++[- >++++ +++<] >++++ +++++ +++++ .<+++ +++[- >++++ ++<]>
.<+++ +++++ [->-- ----- -<]>- ----- ----- --.<+ +++++ ++[-> +++++ +++<]
>++++ +++.+ ++++. <+++[ ->--- <]>-- .+++. <++++ ++++[ ->--- ----- <]>--
--.<+ +++++ +++[- >++++ +++++ <]>++ +++++ +.<++ +[->- --<]> -.+++ +++.<
+++++ ++++[ ->--- ----- -<]>- ---.< +++++ +++[- >++++ ++++< ]>+++ +++.+
+++++ +++.+ +++++ .---- ---.< +++[- >---< ]>-.< +++++ +++[- >---- ----<
]>--- -.<++ +++++ ++[-> +++++ ++++< ]>+++ .<+++ [->-- -<]>- --.-- -.<++
+++++ +[->- ----- --<]> ----- .<+++ +++++ +[->+ +++++ +++<] >++++ ++.<+
+++[- >---- <]>-- ----. <++++ [->++ ++<]> +++++ +++.< +++++ ++++[ ->---
----- -<]>- ----- --.<+ +++++ +++[- >++++ +++++ <]>++ +.--- --.<+ +++++
++[-> ----- ---<] >---- ----- ----- -.<++ +++++ ++[-> +++++ ++++< ]>+++
.<+++ [->-- -<]>- --.+. <+++[ ->+++ <]>+. <++++ +++++ [->-- ----- --<]>
--.<+ +++++ +++[- >++++ +++++ <]>++ .+.<+ ++[-> ---<] >---- --.<+ ++[->
+++<] >++.< +++++ +++[- >---- ----< ]>--. <++++ +[->- ----< ]>--- -----
.---. +++.- --.<+ +++++ ++[-> +++++ +++<] >++++ +++++ .<+++ +[->+ +++<]
>++.- ---.< ++++[ ->+++ +<]>+ .<+++ [->-- -<]>- ----- .++++ +.<++ +++++
+[->- ----- --<]> ----- ---.< +++++ ++++[ ->+++ +++++ +<]>+ ++.<+ ++[->
---<] >---. ---.< +++++ +++[- >---- ----< ]>--- --.<+ +++++ ++[-> +++++
+++<] >++++ +++++ +++++ +.+++ ++.<+ ++[-> ---<] >---. ---.< +++[- >+++<
]>+++ +.<++ +++++ ++[-> ----- ----< ]>-.< ++++[ ->+++ +<]>+ +.<++ ++[->
----< ]>--. <++++ ++++[ ->+++ +++++ <]>++ ++++. +++.+ ++.-- ----- .<+++
[->++ +<]>+ ++++. <++++ +++++ [->-- ----- --<]> --.<+ +++++ ++[-> +++++
+++<] >+.<+ ++[-> +++<] >++++ .<+++ [->-- -<]>- .<+++ +++++ [->-- -----
-<]>- ---.< +++++ ++++[ ->+++ +++++ +<]>+ ++.<+ +++[- >---- <]>-- -.<++
+[->+ ++<]> +.--- ---.< +++[- >+++< ]>+.- ----- ---.. <++++ ++++[ ->---
----- <]>-- ----. <++++ +++++ [->++ +++++ ++<]> +++.- ----. <++++ ++++[
->--- ----- <]>-- ----- ----- ---.< +++++ ++++[ ->+++ +++++ +<]>+ ++.<+
++[-> ---<] >---. ---.< +++++ +++[- >---- ----< ]>--- --.<+ +++++ ++[->
+++++ +++<] >++++ +++++ +++++ .---- ----- .<+++ +[->+ +++<] >+++. ----.
<++++ +++++ [->-- ----- --<]> ---.< +++++ ++++[ ->+++ +++++ +<]>+ +.+.<
+++[- >---< ]>--- ---.< +++[- >+++< ]>++. <++++ ++++[ ->--- ----- <]>--
----- ----- ---.< ++++[ ->--- -<]>- ---.- --.++ +.--- .<+++ +++++ [->++
+++++ +<]>+ +++++ +++++ ++++. <++++ [->++ ++<]> +++++ +.+++ +++.- --.+.
<++++ ++++[ ->--- ----- <]>-- ----- .<+++ [->-- -<]>- --.<+ +++++ +++[-
>++++ +++++ <]>++ .<+++ +[->- ---<] >--.< +++[- >+++< ]>+++ +.+++ +++.<
++++[ ->--- -<]>- --.<

I run it through an online Brainfuck interpreter (link).

The following message is printed:

Hey leets, 
Im glad you found the way to this step.
Search the other 2 files and takeoff to the next step!
<Yours, santa>

At this stage I use binwalk to inspect the pdf for more files. I use the recursive flag with high depth to extract as much as possible. The latest version of binwalk also extracts Zlib's for me which was nice.

 I run:

binwalk -Me -d=10000 decoded.pdf

I get a png image of a Christmas ball (blank) among other various files as output.

Most of the files are rubbish but I do notice one file that contains 25 SHA1 hashes (due to the 40 hex characters). I see if these hashes have been cracked using online tools, they have not. I keep this hashes in mind as I continue.
Hashes:

754c6738acc834944b90b2a9a77bdbacb093f6e4
a1ff6b23bb8ed85b6f0adf9f65f007e9b3e348e0
848fdbfa2d28a1aee0d82c8a707c4d1261f51079
312189262e204aa9027ca64db1d43d1153a860e5
21f4c0b5e077f82739c0748df71610fc6285e768
3ded376898e8843999f1d0df89137308542acac2
a9f2066e530187b9371ada082e11a56127028749
d028c883a63e2232783ea6f54ce6d0091a9d2d84
610bf7f4144cf300ef086254459c427d16767d6a
e56a109aab8b4e9b18e099a8ae1477d8de270149
5de13aa399cf9e3042af96eab88d28def1f58549
a5229747c1437b737ac2cc1567f173d4a4ba12ba
2bb48bc2105ca33f56906d8c6c4b6aec4c16cb58
e7a804128bc3ae8150adeb80ac695fdea0f2b643
12aae009a8d38f6585debaddef79aed5cd858df4
8b7c437b1d8cdbb8a72a9783b3882c87d397c76a
4b1dfd5c05068a37df7ebfef64bb5bc4a8c7daab
7a781addb8ef467884ecb62bbe1bb7d024b0eb83
6d23cafac980692dc7401fd53cf287aec49d4ae2
16f607d441caf3cbda5335bccd2f104da131f2a9
7982fcaa2b59698d3eb5aa05369454b105de6dc3
5e836fbdb63db38d2f9a8604dd90856a9791efce
b5adb5a6cccc137d6851e2fcb5fa276307edf8f4
06004577f5d35b54b80a53a20aedf5895509bffa
1e238015f260d4d485e3e9d64fa17db80eb81708

Visiting the website: http://www.extractpdf.com/
I manage to extract more files from the pdf (that binwalk could not).
I obtain 3 images:

  • a png image of a christmas ball (with "Wrong One" written on it),
    Wrong One Christmas ball
  • a png image of a grey empty Christmas ball, and
    Grey Empty Christmas Ball
  • a jpeg image which appears to contain static.
    JPEG Static Magic Eye Image

Later, I discover that this jpeg is a magic eye image.

I use an online magic eye solver (link) to find the solution.

The result is the following image which looks like hieroglyphs:

Windings Hidden Message

I shortly realise that the characters resemble the Windings font.
I use a character map for Windings 1 to determine what each symbol means (ie what is its ASCII equivalent symbol).

I obtain the following message:

ball=
sha1(
[01]{
25})

Or (on one line): ball=sha1([01]{25})

This seems to be related to the 25 SHA1 hashes I found earlier.
[01]{25} looks like regexp meaning a permutation of 25 0's and 1's.

I write a python script to get all 25 length permutations of 1 and 0, and hash them using SHA1.
The script checks to see if we get a match with any SHA1 sum in our list from earlier (cross reference check).

#!/usr/bin/python

#
# SHA1 Permutation checker
#

import hashlib

# 25 SHA1 sums we found in file earlier
shaList = ["754c6738acc834944b90b2a9a77bdbacb093f6e4",
"a1ff6b23bb8ed85b6f0adf9f65f007e9b3e348e0",
"848fdbfa2d28a1aee0d82c8a707c4d1261f51079",
"312189262e204aa9027ca64db1d43d1153a860e5",
"21f4c0b5e077f82739c0748df71610fc6285e768",
"3ded376898e8843999f1d0df89137308542acac2",
"a9f2066e530187b9371ada082e11a56127028749",
"d028c883a63e2232783ea6f54ce6d0091a9d2d84",
"610bf7f4144cf300ef086254459c427d16767d6a",
"e56a109aab8b4e9b18e099a8ae1477d8de270149",
"5de13aa399cf9e3042af96eab88d28def1f58549",
"a5229747c1437b737ac2cc1567f173d4a4ba12ba",
"2bb48bc2105ca33f56906d8c6c4b6aec4c16cb58",
"e7a804128bc3ae8150adeb80ac695fdea0f2b643",
"12aae009a8d38f6585debaddef79aed5cd858df4",
"8b7c437b1d8cdbb8a72a9783b3882c87d397c76a",
"4b1dfd5c05068a37df7ebfef64bb5bc4a8c7daab",
"7a781addb8ef467884ecb62bbe1bb7d024b0eb83",
"6d23cafac980692dc7401fd53cf287aec49d4ae2",
"16f607d441caf3cbda5335bccd2f104da131f2a9",
"7982fcaa2b59698d3eb5aa05369454b105de6dc3",
"5e836fbdb63db38d2f9a8604dd90856a9791efce",
"b5adb5a6cccc137d6851e2fcb5fa276307edf8f4",
"06004577f5d35b54b80a53a20aedf5895509bffa",
"1e238015f260d4d485e3e9d64fa17db80eb81708" ]

# If we treat the 0's and 1's as binary, our range is between 0 - 0b1111111111111111111111111 (33554431 or 2^25-1)
cur = 0

for cur in range(0, 0b1111111111111111111111111):
  # Convert this number to its 25 length string representation
  input_str = '{:025b}'.format(cur)
  
  hash_object = hashlib.sha1(input_str)
  hex_string = hash_object.hexdigest()
  
  if hex_string in shaList:
    print "Found match for hash: %s, input = %s" % (hex_string, input_str)

This is the result after running the tool (took ~5 minutes to complete):

Found match for hash: 6d23cafac980692dc7401fd53cf287aec49d4ae2, input = 0000000100011101010100110
Found match for hash: 1e238015f260d4d485e3e9d64fa17db80eb81708, input = 0000000100100100000010110
Found match for hash: a9f2066e530187b9371ada082e11a56127028749, input = 0000000101010101010000000
Found match for hash: 754c6738acc834944b90b2a9a77bdbacb093f6e4, input = 0000000111110111110000000
Found match for hash: 610bf7f4144cf300ef086254459c427d16767d6a, input = 0000010111011101111010101
Found match for hash: a5229747c1437b737ac2cc1567f173d4a4ba12ba, input = 0001101010001111110111010
Found match for hash: 312189262e204aa9027ca64db1d43d1153a860e5, input = 0100010100010111110100010
Found match for hash: 5e836fbdb63db38d2f9a8604dd90856a9791efce, input = 0100010100100101101001010
Found match for hash: 7982fcaa2b59698d3eb5aa05369454b105de6dc3, input = 0100010101011101000001110
Found match for hash: b5adb5a6cccc137d6851e2fcb5fa276307edf8f4, input = 0100010101100111010100110
Found match for hash: 21f4c0b5e077f82739c0748df71610fc6285e768, input = 0100010110111100110100010
Found match for hash: 848fdbfa2d28a1aee0d82c8a707c4d1261f51079, input = 0100010111110010010100010
Found match for hash: e7a804128bc3ae8150adeb80ac695fdea0f2b643, input = 0101011101000000000101110
Found match for hash: 4b1dfd5c05068a37df7ebfef64bb5bc4a8c7daab, input = 0110000000101100110001110
Found match for hash: 12aae009a8d38f6585debaddef79aed5cd858df4, input = 0110100101011000001011101
Found match for hash: 8b7c437b1d8cdbb8a72a9783b3882c87d397c76a, input = 0110101111001011100111100
Found match for hash: 2bb48bc2105ca33f56906d8c6c4b6aec4c16cb58, input = 0111100101101111110001110
Found match for hash: 06004577f5d35b54b80a53a20aedf5895509bffa, input = 0111110100011000110010011
Found match for hash: 3ded376898e8843999f1d0df89137308542acac2, input = 0111110100100111110111110
Found match for hash: a1ff6b23bb8ed85b6f0adf9f65f007e9b3e348e0, input = 0111110101010101010111110
Found match for hash: 16f607d441caf3cbda5335bccd2f104da131f2a9, input = 0111110111111010011101101
Found match for hash: e56a109aab8b4e9b18e099a8ae1477d8de270149, input = 1001001110101011111101111
Found match for hash: 5de13aa399cf9e3042af96eab88d28def1f58549, input = 1011110000110010110111100
Found match for hash: d028c883a63e2232783ea6f54ce6d0091a9d2d84, input = 1111111001101101001111111
Found match for hash: 7a781addb8ef467884ecb62bbe1bb7d024b0eb83, input = 1111111010011011101100101

I generated a total of 33554432 (225) hashes.
I find that we obtain 25 results that match our list of SHA1 sums!

I try many things at this stage but eventually decide to order the inputs that generate the 25
SHA1 sums in the same order as the file containing the 25 SHA1 sums.
This is what I get when looking at the 1's and 0's (I added some spaces in for the layout, you'll see why shortly).

0 0 0 0 0 0 0 1 1 1 1 1 0 1 1 1 1 1 0 0 0 0 0 0 0 
0 1 1 1 1 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 1 1 1 1 0 
0 1 0 0 0 1 0 1 1 1 1 1 0 0 1 0 0 1 0 1 0 0 0 1 0 
0 1 0 0 0 1 0 1 0 0 0 1 0 1 1 1 1 1 0 1 0 0 0 1 0 
0 1 0 0 0 1 0 1 1 0 1 1 1 1 0 0 1 1 0 1 0 0 0 1 0 
0 1 1 1 1 1 0 1 0 0 1 0 0 1 1 1 1 1 0 1 1 1 1 1 0 
0 0 0 0 0 0 0 1 0 1 0 1 0 1 0 1 0 1 0 0 0 0 0 0 0 
1 1 1 1 1 1 1 0 0 1 1 0 1 1 0 1 0 0 1 1 1 1 1 1 1 
0 0 0 0 0 1 0 1 1 1 0 1 1 1 0 1 1 1 1 0 1 0 1 0 1 
1 0 0 1 0 0 1 1 1 0 1 0 1 0 1 1 1 1 1 1 0 1 1 1 1 
1 0 1 1 1 1 0 0 0 0 1 1 0 0 1 0 1 1 0 1 1 1 1 0 0 
0 0 0 1 1 0 1 0 1 0 0 0 1 1 1 1 1 1 0 1 1 1 0 1 0 
0 1 1 1 1 0 0 1 0 1 1 0 1 1 1 1 1 1 0 0 0 1 1 1 0 
0 1 0 1 0 1 1 1 0 1 0 0 0 0 0 0 0 0 0 1 0 1 1 1 0 
0 1 1 0 1 0 0 1 0 1 0 1 1 0 0 0 0 0 1 0 1 1 1 0 1 
0 1 1 0 1 0 1 1 1 1 0 0 1 0 1 1 1 0 0 1 1 1 1 0 0 
0 1 1 0 0 0 0 0 0 0 1 0 1 1 0 0 1 1 0 0 0 1 1 1 0 
1 1 1 1 1 1 1 0 1 0 0 1 1 0 1 1 1 0 1 1 0 0 1 0 1 
0 0 0 0 0 0 0 1 0 0 0 1 1 1 0 1 0 1 0 1 0 0 1 1 0 
0 1 1 1 1 1 0 1 1 1 1 1 1 0 1 0 0 1 1 1 0 1 1 0 1 
0 1 0 0 0 1 0 1 0 1 0 1 1 1 0 1 0 0 0 0 0 1 1 1 0 
0 1 0 0 0 1 0 1 0 0 1 0 0 1 0 1 1 0 1 0 0 1 0 1 0 
0 1 0 0 0 1 0 1 0 1 1 0 0 1 1 1 0 1 0 1 0 0 1 1 0 
0 1 1 1 1 1 0 1 0 0 0 1 1 0 0 0 1 1 0 0 1 0 0 1 1 
0 0 0 0 0 0 0 1 0 0 1 0 0 1 0 0 0 0 0 0 1 0 1 1 0

After some time I see that each corner of the binary grid contains a sequence of 0's which is odd. I think back to HACKvent challenges and they almost always end up with you scanning a QR code.
This bit grid looks like a QR code!

After some research I determine that the code is a Version 2 QR code (25 x 25 pixels).
I write a script that takes in the 1's and 0's and outputs an image.
I invert all the bits before feeding it to my program so that the right parts are black or white.
This generated a QR image!

Original QR Code Image

I enlarge the image (zooming in on photo viewer) and try to scan it but fail.
Online tools also fail to scan it.

There are obviously errors in the QR code (by checking the QR specification), namely:

  • Finder pattern incorrect
  • Dark module is white (a single black pixel at y coord 17 (in case of Version 2))
  • Alignment pattern incorrect
  • Check bits and redundant copies of check bits are incorrect

I assume that the issue is with the check bits so I write a python script to determine the correct number of check bits. This script is based on the content here.

#!/usr/bin/python

#
# Generate 15 check bits for a Version 2 QR code
#
# Based on: http://www.thonky.com/qr-code-tutorial/format-version-information
#

data = 0b01010   # ECC error level (2 bits) | mode (3 bits)
                 # ECC: L = 01, M = 00, Q = 11, H = 10
                 # Modes: see http://www.thonky.com/qr-code-tutorial/mask-patterns
  
generator = 0b10100110111
XOR_res = 0b101010000010010 # QR spec says to XOR with these bits (mask)

#format string
fs = data << 10

counter = 1
while len("{0:b}".format(fs)) >= 11:
  print "Division number:", counter
  
  # Pad geneerator so it is same size as fs
  pad_gen = generator << len("{0:b}".format(fs)) - len("{0:b}".format(generator))

  # pad_gen ^ fs
  res = pad_gen ^ fs

  print "FS:", "{0:b}".format(fs), "Length:", len("{0:b}".format(fs))
  print "Pad Gen", "{0:b}".format(pad_gen), "Length:", len("{0:b}".format(pad_gen))
  print "Res", "{0:b}".format(res), "Length:", len("{0:b}".format(res))

  # Update fs
  fs = res
  
  print "\n",
  counter += 1
  
#Fs now contains result
combined = (data << len("{0:b}".format(fs))) + fs
print "combined:", "{0:b}".format(combined), "Length:", len("{0:b}".format(combined))

# Print final fs, padding 0's as needed to present 15 check bits
final_fs = combined ^ XOR_res
print "Final Format String:", "{:015b}".format(final_fs), "Length:", len("{:015b}".format(final_fs))

I try many inputs and eventually get some partial results using the following parameters:

ECC = M,
Mode = alphanumeric, mode 2 (111)
Data: 0b00010
Final Format String: 101111001111100 (Length: 15)

I generate a modified version of the QR code which fixes all of the above issues and I get a partial result:

HV15-W!loÄpclҋ¾¬5R‡³4s-WÌ

Note: I got the above result using a good QR scanner (link) which detects a lot of errors and offers debugging. My QR code still had errors in it but it was able to decode some of it anyway.

The beginning of the result matches our flag pattern but the middle is all messed up.

I ask for a clue on the hacking-lab IRC channel and somebody tells me I need to solve the errors using "one big change". This leads me to believe I need to do something simple like swap rows etc.
I learn that swapping rows does not correct the issues.

However, if I take an inner block (square) where the endpoints are determined by the error in each finder pattern (and alignment pattern) and invert the bits, I can fix the Finder pattern error, the Alignment pattern error as well as the Dark Module error all in one move!

Illustration showing bits to invert (highlighted yellow):

QR Code Inner Square Bits to Invert

I guess that this could solve the check bits too. I modify my QR generator script so it inverts all bits within this inner square and produce a QR code. This is the modified (final) script:

#!/usr/bin/python

#
# Generate QR code from binary
#

import sys, Image

bin_list = [
"""1111111000001000001111111
1000001010101010101000001
1011101000001101101011101
1011101011101000001011101
1011101001000011001011101
1000001011011000001000001
1111111010101010101111111
0000000110010010110000000
1111101000100010000101010
0110110001010100000010000
0100001111001101001000011
1110010101110000001000101
1000011010010000001110001
1010100010111111111010001
1001011010100111110100010
1001010000110100011000011
1001111111010011001110001
0000000101100100010011010
1111111011100010101011001
1000001000000101100010010
1011101010100010111110001
1011101011011010010110101
1011101010011000101011001
1000001011100111001101100
1111111011011011111101001
"""]

altered_bin_list = []

# Invert inner block
row = 0
col = 0

for line in bin_list:
  for i in line:
    bit = i
    
    # Ignore new lines
    if i == '\n':
      continue
      
    if (row >= 7 and row <= 17 and col >= 7 and col <= 17):
      bit = "0" if (i == "1") else "1"

    altered_bin_list.append(bit)

    col += 1;

    if col >= 25:
      altered_bin_list.append('\n')
      row += 1;
      col = 0


# Generate altered image
im = Image.new('RGB', (25, 25))
data = []

for line in altered_bin_list:
  for i in line:
      if i == "0":
        data.append((255,255,255))
      elif i == "1":
        data.append((0,0,0))
    
im.putdata(data)
im.save('qr.png')

This produced a valid QR code that scans!

Note: QR code images have all been enlarged to 350x350 pixels.
Refer to downloads at the top of this post for 25x25 pixel versions.

Final QR Code for Santa Leak

We use a QR reader to fetch the flag:

HV15-W!ll-R0ck-t#i$-xM4s-H0b0

Reading this as English:
HV15 Will Rock this xMas Hobo

Done!


Leave a comment

(required)(will not be published)(required)

Comments

There are no comments yet. Be the first to add one!