Home Hackvent 2018 Teaser
Writeup
Cancel

Teaser

Challenge

Solution

Stage 1

The image contains braille code. We translate it to http://bit.ly/2TJvxHt, which leads us the following image:

this QR decodes to Rushed by ..

It turns out the bit.ly link actually translated to https://hackvent.hacking-lab.com/T34s3r_MMXVIII/index.php?flag=UI18-GAUa-lXhq-htyV-w2Wr-0yiV but redirected. The flag parameter when ROT13’d gives us the first flag: HV18-TNHn-yKud-uglI-j2Je-0lvI

When we fill in the correct flag in the url, we get to the next stage: https://hackvent.hacking-lab.com/T34s3r_MMXVIII/index.php?flag=HV18-TNHn-yKud-uglI-j2Je-0lvI, which redirects to https://hackvent.hacking-lab.com/T34s3r_MMXVIII/ZOoxjUSe1OVB7OPoVrsX.pdf

pdf file

Ok, there is lots here, lets get all the elements of the pdf with http://extractpdf.com

Stage 2

We find some Morse code text in the pdf:

1
2
3
HACKvent 2018

.... ...- .---- ---.. -....- --. --- .-. .. -....- --.. .-. ... -... -....- ..- ..-. .- . -....- - ... -.... -.-. -....- -.-. ...- - -

this decodes to: HV18-GORI-ZRSB-UFAE-TS6C-CVTT, which is our second flag

Stage 3

We also find some images in the pdf:



The stereogram image can be decoded here and gives:

we clean it up a bit with GIMP so that we can scan it:

and this is our 3rd flag: HV18-p2LK-DNcI-YKw7-T9Ad-mH3v

Stage 4

Next, we try binwalk on the pdf:

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
$ binwalk stage2.pdf
DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             PDF document, version: "1.7"
404           0x194           Unix path: /PDF/Text/ImageB/ImageC/ImageI] >>/MediaBox[ 0 0 595.32 841.92] /Contents 4 0 R/Group<</Type/Group/S/Transparency/CS/DeviceRGB>>
621           0x26D           Zlib compressed data, default compression
1751          0x6D7           Unix path: /Type/XObject/Subtype/Image/Width 1000/Height 340/ColorSpace/DeviceRGB/BitsPerComponent 8/Filter/DCTDecode/Interpolate true/Leng
1899          0x76B           JPEG image data, JFIF standard 1.01
1929          0x789           TIFF image data, big-endian, offset of first image directory: 8
51778         0xCA42          Unix path: /Type/ExtGState/BM/Normal/ca 1>>
51831         0xCA77          Unix path: /Type/Font/Subtype/TrueType/Name/F1/BaseFont/BCDEEE+Calibri/Encoding/WinAnsiEncoding/FontDescriptor 8 0 R/FirstChar 32/LastChar
51998         0xCB1E          Unix path: /Type/FontDescriptor/FontName/BCDEEE+Calibri/Flags 32/ItalicAngle 0/Ascent 750/Descent -250/CapHeight 750/AvgWidth 521/MaxWidth
52237         0xCC0D          Unix path: /Type/ExtGState/BM/Normal/CA 1>>
52291         0xCC43          Unix path: /Type/XObject/Subtype/Image/Width 781/Height 781/ColorSpace/DeviceRGB/BitsPerComponent 8/Interpolate false/Filter/FlateDecode/Le
52442         0xCCDA          Zlib compressed data, default compression
187860        0x2DDD4         Unix path: /Type/Font/Subtype/TrueType/Name/F2/BaseFont/BCDFEE+Arial-Black/Encoding/WinAnsiEncoding/FontDescriptor 12 0 R/FirstChar 32/Last
188034        0x2DE82         Unix path: /Type/FontDescriptor/FontName/BCDFEE+Arial-Black/Flags 32/ItalicAngle 0/Ascent 1101/Descent -212/CapHeight 716/AvgWidth 552/MaxW
188279        0x2DF77         Unix path: /Type/Font/Subtype/TrueType/Name/F3/BaseFont/TimesNewRomanPS-BoldMT/Encoding/WinAnsiEncoding/FontDescriptor 14 0 R/FirstChar 32/
188456        0x2E028         Unix path: /Type/FontDescriptor/FontName/TimesNewRomanPS-BoldMT/Flags 32/ItalicAngle 0/Ascent 891/Descent -216/CapHeight 677/AvgWidth 427/M
188698        0x2E11A         Unix path: /Type/XObject/Subtype/Image/Width 200/Height 200/ColorSpace/DeviceRGB/BitsPerComponent 8/Filter/DCTDecode/Interpolate true/Lengt
188844        0x2E1AC         JPEG image data, JFIF standard 1.01
188874        0x2E1CA         TIFF image data, big-endian, offset of first image directory: 8
195577        0x2FBF9         Zlib compressed data, default compression
196145        0x2FE31         Zlib compressed data, default compression
215877        0x34B45         Zlib compressed data, default compression
231310        0x3878E         Unix path: /Type/Metadata/Subtype/XML/Length 3075>>
231494        0x38846         Unix path: /www.w3.org/1999/02/22-rdf-syntax-ns#">
231734        0x38936         Unix path: /purl.org/dc/elements/1.1/">
232171        0x38AEB         Unix path: /ns.adobe.com/xap/1.0/mm/">
234681        0x394B9         Zlib compressed data, default compression
236069        0x39A25         RAR archive data, first volume type: MAIN_HEAD
236135        0x39A67         Zip archive data, encrypted at least v2.0 to extract, compressed size: 210390, uncompressed size: 210378, name: z.zip
446647        0x6D0B7         End of Zip archive

