Refactor isMybatisXmlOrAnnotationSqlInjection

This commit is contained in:
haby0
2021-12-08 18:59:55 +08:00
parent daf6a4ce07
commit 1d321c692b
4 changed files with 115 additions and 138 deletions

View File

@@ -44,10 +44,12 @@ private class MyBatisAnnotationSqlInjectionConfiguration extends TaintTracking::
from
MyBatisAnnotationSqlInjectionConfiguration cfg, DataFlow::PathNode source,
DataFlow::PathNode sink, IbatisSqlOperationAnnotation isoa
DataFlow::PathNode sink, IbatisSqlOperationAnnotation isoa, MethodAccess ma
where
cfg.hasFlowPath(source, sink) and
isMybatisXmlOrAnnotationSqlInjection(sink.getNode(), _, isoa)
ma.getAnArgument() = sink.getNode().asExpr() and
myBatisSqlOperationAnnotationFromMethod(ma.getMethod(), isoa) and
isMybatisXmlOrAnnotationSqlInjection(sink.getNode(), ma, getAMybatisAnnotationSqlValue(isoa), _)
select sink.getNode(), source, sink,
"MyBatis annotation SQL injection might include code from $@ to $@.", source.getNode(),
"this user input", isoa, "this SQL operation"

View File

@@ -30,7 +30,7 @@ private class PropertiesFlowConfig extends DataFlow2::Configuration {
}
/** Get the key value of Mybatis Configuration Variable. */
string getAnMybatisConfigurationVariableKey() {
string getAMybatisConfigurationVariableKey() {
exists(PropertiesFlowConfig conf, DataFlow::Node n |
propertiesKey(n, result) and
conf.hasFlowTo(n)
@@ -44,134 +44,124 @@ class ListType extends RefType {
}
}
/** Gets a call to the MyBatis mapper xml method. */
MethodAccess getMyBatisMapperXmlMethodAccess(XMLElement xmle) {
exists(MyBatisMapperSqlOperation mbmxe |
mbmxe.getMapperMethod() = result.getMethod() and
(
mbmxe.getAChild*() = xmle
or
exists(MyBatisMapperSql mbms |
mbmxe.getInclude().getRefid() = mbms.getId() and
mbms.getAChild*() = xmle
)
/** Holds if the specified method uses MyBatis Mapper XMLElement. */
predicate myBatisMapperXMLElementFromMethod(Method method, MyBatisMapperXMLElement mmxx) {
exists(MyBatisMapperSqlOperation mbmxe | mbmxe.getMapperMethod() = method |
mbmxe.getAChild*() = mmxx
or
exists(MyBatisMapperSql mbms |
mbmxe.getInclude().getRefid() = mbms.getId() and
mbms.getAChild*() = mmxx
)
)
}
/** Gets a call to the MyBatis SQL operation annotation method. */
MethodAccess getMyBatisSqlOperationAnnotationMethodAccess(IbatisSqlOperationAnnotation isoa) {
/** Holds if the specified method uses Ibatis Sql Operation Annotation. */
predicate myBatisSqlOperationAnnotationFromMethod(Method method, IbatisSqlOperationAnnotation isoa) {
exists(MyBatisSqlOperationAnnotationMethod msoam |
msoam = result.getMethod() and
msoam = method and
msoam.getAnAnnotation() = isoa
)
}
/** Get the #{...} or ${...} parameters in the Mybatis mapper xml file. */
private string getAnMybatisXmlSetValue(XMLElement xmle) {
result = xmle.getTextValue().trim().regexpFind("(#|\\$)\\{[^\\}]*\\}", _, _)
string getAMybatisXmlSetValue(XMLElement xmle) {
result = xmle.getTextValue().regexpFind("(#|\\$)\\{[^\\}]*\\}", _, _)
}
/** Get the #{...} or ${...} parameters in the Mybatis sql operation annotation value. */
private string getAMybatisAnnotationSqlValue(IbatisSqlOperationAnnotation isoa) {
result = isoa.getSqlValue().trim().regexpFind("(#|\\$)\\{[^\\}]*\\}", _, _)
string getAMybatisAnnotationSqlValue(IbatisSqlOperationAnnotation isoa) {
result = isoa.getSqlValue().regexpFind("(#|\\$)\\{[^\\}]*\\}", _, _)
}
/** Holds if it is SQL injection of MyBatis xml or MyBatis annotation. */
bindingset[setValue]
predicate isMybatisXmlOrAnnotationSqlInjection(
DataFlow::Node node, XMLElement xmle, IbatisSqlOperationAnnotation isoa
//DataFlow::Node node, MyBatisMapperXMLElement xmle, IbatisSqlOperationAnnotation isoa
DataFlow::Node node, MethodAccess ma, string setValue, MyBatisMapperXMLElement mmxe
) {
exists(MethodAccess ma, string setValue |
(
ma = getMyBatisMapperXmlMethodAccess(xmle) and
setValue = getAnMybatisXmlSetValue(xmle)
or
ma = getMyBatisSqlOperationAnnotationMethodAccess(isoa) and
setValue = getAMybatisAnnotationSqlValue(isoa)
) and
not setValue.regexpMatch("\\$\\{" + getAnMybatisConfigurationVariableKey() + "\\}") 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(int i, Annotation annotation |
setValue
.matches("%${" + annotation.getValue("value").(CompileTimeConstantExpr).getStringValue()
+ "%}") and
annotation.getType() instanceof TypeParam and
ma.getArgument(i) = 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
(
setValue.matches("%${param" + (i + 1) + "%}")
or
setValue.matches("%${arg" + i + "%}")
) and
not setValue = "${" + getAnMybatisConfigurationVariableKey() + "}" 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
setValue.matches("%${" + t.getAField().getName() + "%}") and
ma.getArgument(i) = node.asExpr()
)
or
// 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, MyBatisMapperForeach mbmf |
mbmf = xmle and
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
setValue.matches("%${%}") and
ma.getArgument(i) = node.asExpr()
)
or
// This method has only one parameter and the parameter is not annotated with `@Param`.
// 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
setValue.matches("%${%}") and
ma.getAnArgument() = node.asExpr()
)
not setValue.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(int i, Annotation annotation |
setValue
.matches("${" + annotation.getValue("value").(CompileTimeConstantExpr).getStringValue() +
"%}") and
annotation.getType() instanceof TypeParam and
ma.getArgument(i) = 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
(
setValue.matches("${param" + (i + 1) + "%}")
or
setValue.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
setValue.matches("${" + t.getAField().getName() + "%}") and
ma.getArgument(i) = node.asExpr()
)
or
// 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, MyBatisMapperForeach mbmf |
mbmf = mmxe and
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
setValue.matches("${%}") 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
setValue.matches("${%}") and
ma.getAnArgument() = node.asExpr()
)
)
}

View File

@@ -45,10 +45,12 @@ private class MyBatisMapperXmlSqlInjectionConfiguration extends TaintTracking::C
from
MyBatisMapperXmlSqlInjectionConfiguration cfg, DataFlow::PathNode source, DataFlow::PathNode sink,
XMLElement xmle
MyBatisMapperXMLElement mmxe, MethodAccess ma
where
cfg.hasFlowPath(source, sink) and
isMybatisXmlOrAnnotationSqlInjection(sink.getNode(), xmle, _)
ma.getAnArgument() = sink.getNode().asExpr() and
myBatisMapperXMLElementFromMethod(ma.getMethod(), mmxe) and
isMybatisXmlOrAnnotationSqlInjection(sink.getNode(), ma, getAMybatisXmlSetValue(mmxe), mmxe)
select sink.getNode(), source, sink,
"MyBatis Mapper XML SQL injection might include code from $@ to $@.", source.getNode(),
"this user input", xmle, "this SQL operation"
"this user input", mmxe, "this SQL operation"

View File

@@ -37,7 +37,10 @@ class MyBatisMapperXMLElement extends XMLElement {
* An MyBatis Mapper sql operation element.
*/
abstract class MyBatisMapperSqlOperation extends MyBatisMapperXMLElement {
abstract string getId();
/**
* Gets the value of the `id` attribute of MyBatis Mapper sql operation element.
*/
string getId() { result = this.getAttribute("id").getValue() }
/**
* Gets the `<include>` element in a `MyBatisMapperSqlOperation`.
@@ -58,11 +61,6 @@ abstract class MyBatisMapperSqlOperation extends MyBatisMapperXMLElement {
*/
class MyBatisMapperInsert extends MyBatisMapperSqlOperation {
MyBatisMapperInsert() { this.getName() = "insert" }
/**
* Gets the value of the `id` attribute of this `<insert>`.
*/
override string getId() { result = this.getAttribute("id").getValue() }
}
/**
@@ -70,11 +68,6 @@ class MyBatisMapperInsert extends MyBatisMapperSqlOperation {
*/
class MyBatisMapperUpdate extends MyBatisMapperSqlOperation {
MyBatisMapperUpdate() { this.getName() = "update" }
/**
* Gets the value of the `id` attribute of this `<update>`.
*/
override string getId() { result = this.getAttribute("id").getValue() }
}
/**
@@ -82,11 +75,6 @@ class MyBatisMapperUpdate extends MyBatisMapperSqlOperation {
*/
class MyBatisMapperDelete extends MyBatisMapperSqlOperation {
MyBatisMapperDelete() { this.getName() = "delete" }
/**
* Gets the value of the `id` attribute of this `<delete>`.
*/
override string getId() { result = this.getAttribute("id").getValue() }
}
/**
@@ -94,15 +82,10 @@ class MyBatisMapperDelete extends MyBatisMapperSqlOperation {
*/
class MyBatisMapperSelect extends MyBatisMapperSqlOperation {
MyBatisMapperSelect() { this.getName() = "select" }
/**
* Gets the value of the `id` attribute of this `<select>`.
*/
override string getId() { result = this.getAttribute("id").getValue() }
}
/**
* A `<select>` element in a `MyBatisMapperXMLElement`.
* A `<sql>` element in a `MyBatisMapperXMLElement`.
*/
class MyBatisMapperSql extends MyBatisMapperXMLElement {
MyBatisMapperSql() { this.getName() = "sql" }