Scavenger Hunt Solution
Published on , 1976 words, 8 minutes to read
On November 22, I sent a tweet that contained the following text:
#467662 #207768 #7A7A6C #6B2061 #6F6C20 #6D7079
#7A6120 #616C7A #612E20 #5A6C6C #206F61 #61773A
#2F2F6A #6C6168 #6A6C68 #752E6A #736269 #2F6462
#796675 #612E6E #747020 #6D7679 #207476 #796C20
#70756D #767974 #686170 #76752E
This was actually the first part of a scavenger hunt/mini CTF that I had set up in order to see who went down the rabbit hole to solve it. I've had nearly a dozen people report back to me telling that they solved all of the puzzles and nearly all of them said they had a lot of fun. Here's how to solve each of the layers of the solution and how I created them.
Layer 1
The first layer was that encoded tweet. If you notice, everything in it is
formatted as HTML color codes. HTML color codes just so happen to be encoded in
hexadecimal. Looking at the codes you can see 20
come up a lot, which happens
to be the hex-encoded symbol for the spacebar. So, let's turn this into a
continuous hex string with s/#//g
and s/ //g
:
If you've seen a %20
in a URL before, that is the URL encoded form of the
spacebar!
4676622077687A7A6C6B20616F6C206D7079
7A6120616C7A612E205A6C6C206F6161773A
2F2F6A6C61686A6C68752E6A7362692F6462
796675612E6E7470206D7679207476796C20
70756D76797468617076752E
And then turn it into an ASCII string:
Fvb whzzlk aol mpyza alza. Zll oaaw://jlahjlhu.jsbi/dbyfua.ntp mvy tvyl pumvythapvu.
Wait, what? this doesn't look like much of anything...wait, look at the
oaaw://
. Could that be http://
?
Indeed it is my perceptive shark friend! Let's decode the rest of the string using the Caeser Cipher:
You passed the first test. See http://cetacean.club/wurynt.gmi for more information.
Now we're onto something!
Layer 2
Opening http://cetacean.club/wurynt.gmi we see the following:
wurynt
a father of modern computing,
rejected by his kin,
for an unintentional sin,
creator of a machine to break
the cipher that this message is encoded inbq cr di ej kw mt os px uz gh
VI 1 1 I 17 1 III 12 1
qghja xmbzc fmqsb vcpzc zosah tmmho whyph lvnjj mpdkf gbsjl tnxqf ktqia mwogp eidny awoxj ggjqz mbrcm tkmyd fogzt sqkga udmbw nmkhp jppqs xerqq gdsle zfxmq yfdfj kuauk nefdc jkwrs cirut wevji pumqt hrxjr sfioj nbcrc nvxny vrphc r
Correction for the last bit
gilmb egdcr sowab igtyq pbzgv gmlsq udftc mzhqz exbmx zaxth isghc hukhc zlrrk cixhb isokt vftwy rfdyl qenxa nljca kyoej wnbpf uprgc igywv qzuud hrxzw gnhuz kclku hefzk xtdpk tfjzu byfyi sqmel gweou acwsi ptpwv drhor ahcqd kpzde lguqt wutvk nqprx gmiad dfdcm dpiwb twegt hjzdf vbkwa qskmf osjtk tcxle mkbnv iqdbe oejsx lgqc
Hmm, "a father of computing", "rejected by his kin", "an unintentional sin", "creator of a machine to break a cipher" could that mean Alan Turing? He made something to break the Enigma cipher and was rejected by the British government for being gay right?
Indeed. Let's punch these settings into an online enigma machine and see what we get:
congr adula tions forfi gurin goutt hisen igmao famys teryy ouhav egott enfar
thert hanan yonee lseha sbefo rehel pmebr eakfr eefol lowth ewhit erabb ittom
araht tpyvz vgjiu ztkhf uhvjq roybx dswzz caiaq kgesk hutvx iplwa donio n
httpc olons lashs lashw hyvec torze dgamm ajayi ndigo ultra zedfi vetan gokil
ohalo fineu ltrah alove ctorj ayqui etrho omega yotta betax raysi xdonu tseve
nsupe rwhyz edzed canad aasia indig oasia twoqu ietki logam maeps ilons uperk
iloha loult rafou rtang ovect orsev ensix xrayi ndigo place limaw hyasi adelt
adoto nion
And here is where I messed up with this challenge. Enigma doesn't handle
numbers. It was designed to encode the 26 letters of the Latin alphabet. If you
look at the last bit of the output you can see onio n
and o nion
. This
points you to a Tor hidden
service, but because
I messed this up the two hints point you at slightly wrong onion addresses (tor
hidden service addresses usually have numbers in them). Once I realized this, I
made a correction that just gives away the solution so people could move on to
the next step.
Onwards to http://yvzvgjiuz5tkhfuhvjqroybx6d7swzzcaia2qkgeskhu4tv76xiplwad.onion/!
Layer 3
Open your tor browser and punch in the onion URL. You should get a page that looks like this:
This shows some confusing combinations of letters and some hexadecimal text. We'll get back to the hexadecimal text in a moment, but let's take a closer look at the letters. There is a hint here to search the plover dictionary. Plover is a tool that allows hobbyists to learn stenography to type at the rate of human speech. My moonlander has a layer for typing out stenography strokes, so let's enable it and type them out:
Follow the white rabbit
Go to/test. w a s m
Which we can reinterpret as:
Follow the white rabbit
Go to /test.wasm
The joke here is that many people seem to get stenography and steganography confused, so that's why there's stenography in this steganography challenge!
Going to /test.wasm we get a WebAssembly download. I've uploaded a copy to my blog's CDN here.
Layer 4
Going back to that hexadecimal text from above, we see that it says this:
go get tulpa.dev/cadey/hlang
This points to the source repo of hlang, which is
a satirical "programming language" that can only print the letter h
(or the
lojbanic h '
for that sweet sweet internationalisation cred). Something odd
about hlang is that it uses WebAssembly to execute
all programs written in it (this helps it reach its "no sandboxing required" and
"zero* dependencies" goals).
Let's decompile this WebAssembly file with
wasm2wat
$ wasm2wat /data/test.wasm
<output too big, see https://git.io/Jkyli>
Looking at the decompilation we can see that it imports a host function h.h
as
the hlang documentation suggests and then constantly calls it a bunch of times:
(module
(type (;0;) (func (param i32)))
(type (;1;) (func))
(import "h" "h" (func (;0;) (type 0)))
(func (;1;) (type 1)
i32.const 121
call 0
i32.const 111
call 0
i32.const 117
call 0
; ...
There's a lot of 32
in the output. 32
is the base 10 version of 0x20
,
which is the space character in ASCII. Let's try to reformat the numbers to
ascii characters and see what we get:
you made it, this is the end of the line however. writing all of this up takes a lot of time. if you made it this far, email me@christine.website to get your name entered into the hall of heroes. be well.
How I Implemented This
Each layer was designed independently and then I started building them together later.
One of the first steps was to create the website for Mara's Realm. I started by
writing out all of the prose into a file called index.md
and then I ran
sw using Pandoc for
markdown conversion.
Then I created the WebAssembly binary by locally hacking a copy of hlang to
allow arbitrary strings. I stuck it in the source directory for the website and
told sw
to not try and render it as markdown.
Once I had the HTML source, I copied it to a machine on my network at
/srv/http/marahunt
using this command:
$ rsync \
-avz \
site.static/ \
root@192.168.0.127:/srv/http/marahunt
And then I created a tor hidden service using the services.tor.hiddenServices options:
services.tor = {
enable = true;
hiddenServices = {
"hunt" = {
name = "hunt";
version = 3;
map = [{
port = 80;
toPort = 80;
}];
};
};
};
Once I pushed this config to that server, I grabbed the hostname from
/var/lib/tor/onion/hunt/hostname
and set up an nginx virtualhost:
services.nginx = {
virtualHosts."yvzvgjiuz5tkhfuhvjqroybx6d7swzzcaia2qkgeskhu4tv76xiplwad.onion" =
{
root = "/srv/http/marahunt";
};
};
And then I pushed the config again and tested it with curl:
$ curl -H "Host: yvzvgjiuz5tkhfuhvjqroybx6d7swzzcaia2qkgeskhu4tv76xiplwad.onion" http://127.0.0.1 | grep title
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 3043 100 3043 0 0 2971k 0 --:--:-- --:--:-- --:--:-- 2971k
<title>Mara's Realm</title>
.headerSubtitle { font-size: 0.6em; font-weight: normal; margin-left: 1em; }
<a href="index.html">Mara's Realm</a> <span class="headerSubtitle">sh0rk in the cloud</span>
Once I was satisfied with the HTML, I opened up an enigma encoder and started writing out the message congradulating the user for figuring out "this enigma of a mystery". I also included the onion URL (with the above mistake) in that message.
Then I started writing the wurynt page on my gemini server. wurynt was coined by blindly pressing 6 keys on my keyboard. I added a little poem about Alan Turing to give a hint that this was an enigma cipher and then copied the Enigma settings on the page just in case. It turned out that I was using the default settings for the Cryptee Enigma simulator, so this was not needed; however it was probably better to include them regardless.
This is where I messed up as I mentioned earlier. Once I realized my mistake in trying to encode the onion address twice, I decided it would be best to just give away the answer on the page, so I added the correct onion URL to the end of the enigma message so that it wouldn't break flow for people.
The final part was to write and encode the message that I would tweet out. I opened a scratch buffer and wrote out the "You passed the first test" line and then encoded it using the ceasar cipher and encoded the result of that into hex. After a lot of rejiggering and rewriting to make it have a multiple of 3 characters of text, I reformatted it as HTML color codes and tweeted it without context.
Feedback I Got
Some of the emails and twitter DM's I got had some useful and amusing feedback. Here's some of my favorites:
my favourite part was the opportunity to go down different various rabbit holes (I got to learn about stenography and WASM, which I'd never looked into!)
I want to sleep. It's 2 AM here, but a friend sent me the link an hour ago and I'm a cat, so the curiosity killed me.
That was a fun little game. Thanks for putting it together.
oh noooo this is going to nerd snipe me
I'm amused that you left the online enigma emulator on default settings.
I swear to god I'm gonna beach your orca ass
Improvements For Next Time
Next time I'd like to try and branch out from just using ascii. I'd like to throw other encodings into the game (maybe even have a stage written in EBCDIC formatted Esperanto or something crazy like that). I was also considering having some public/private key crypto in the mix to stretch people's skillsets.
Something I will definitely do next time is make sure that all of the layers are solveable. I really messed up with the enigma step and I had to unblock people by DMing them the answer. Always make sure your puzzles can be solved.
Hall of Heroes
(in no particular order)
- Saphire Lattice
- Open Skies
- Tralomine
- AstroSnail
- Dominika
- pbardera
- Max Hollman
- Vojtěch
- [object Object]
- Bytewave
Thank you for solving this! I'm happy this turned out so successfully. More to come in the future.
🙂
Facts and circumstances may have changed since publication. Please contact me before jumping to conclusions if something seems wrong or unclear.
Tags: ctf, wasm, steganography, stenography