# we see there are files appended, we extract with:
$ binwalk -e stage2.pdf

Reveals an encrypted zip file

TODO

Stage 5

The zip file also contained this QR code image:

Which looks suspiciously like a challenge from HACKvent 2015, my writeup here

Extracting these to their component channels:



These won’t scan on their own, because they are missing timing and masking areas, but a lot of the information is there. We use QRazyBox to restore these missing pieces and are able to find our flag:

1
2
3
red:   HV18-3I5a-
green: Rnrl-s28r-
blue:  SRHj-Lhzx

So our complete flag is:

1
HV18-3I5a-Rnrl-s28r-SRHj-Lhzx

Stage 6

The zip file also contained a Santa.txt file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Santa has caught up with the information age and does not trust
clear-text commands anymore.
He has decided that all communications
have to be encrypted to prevent an unfriendly take-over of his team.
Santa chooses a simple, secure, and toolless encryption scheme.
However, his team's memory capacity is limited and so he can only use
their names (Dasher, Dancer, Prancer, Vixen, Comet, Cupid, Donder and
Blitzen) as keys.



Where is the team headed to?

STTYN YATLOEP DNEA ONBL TGNTO MHEHH EISTIARIB FHSRA LD IIONA NL HERUV LN17-PTAA-RTON-RDOE-MCTN-AHCO

TODO

Stage 7

The zip file also contained the following image:

This appears to be an IBM-029 punch card by the faint lettering on the right.
Fortunately wikipedia
has a full translation of that format:

I’m arbitrarily going to number the colums to be -2, -1, 0 (the row of 0s), 1 .. 9 because that makes sense to me?

ColumnValues
16, -1
22
39 0
4-1
5-2 8
64
77 -1
81 -2
93 -1
105
112
1290
134
14-1
153
166 -2
171
189
19-1
20-2 8
210 5
221
238
24-1
250
263 -1
274 -2
28-1 3 8
29-1
300 2
31-2 3
32-2 8
330
34-1
350
36-1 3
371
380 2
39-1
404
410 6
423
4302
44-1
450
46-1 4
473
488 2 -1
49-1
503
511
522
534 6
54-1
55-2 8
569 0
573
583 -2
59-1
605
612 4
621
63-1 2
64-1
65-2 3
663
67-2 0 2 8
68_
69-1
700 5
710 9
723
730 6
74-1
75-2 3
760 6
770 2
78-2 3
79-1
801

We then use the translation matrix we found to write a small python script
Which will output the following when run:

1
O2Z-H4PAL52¿4-3F19-HV18-0LD$-SCH0-0L1S-4W3S-0M3!-312¿-HZ3C-5¿1K-C3¿¿-VZ3W-CWSC-1¿

(¿ used when couldn’t identify a character correctly, maybe transcription mistakes by me.)
But the flag is clearly visible in the center: HV18-0LD$-SCH0-0L1S-4W3S-0M3!

Stage 8

The zip file also contained the file teaser.pls

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
CREATE OR REPLACE FUNCTION checkHV18teaser wrapped
a000000
b2
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
8
37f 22f
9bGsDUl+WXOiRvCg6f+CmODMp3UwgwIJ+txqfC9Dgz+VaPwOTWaoKj5jW2QUCQapzabE52vy
50h+W7xBf1NE2/Fa93pBwUJtOxLvz1WIU75VaGjqH5M6oL4/aHovVUteU3Arw0eLvVRDEvbd
0mLqbyd4kEtMnI76J3vuLaHC1mkYuwEN6bdmd3GQPBtHV4fWHr5OM5B81yX+kw0560LKdDx8
mVHJvc7y7vShjsCpFgEUd6sfN3ZbOkjjbg+AJSGuIjZzvT7vkQwM5wcQL73C6+BCiaaEG2ja
r+3zqCSk6QzcKvIwuBwXf9UHGL4YS47JO3EOmIPOy8VQYfY1M9g6UeieqOftVm/Pr8smR11r
UWM8kk1WTmMvY13s1Klpr7tFnzwjmnSnTP9Exz/dV5+cU3mlgyjqkAIsWnGqDGKMfVahOHSc
Bzalmd+HDxxBF39ymrsGHfBUv0gAPtnYVCVWiG0Q9ij5DbBffRrsx4uOYuAqJ4KwT5vNpKon
MSMAM3ZsIFVQgfnY/sfkB+jfGEuldGYiui7zvIMSHVDfPEE=

