利用CodeQL进行源代码安全审计

admin 2025年1月10日11:53:53评论11 views字数 3775阅读12分35秒阅读模式
利用CodeQL进行源代码安全审计
概  述
CodeQL 是Github 安全实验室推出的基于语法分析的代码审计工具。CodeQL将语法树抽离出来,提供了使用QL查询代码的方案,增强了基于数据分析的灵活度。

官网

利用CodeQL进行源代码安全审计

利用CodeQL进行源代码安全审计
安 装 使 用
  • 安装

1.     直接下载解压即可
2.    VS code安装 CodeQL 插件,并在 cli 填写下载的 CodeQL 路径,windows 下可以填写 codeql.cmd 的路径;其他配置默认。

利用CodeQL进行源代码安全审计

3.    为了方便使用,可以添加环境变量

利用CodeQL进行源代码安全审计

  • 使用

  1. 下载CodeQL的demo文件

    git clone --recursive https://github.com/github/vscode-codeql-starter/

    TIPS:这里需要带上 --recursive 参数

    下载完成后,在vscode中 open object

  2. 进入到待审计代码的目录下,创建CodeQL数据库

    codeql database create <database> --language=<language-identifier>
    • <database>:要创建的新数据库的路径。执行命令时将创建此目录,无法指定现有目录。

    • --language :用于为其创建数据库的语言的标识符。

    利用CodeQL进行源代码安全审计

  3. VScode 中侧边栏打开 CodeQL, add a codeql database :From a folder,选择数据库目录即可

  4. 找到对应语言的 Demo ql文件,右键 -> Run Query

  • 云平台

平台地址:https://lgtm.com/query
  1. 选择语言类型

利用CodeQL进行源代码安全审计
2.  选择项目
这里有许多的测试项目,也可以打开Github上的开源项目

利用CodeQL进行源代码安全审计

输入URL,点击 Follow

利用CodeQL进行源代码安全审计

3.  选择项目后会自动分析,也可以编写自己的QL语句

利用CodeQL进行源代码安全审计

利用CodeQL进行源代码安全审计
基 础 语 法
更多语法查询见官网:https://codeql.github.com/codeql-standard-libraries/
from /* ... 变量声明... */
where /* ... 逻辑公式 ... */
select /* ... 表达式 ... */

eg.

from int a, int b
where a = 1 and b = 2
select a + b
TIPS:需要注意的是,from 语句只声明变量,不赋值
上述的 Demo 仅仅是最基本的语法,QL语句还支持定义谓词、类等。除此之外,还有相关聚合操作等,在此就不做赘述。
利用CodeQL进行源代码安全审计
XSS Demo分析
XSS Demo
var param = unescape(location.hash);
document.write(param);
获取 location.hash 的值,并直接document.write输出
其中,param 变量接收数据,称为 source,document.write 是漏洞触发点,称为 sink。一般的审计思路是定位到 sink,然后进行回溯,判断参数是否可控,当然,反之也成立。
因为CodeQL是基于语法进行分析,在编写QL语句之前,我们需要先了解相关的语法节点,可以使用在线的解析网站解析成语法树:https://esprima.org/demo/parse.html

利用CodeQL进行源代码安全审计

也可以直接使用 VS Code 插件 CodeQL 里的 AST Viewer 功能

利用CodeQL进行源代码安全审计

  • 编写QL语句查找 Sink
    import javascript

    from Expr dollarArg,CallExpr dollarCall
    where dollarCall.getCalleeName() = "write" and
      dollarCall.getReceiver().toString() = "document" and
      dollarArg = dollarCall.getArgument(0)
    select dollarArg
    查找函数名为 write,同时输出第一个参数
  • 根据Sink输出的参数,编写QL语句查找Source

    import javascript

    from CallExpr dollarCall
    where dollarCall.getCalleeName() = "unescape" and
      dollarCall.getArgument(0).toString() = "location.hash"
    select dollarCall

    查找调用的函数名为 unescape ,同时第一个参数为 location.hash

为了得到一条可以利用的链,需要进行污点追踪,这里根据官方文档套模板,重写 isSource 和 isSink 谓词,得到如下:
class XSSTracker extends TaintTracking::Configuration {
XSSTracker() {
  // unique identifier for this configuration
  this = "XSSTracker"
}

override predicate isSource(DataFlow::Node nd) {
  exists(CallExpr dollarCall |
    nd.asExpr() instanceof CallExpr and
    dollarCall.getCalleeName() = "unescape" and
    dollarCall.getArgument(0).toString() = "location.hash" and
    nd.asExpr() = dollarCall
  ) 
}

override predicate isSink(DataFlow::Node nd) {
  exists(CallExpr dollarCall |
    dollarCall.getCalleeName() = "write" and
    dollarCall.getReceiver().toString() = "document" and
    nd.asExpr() = dollarCall.getArgument(0)
  )
}
}

from XSSTracker pt, DataFlow::Node source, DataFlow::Node sink
where pt.hasFlow(source, sink)
select source,sink
利用CodeQL进行源代码安全审计
Grafana plugin任意文件读取漏洞
详细的漏洞分析和复现,见文章:Grafana plugin 任意文件读取漏洞( CVE-2021-43798 )分析复现
  1. 定位source

    通过 Params 函数获取参数,直接定位到相应的调用

    import go


    private class Mysource extends DataFlow::Node{
      Mysource(){
          exists( DataFlow::CallExpr call|call.getCalleeName().toString() = "Params"| call =this.asExpr())
      }
    }


    from Mysource s
    select s
  2. 定位 sink

    private class Mysink extends DataFlow::Node{
      Mysink(){
          exists( Function f,CallExpr call |f.hasQualifiedName("os", "Open") and call.getTarget() = f and call.getAnArgument()
    =this.asExpr())
      }
    }
  3. 套进模板里,查找调用链

    import go
    import semmle.go.security.TaintedPathCustomizations::TaintedPath 
    import DataFlow::PathGraph


    private class Mysource extends DataFlow::Node{
      Mysource(){
          exists( DataFlow::CallExpr call|call.getCalleeName().toString() = "Params"| call =this.asExpr())
      }
    }

    private class Mysink extends DataFlow::Node{
      Mysink(){
          exists( Function f,CallExpr call |f.hasQualifiedName("os", "Open") and call.getTarget() = f and call.getAnArgument()
    =this.asExpr())
      }
    }



    class Configuration extends TaintTracking::Configuration{
    Configuration(){ this = "xxx"}
    override predicate isSource(DataFlow::Node source) { source instanceof Mysource }
    override predicate isSink(DataFlow::Node sink) { sink instanceof Mysink }
    }

    from Configuration cfg, DataFlow::PathNode sink, DataFlow::PathNode source
    where cfg.hasFlowPath(source, sink)
    select sink.getNode(),source,sink,"file travel logic path is @",source.getNode(),"Here"

利用CodeQL进行源代码安全审计

原文始发于微信公众号(SAINTSEC):利用CodeQL进行源代码安全审计

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年1月10日11:53:53
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   利用CodeQL进行源代码安全审计https://cn-sec.com/archives/3614454.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息