xesite/blog/scavenger-hunt-solution-202...

12 KiB

title date tags
Scavenger Hunt Solution 2020-11-25
ctf
wasm
steganography
stenography

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 in

bq 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:

Mara'sRealm

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.

🙂