免责声明
本文档所述漏洞详情及复现方法仅限用于合法授权的安全研究和学术教育用途。任何个人或组织不得利用本文内容从事未经许可的渗透测试、网络攻击或其他违法行为。使用者应确保其行为符合相关法律法规,并取得目标系统的明确授权。
前言:
我们建立了一个更多,更全的知识库。每日追踪最新的安全漏洞,追中25HW情报。
更多详情:
https://pc.fenchuan8.com/#/index?forum=101997&yqm=DHL4E
代码审计
源代码如下
@RequiresRoles("admin")
@Log(title = "创建表", businessType = BusinessType.OTHER)
@PostMapping("/createTable")
@ResponseBody
public AjaxResult create(String sql)
{
try
{
// 过滤SQL中的关键字
SqlUtil.filterKeyword(sql);
// 解析SQL语句,生成SQLStatement列表
List<SQLStatement> sqlStatements = SQLUtils.parseStatements(sql, DbType.mysql);
// 用于存储表名的列表
List<String> tableNames = new ArrayList<>();
// 遍历解析后的SQL语句
for (SQLStatement sqlStatement : sqlStatements)
{
// 判断是否为创建表的语句
if (sqlStatement instanceof MySqlCreateTableStatement)
{
MySqlCreateTableStatement createTableStatement = (MySqlCreateTableStatement) sqlStatement;
// 调用服务创建表
if (genTableService.createTable(createTableStatement.toString()))
{
// 获取表名并去除反引号
String tableName = createTableStatement.getTableName().replaceAll("`", "");
tableNames.add(tableName);
}
}
}
// 根据表名列表查询数据库表信息
List<GenTable> tableList = genTableService.selectDbTableListByNames(tableNames.toArray(new String[tableNames.size()]));
// 获取当前操作人的登录名
String operName = Convert.toStr(PermissionUtils.getPrincipalProperty("loginName"));
// 导入生成的表结构
genTableService.importGenTable(tableList, operName);
// 返回成功结果
return AjaxResult.success();
}
catch (Exception e)
{
// 记录异常日志
logger.error(e.getMessage(), e);
// 返回错误信息
return AjaxResult.error("创建表结构异常[" + e.getMessage() + "]");
}
}
其中filterKeyword()过滤了很多SQL关键字
/**
* SQL关键字检查
*/
public static void filterKeyword(String value)
{
if (StringUtils.isEmpty(value))
{
return;
}
String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|");
for (String sqlKeyword : sqlKeywords)
{
if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1)
{
throw new UtilException("参数存在SQL注入风险");
}
}
}
看一下SQL_REGEX
注意这里的按照|进行分割的时候,每个关键字如select后边有一个空格,我们可以使用/**/来绕过字符串中空格的匹配
可以调用create函数,因为我们api的功能就是createTable功能
看一下genTableService.createTable(createTableStatement.toString())
继续跟进createTable(sql)
也就是我们写的整个sql字符串会被当做SQL语句执行,且不存在所谓预编译
测试执行