The Cicada 3301 Mystery (Puzzle 1 - 2nd Chance Solve)
This blog is an extension to Puzzle 1, so spoilers are ahead. Please head back and read Puzzle 1 for this to make any sense.
To refresh the memory. This used to be the homepage of http://845145127.com
including the locations of the very end of puzzle 1.
At one point that page updated and it looked like the puzzle was over. The page was blank and viewing the source left an uninteresting snippet.
Though just blogging about this you can tell something is up. There is far too much white space in this. So we can open up vim
and let :set list
add some visibility to these hidden characters.
So yeah it appears we have a mix of tabs and spaces. So it was time to convert this into a sane format. Using a mix of bash and vim we can get this blob into binary.
vim whitepage.txt
:set list
:set listchars=tab:0x,space:1
Now with the visible 0,1 and x. We can use a quick bash replacement to drop those "x" (Which are just the extended placeholders of a tab).
echo "${STRING//x}"
Which meant we now had a full binary file of the decoded message.
So a simple binary to ASCII and we get:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
162667212858
414974253863
598852142735
876873892385
935691396441
316744223127
427566844663
644169769482
889296759263
963846244281
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.11 (GNU/Linux)
iQIcBAEBAgAGBQJPDRkvAAoJEBgfAeV6NQkPVuMP/3ZyAgwsko/B2T9Ew1yqAKVy
K9//wIWCRvMyZ4k79ApqvOJAlezgHTsAM8XG/I71bAG+2wMOXNJfTj/SFONEEbS5
BOp9UP7LHn1j3NKoESrDzsKd+u3oHoNnhs628aLrc8uDqbn/6DNUnObu5Tn3unu0
zZ3NjSs/A5QQX8O56RsK81eSJ2fifbr4NYfHBeUTeVe17nsr48WQI7qc9UVWlPsM
91FWsvhX+WohX8DyFWJmtz0lLvmh3jN+oE8WFPTVbcVCM+eiDt0TqkUNlmq/fxbd
X2Sbs8zMxDXNQWrw58TcSC6oLfXSXZnjh8uTMwrQ0tNdRXHDndgPiurXz62XjVjf
4AhSXBoXF9CHTOyGGEqvfNvFMKyz968iMZDXDNBrM8pkxx1xBHhAnoEznVpeMhII
+IfBTnV8x9lSNgFhmham5eEZlWvqRidqes8EAriqGA6uZokCq7X1IeMHo52ACWmP
2bJsCV5wZDc52c3JnwKe+cAcbsA4OWCNEH29lAsgFw5079BP8lkpY3AH2+8kqs0X
QvqsaMuUq5ZHEaZMgdD0VKYlRrKdhOiDjtJVxoXk1b7YBOV8dZZBXJbEIbTvaof4
yhgUObovx/VFGmsenp+j3nBCxgEO22SgNW3B3pN0yuIMCqccWEZ1nME/QOwLa85n
HOmElIXvK13Q9m545RtP
=Q1Fy
-----END PGP SIGNATURE-----
This verifies correctly with 3301 key, but strangely doesn't include the sign off of "3301" which every other message had. Was this a mistake?
Looking at these numbers you should be able to spot that these numbers were the image URLs for the beginning of puzzle 1. For example, the first number was where the QR code went for the location in Sydney.
Though what did they mean when all listed together. If you grouped them by the poem they led to. You would get the groupings of:
Agrippa
A poem of fading death, named for a king
Meant to be read only once and vanish
Alas, it could not remain unseen.
644169769482
876873892385
935691396441
963846244281
Encyclopaedia Britannica
In twenty-nine volumes, knowledge was once contained.
How many lines of the code remained when the Mabinogion paused?
Go that far in from the beginning and find my first name.
162667212858
316744223127
414974253863
427566844663
598852142735
889296759263
With the domains thus far involving numbers. It was learned (via irc) that adding the largest number from both groups resulted in 1853143003544
and a new TLD of .tk
was used so http://1853143003544.tk
was the location to head.
This domain didn't have much but a TXT record that said - "Go to my largest part". So parts of numbers probably point to the numbers (factors) that make up that number. If you wanted to dump the factorization of the number in the URL you'd get
1,853,143,003,544
= 2
* 2
* 2
* 7
* 33,091,839,349
So if you tried to visit that domain (http://33091839349.tk
) you'd get an image.
Of course after downloading this image and running "outguess" over it. You'd get:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Miss round 1?
Care for a second chance?
http://i.imgur.com/hkdgl.png
1:22
3:33
3:40
6:19
4:7
4:8
4:7
9:23
12:12
4:16
16:7
11:16
1:7
5:14
3:35
2:2
4:26
1:12
3:11
3:28
4:23
18:18
Good luck.
3301
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.11 (GNU/Linux)
iQIcBAEBAgAGBQJPDTiqAAoJEBgfAeV6NQkPytAP/1UQeylwYBjQWuzr8pbERN04
wlZVCG5SjGanqfQKxSa5Z8YOVR9LRlT6sHBKgfOEDztj+6RM2SvX7Y2Mw9mA4g90
7bizXi8IkP5K3tMvNxcA6uEy0rSchJt4D93zzjPvS7lm6jGO1gJQXrwMUd4fACs1
hxZ3Cxva0MIS+r8MxRJbU/mKbDvnEr6fwUaB6m1vFgYW6wTM4qS2ES04ic+mlkbR
0/eRPnFIBb06XjQpijDAGiisgVORaUUrUJGX+Wnp61CVobBmKwUdw3n0AxbKRC3/
Nja0HYvXexWDiRgxE2YjZyq0Pk4TnZoXxTnyS6NKZ1i1Qxysu4xUfuLO3L6zrnQP
TT4EsqDxzMAKQuJS6qo/pLsdCqyXO1Jr4wIlaG5AGYKMjt9k61Y12WLiqhpLTgzd
AtUTejZzThXqbhmm4wPCVk+Vsik8PLs/Ts6DooB3Ki3AtRtldmeCXdo+mid3Og+A
IKw9+KtB2JDd4a6DbsNbRDIt1pZmyPA3xmmksc8ogx/CEPATPwL7JaN5UwVNSZKM
6MiJ1+b39Kx9ckQ8vSuYa5cALDiUwZPddopl29zbVmKciW+sDGfSiSroXw7khNJs
dzmJ4hR2R7Bbv23Ngxt/M/J5/udOd0AaeDKMxfLNnaLqm8FiaWqnL5TwkzMFnad7
RKpOZq9TkllIE6NBB4wc
=mGcB
-----END PGP SIGNATURE-----
It was weird that the decoded image message also linked another image.
This 2nd image was a PNG so our chance of running outguess over it wouldn't work. Looking at this image though looked like a fiery right and sun sky on the left. Presumably relating to heaven and hell so off to Google search we went.
Now the 2nd image looked like the same type of design, but the pattern didn't match. So it was time to do some research on William Blake's The Marriage of Heaven and Hell book.
Strangely an interesting book with only 9 copies made, but I assume if you are making a custom drawn book in 1789 you don't have a copier for mass production. Thankfully a website existed to view the pages and an extracted copy of the text.
It was plate 4 (The Voice of the Devil) that ended up matching the above image. So now it was time to attempt the book code. Our book code only included 2 numbers, so guessing that is correlates to line number and letter we attempt to decode this.
This turns out to be a minor struggle as nothing appears to be coming out as sane text, but the text adaption online doesn't seem to match the lines from the blurry image. If we take the effort to restructure the text to match the text - our decoding begins to work.
1-22
-c
3-33
-g
3-40
-i
6-19
-n
4-7
-i
4-8
-z
4-7
-i
9-23
-g
12-12
-l
4-16
-y
16-7
-a
11-16
-o
1-7
-b
5-14
-y
3-35
-p
2-2
-h
4-26
-.
1-12
-o
3-11
-n
3:28
-i
4:23
-o
18-18
-n
That spelled out another onion - cginiziglyaobyph.onion
which prompted for an email. At this point in the story we can look towards individuals who leaked these details online - the test of time leaves the random verified signed messages, but its assumed the participants who registered in time got an iteration of the message below.
This message is strange, because its almost like Cicada doesn't want you to share it, but at the same time gives you a hint there is another chance to get your own specific copy. You would only see that though if someone shared a copy or told you about it.
This is one of the only discovered snippets online that properly verifies as a Cicada signature from this stage in this puzzle (that I could find). An interesting point as well as a few years later Cicada begins Puzzle 3 with a similar breaking of a small private key.
This key (112 digits) is thankfully a bit smaller than Puzzle 3 (130 digits), so lets break it. We will use the power of a decade later of technologies and boot up rsacrack docker container that wraps cado-nfs on my own hardware.
ibotpeaches@foundation# time docker run -it b4den/rsacrack 7880891639791153779530258464649693928733411681581577103690292822333198036864739065916344030512697162068582555837
[*] pubkey.e: 65537
[*] pubkey.n: 7880891639791153779530258464649693928733411681581577103690292822333198036864739065916344030512697162068582555837
[*] Key looks like 372 bits
[*] Using cadonfs to compute primes
[*] results are: [u'98007492061325958997349177934627388613835953553459586261', u'80411114232571782218163489375797613948878398942588985417', 65537L]
[*] Key extraction done.
-----BEGIN RSA PRIVATE KEY-----
MIHsAgEAAi8NG6bqKiUXBAidW7CmF+WTr6owpY+MprGwQrOWuRVHb0ko2zdWiiXkZH1Kv31gvQID
AQABAi8F4v06/GJby7vyr1LNxL2dba6I2lF1YQP3C1P+Jz85VYigO/4Am+s2SkB+4BkDAQIYA/8+
91K7m8jMN43hdcKwWiVe54kqoyTVAhgDR4f9CRgCSsFj8Vvilwjw0LfevXiIYEkCGAPbGvIekjHj
y6NiHDcBY+HDuGDQG2gI0wIYALzWdGz/0rb/ydL/oNbpR8aRo4MjE4uHAhgDx+oNUAE2EddY+lDq
EqRxF5D0juT2Kwg=
-----END RSA PRIVATE KEY-----
real 94m12.942s
user 0m0.214s
sys 0m0.115s
94 minutes later we've got ourselves a broken key.
Now that we've found the two primes (q
and p
) - we can write a small program to decrypt the original payload.
use Crypt::RSA;
my $algo = new Crypt::RSA;
my $keychain = new Crypt::RSA::Key;
my ($public, $private) = $keychain->generate(
'q' => '98007492061325958997349177934627388613835953553459586261',
'p' => '80411114232571782218163489375797613948878398942588985417',
'e' => '65537'
);
$encrypted = "
-----BEGIN COMPRESSED RSA ENCRYPTED MESSAGE-----
Version: 1.99
Scheme: Crypt::RSA::ES::OAEP
eJwBzQAy/zEwADE4OABDeXBoZXJ0ZXh0Ca4y//uzl/HvFoP9Klf53nEFH0T4c+ui5de8+vqGOnZc
9DlrsWQe+xxVaPaYgKAD9Wn9VWQ6A5o254r5pa4hkDIY5RRmVfqOm88HJpGdbGGTckyEwJapCLDT
tHzZWAZ0FIVj6fH2whErHoVmZ82zQJ64OLtzr1gYk+2kIZuqtLclV9RDhs6j7meTaod2BDrF26tY
d33awv0txxrgXRhd/FDFVtKb0K84cQs2xtO/9A0yLs5GEK2xpG2yM4AeWWft
=I1r6Wtzf/29vTggAK+ELEA==
-----END COMPRESSED RSA ENCRYPTED MESSAGE-----
";
print $algo->decrypt(
Cyphertext => $encrypted,
Key => $private,
Armour => 1,
);
All that is left is a quick execution to get our decoded message.
➜ ~ perl puzzle1-2ndchance.pl
33521494043430258676
It looks that was the answer to this specific participant's puzzle to be entered on the onion given. Unlucky for some - that page ended up crashing with a 500 Internal Error for a day or so.
Once back and working the correct password led to a successful message from Cicada saying - "Correct. We'll email you." A few days later an email arrived instructing to head back to the specific onion and assigned number.
So taking the specific participant specific number (From Cicada email) to the onion - sq6wmgv2zcsrix6t.onion/{number}
resulted in another longer new message.
Unfortunately for those who decided to leak these communications in the public got a different message.
For those that didn't share their message - the next puzzle looked to be related to music.
Just need to translate our armored (ascii) message back into binary with gpg.
gpg2 --decrypt song.txt > song.bin
One quick file command it was obvious what we were dealing with.
➜ file song.bin
song: Standard MIDI data (format 1) using 3 tracks at 1/96
It was a MIDI data file! Lucky we are running on Ubuntu, because this looks like a job for midicsv to translate this into something more readable. The output is quite large from this nearing almost 3,000 lines so I'll leave a snippet and the full file linked.
➜ midicsv song.bin song.csv
If you take a look at the large decoding - there appears to be four tracks in this file. We can find the two audio tracks that both Audacity and our CSV dump of the file show.
There is data here we want to get which probably has something to do with that chorus and MIDI data information. The lyrics in the beginning of the message hinted at the track which was A Sony of Liberty by William Blake - this was plate 27 in the The Marriage of Heaven and Hell from earlier in this puzzle.
One thing I noticed looking at the more zoomed in Audacity picture is these notes aren't as random as a usual song. This is probably a musical cryptogram - as it was popular in early times to hide messages within a song. Early time cryptograms would match letters to note names, but notes run out around G. I know from just glancing at this output that we have more than 7 characters.
So a simple way to do this to just to assign a consistent character to each consistent note value and brute-force it. Though, with how many possible characters or numbers or special characters - there is probably another way.
There was a hint in the puzzle with - "Let the Chorus be your guide to the depths". Maybe instead of guessing we have a sure fire way to obtain note to character mappings. Though, each track has respectively 1,124 items and 1,436 items compared to our character count of the chorus at 302 characters so we probably only care about the events of each individual note (play/stop).
Though, we need to understand more about the data stream we have - which thanks to the man page for midicsv is quite documented.
So the first few lines of track 1 show:
track, time, event, channel, note, velocity
2, 0, Note_on_c, 1, 63, 96
2, 192, Note_off_c, 1, 63, 0
2, 192, Note_on_c, 1, 61, 96
2, 216, Note_off_c, 1, 61, 0
So to make a mapping between notes and characters. We have to toss out track
, event
and channel
for obvious reasons (they are constant). Velocity
looked promising, but it alternates between 0
and 96
. So that only leaves the columns time
and note
to build a mapping from.
So if we started with the two notes above. It would be
Time
-Note
-Letter
192
-63
-L
216
-61
-e
This doesn't look right because those time entries don't exist in Track 4 and rise to insane numbers. Though, if we subtracted the off time event from on, we could get the total length the note was played. That set then becomes:
Time Played
-Note
-Letter
192
-63
-L
24
-61
-e
This is promising. So we could script this now to read track 3, extract the length of each note along with its literal MIDI note value and proceed 1 character at a time through the chorus. Once we are done with that, we can take those mappings of length/note and use those to solve Track 4.
Time
grows to some insane values so it probably isn't seconds or milliseconds - it must be some unit for MIDI measurement. Unfortunately for us using track 3 to decode track 4 results in nonsense string of characters.
rorrrrrorrrrrorrrrrrrrorrrrrorrrrrorrrrrrrrorrrrrorrrrrorrrrrrr
We probably have to skip characters once used, so once we use the letters l e t
from the chorus - no more l
or e
or t
.
Perhaps our key was wrong. Since it was Time - Note
. Though, even Note - Time
doesn't give anything real. So we must be doing this wrong. No matter how many times we swap Track 3/4 and tweak our alphabet we aren't having much luck. So it took a bit of manually debugging and diffing. If we spell out what we know, then dump out what we don't know. We see a pattern.
We can see a pattern starting that none of our matches ever finds a corresponding Time Length-Note
match unless we subtract 12 from the Length. At that point we get something readable immediately. Still confused on the magic aspect of 12, it must have to do with the tempo or some time signature between the tracks. As you can see The Secret goes up nearly twice as fast with units vs The Chorus, but my knowledge of MIDI is not good enough to dig into that further.
verg,oodgokhaveproventoyejostdedibatedtobojethisfartoattainenli,htenjentbreatea,p,cegforgokrejailaddressandkploadittothejitcegserversthenenbrgptthethefollowin,wordlistksin,thebibadathreethree eroonepkylibcegsi,nitwithgokrcegsendtheasbiiarjokredbipherte ttothe,jailaddressfrojwhibhgokrebeivedgokrnkjyersgokrwordsarejksbledroppkjpcneeyirdwatbhyellyallhajjerpobcethoochokseyracefeathercnottibcetyoatyo joonblokdyklybarria,eyo trags karebkpstationjatbhtoothovenhorsepenbiltragbollartreesbissorsyrkshdraweryribcarjen,inesobckjyrellasheepapplehornhatbhainforcsbrew
This is good, but not great. We can see the start of a phrase but a few characters are just not right. I can tell the commas were probably not expected in this character set. If we remove all non-alphabet characters and output the intended key (instead of an empty character) for mismatches our decoded message becomes:
verygoodyouhaveproventobemostdedicatedtocomethisfartoattainenlightenmentcreateagpgkeyforyouremailaddressanduploadittothemitkeyserversthenencryptthethefollowingwordlistusingthecicadathreethree[44-384]eroonepublickeysignitwithyourkeysendtheasciiarmouredcipherte[55-192]ttothegmailaddressfromwhichyoureceivedyournumbersyourwordsaremuscledroppumpkneebirdwatchbellballhammerpockethookhousebrakefeatherknotticketboatbo[55-192]mooncloudbulbcarriagebo[55-192]trays[43-192]uarecupstationmatchtoothovenhorsepenciltraycollartreescissorsbrushdrawerbrickarmenginesockumbrellasheepapplehornhatchainforkscrew
Still isn't perfect, since we have a few unknown pairs outputted. Though, we might be able to detect the proper value for the unknown pairs from context of the word or sentence.
44-384
-z
- three three zero one55-192
-x
- ciphertext43-192
-q
- square
Very good you have proven to be most dedicated to come this far to attain enlightenment.
Create a GPG key for your email address and upload it to the MIT Key Servers, then encrypt then following word list using the cicada three three zero one public key.
Sign it with your key, send the ASCII armoured ciphertext to the gmail address from which you received your numbers.
Your words are:
muscle drop pump knee bird watch bell ball hammer pocket hook house brake feather knot ticket boat box moon cloud bulb carriage box tray square cup station match tooth oven horse pencil tray collar tree scissors brush drawer brick arm engine sock umbrella sheep apple horn hat chain fork screw
There was probably a much easier way to do this versus writing PHP code, but alas the source.
<?php
$chorus = <<<TEXT
Let the Priests of the Raven of dawn, no longer in deadly black, with hoarse note curse the sons of joy. Nor his accepted brethren, whom tyrant, he calls free: lay the bound or build the roof. Nor pale religious letchery call that virginity, that wishes but acts not!
For every thing that lives is Holy.
TEXT;
// Only keep the alphabet
$chorus = Str::lower(preg_replace("/[^A-Za-z]/", '', $chorus));
$midiFilePath = storage_path('song.csv');
$csv = Reader::createFromPath($midiFilePath, 'r');
[$track1, $track2, $track3, $track4] = collect($csv->getRecords())->groupBy('0');
$mapping = [];
$index = 0;
$onNoteCache = [];
$usedLetters = [];
$track4 = $track4->filter(function (array $record): bool {
$key = trim(Arr::get($record, '2'));
return $key === 'Note_off_c' || $key === 'Note_on_c';
})->each(function (array $record) use (&$onNoteCache, &$mapping, &$index, &$usedLetters, $chorus) {
$key = trim(Arr::get($record, '2'));
// Store this note for the next "off" event.
if ($key === 'Note_on_c') {
$onNoteCache = $record;
return;
}
$length = Arr::get($record, '1') - Arr::get($onNoteCache, '1');
$mappingKey = trim(Arr::get($record, '4') . '-' . $length);
if (!Arr::has($mapping, $mappingKey)) {
$character = $chorus[$index++];
while (in_array($character, $usedLetters)) {
$character = $chorus[$index++];
}
$usedLetters[] = $character;
$mapping[$mappingKey] = $character ?? null;
}
});
$decodedMessage = '';
$track3 = $track3->filter(function (array $record): bool {
$key = trim(Arr::get($record, '2'));
return $key === 'Note_off_c' || $key === 'Note_on_c';
})->each(function (array $record) use (&$onNoteCache, &$mapping, &$decodedMessage) {
$key = trim(Arr::get($record, '2'));
// Store this note for the next "off" event.
if ($key === 'Note_on_c') {
$onNoteCache = $record;
return;
}
$length = Arr::get($record, '1') - Arr::get($onNoteCache, '1');
$mappingKey = trim((Arr::get($record, '4') - 12) . '-' . $length);
if (Arr::has($mapping, $mappingKey)) {
$decodedMessage .= $mapping[$mappingKey];
} else {
$decodedMessage .= "[{$mappingKey}]";
}
});
$this->info($decodedMessage);
- It uses Laravel Collections & Helpers and The PHP League/CSV package
At this point these words were signed with the Cicada GPG key and sent in an email to Cicada. The public/private key exchange was almost complete with the participant uploading their own to MIT Key Servers for Cicada to retrieve. Unlucky for the participant messages above - we won't find a key for the email. Presumably because this participant shared their message - not solve it. At least we got the chance to solve it 10 years later.
With that the puzzle went highly unverified. Those who got past challenge after challenge grouped together with like minded individuals to solve whatever came next. This ended with a username/password onion where it was presumed all those who solved it joined together for something that remains unknown.
Note - All signed messages were found via Pastebin search and crossed checked against Cicada known key to ensure validity.
- Puzzle 1 (2012)
- Puzzle 1 - 2nd Chance (2012) - You are here
- Puzzle 2 (2013)
- Puzzle 3 - Part 1 (2014)
- Puzzle 3 - Part 2 (2014)
- Puzzle 3 - Part 3 (2014)