CodeQL分析python代码1-python代码的基本查询
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
CodeQL分析python代码1-python代码的基本查询
前⾔
我们已经学习了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的表达式,参数,函数等,这样才能在⾃⼰独⽴审计的时候举⼀反三。
python代码的基本查询
学习使⽤LGTM编写和运⾏简单的CodeQL查询
(为什么这⾥是⽤LGTM在线环境⽽不是VSCode本地环境,估计是官⽅⼈员觉得在线环境⽅便讲解并且有demo吧)
关于查询
我们编写查询找到项⽬中的只包含pass语句的if语句——实际上,这些代码都是多余的,例如
if error:pass
在LGTM平台上运⾏查询
import python
from If ifstmt, Stmt pass
where pass = ifstmt.getStmt(0) and
pass instanceof Pass
select ifstmt, "This 'if' statement is redundant."
如果⼀切正常,Run按钮会变为绿⾊,表⽰我们可以运⾏查询
点击Run,可以看到查询操作的进度
查询需要⼀些时间才能返回结果。
查询完成后,结果会显⽰在项⽬名称下⽅,查询结果展⽰在两列中,对应select查询语句中的两个表达式。
第⼀列对应表达式ifstmt,并能够点击跳转到项⽬源代码中ifstmt出现的位置,第⼆列是警报信息
我们可以点击匹配查询语句的代码进⾏跳转,在代码查看器中查看语句。
可以看到已经被⾼亮出来了
关于我们刚才的查询语句
在初始的import语句之后,这个简单的查询包含了三个部分,在我们之前的QL语法部分我们已经学习过了,即from,where,select
接下来详细解释各部分的含义
import python
每个查询都以⼀个或多个import语句开始,这⾥是导⼊python的标准codeql库,⽤来解析python项⽬代码,⽣成相应的语法树,进⽽被我们的codeql查询语句查询,找到相应的结果
from If ifstmt Stmt pass
这⾥定义了ifstmt和pass两个变量,其类型分别为If (python中的if语句)和 Stmt(statement python中的语句)
当然这个时候我们不认识If和Stmt类型,在python.qll⾥⾯我们可以看到python语⾔的codeql标准库,后⾯我们会在提到,现在只需要对这两个类型有个模糊的认识即可。
where pass=ifstmt.getStmt(0) and pass instanceof Pass
这⾥⽤到了getStmt()⽅法,我们可以直接查看源代码是如何实现的
/** INTERNAL: See the class `If` for further information. */
library class If_ extends @py_If, Stmt {
/** 省略 */
/** Gets the nth if-true statement of this if statement. */
Stmt getStmt(int index) { result = this.getBody().getItem(index) }
可以看出来getStmt(int index)是为了获取 if 语句主体结构中的第⼀个元素
所以这句话的意思是获取if语句中的第⼀个元素,并且这个元素必须是Pass类型的
接下来的这句话select ifstmt, "This 'if' statement is redundant.",显⽰查询的结果
不断改进你的查询
编写查询实质上是⼀个迭代的过程,就像我们编写poc脚本⼀样,需要根据返回的结果不断改进使其臻⾄完美。
我们编写⼀个简单的QL查询,然后在运⾏它的时候,根据获得的结果会发现很多以前没有考虑过的⽰例或者改进的机会
去除误报
浏览我们的基础查询的结果可以发现还有很⼤的进步空间。
在结果中,我们能够找到很多if带有else分⽀的语句,这种情况下的pass就不是冗余的,例如:
if cond():
pass
else:
do_something()
在这种情况下,带有pass的if语句并不是冗余的。
对于这种情况我们可以修改原有的QL查询让它在具有else分⽀时忽略pass语句
要排除if具有else分⽀的语句,我们需要修改原来的where⼦句,添加条件
and not exists(ifstmt.getOrelse()),现在where语句变成了
where pass = ifstmt.getStmt(0) and
pass instanceof Pass and
not exists(ifstmt.getOrelse())
点击运⾏之后可以看到过滤之后的查询结果变少了,因为不再包含if带有else分⽀的语句
END
建了⼀个微信的安全交流群,欢迎添加我微信备注进群,⼀起来聊天吹⽔哇,以及⼀个会发布安全相关内容的公众号,欢迎关注
GIF GIF。