With a simple google search query "Queen cryptography" we find this image. After the decoding we get HTBRR THEBABINGTONPLT
with a bit of formatting the flag is HTB{THEBABINGTONPLOT}
.
The zip contains one folder for each letter. Some folders contain numbers, but all files have 0 byte length.
Our first guess was the numbers might be indices where the letter appears in the flag. We quickly set up a large bash command which finds all files, converts the format to <number> <letter>
sort these entries numerically, strip away the newlines and spaces and decode the result from base64. This successfully prints the flag.
find . \
| grep '/./' \
| tr '/' ' ' \
| awk '{ print $3 " " $2; }' \
| sort --numeric-sort \
| grep -Eo ' .' \
| tr -d '\n' \
| tr -d ' ' \
| base64 --decode
The goal is to be able to find a login to a given website. The only program code seems to be in Canvas.zip/js/login.js
. The code is heavily obfuscated. Many of the strings are encoded using '\x71'
instead of 'q'
.
The last line seems to call String.fromCharCode
which is often used to hide texts from being easily readable:
res=String['\x66\x72\x6f\x6d\x43\x68\x61\x72\x43\x6f\x64\x65'](0x48,0x54,0x42,0x7b,0x57,0x33,0x4c,0x63,0x30,0x6d,0x33,0x5f,0x37,0x30,0x5f,0x4a,0x34,0x56,0x34,0x35,0x43,0x52,0x31,0x70,0x37,0x5f,0x64,0x33,0x30,0x62,0x46,0x75,0x35,0x43,0x34,0x37,0x31,0x30,0x4e,0x7d,0xa);
which es equivalent to:
res = String.fromCharCode(0x48,0x54,0x42,0x7b,0x57,0x33,0x4c,0x63,0x30,0x6d,0x33,0x5f,0x37,0x30,0x5f,0x4a,0x34,0x56,0x34,0x35,0x43,0x52,0x31,0x70,0x37,0x5f,0x64,0x33,0x30,0x62,0x46,0x75,0x35,0x43,0x34,0x37,0x31,0x30,0x4e,0x7d,0xa);
which again es equivalent to the flag:
res = "HTB{W3Lc0m3_70_J4V45CR1p7_d30bFu5C4710N}\n";
We get a .zip file, binwalk -e archive.zip
extracts a image of Stefan Hawking, which in turn has a flag.txt that can be extracted steghide extract -sf hawking
with the password hawking
. We get a base64 string the can be easily decoded with "form base64" and "Rot 14" CyberChef 🎜
We get a zip file with a password, fcrackzip shows us that the password is the filename of the unzipped file. With this information we can write a little script to automate the process.
unzzip() {
zipfile="$1"
next="$(unzip -Z1 "$zipfile" | head -n1)"
if echo "$next" | grep "\.zip$"; then
unzip -P "${next%%.*}" "$zipfile"
unzzip "$next"
fi
}
unzzip "37366.zip"
The last file is 6969.zip
it seems to have different password, so we use fcrackzip -b 6969.zip -u -D -p ./rockyou.txt -v
to find the new password letmeinplease
. This reveals a SQLLite database with songs, strings DoNotTouch | grep "HTB"
gives us the flag.
We get two images and a html file which asks for a password. Sadly at first glance the password check seems well designed with no obvious flaws which could be attacked. Even more sadly though the password is not Mimbulus mimbletonia. Clearly the two image files have to contain a hint to the password.
Running binwalk
on the included socute.jpg
reveals a hidden file called donotshare
. It contains a pickle serialized python object. Using the following snippet it can be decoded:
fd = open("donotshare", "rb")
x = pickle.load(fd)
print(x)
x consists of two layer nested arrays including tuples each with a character and a number:
[[(' ', 163)], [(' ', 1), ('.', 1), ('d', 1), ('8', 4), ('b', 1), ('.', 1), (' ', 12), ('d', 1), ('8', 3), (' ', 7), ('8', 3), (' ', 2), ('.', 1), ('d', 1), ('8', 4), ('b', 1), ('.', 1), (' ', 22), ('d', 1), ('8', 4), (' ', 2), ('8', 3), ('b', 1), (' ', 4), ('8', 3), (' ', 8), ('8', 7), ('b', 1), ('.', 1), (' ', 3), ('.', 1), ('d', 1), ('8', 4), ('b', 1), ('.', 1), (' ', 2), ('8', 9), (' ', 2), ('8', 9), (' ', 2), ('8', 3), (' ', 5), ('8', 3), (' ', 15)],
# and so on ...
Our first attempt to use the same algorithm as for the misDIRection challenge was unsuccessful. Instead each entry in the outer list corresponds to one line in the output. For each tuple in the inner lists the number represents how often the character has to be repeated. Running the following line reconstructs the full message, an ASCII art text:
>>> print(*[*map(lambda l: "".join([*map(lambda k: k[1] * k[0], l)]), x)], sep="\n")
.d8888b. d888 888 .d8888b. d8888 888b 888 8888888b. .d8888b. 888888888 888888888 888 888
d88P Y88b d8888 888 d88P Y88b d8P888 8888b 888 888 Y88b d88P Y88b 888 888 888 888
888 888 888 888 888 888 d8P 888 88888b 888 888 888 888 888 888 888 888 888
888 888 888 888 .d88888 888 888 888 888 d8P 888 888Y88b 888 888 d88P 888 888 8888888b. 8888888b. 888 888 88888b.d88b.
888 88888 888 888 888 d88" 888 888 888 888 888 d88 888 888 Y88b888 8888888P" 888 888 "Y88b "Y88b 888 888 888 "888 "88b
888 888 888 888 888 888 888 888 888 888888 Y88 88P 8888888888 888 Y88888 888888 888 T88b 888 888 888 888 888 888 888 888 888
Y88b d88P Y88b 888 888 Y88b 888 Y88b d88P Y8bd8P 888 888 Y8888 888 T88b Y88b d88P Y88b d88P Y88b d88P Y88b. .d88P 888 888 888
"Y8888P88 "Y88888 8888888 "Y88888 "Y8888P" Y88P 888 888 Y888 888 T88b "Y8888P" "Y8888P" "Y8888P" "Y88888P" 888 888 888
This password can be fed into the html page from the original zip to retrieve the flag.
We get a txt file, a start rule an end rule and a hint to a 1-byte key length XOR encryption.
By examining the first 15 lines of the given txt file we can see the first number gives the source state, the middle number the value and the third value the target state. We thus simply have to parse all state transitions, start with the given start state 69420
and end when we reach state 999
, you can find our simple python solution below.
Out of this we get a list of bytes which we output hex encoded so we can easily pass it to CyberChef 🎜 and use its builtin XOR key breaking method to brute force the XOR key.
Afterwards we can use the found key 69
to decode the full message, again using CyberChef 🎜
from binascii import hexlify
rules = {}
with open("deterministic.txt", "r") as fd:
for line in fd.readlines():
try:
src, val, target = map(int, line.split(" "))
rules[src] = (target, val)
except:
pass # just ignore invalid lines lol
curr = 69420
values = []
while curr != 999:
curr, val = rules[curr]
values.append(val)
print(hexlify(bytearray(values)))
The downloaded file contains an image with a morse code and a zip file flag_999.zip
. The morse code decodes to 9
which is the password of the given zipfile.
This zip again has a morse code encoded zip file flag_998.zip
, you can see where this is going.
Thus we have to write a script which decodes the morse and unzips the file recusively. Here is our solution which unpacks all 1000 zip files until finally we extract a file called flag
.
import os
import cv2
import subprocess
# src: https://gist.github.com/mohayonao/094c71af14fe4791c5dd
char2morse = {
"0": "-----",
"1": ".----",
"2": "..---",
"3": "...--",
"4": "....-",
"5": ".....",
"6": "-....",
"7": "--...",
"8": "---..",
"9": "----.",
"a": ".-",
"b": "-...",
"c": "-.-.",
"d": "-..",
"e": ".",
"f": "..-.",
"g": "--.",
"h": "....",
"i": "..",
"j": ".---",
"k": "-.-",
"l": ".-..",
"m": "--",
"n": "-.",
"o": "---",
"p": ".--.",
"q": "--.-",
"r": ".-.",
"s": "...",
"t": "-",
"u": "..-",
"v": "...-",
"w": ".--",
"x": "-..-",
"y": "-.--",
"z": "--..",
".": ".-.-.-",
",": "--..--",
"?": "..--..",
"!": "-.-.--",
"-": "-....-",
"/": "-..-.",
"@": ".--.-.",
"(": "-.--.",
")": "-.--.-"
}
morse2char = {}
for key in char2morse:
morse2char[char2morse[key]] = key
def list_files_in_zip(filename):
output = subprocess.check_output(['unzip', '-l', filename])
lines = output.decode().split("\n")[3 : -3]
files = list(map(lambda x: x.split(" ")[-1], lines))
return files
def unzip_files(filename, password):
os.system(f"unzip -P {password} -o {filename}")
def read_morse(filename):
img = cv2.imread(filename, cv2.IMREAD_COLOR)
b, r, g = img[1][1]
height, width, channel = img.shape
result = ""
for y in range(1, height, 2):
morse = ""
wasHigh = False
highLength = 0
for x in range(1, width):
if b == img[y][x][0] and r == img[y][x][1] and g == img[y][x][2]:
if not wasHigh:
wasHigh = True
highLength = 0
highLength += 1
elif wasHigh:
wasHigh = False
if highLength == 1:
morse += '.'
else:
morse += '-'
if morse in morse2char:
result += morse2char[morse]
else:
print("WARNING: unknown morse sequence", morse)
return result
pw = "hackthebox"
zipfile = "M0rsarchive.zip"
imagefile = "pwd.png"
if __name__ == "__main__":
while zipfile:
print(zipfile, imagefile, pw)
files = list_files_in_zip(zipfile)
unzip_files(zipfile, pw)
print(files)
zipfile = list(filter(lambda x: x.endswith(".zip"), files))[0]
imagefile = list(filter(lambda x: x.endswith(".png"), files))[0]
pw = read_morse(imagefile)