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):
These are all the files/scripts I ended up with after the challenge (zipped):
Download Solution Files and Scripts
The Challenge
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
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.
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):
1 |
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.
1 2 |
D:\Desktop> SFind 2.wav 2.wav:3.txt Size: 490901 |
I extract the contents and feed it to notepad:
1 |
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#!/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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
+++++ +++[- >++++ ++++< ]>+++ +++++ .<+++ ++[-> +++++ <]>++ ++.<+ +++[- >++++ <]>++ ++.<+ +++++ +++[- >---- ----- <]>-- ----- -.<++ +++++ +[->+ +++++ ++<]> +++++ +++++ ++.-- ----- ..<++ +[->+ ++<]> +++++ +.-.< +++++ +++[- >---- ----< ]>--- ----. <+++[ ->--- <]>-- -.<++ ++[-> ----< ]>--- .---. <++++ +++[- >++++ +++<] >++++ +++++ +++++ .<+++ +++[- >++++ ++<]> .<+++ +++++ [->-- ----- -<]>- ----- ----- --.<+ +++++ ++[-> +++++ +++<] >++++ +++.+ ++++. <+++[ ->--- <]>-- .+++. <++++ ++++[ ->--- ----- <]>-- --.<+ +++++ +++[- >++++ +++++ <]>++ +++++ +.<++ +[->- --<]> -.+++ +++.< +++++ ++++[ ->--- ----- -<]>- ---.< +++++ +++[- >++++ ++++< ]>+++ +++.+ +++++ +++.+ +++++ .---- ---.< +++[- >---< ]>-.< +++++ +++[- >---- ----< ]>--- -.<++ +++++ ++[-> +++++ ++++< ]>+++ .<+++ [->-- -<]>- --.-- -.<++ +++++ +[->- ----- --<]> ----- .<+++ +++++ +[->+ +++++ +++<] >++++ ++.<+ +++[- >---- <]>-- ----. <++++ [->++ ++<]> +++++ +++.< +++++ ++++[ ->--- ----- -<]>- ----- --.<+ +++++ +++[- >++++ +++++ <]>++ +.--- --.<+ +++++ ++[-> ----- ---<] >---- ----- ----- -.<++ +++++ ++[-> +++++ ++++< ]>+++ .<+++ [->-- -<]>- --.+. <+++[ ->+++ <]>+. <++++ +++++ [->-- ----- --<]> --.<+ +++++ +++[- >++++ +++++ <]>++ .+.<+ ++[-> ---<] >---- --.<+ ++[-> +++<] >++.< +++++ +++[- >---- ----< ]>--. <++++ +[->- ----< ]>--- ----- .---. +++.- --.<+ +++++ ++[-> +++++ +++<] >++++ +++++ .<+++ +[->+ +++<] >++.- ---.< ++++[ ->+++ +<]>+ .<+++ [->-- -<]>- ----- .++++ +.<++ +++++ +[->- ----- --<]> ----- ---.< +++++ ++++[ ->+++ +++++ +<]>+ ++.<+ ++[-> ---<] >---. ---.< +++++ +++[- >---- ----< ]>--- --.<+ +++++ ++[-> +++++ +++<] >++++ +++++ +++++ +.+++ ++.<+ ++[-> ---<] >---. ---.< +++[- >+++< ]>+++ +.<++ +++++ ++[-> ----- ----< ]>-.< ++++[ ->+++ +<]>+ +.<++ ++[-> ----< ]>--. <++++ ++++[ ->+++ +++++ <]>++ ++++. +++.+ ++.-- ----- .<+++ [->++ +<]>+ ++++. <++++ +++++ [->-- ----- --<]> --.<+ +++++ ++[-> +++++ +++<] >+.<+ ++[-> +++<] >++++ .<+++ [->-- -<]>- .<+++ +++++ [->-- ----- -<]>- ---.< +++++ ++++[ ->+++ +++++ +<]>+ ++.<+ +++[- >---- <]>-- -.<++ +[->+ ++<]> +.--- ---.< +++[- >+++< ]>+.- ----- ---.. <++++ ++++[ ->--- ----- <]>-- ----. <++++ +++++ [->++ +++++ ++<]> +++.- ----. <++++ ++++[ ->--- ----- <]>-- ----- ----- ---.< +++++ ++++[ ->+++ +++++ +<]>+ ++.<+ ++[-> ---<] >---. ---.< +++++ +++[- >---- ----< ]>--- --.<+ +++++ ++[-> +++++ +++<] >++++ +++++ +++++ .---- ----- .<+++ +[->+ +++<] >+++. ----. <++++ +++++ [->-- ----- --<]> ---.< +++++ ++++[ ->+++ +++++ +<]>+ +.+.< +++[- >---< ]>--- ---.< +++[- >+++< ]>++. <++++ ++++[ ->--- ----- <]>-- ----- ----- ---.< ++++[ ->--- -<]>- ---.- --.++ +.--- .<+++ +++++ [->++ +++++ +<]>+ +++++ +++++ ++++. <++++ [->++ ++<]> +++++ +.+++ +++.- --.+. <++++ ++++[ ->--- ----- <]>-- ----- .<+++ [->-- -<]>- --.<+ +++++ +++[- >++++ +++++ <]>++ .<+++ +[->- ---<] >--.< +++[- >+++< ]>+++ +.+++ +++.< ++++[ ->--- -<]>- --.< |
I run it through an online Brainfuck interpreter (link).
The following message is printed:
1 2 3 4 |
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:
1 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
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),
- a png image of a grey empty Christmas ball, and
- a jpeg image which appears to contain static.
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 heiroglyphs:
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:
1 2 3 4 |
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).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
#!/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):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
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).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
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!
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
#!/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:
1 2 3 4 |
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):
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
#!/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 350×350 pixels.
Refer to downloads at the top of this post for 25×25 pixel versions.
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!