A while ago, I took part in a competition called 'CyberStart', which is a gamified introductory challenge site for young people to spark an interest in a career in computer security.
I was then emailed (on account of my
exceptional performance ??) with an invite-only challenge which is accessible at the URL valhalla.joincyberdiscovery.com.
So, let's take a crack at it. It might be hard, but hey, it's for kids.
curl 'https://valhalla.joincyberdiscovery.com/' -o valhalla head valhalla ᚿᛔᚵᚬᚱᛀᚤᚡᚠᚰᚠᚠᚠᚠᚠᚠᚠᚠᚠᚠᚠᚠᚬᚠᚠᛐᚠᚡᚠᚠᚠᚠᛀᚡᚤᚠᚠ...
Oh, these are characters from the Runic block. I get why the challenge is called Valhalla now.
Research & Analysis
The Runic Unicode block can contain 96 code points, but only 89 are assigned.1
Let's mess around with the file that we've been given.
# With the downloaded 'valhalla' file in the working directory: with open("valhalla") as f: valhalla = f.read() runic_block = range(0x16A0, 0x16FF + 1) valhalla = "".join(c for c in valhalla if ord(c) in runic_block) min(valhalla) '\u16a0' max(valhalla) '\u16e0'
Hm, it looks like our lowest and highest used codepoints in the Runic block are
0x40 apart. How about base 64?
First, we translate from the Runic characters to Python's standard base64 alphabet:
# In the same context as we defined 'valhalla' import string standard_b64 = string.ascii_uppercase + string.ascii_lowercase + string.digits + "+/=" runic_b64 = "".join(map(chr, range(0x16a0, 0x16e0 + 1))) # Both standard_b64 and runic_b64 are 65 characters long. def translate_to_b64(s): translation_table = str.maketrans(runic_b64, standard_b64) return s.translate(translation_table) valhalla_b64 = translate_to_b64(valhalla) valhalla_b64[:16] 'f0VMRgEBAQAAAAAA'
That's a lot of consecutive
As, that'd be a bunch of
0x0 bytes in base64. It looks like we're on the right track.
So, we try decoding
valhalla_b64 using Python's built-in
>>> # Same context, again. >>> import base64 >>> valhalla_bytes = base64.standard_b64decode(valhalla_b64) >>> >>> # Maybe it's text? >>> valhalla_bytes.decode("utf-8") UnicodeDecodeError: 'utf-8' codec can't decode byte 0x80 in position 24: invalid start byte >>> # Ah. Let's just write it out then. >>> with open("valhalla.out", "wb") as f: ... f.write(valhalla_bytes) 14164 >>> # Neat, that's how many bytes we wrote out. >>> exit()
Alright, we now have a file,
valhalla.out. Let's take a look at it:
file ./valhalla.out valhalla.out: ELF 32-bit LSB pie executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, BuildID[sha1]=1e5f459df47978fe95f7857cad7afeccde0143d6, not stripped
Alright, we have a 32-bit Linux executable. Since I don't have
multilib on my Arch install I'm going to start a Docker container to have a look at this.
base64 valhalla.out | xclip -selection clipboard docker run -it 32bit/ubuntu:16.04 bash ... root@some-container:/# cat | base64 -d > valhalla.out [paste and hit ctrl-d] chmod +x valhalla.out ./valhalla.out Are you Elite? Not yet
Alright, we got it running, but it won't give us any useful information yet. Let's load it up in
$ apt install radare2 $ r2 valhalla.out [0x00001180]> aaa
Patching the binary is really easy,
sym._start has a jump to
sym.exit which is always fulfilled, we reverse the condition from
jz and get our solution:
[0x00001180]> V (in visual mode, swap the 'jnz sym.exit' for a 'jz sym.exit')
./valhalla.out Are you Elite? Yes you are! Flag: Badge_Of_Honour_Please