Damjan Cvetko

Damjan Cvetko

Developer, System Architect, Hacker.

8 minutes read

Challenge: Santa trusted 3 elves with his secrets. They couldn’t agree on the encryption scheme to use, so each of them chose a different one. Can you decrypt each of the messages in the attached archive and recover the secret.

Attached file: parts.zip with 4 files: part1.enc, part1.pem, part2.gpg, part3.enc

This was by far the hardest or at least the more complex or all challenges. It was composed of 4 steps.

Part1 - RSA

The type of the first part is obvious, by looking at least at part1.enc. Let’s inspect it with openssl.

Ok, so if we look at part1.enc, we see its a binary blob, with some text in it. Highly likely it’s related to the X509 certificate. More or less everything this world is a ASN.1 structure, so lets try our luck.

pkcs7-envelopedData tells us its a CMS (Cryptographic Message Syntax) structure. Commonly used in mail (S/MIME) or just to pack and encrypt some data with one or more public keys and certificates. The purpose is to be able to encrypt something, and let more than one recipient decrypt it. It rougly contains:

  1. A list with descriptions of the certificates
  2. For each of these a symetric key, encrypted with each certificates public key (rsa in this case)
  3. An IV
  4. The encrypted data with that one symetric key (aes-256-cbc in our case)

Ok, so now we know, but where do we get our RSA private key?

Lets look back at the certificte and the public key. This exponent is quite strange, it’s usually something like 65537 or simillary small.

Well thats an intentional vulnerability! If the exponent is very large, compared to modulus, one can factor the needed components for the private key. Let’s find a tool for it.

https://github.com/Ganapati/RsaCtfTool

openssl x509 -in part1.pem -noout -pubkey > part1.key
./RsaCtfTool.py --publickey part1.key  --private > part1.priv
openssl cms -decrypt -inkey part1.priv -in part1.enc -inform der > part1.result

Part 2 - PGP Bruteforce

The extension already hints to it, but lets check:

$ file part2.gpg
part2.gpg: GPG symmetrically encrypted data (AES256 cipher)

To be honest, I was going around this one for quite a while and didn’t solve it by myself. I was looking for some waknes, something smart, but in the end it was just a bruteforce, a password found in SecLists again. Thanks to Klemen for this one.

gpg2john part2.gpg > part2.hash
john part2.hash -w=...

...
0g 0:00:00:00 DONE (2020-01-16 22:16) 0g/s 5.780p/s 5.780c/s 5.780C/s santa123

And decrypt:

gpg -o part2.result part2.gpg

Part 3 - Citrix

Here I made the mistake of trying to decode this myself, instead of looking for a known encryption/encoding. Did did get some interesing results. Seeing that both part1 and part2 results were 260 bytes long (259 hex nibbles and one newline) chances were good that part3 was going to be the sane. The size of part3.enc was exactly 1036 (and one newline). And 1036/4 = 259. The contents consists exclusevly of letters ABCDEFGHIJKLMNOP. Thats 16 distinct, sequentlial letters. If I replace them with 0123456789abcdef, I get a valid hex stream and if I decode this to bytes, I get 518 bytes!

A bit of PHP:

