CodeQL分析python代码3-python中的函数

前言

我们已经学习了QL的基础语法,已经可以对问题进行简单的查询了。但对于某一种特定的语言,以我们现在的基础还是不能对其项目代码进行清晰描述。

比如,我们想要获取python编写的flaskweb应用中可能存在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/

python中的函数

我们可以使用CodeQL标准库中的语法类来查找python函数并识别对它们的调用

查找所有名为"get ..."的函数

在这个例子中,我们在一个项目中寻找所有的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开头的字符串,%在这里表示通配符,代表其他字符

可以看到匹配到的结果如下

查找所有名为"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开头的方法了

找到名为"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的调用,而不是简单地匹配调用的变量名称

END

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

GIF GIF
posted @ 2022-01-29 17:21  春告鳥  阅读(260)  评论(0编辑  收藏  举报