我们已经学习了QL
的基础语法,已经可以对问题进行简单的查询了。但对于某一种特定的语言,以我们现在的基础还是不能对其项目代码进行清晰描述。
比如,我们想要获取python
编写的flask
web应用中可能存在SSTI漏洞的点
from flask import Flask from flask import request from flask import config from flask import render_template_string app = Flask(__name__) app.config['SECRET_KEY'] = "flag{SSTI_123456}" @app.route('/') def hello_world(): return 'Hello World!' @app.errorhandler(404) def page_not_found(e): template = ''' {%% block body %%} <div class="center-content error"> <h1>Oops! That page doesn't exist.</h1> <h3>%s</h3> </div> {%% endblock %%} ''' % (request.args.get('404_url')) return render_template_string(template), 404 if __name__ == '__main__': app.run(host='0.0.0.0',debug=True)
可以看到这里我们需要检测代码中是否存在request.args.get()
获取的参数,并追踪该方式获得的参数404_url
在后续的过程中是否经过了过滤,又或者会不会有一个等式405_test=404_url+"test code"
,导致405_test
参数实际上也被污染了。最后看这些参数是否会回显render_template_string()
到页面上。
整个过程需要考虑到参数在代码中的运行流程,所以传统的正则表达式匹配敏感字符在这种情况下就捉襟见肘了。
所以我们还需要学习codeql
对python代码进行查询的相关基础知识,比如python的表达式,参数,函数等,这样才能在自己独立审计的时候举一反三。
官方教程链接:https://codeql.github.com/docs/codeql-language-guides/codeql-for-python/
当然codeql
也支持其他语言的查询,链接为:
https://codeql.github.com/docs/codeql-language-guides/
我们可以使用CodeQL
标准库中的语法类来查找python函数并识别对它们的调用
在这个例子中,我们在一个项目中寻找所有的getter
访问器,从java迁移到python生活的程序猿通常会编写大量的getter
访问器和setter
生成器方法,而不是使用其类属性。有时候我们需要找到这些方法,这时候就应该利用成员谓词Function.getName()
,借此找到QL数据库所有的getter
函数
很多时候可以自己去手敲一遍代码而不是直接复制查询,加深记忆。
import python from Function f where f.getName().matches("get%") select f,"this is a func called get..."
这里类似与re
库里面的正则表达式匹配match
,而规则get%
表示以get
开头的字符串,%
在这里表示通配符,代表其他字符
可以看到匹配到的结果如下
但是显然,在这个例子中
得到的结果并不是类方法,而是一个简单的函数,由于我们只对方法感兴趣,所以可以使用Function.isMethod()
谓词来改进查询
import python from Function f where f.getName().matches("get%") and f.isMethod() select f,"this is a method called get..."
现在匹配到的结果就是以get
开头的方法了
我们可以进一步修改查询,使其返回的函数中只有一行(为什么只有一行,这是由我们的需要所决定的,查找项目中的getter
访问器,而访问器只会返回一行,根据这个特征来修改QL查询代码),因此我们需要统计返回的每个方法中的代码行数来满足我们的需要
import python from Function f where f.getName().matches("get%") and f.isMethod() and count( f.getAStmt() )=1 select f,"This function is (probably) a getter."
虽然现在的结果已经进一步筛选了,但是很多仍然不是我们想要的结果,说明还有改进的空间
这是正确的结果
这是错误的结果
在代码中存在eval
的时候,如果参数可控,就很可能会导致代码执行漏洞的出现,下面的而这个示例来检测eval
在代码中的存在
import python from Call call,Name name where call.getFunc()=name and name.getId()="eval" select call,"call to 'eval'"
在上面的代码中,call.getFunc()=name and name.getId()="eval"
表示调用的函数的名称为eval
,此外,Call
类表示python程序的调用,而谓词getFunc()
则用于获取被调用的表达式,谓词getId()
用于获取名称表达式的标识符(字符串)
查看结果,这里是典型的代码执行并回显漏洞代码:
@app.route('/command') def command(): if request.values.get('cmd'): sys.stdout = io.StringIO() cmd = request.values.get('cmd') return Response('<p>输入的值为:%s</p>' %str(eval(cmd))) else: return Response('<p>请输入cmd值</p>')
由于python的动态特性,该查询将返回具有eval(...)
形式的所有调用,无论它是否是对内置函数eval
的调用,在后面的教程中,我们将介绍如何使用类型推断库来查找对内置函数eval
的调用,而不是简单地匹配调用的变量名称
建了一个微信的安全交流群,欢迎添加我微信备注进群
,一起来聊天吹水哇,以及一个会发布安全相关内容的公众号,欢迎关注