当前位置:   article > 正文

MyBatisAnnotationSqlInjection.ql学习

MyBatisAnnotationSqlInjection.ql学习

源码位置

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"
  • 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
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61

关注函数

MyBatisAnnotatedMethodCallArgument
IbatisSqlOperationAnnotation
myBatisSqlOperationAnnotationFromMethod
getAMybatisAnnotationSqlValue
isMybatisXmlOrAnnotationSqlInjection
isMybatisCollectionTypeSqlInjection
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
MyBatisAnnotatedMethodCallArgument

函数位于sink点

override predicate isSink(DataFlow::Node sink) {
    sink instanceof MyBatisAnnotatedMethodCallArgument
  }
  • 1
  • 2
  • 3

查看其内部代码:

MyBatisAnnotatedMethodCallArgument() {
    exists(MyBatisSqlOperationAnnotationMethod msoam, MethodAccess ma | ma.getMethod() = msoam |
      ma.getAnArgument() = this.asExpr()
    )
  }
  • 1
  • 2
  • 3
  • 4
  • 5

存在函数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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

执行结果如图(java-sec-code为例):
在这里插入图片描述于是也就解释了MyBatisAnnotatedMethodCallArgument函数的作用,即找出被@Select、@Delete、@Update、@Insert注解过的方法

IbatisSqlOperationAnnotation

继承自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()
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

解释为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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

查询结果为:
在这里插入图片描述获取到了2个@Select,可能是获取方法上的注释吧

myBatisSqlOperationAnnotationFromMethod

官方:
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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

结果:
在这里插入图片描述而当我注释掉这个函数时的结果:
在这里插入图片描述
个人感觉这函数存在是为了将方法和对应的注释绑定在一起?

getAMybatisAnnotationSqlValue

看名称应该是获取到注释里面的值,官方原话为:
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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

结果:
在这里插入图片描述

isMybatisXmlOrAnnotationSqlInjection & isMybatisCollectionTypeSqlInjection

gpt解释为:

isMybatisXmlOrAnnotationSqlInjection函数用于检测MyBatis XML或注解中的SQL注入漏洞。它可能会检查XML或注解中的动态SQL语句,以及是否存在未经过适当处理的用户输入。

isMybatisCollectionTypeSqlInjection函数用于检测MyBatis中集合类型的SQL注入漏洞。在MyBatis中,集合类型的参数可以通过foreach标签进行迭代,如果未正确处理用户输入,可能导致SQL注入漏洞。
  • 1
  • 2
  • 3

由于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()
    )
  )
}
  • 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
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108

内容有注解及用例,这里不作解释
主要针对unsafeExpression来进行的相关判断,原来unsafeExpression被赋值为${username}#{username}的2条数据,而后通过isMybatisXmlOrAnnotationSqlInjection函数判断,输出易受攻击的${username}

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/514687
推荐阅读
相关标签
  

闽ICP备14008679号