So we open the PDF file to find 1 QR image in a ball. We scan it and we get the text
Oooops ! .
We then use an online tool to extract all images from the PDF (link). I do this because PDF files store all image files, even if they are completely overlapped by other images.
We then find 3 different images:
As it turns out, the QR image square was overlapping the other QR image in the ball. So we simply scan the QR image in the ball and get the flag.
This challenge was very easy to solve as I had completed the teaser earlier.
First I convert the hexadecimal number provided (as it starts with 0x) to binary.
The result is 625 bits (which does not divide nicely by 8 so it is probably not an ASCII message).
However, its likely to be a QR code version 2 which is 25×25 = 625 pixels large.
In this case, every 1 corresponds to a black pixel and every 0 corresponds to a white pixel.
This is obvious by arranging the bits into a 25 by 25 grid and adding some spacing :
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
1 1 1 1 1 1 1 0 0 0 0 0 1 0 0 1 1 0 1 1 1 1 1 1 1
1 0 0 0 0 0 1 0 1 0 1 0 0 1 1 1 1 0 1 0 0 0 0 0 1
1 0 1 1 1 0 1 0 0 0 0 0 1 0 1 1 1 0 1 0 1 1 1 0 1
1 0 1 1 1 0 1 0 1 1 1 0 1 1 1 1 0 0 1 0 1 1 1 0 1
1 0 1 1 1 0 1 0 0 1 0 0 0 0 0 0 1 0 1 0 1 1 1 0 1
1 0 0 0 0 0 1 0 1 1 0 0 0 1 1 1 0 0 1 0 0 0 0 0 1
1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1
0 0 0 0 0 0 0 0 0 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0
1 1 1 1 1 0 1 1 1 1 0 0 0 1 0 1 1 1 0 1 0 1 0 1 0
1 1 1 1 1 0 0 1 0 0 1 1 1 1 0 0 0 0 0 0 0 1 0 1 0
0 0 1 1 1 0 1 0 1 0 1 0 1 0 1 1 1 1 0 0 1 0 0 0 1
1 0 0 0 1 1 0 1 0 0 0 1 1 0 1 1 1 1 1 1 0 1 0 0 0
1 0 1 1 0 1 1 0 1 1 1 0 1 0 1 0 0 0 1 0 1 0 1 1 1
1 1 1 1 0 1 0 1 0 1 0 0 0 1 0 0 0 1 0 1 0 0 0 1 1
1 0 1 1 1 1 1 0 1 1 0 0 1 0 1 1 1 1 1 1 1 0 0 1 1
1 0 1 1 0 0 0 0 1 1 1 0 1 0 0 1 0 0 1 0 0 1 0 0 1
1 0 1 1 1 0 1 1 0 1 0 0 0 1 1 1 1 1 1 1 1 1 1 0 0
0 0 0 0 0 0 0 0 1 0 0 1 0 1 0 0 1 0 0 0 1 0 1 0 1
1 1 1 1 1 1 1 0 1 0 0 0 0 0 1 0 1 0 1 0 1 0 0 1 1
1 0 0 0 0 0 1 0 0 1 0 0 0 0 0 1 1 0 0 0 1 0 0 1 1
1 0 1 1 1 0 1 0 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 0
1 0 1 1 1 0 1 0 1 1 1 1 1 0 1 0 0 1 1 1 1 1 0 1 1
1 0 1 1 1 0 1 0 1 0 1 0 1 1 0 1 0 0 0 1 0 0 0 0 1
1 0 0 0 0 0 1 0 1 0 1 0 0 0 0 1 1 1 1 0 1 0 0 0 1
1 1 1 1 1 1 1 0 1 1 0 0 0 1 0 0 0 1 1 1 0 1 0 1 1
I use the same script I used in the teaser (link) to generate a QR code.
This is the QR code generated (enlarged to 350px * 350px):
We scan the QR code using a really clever online scanner (link) that has some extra error correcting capabilities (compared to other QR readers).
We get the flag:
HV15-aFsf-4ea1-2eGg-Llr4-pB5A
At first I was convinced this was a Vigenere cipher but this is not the case.
After a clue from M from the hacking-lab IRC channel I was able to figure out the cipher used is the Scytale cipher.
This is somewhat obvious as the
- characters from the nugget are all grouped at the end of the ciphertext.
The length is also obvious by simply looking at the end of the ciphertext, l = 5.
We use an online scytale solver (link) to get the flag.
We clearly have to either scan every QR code or find the correct QR code.
So we simply use an online tool that splits up GIF files based on their frames (link).
We download all the frames (29 images exist so its likely that each frame corresponds to one character in the nugget).
This turns out to be the case, we scan every code in order to get the flag.
Clue: I wonder if this is the native language of one of you!
pagh wa'vatlh netlh wa'maH wa'maH wa' SaD wa' SaD wa'vatlh wa' wa'maH wa'maH wa'vatlh wa'maH wa' wa'maH wa'maH netlh pagh wa'maH wa' wa'vatlh wa' wa'vatlh SaD SaD wa' wa'vatlh netlh wa'maH wa' wa'maH wa'maH wa'vatlh wa' wa'maH wa'maH wa' wa' wa'vatlh SaD wa' wa'maH wa'maH wa'maH wa'vatlh wa'maH SaD wa'maH wa' wa'maH wa'maH wa' wa' wa' wa'maH wa'vatlh wa' wa'vatlh wa' SaD wa' SaD wa'maH wa' wa' wa'maH wa' SaD wa'maH wa' wa'maH wa'maH wa'vatlh wa'maH wa' wa'vatlh wa' wa'maH wa' wa' wa'maH wa' netlh wa'maH wa'vatlh wa' wa' wa' wa'vatlh wa'maH wa' wa'maH wa'maH SaD wa' wa'vatlh wa'maH SaD wa'vatlh wa' wa'maH SaD wa' wa'maH SaD
Solution
After some Googling I realise this is the Klingon language from Star Trek.
I try some online translators (including Bing!) and it is clear that the message is a series of numbers that contain 0 or 1 only.
This is most likely binary that represents ASCII text.
I write a small script to convert each component in the text (based on a Klingon number guide):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
importsys
text="""pagh wa'vatlh netlh wa'maH wa'maH wa' SaD wa' SaD wa'vatlh wa' wa'maH wa'maH wa'vatlh wa'maH wa' wa'maH wa'maH netlh pagh wa'maH wa' wa'vatlh wa' wa'vatlh SaD SaD wa' wa'vatlh netlh wa'maH wa' wa'maH wa'maH wa'vatlh wa' wa'maH wa'maH wa' wa' wa'vatlh SaD wa' wa'maH wa'maH wa'maH wa'vatlh wa'maH SaD wa'maH wa' wa'maH wa'maH wa' wa' wa' wa'maH wa'vatlh wa' wa'vatlh wa' SaD wa' SaD wa'maH wa' wa' wa'maH wa' SaD wa'maH wa' wa'maH wa'maH wa'vatlh wa'maH wa' wa'vatlh wa' wa'maH wa' wa' wa'maH wa' netlh wa'maH wa'vatlh wa' wa' wa' wa'vatlh wa'maH wa' wa'maH wa'maH SaD wa' wa'vatlh wa'maH SaD wa'vatlh wa' wa'maH SaD wa' wa'maH SaD"""
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):
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):
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
#
importsys,string
content=[]
f=sys.argv[1]
s=open(f,'r')
foriins:
content.append(i.strip('\r\n'))
s.close()
g=open('decoded','w+')
foriincontent:
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.
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).
# If we treat the 0's and 1's as binary, our range is between 0 - 0b1111111111111111111111111 (33554431 or 2^25-1)
cur=0
forcur inrange(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()
ifhex_string inshaList:
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
# 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
forline inbin_list:
foriinline:
bit=i
# Ignore new lines
ifi=='\n':
continue
if(row>=7androw<=17andcol>=7andcol<=17):
bit="0"if(i=="1")else"1"
altered_bin_list.append(bit)
col+=1;
ifcol>=25:
altered_bin_list.append('\n')
row+=1;
col=0
# Generate altered image
im=Image.new('RGB',(25,25))
data=[]
forline inaltered_bin_list:
foriinline:
ifi=="0":
data.append((255,255,255))
elifi=="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
To solve this challenge I simply performed a dictionary attack on the zip file until I had discovered the correct password. Linux tools available are fairly slow and may only test 1000-10000 passwords each second so I decided to use Accent Zip password recovery which is a commercial tool capable of testing upto 600000 passwords a second. The answer was found in under 2 seconds.
Security Questions
1. The security problem with ZIP files is that there is that they are vulnerable to brute force attacks as it is a client side security scheme.
2. Due to the nature of ZIP files, the only method of attack is a bruteforce attack. I used a third party tool ‘Accent Zip password recovery’ to bruteforce the password for this zip. Luckily, the password was short (5 characters) and was a common dictionary word.
The password was: close
3. As ZIP files are vulnerable to bruteforce attacks. The password creator could ensure:
their password is of a long size (i.e. 10+ characters) making bruteforce attacks take much longer
their password is not a common dictionary word and contains symbols/numbers/uppercase/lowercase characters
I log into the system to discover I am in a restricted shell.
Some testing reveals I can run commands like ls but cannot run cat etc.
I realise that I can use the
/ character in arguments but not in the command name.
So I can’t call any commands by path.
Thus I can only execute commands in the local
./bin directory.
Lets see what is in the directory:
1
2
$ls./bin
lsping tee
Okay so I can run these three commands. They behave as expected.
I look around the machine and notice a directory
/home/restricted which my user restricted1 also owns but this is simply a copy of the
/home/restricted1 directory and thus a dead end.
I then realise that
/home/restricted1/ping is world writeable.
I can thus use
tee and
echo to write whatever I want to it.
Initially I wrote shell code to the program but this is an issue as it would not spawn a shell in POSIX mode but rather console mode.
Instead I use the flowing commands:
1
2
echo-e'#!/bin/bash'|tee-./bin/ping
echo-e' /bin/bash '|tee-a./bin/ping
Then I run the
ping program and a (regular) shell is spawned.
I am no longer in a restricted shell.
I set my ENV path so I don’t have to specify full paths to programs:
1
2
export PATH=$PATH:/bin
export PATH=$PATH:/usr/bin
Now I need to escalate privileges from my regular shell.
I try to find world writeable files.
I run:
1
find/-perm-0002-typef-print
And keep the results in mind as I explore other things.
I check many things, one of them being the
/etc/ directory for cron jobs.
I see that the
cron.minutely directory contains one program
mtr that runs every minute but it is not world writeable.
I still check the file using:
1
cat/etc/cron.minutely/mtr
It turns out that
/usr/bin/mtr-check is called as part of this cronjob.
I check my list from before to discover that this particular file happens to be world writeable!
So we can write any program and it will be executed by root as a cronjob.
We decide to write the following to the file (using echo and file redirection):
Essentially, we are copying
/bin/sh to our directory and setting the s flag using chmod 4755 (so we setuid upon execution). So when we run rootbash, it should give us a root shell.
I go drink a cup of water (enough time for the minutely cronjob to run) and come back.
I then see my rootbash program!
I run it and a shell is spawned.
1
2
$whoami
root
Then I read the file at
/root/secret.txt to capture the flag.
1
2
3
4
$cat/root/secret.txt
TEAM GOTWURZEL
....etc
Security Questions:
1. Explain the security problem
The issue here is that I was able to break out of the restricted shell because the
/home/restricted1/bin/ping program was world writeable. If it was not, I wouldn’t be able to leave the restricted shell that way.
Now assume I someone managed to break out of the restricted shell. The second issue is that a cronjob executed by root called a program that was world writeable.
So the issues are simply permission issues.
2. Explain your attack, How you were able to get the /root/secret.txt file
Read above!
In summary, I spawned a shell using the world writeable ping program.
Then I spawned a root shell by executing a cronjob as root which put a copy of
/bin/sh in my directory with the s flag set. With the root shell, I read the secret file.
3. Add some proofing information to your solution (traces, exploit code, gold nugget)
All commands are above.
4. What do you recommend for protection?
Simply fix the permissions of the
/home/restricted1/bin/ping and
/usr/bin/mtr-check so they are not writeable!