使用CodeQL寻找SSTI漏洞

污点追踪概述

污点追踪是指分析项目代码运行过程中可能存在安全隐患或者受污染的数据的流动情况。

在代码自动化安全审计的理论中,有一个最核心的三元组概念,就是source,sinksanitizer

  • source 是指漏洞污染链条的输入点,比如获取http请求的参数部分
  • sink 是指漏洞污染链条的执行点,比如SQL注入漏洞,最终执行SQL语句的函数就是sink
  • sanitizer 又叫做净化函数,是指在整个漏洞链条当中,如果存在一个方法阻断了整个传递链,那么这个方法就叫sanitizer

回忆我们平时审计代码的过程,是否是sourcesink同时存在,并且source -> sink 的链路是通的,才表示当前漏洞是存在的

CodeQL中进行污点追踪

这里给出一个简易版的flask SSTI漏洞的代码

from flask import Flask, request,render_template, render_template_string app = Flask(__name__) app.config['SECRET_KEY'] = '\xca\x0c\x86\x04\x98@\x02b\x1b7\x8c\x88]\x1b\xd7"+\xe6px@\xc3#\\' @app.route('/ssti') def ssti(): if request.values.get('name'): name = request.values.get('name') template = "<p>{name}<p1>".format(name=name) return render_template_string(template) # template = Template('<p>%s<p1>' %name) # return template.render() # template = "<p>{{ name }}<p1>" # return render_template_string(template, name=name) else: return render_template_string('<p>输入name值</p>') if __name__ == '__main__': app.run()

我们直接使用全局污点追踪TaintTracking::Configuration

官方文档中的模板为

import python class MyTaintTrackingConfiguration extends TaintTracking::Configuration { MyTaintTrackingConfiguration() { this = "..." } override predicate isSource(DataFlow::Node source) { ... } override predicate isSink(DataFlow::Node sink) { ... } }

我们通过isSource(DataFlow::Node source)方法来设置source,在一个典型的flask应用中,思考一下我们的source是什么。显然,作为一个web应用,source理所当然是用户的输入,在flask中有很多种方式获取用户的输入,例如:request.form.get('title')或者request.values.get('name')

CodeQL中封装的RemoteFlowSource表示来自远程网络输入的数据流,也就是用户的网络输入,里面也包括了flask的输入,我们直接调用即可

override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }

这里我们暂且把instanceof理解为这种类型的意思

上述代码的sink在哪里呢,显然是

return render_template_string(template)

这里使用codeql中的API图进行定义

override predicate isSink(DataFlow::Node sink) { exists(DataFlow::CallCfgNode call | call = API::moduleImport("flask").getMember("render_template_string").getACall() and sink = call.getArg(0) ) }

exists子查询,是CodeQL谓词语法里非常常见的语法结构,它根据内部的子查询返回true or false,来决定筛选出哪些数据。

将存在render_template_string函数调用的第一个参数作为sink

设置好了sourcesink后,首尾如果能够连通,一个受污染的变量,能够流转到危险函数,才证明漏洞存在。连通工作是由CodeQL引擎本身来完成的,我们使用其中的config.hasFlow(src, sink)方法来判断即可

整体代码如下

import python import semmle.python.dataflow.new.DataFlow import semmle.python.dataflow.new.TaintTracking import semmle.python.dataflow.new.RemoteFlowSources import semmle.python.Concepts import semmle.python.ApiGraphs class SSTIVulConfig extends TaintTracking::Configuration { SSTIVulConfig() { this = "RemoteToFileConfiguration Tracking" } override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } override predicate isSink(DataFlow::Node sink) { exists(DataFlow::CallCfgNode call | call = API::moduleImport("flask").getMember("render_template_string").getACall() and sink = call.getArg(0) ) } } from SSTIVulConfig config, DataFlow::Node src, DataFlow::Node sink where config.hasFlow(src, sink) select src,sink

运行查询最终得到SSTI漏洞

参考链接

END

建了一个微信的安全交流群,欢迎添加我微信备注进群,一起来聊天吹水哇,以及一个会发布安全相关内容的公众号,欢迎关注 😃

GIF GIF

__EOF__

本文作者春告鳥
本文链接https://www.cnblogs.com/Cl0ud/p/15959434.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   春告鳥  阅读(605)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
历史上的今天:
2020-03-03 Leetcode学习笔记(1)
点击右上角即可分享
微信分享提示