4 minutes
Longbottom’s Locker
Note to fellow-HTBers: Only write-ups of retired HTB machines or challenges are allowed.
Challenge info ¶
Longbottom’s Locker [by felli0t]
Break into Longbottom’s vault and steal his secrets.
The challenge ¶
We start of by downloading the Longbottom’s_Locker.zip file and verifying it’s sha256sum with the hash displayed on the challenge page.
$ echo "5951f11e77b70c6a2e7130ee638bdec53e789df6a95de6782a66fbf32b841f94 Longbottom's_Locker.zip" | sha256sum -c -
Longbottom's_Locker.zip: OK
We then proceed to unzip this file using the password provided on the challenge page.
$ unzip Longbottom\'s_Locker.zip
Archive: Longbottom's_Locker.zip
[Longbottom's_Locker.zip] index.html password:
inflating: index.html
inflating: neville.gif
inflating: socute.jpg
Let’s open the index.html in the browser to see what it contains.

It looks like we need to find a password.
Looking at the source code, we see the web application uses CryptoJS and has an encrypted messages which is encrypted using HMAC-SHA256.
To bruteforce this, you’d have to find the SHA256 hash that results in a correct decryptedHMAC and then bruteforce this hash to find the correct passphrase for the decryption to succeed. This won’t be feasible.
<script>/**
* Crypto JS 3.1.9-1
* Copied as is from https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/crypto-js.js
*/
[ ... ]
</script>
<script>
document.getElementById('neville-locker-form').addEventListener('submit', function(e) {
e.preventDefault();
var passphrase = document.getElementById('passwd').value,
encryptedMsg = '4cce4470203e10b395ab1787a22553a5b2503d42a965da813676d929cc16f76cU2FsdGVkX19FvUyhqWoQKHXNLBL64g8acK4UQoP6XZQ/n4MRL3rgQj8TJ/3r8Awtxte2V9s+RLfQHJOHGwYtctqRa/H2BetmxjwGG+LYKUWC8Z6WBoYbecwtATCOuwewnp+VKBzsWLme+3BZyRgKEA==',
encryptedHMAC = encryptedMsg.substring(0, 64),
encryptedHTML = encryptedMsg.substring(64),
decryptedHMAC = CryptoJS.HmacSHA256(encryptedHTML, CryptoJS.SHA256(passphrase).toString()).toString();
if (decryptedHMAC !== encryptedHMAC) {
alert('Bad passphrase!');
return;
}
var plainHTML = CryptoJS.AES.decrypt(encryptedHTML, passphrase).toString(CryptoJS.enc.Utf8);
document.write(plainHTML);
document.close();
});
</script>
Let’s go back a bit and check what the zip-file contains.
Note that there’s another image file in the zip file, which isn’t used in the web application (it’s there but commented out).

Running strings on the image returns something interesting, it appears there’s some data hidden in the image.
$ strings socute.jpg
[ ... ]
donotshareUX
[ ... ]
__MACOSX/._donotshareUX
[ ... ]
Let’s run binwalk to see if there’s anything hidden in the JPG file. And it appears there’s a zip hidden inside the image.
$ binwalk -e socute.jpg
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 JPEG image data, JFIF standard 1.01
97465 0x17CB9 Zip archive data, at least v2.0 to extract, name: donotshare
99087 0x1830F Zip archive data, at least v1.0 to extract, name: __MACOSX/
99142 0x18346 Zip archive data, at least v2.0 to extract, name: __MACOSX/._donotshare
99574 0x184F6 End of Zip archive, footer length: 22
Thanks to the -e
parameter, the zip and its contents are extracted to a folder named _socute.jpg.extracted
.
Looking at the donotshare file, we see a lot of random strings.
$ cd _socute.jpg.extracted/
$ head donotshare
(lp1
(lp2
(S' '
I163
tp3
aa(lp4
(S' '
I1
tp5
a(S'.'
I got a bit confused by this part, but entering one of these lines in a search engine led me to a Python challenge on Github.
I downloaded the script that belonged to this challenge, modified the filename inside (instead of renaming donotshare to the filename used in the script) and ran it.
To properly see the result of this script, you might need to configure your terminal to use a smaller font size. I was lucky and it immediately fit my screen.
$ python pickling.py
.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

Getting the flag ¶
We go back to the web application and enter this text as the passphrase. Pay attention to o / O / 0.
Gu1d0-v4N-R055Um
This passphrase is accepted and we get an alert containing the flag :)

HTB{n3v1LL3_Da_burM3s3-pyth0n_sL4y3r}