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.

Longbottom’s Locker requires a password

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).

socute.jpg

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 
                                                                    
A screenshot of the terminal output

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

Longbottom’s Locker - Alert
HTB{n3v1LL3_Da_burM3s3-pyth0n_sL4y3r}