<?php
$line = trim(file_get_contents("part3.enc"));
$line = str_replace(str_split("ABCDEFGHIJKLMNOP"),str_split("0123456789abcdef"), $line);
$raw = hex2bin($line);
echo $raw;
zobo@BLACKBOX:/mnt/c/temp/day21$ php cnt3.php | xxd
00000000: 9d38 ad08 9e3b a80d cd68 fe5b cb6e af0a  .8...;...h.[.n..
00000010: 9732 f550 c065 f653 c366 f550 9035 f154  .2.P.e.S.f.P.5.T
00000020: 9035 a603 9336 aa0f c96c a80d 9b3e ab0e  .5...6...l...>..
00000030: 9e3b ae0b 9c39 a80d 9035 a90c cb6e fa5f  .;...9...5...n._
00000040: 9f3a ac09 993c a80d cc69 a80d 983d af0a  .:...<...i...=..
00000050: 9732 a306 9134 a500 9732 ae0b 983d f95c  .2...4...2...=.\
00000060: 9f3a fc59 9a3f a80d 9134 a207 c663 a207  .:.Y.?...4...c..
00000070: 9237 a005 9431 ac09 9e3b fc59 cc69 af0a  .7...1...;.Y.i..
00000080: cc69 fe5b 9b3e ab0e c86d fe5b 9b3e f95c  .i.[.>...m.[.>.\
00000090: ca6f a90c c86d fe5b ce6b ac09 9f3a ac09  .o...m.[.k...:..
000000a0: 9530 f154 9237 a104 9237 f752 9431 a005  .0.T.7...7.R.1..
000000b0: 983d a90c c86d ae0b 9b3e f95c cc69 ae0b  .=...m...>.\.i..
000000c0: ca6f fb5e 9e3b fc59 983d a104 c267 a702  .o.^.;.Y.=...g..
000000d0: 9035 a90c 993c f85d cf6a fd58 993c fb5e  .5...<.].j.X.<.^
000000e0: cd68 fc59 c461 f451 c560 f653 9237 f154  .h.Y.a.Q.`.S.7.T
000000f0: 9035 a80d 983d aa0f 9c39 a401 9732 a401  .5...=...9...2..
00000100: 9035 a80d 9d38 ae0b cc69 fc59 cb6e aa0f  .5...8...i.Y.n..
00000110: 9d38 ad08 c96c fe5b cc69 fe5b 9f3a f95c  .8...l.[.i.[.:.\
00000120: cb6e f356 9530 a005 c267 f752 c164 f85d  .n.V.0...g.R.d.]
00000130: 9c39 a401 c663 a207 c164 f451 cd68 a90c  .9...c...d.Q.h..
00000140: 9c39 a401 c663 a306 c267 a702 c164 a401  .9...c...g...d..
00000150: c762 f356 c762 a306 9a3f ae0b 9c39 ae0b  .b.V.b...?...9..
00000160: 9c39 ae0b 9f3a fe5b cc69 a90c 983d ab0e  .9...:.[.i...=..
00000170: c96c f154 c267 a603 9f3a fd58 9b3e ac09  .l.T.g...:.X.>..
00000180: 9d38 fb5e 9d38 ab0e 9f3a fd58 c96c af0a  .8.^.8...:.X.l..
00000190: 993c ac09 9d38 f95c ca6f fe5b c762 a306  .<...8.\.o.[.b..
000001a0: c560 f451 c560 a702 c461 f653 9035 a005  .`.Q.`...a.S.5..
000001b0: c663 f451 cc69 fc59 c461 f356 c560 f356  .c.Q.i.Y.a.V.`.V
000001c0: 9035 a500 9633 a401 9237 a702 9237 f154  .5...3...7...7.T
000001d0: 9336 aa0f 9b3e a207 9237 a104 c366 f653  .6...>...7...f.S
000001e0: cf6a ad08 9c39 ab0e ca6f f95c c86d f95c  .j...9...o.\.m.\
000001f0: cf6a ad08 cf6a f85d cd68 a80d 983d a005  .j...j.].h...=..
00000200: c663 f451 c366                           .c.Q.f

Ok, I can cleary see some patterns here. Each other byte has MSB hi or low. Whar if I XOR them with each other?

<?php
$line = trim(file_get_contents("part3.enc"));
$line = str_replace(str_split("ABCDEFGHIJKLMNOP"),str_split("0123456789abcdef"), $line);
$raw = hex2bin($line);
$quads = str_split($raw, 2);
$ret = "";
foreach ($quads as $q) {
        $ret .= chr(ord($q[0])^ord($q[1]));
}
echo $ret;
zobo@BLACKBOX:/mnt/c/temp/day21$ php cnt3.php | xxd
00000000: a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5  ................
00000010: a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5  ................
00000020: a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5  ................
00000030: a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5  ................
00000040: a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5  ................
00000050: a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5  ................
00000060: a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5  ................
00000070: a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5  ................
00000080: a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5  ................
00000090: a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5  ................
000000a0: a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5  ................
000000b0: a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5  ................
000000c0: a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5  ................
000000d0: a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5  ................
000000e0: a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5  ................
000000f0: a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5 a5a5  ................
00000100: a5a5 a5                                  ...

All the same? Ok that can’t be a coincidence! I’m on to something!

Around that time (mind you, this was going on for days) they released the hint:

Hint: S/MIME, PGP Bruteforce, Citrix

After this it was easy, I found an online tool again to decrypt this CTX1 format: https://gchq.github.io/CyberChef/#recipe=Citrix_CTX1_Decode()

I think I was preety close, even the first caracter was corect (seeing as part1 and part2 also started with 80). Cleary there is some sort of stream encoding going on, but you can read about it here: https://www.nccgroup.trust/us/about-us/newsroom-and-events/blog/2014/january/good-fun-with-bad-crypto/

Part 3 - Putting it all together

Ok now what.. we have 3 results, each a stream of hex nibbles, all 3 are 259 bytes long (+ one byte newline). I can’t even hex decode this?

But whats this in the front?

zobo@BLACKBOX:/mnt/c/temp/day21$ cat *.result
8015d8f8bc67d886a476e2e935...
80233ba537318e6c022f28d965a...
8036e35d8b5656eaa659ca305024...

After quite some attempts, I decided to cut the initial 3 bytes, the 80X, and was left with an odd number of nibbles. To spare you the boring attempts, I then XOR-ed all 3 byte streams:

<?php
$part1 = hex2bin(substr(trim(file_get_contents("part1.result")),3));
$part2 = hex2bin(substr(trim(file_get_contents("part2.result")),3));
$part3 = hex2bin(substr(trim(file_get_contents("part3.result")),3));
$ret = "";
for ($i=0;$i<strlen($part1);$i++) {
    $ret .= chr(ord($part1[$i])^ord($part2[$i])^ord($part3[$i]));
}
echo $ret;
zobo@BLACKBOX:/mnt/c/temp/day21$ php cnt5.php | xxd
00000000: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000010: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000020: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000030: 0000 0000 0000 0000 0000 0001 007d 0033  .............}.3
00000040: 0033 0072 0074 005f 0073 0061 006d 0078  .3.r.t._.s.a.m.x
00000050: 005f 0045 0048 0054 005f 0044 006e 0075  ._.E.H.T._.D.n.u
00000060: 0030 0052 0041 005f 2019 006e 0031 006b  .0.R.A._ ..n.1.k
00000070: 0063 0030 0072 007b 0073 0061 006d 0078  .c.0.r.{.s.a.m.x

Ohoho! Lets flip it!

00000000: 7800 6d00 6100 7300 7b00 7200 3000 6300  x.m.a.s.{.r.0.c.
00000010: 6b00 3100 6e00 1920 5f00 4100 5200 3000  k.1.n.. _.A.R.0.
00000020: 7500 6e00 4400 5f00 5400 4800 4500 5f00  u.n.D._.T.H.E._.
00000030: 7800 6d00 6100 7300 5f00 7400 7200 3300  x.m.a.s._.t.r.3.
00000040: 3300 7d00 0100 0000 0000 0000 0000 0000  3.}.............
00000050: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000060: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000070: 0000 0000 0000 0000 0000 0000 0000 0000  ................

And to spare you, there was another catch!

zobo@BLACKBOX:/mnt/c/temp/day21$ php cnt5.php | iconv -f utf16 -t utf8
xmas{r0ck1n’_AR0unD_THE_xmas_tr33}

The apostof is some not the regular ASCII one, but one from the UNICODE space. And exactly that had to be submitted.

Flag: xmas{r0ck1n’_AR0unD_THE_xmas_tr33}

What did I learn: It’s usually less complicated than you think and CTFs will use at least some existing standard. I am stuborn beyond what’s good for me.

Recent posts

See more

Categories

About