越来越懒了,这次复现一下上月爆出的c3p0数据库连接池XXE的漏洞,跟了一下,主要还是对用户自定义的XML文件没进行任何检查。还是有点鸡肋的漏洞,但是怎么使用还是要看各位操作吧。
受影响版本:<=0.9.5.2
用过c3p0数据库连接池的朋友们都知道,c3p0这个库读的默认配置文件是 c3p0-config.xml, 由于开发者可以任意编辑c3p0-config.xml,且c3p0源码中直接解析了xml文档导致了这个漏洞的产生。
构造一下环境:
dbUtil.java:
package com.test.util;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
public class dbUtils {
private static DataSource dataSource = null;
static {
dataSource = new ComboPooledDataSource("mysql");
}
/**
* 返回连接
* @return
*/
public static Connection getConnection(){
Connection conn=null;
try {
conn = dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
public static void closeConn(Connection conn){
try {
if(conn!=null && conn.isClosed()){
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
test.java
package com.test.junit;
import com.test.util.dbUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import static org.junit.Assert.*;
public class c3p0Test {
@Test
public void test(){
Connection conn = dbUtils.getConnection();
try {
PreparedStatement stmt= conn.prepareStatement("select user() ");
ResultSet rs=stmt.executeQuery();
while(rs.next()){
System.out.println(rs.getString(1));
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
c3p0-config.xml
在c3p0-config声明之前插入poc.
然后测试test.java
我们根据cve的提示找到漏洞触发点
c3p0 0.9.5.2 allows XXE in extractXmlConfigFromInputStream in com/mchange/v2/c3p0/cfg/C3P0ConfigXmlUtils.java during initialization.
学习过Java的朋友都知道,Java解析xml常用的有四种方法:DOM,SAX,JDOM,DOM4J,而C3P0数据库连接池采用的DOM,而且在源码中也能看到
默认读取的配置文件是c3p0-config.xml
知道了产生的地方,现在我们一步一步来跟进漏洞产生的步骤,在初始化数据库连接池库打下断点。
进入Debug模式。
跟到 AbstractComboPooledDataSource 方法中
再跟进,此处调用了AbstractPoolBackedDataSource类中的构造器
跟进,调用了 PoolBackedDataSourceBase 类中的 PoolBackedDataSourceBase方法
但是值得注意的是,在执行这个方法之前,PoolBackedDataSourceBase类会初始化一些东西
我们需要关注的是dataSourceName这个变量,跟进initializeStringPropertyVar这个方法看下详细内容。
发现并不重要,但是往上看,会发现 C3P0Config这个类,有个静态代码块,初始化的时候加载了一些东西
继续跟进一下findLibraryC3P0Config方法
从代码中可以看到cfgFinder是调用了 C3P0ConfigFinder接口的findConfig方法
搜索一下,我们可以看到C3P0ConfigFinder接口只有一个实现类 DefaultC3P0ConfigFinder
跟进方法,可以看到XML的调用趋势了
调用了开头说的C3P0ConfigXmlUtils类中的另外一个方法中,跟进extractXmlConfigFromDefaultResource方法。
这里们可以看到此处调用了最终存在缺陷的函数,我们可以从代码中看到
is = C3P0ConfigUtils.class.getResourceAsStream(XML_CONFIG_RSRC_PATH);
我们可以在此类的头部看到XML_CONFIG_RSRC_PATH的初始化变量为c3p0-config.xml
代码中将is复制后,将is作为实参传入了 extractXmlConfigFromInputStream 方法中
is = C3P0ConfigUtils.class.getResourceAsStream(XML_CONFIG_RSRC_PATH);
if ( is == null )
{
warnCommonXmlConfigResourceMisspellings();
return null;
}
else
return extractXmlConfigFromInputStream( is );
}
再跟进extractXmlConfigFromInputStream方法,也就是最先说的缺陷函数模块
这样,这个漏洞产生的流程就完整的呈现了出来。
修改 extractXmlConfigFromInputStream函数内容,如下
public static C3P0Config extractXmlConfigFromInputStream(InputStream is) throws Exception
{
DocumentBuilderFactory fact = DocumentBuilderFactory.newInstance();
DocumentBuilder db = fact.newDocumentBuilder();
fact.setExpandEntityReferences(false);
Document doc = db.parse( is );
return extractConfigFromXmlDoc(doc);
}
将setExpandEntityReferences设置为false
即不允许dom扩展出xml中的实体引用节点。
漏洞确实有点鸡肋,但是思路猥琐点会有大用处,怎么操作看各位发挥。
拨开云雾见天日 守得云开见月明~