赞
踩
java\ql\src\experimental\Security\CWE\CWE-089
/** * @name SQL injection in MyBatis annotation * @description Constructing a dynamic SQL statement with input that comes from an * untrusted source could allow an attacker to modify the statement's * meaning or to execute arbitrary SQL commands. * @kind path-problem * @problem.severity error * @precision high * @id java/mybatis-annotation-sql-injection * @tags security * external/cwe/cwe-089 */ import java import DataFlow::PathGraph import MyBatisCommonLib import MyBatisAnnotationSqlInjectionLib import semmle.code.java.dataflow.FlowSources private class MyBatisAnnotationSqlInjectionConfiguration extends TaintTracking::Configuration { MyBatisAnnotationSqlInjectionConfiguration() { this = "MyBatis annotation sql injection" } override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } override predicate isSink(DataFlow::Node sink) { sink instanceof MyBatisAnnotatedMethodCallArgument } override predicate isSanitizer(DataFlow::Node node) { node.getType() instanceof PrimitiveType or node.getType() instanceof BoxedType or node.getType() instanceof NumberType } override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { exists(MethodAccess ma | ma.getMethod().getDeclaringType() instanceof TypeObject and ma.getMethod().getName() = "toString" and ma.getQualifier() = node1.asExpr() and ma = node2.asExpr() ) } } from MyBatisAnnotationSqlInjectionConfiguration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, IbatisSqlOperationAnnotation isoa, MethodAccess ma, string unsafeExpression where cfg.hasFlowPath(source, sink) and ma.getAnArgument() = sink.getNode().asExpr() and myBatisSqlOperationAnnotationFromMethod(ma.getMethod(), isoa) and //将@Select等的注解值赋给isoa unsafeExpression = getAMybatisAnnotationSqlValue(isoa) and // ${username} ( isMybatisXmlOrAnnotationSqlInjection(sink.getNode(), ma, unsafeExpression) //java-sec-code是在这个语句中为true or isMybatisCollectionTypeSqlInjection(sink.getNode(), ma, unsafeExpression) ) select sink.getNode(), source, sink, "MyBatis annotation SQL injection might include code from $@ to $@.", source.getNode(), "this user input", isoa, "this SQL operation"
MyBatisAnnotatedMethodCallArgument
IbatisSqlOperationAnnotation
myBatisSqlOperationAnnotationFromMethod
getAMybatisAnnotationSqlValue
isMybatisXmlOrAnnotationSqlInjection
isMybatisCollectionTypeSqlInjection
函数位于sink点
override predicate isSink(DataFlow::Node sink) {
sink instanceof MyBatisAnnotatedMethodCallArgument
}
查看其内部代码:
MyBatisAnnotatedMethodCallArgument() {
exists(MyBatisSqlOperationAnnotationMethod msoam, MethodAccess ma | ma.getMethod() = msoam |
ma.getAnArgument() = this.asExpr()
)
}
存在函数MyBatisSqlOperationAnnotationMethod
,解释为被@Select、@Delete、@Update、@Insert
注解过的方法。
修改器查询语句来解释:
from
MyBatisAnnotationSqlInjectionConfiguration cfg, DataFlow::PathNode source,
DataFlow::PathNode sink,MethodAccess ma,MyBatisSqlOperationAnnotationMethod msoam
where
cfg.hasFlowPath(source, sink)
and ma.getMethod() = msoam
and ma.getAnArgument() = sink.getNode().asExpr()
select
ma.getMethod(),msoam
执行结果如图(java-sec-code为例):
于是也就解释了MyBatisAnnotatedMethodCallArgument
函数的作用,即找出被@Select、@Delete、@Update、@Insert
注解过的方法
继承自Annotation
class IbatisSqlOperationAnnotation extends Annotation {
IbatisSqlOperationAnnotation() {
this.getType() instanceof IbatisSelectAnnotationType or
this.getType() instanceof IbatisDeleteAnnotationType or
this.getType() instanceof IbatisInsertAnnotationType or
this.getType() instanceof IbatisUpdateAnnotationType
}
/**
* Gets this annotation's SQL statement string.
*/
string getSqlValue() {
result = this.getAValue("value").(CompileTimeConstantExpr).getStringValue()
}
}
解释为Ibatis SQL操作注释,感觉和MyBatisSqlOperationAnnotationMethod
函数类似,编写查询语句查看:
from
MyBatisAnnotationSqlInjectionConfiguration cfg, DataFlow::PathNode source,
DataFlow::PathNode sink, Method method, IbatisSqlOperationAnnotation isoa, MethodAccess ma
where
cfg.hasFlowPath(source, sink)
and ma.getMethod() = method
and method.getAnAnnotation() = isoa
select
isoa
查询结果为:
获取到了2个@Select
,可能是获取方法上的注释吧
官方:
Holds if the specified method has Ibatis Sql operation annotation isoa.
查询语句:
from
MyBatisAnnotationSqlInjectionConfiguration cfg, DataFlow::PathNode source,
DataFlow::PathNode sink, IbatisSqlOperationAnnotation isoa, MethodAccess ma
where
cfg.hasFlowPath(source, sink)
and ma.getAnArgument() = sink.getNode().asExpr()
and myBatisSqlOperationAnnotationFromMethod(ma.getMethod(), isoa)
and unsafeExpression = getAMybatisAnnotationSqlValue(isoa)
select
ma.getMethod(), isoa
结果:
而当我注释掉这个函数时的结果:
个人感觉这函数存在是为了将方法和对应的注释绑定在一起?
看名称应该是获取到注释里面的值,官方原话为:
Gets a #{...} or ${...} expression argument in annotation isoa.
也就是说从isoa
注解里提取出包含#{}
或${}
的内容
查询语句:
from
MyBatisAnnotationSqlInjectionConfiguration cfg, DataFlow::PathNode source,
DataFlow::PathNode sink, IbatisSqlOperationAnnotation isoa, MethodAccess ma,
string unsafeExpression
where
cfg.hasFlowPath(source, sink)
and
ma.getAnArgument() = sink.getNode().asExpr()
and myBatisSqlOperationAnnotationFromMethod(ma.getMethod(), isoa)
and unsafeExpression = getAMybatisAnnotationSqlValue(isoa)
select
ma.getMethod(), isoa, unsafeExpression
结果:
gpt解释为:
isMybatisXmlOrAnnotationSqlInjection函数用于检测MyBatis XML或注解中的SQL注入漏洞。它可能会检查XML或注解中的动态SQL语句,以及是否存在未经过适当处理的用户输入。
isMybatisCollectionTypeSqlInjection函数用于检测MyBatis中集合类型的SQL注入漏洞。在MyBatis中,集合类型的参数可以通过foreach标签进行迭代,如果未正确处理用户输入,可能导致SQL注入漏洞。
由于java-sec-code未包含foreach
标签,故第二个没有去实验,实验的是第一个
isMybatisXmlOrAnnotationSqlInjection
源码为:
/** * Holds if `node` is an argument to `ma` that is vulnerable to SQL injection attacks if `unsafeExpression` occurs in a MyBatis SQL expression. * * This case currently assumes all `${...}` expressions are potentially dangerous when there is a non-`@Param` annotated, collection-typed parameter to `ma`. */ bindingset[unsafeExpression] predicate isMybatisCollectionTypeSqlInjection( DataFlow::Node node, MethodAccess ma, string unsafeExpression ) { not unsafeExpression.regexpMatch("\\$\\{" + getAMybatisConfigurationVariableKey() + "\\}") and // The parameter type of the MyBatis method parameter is Map or List or Array. // SQL injection vulnerability caused by improper use of this parameter. // e.g. // // ```java // @Select(select id,name from test where name like '%${value}%') // Test test(Map map); // ``` exists(int i | not ma.getMethod().getParameter(i).getAnAnnotation().getType() instanceof TypeParam and ( ma.getMethod().getParameterType(i) instanceof MapType or ma.getMethod().getParameterType(i) instanceof ListType or ma.getMethod().getParameterType(i) instanceof Array ) and unsafeExpression.matches("${%}") and ma.getArgument(i) = node.asExpr() ) } /** * Holds if `node` is an argument to `ma` that is vulnerable to SQL injection attacks if `unsafeExpression` occurs in a MyBatis SQL expression. * * This accounts for: * - arguments referred to by a name given in a `@Param` annotation, * - arguments referred to by ordinal position, like `${param1}` * - references to class instance fields * - any `${}` expression where there is a single, non-`@Param`-annotated argument to `ma`. */ bindingset[unsafeExpression] predicate isMybatisXmlOrAnnotationSqlInjection( DataFlow::Node node, MethodAccess ma, string unsafeExpression ) { not unsafeExpression.regexpMatch("\\$\\{" + getAMybatisConfigurationVariableKey() + "\\}") and ( // The method parameters use `@Param` annotation. Due to improper use of this parameter, SQL injection vulnerabilities are caused. // e.g. // // ```java // @Select(select id,name from test order by ${orderby,jdbcType=VARCHAR}) // void test(@Param("orderby") String name); // ``` exists(Annotation annotation | unsafeExpression .matches("${" + annotation.getValue("value").(CompileTimeConstantExpr).getStringValue() + "%}") and annotation.getType() instanceof TypeParam and ma.getAnArgument() = node.asExpr() ) or // MyBatis default parameter sql injection vulnerabilities.the default parameter form of the method is arg[0...n] or param[1...n]. // e.g. // // ```java // @Select(select id,name from test order by ${arg0,jdbcType=VARCHAR}) // void test(String name); // ``` exists(int i | not ma.getMethod().getParameter(i).getAnAnnotation().getType() instanceof TypeParam and ( unsafeExpression.matches("${param" + (i + 1) + "%}") or unsafeExpression.matches("${arg" + i + "%}") ) and ma.getArgument(i) = node.asExpr() ) or // SQL injection vulnerability caused by improper use of MyBatis instance class fields. // e.g. // // ```java // @Select(select id,name from test order by ${name,jdbcType=VARCHAR}) // void test(Test test); // ``` exists(int i, RefType t | not ma.getMethod().getParameter(i).getAnAnnotation().getType() instanceof TypeParam and ma.getMethod().getParameterType(i).getName() = t.getName() and unsafeExpression.matches("${" + t.getAField().getName() + "%}") and ma.getArgument(i) = node.asExpr() ) or // This method has only one parameter and the parameter is not annotated with `@Param`. The parameter can be named arbitrarily in the SQL statement. // If the number of method variables is greater than one, they cannot be named arbitrarily. // Improper use of this parameter has a SQL injection vulnerability. // e.g. // // ```java // @Select(select id,name from test where name like '%${value}%') // Test test(String name); // ``` exists(int i | i = 1 | ma.getMethod().getNumberOfParameters() = i and not ma.getMethod().getAParameter().getAnAnnotation().getType() instanceof TypeParam and unsafeExpression.matches("${%}") and ma.getAnArgument() = node.asExpr() ) ) }
内容有注解及用例,这里不作解释
主要针对unsafeExpression
来进行的相关判断,原来unsafeExpression
被赋值为${username}
和#{username}
的2条数据,而后通过isMybatisXmlOrAnnotationSqlInjection
函数判断,输出易受攻击的${username}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。