/

These are wrapped oracle functions and we can unwrap it using https://www.codecrete.net/UnwrapIt/

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
FUNCTION checkHV18teaser(FLAG VARCHAR2) RETURN NUMBER IS
A VARCHAR2(4);
B NUMBER(10);
C NUMBER(10);
H VARCHAR(40);
BEGIN
A := SUBSTR(FLAG,1,4);
IF NOT (A = 'HV18') THEN
  RETURN 0;
END IF;

B := TO_NUMBER(SUBSTR(FLAG,6,2));
C := TO_NUMBER(SUBSTR(FLAG,8,2));
IF NOT (((B * C) = 6497) AND (B < C)) THEN
  RETURN 0;
END IF;

A := SUBSTR(FLAG,11,4);
SELECT STANDARD_HASH(A, 'MD5') INTO H FROM DUAL;
IF NOT (H = 'CF945B5A36D1D3E68FFF78829CC8DBF6') THEN
  RETURN 0;
END IF;

IF NOT ((UTL_RAW.CAST_TO_VARCHAR2(UTL_RAW.BIT_XOR (UTL_RAW.CAST_TO_RAW(SUBSTR(FLAG,16,4)), UTL_RAW.CAST_TO_RAW(SUBSTR(FLAG,21,4)))) = 'zvru') AND (TO_NUMBER(SUBSTR(FLAG,21,4)) = SQRT(8814961))) THEN
  RETURN 0;
END IF;

IF NOT ( UTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_ENCODE(UTL_RAW.CAST_TO_RAW(SUBSTR(FLAG,26,4)))) = 'RjBtMA==') THEN
  RETURN 0;
END IF;

DBMS_OUTPUT.PUT_LINE(A);
RETURN 1;
END;

so this is the function that validates the flag, we reverse engineer it to find the flag. Next is the code again, with some comments:

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
FUNCTION checkHV18teaser(FLAG VARCHAR2) RETURN NUMBER IS
A VARCHAR2(4);
B NUMBER(10);
C NUMBER(10);
H VARCHAR(40);
BEGIN

/* flag starts with "HV18", no surprises there */
A := SUBSTR(FLAG,1,4);
IF NOT (A = 'HV18') THEN
  RETURN 0;
END IF;

/* "7389"  (73 * 89 = 6497) */
B := TO_NUMBER(SUBSTR(FLAG,6,2));
C := TO_NUMBER(SUBSTR(FLAG,8,2));
IF NOT (((B * C) = 6497) AND (B < C)) THEN
  RETURN 0;
END IF;


/* "H0b0" has the required md5 hash (found via hashkiller.co.uk) */
A := SUBSTR(FLAG,11,4);
SELECT STANDARD_HASH(A, 'MD5') INTO H FROM DUAL;
IF NOT (H = 'CF945B5A36D1D3E68FFF78829CC8DBF6') THEN
  RETURN 0;
END IF;

/* next two segements when XOR'ed must make 'zvru', and
   second of the two segments is 2969 (square root of 8814961)
   "HODL-2969" */
IF NOT ((UTL_RAW.CAST_TO_VARCHAR2(UTL_RAW.BIT_XOR (UTL_RAW.CAST_TO_RAW(SUBSTR(FLAG,16,4)), UTL_RAW.CAST_TO_RAW(SUBSTR(FLAG,21,4)))) = 'zvru') AND (TO_NUMBER(SUBSTR(FLAG,21,4)) = SQRT(8814961))) THEN
  RETURN 0;
END IF;

/* "F0m0" (last segment must match the given base64 string) */
IF NOT ( UTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_ENCODE(UTL_RAW.CAST_TO_RAW(SUBSTR(FLAG,26,4)))) = 'RjBtMA==') THEN
  RETURN 0;
END IF;

DBMS_OUTPUT.PUT_LINE(A);
RETURN 1;
END;
1
HV18-7389-H0b0-HODL-2969-F0m0

Flag