CodeQL分析python代码7-使用 API 图

API 图是一个用于引用外部库中定义的函数,类和方法的统一接口

关于本文

本文介绍如何使用API图来引用python第三方库代码中定义的类和函数。在定义例如flask的用户输入这种外部输入流等内容时,我们可以使用API图来更方便地引用外部库函数。

模块导入

API图中最常见的入口点是导入外部模块的modulepackage。我们可以使用模块semmle.python.ApiGraphs中定义的方法API::moduleImport访问与re库对应的API图节点,如下

import python
import semmle.python.ApiGraphs

select API::moduleImport("re")

该查询会显示与我们所输入模块(这里是re)对应的API图节点。该节点表示re模块是否被导入这件事,并不是程序中发生导入的某一个位置,同时每个项目最多会有一个结果。

当然这不是我们想要的,我们往往想要获得某一个模块在程序中调用的具体的位置,这时候我们可以使用getAUse()方法,查询对于re模块的所有引用

import python
import semmle.python.ApiGraphs

select API::moduleImport("re").getAUse()

需要注意的是,getAUse()方法考虑了局部数据流,因此my_re_compile在以下代码段中被正确识别为对re.compile函数的引用

from re import compile as re_compile

my_re_compile = re_compile

r = my_re_compile(".*")

如果我们只需要立即使用而不考虑局部数据流,可以使用getAnImmediateUse()方法

需要注意的是,传入的模块名称不得包含任何点。所以如果我们想要导入flask.views的时候,如果使用API::moduleImport("flask.views"),那么结果往往是错误的。这时我们应该分解为views对API图节点flask成员的访问,这也是马上要介绍的内容

访问属性

给定API图中的一个节点,我们可以使用getMember方法访问其属性,比如对上面上面的代码来说,我们要找到re.compile的引用

import python
import semmle.python.ApiGraphs

select API::moduleImport("re").getMember("compile").getAUse()

除了getMember()方法外,我们还可以使用getUnknownMember()方法查找对静态名称未知的API组件的引用。我们也可以使用getAMember()访问所有成员,包括已知成员和未知成员

调用和类实例化

要跟踪外部库中定义的类的示例,或调用外部定义函数的结果,我们可以使用getReturn()方法,下面我们查找所有使用re.compile()作为返回值的地方

import python
import semmle.python.ApiGraphs

select API::moduleImport("re").getMember("compile").getReturn().getAUse()

需要注意的是,查询结果包括了所有对re.compile的使用,包括可通过局部数据流访问的那些。要获得对re.compile的调用,我们可以使用getAnImmediateUse()而不是getAUse()。更好的一种方式是使用getACall(),这样就可以避免getReturn后面跟着getAnImmediateUse()

另外,API图不区分类实例化和函数调用,对它而言两者都只是调用API图节点的地方

子类

对于很多库来说,主要的使用方式是扩展一个或多个库,我们想要在API图中跟踪这一点,可以使用getASubclass()方法获取与该节点的所有直接子类对应的API图节点。要查找所有子类,只需要重复使用*+应用在方法上,例如getASubclass*

flask.views.View中有一个预定义的子类MethodView,要查找所有的子类View,我们需要显示包含MethodView子类

import python
import semmle.python.ApiGraphs

API::Node viewClass() {
  result =
    API::moduleImport("flask").getMember("views").getMember(["View", "MethodView"]).getASubclass*()
}

select viewClass().getAUse()

内置函数和类

我们可以使用API::builtin方法访问内置函数和类,将内置函数的名称作为参数提供

例如,查找对内置函数open的所有调用

import python
import semmle.python.ApiGraphs

select API::builtin("open").getACall()

END

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

GIF GIF
posted @ 2022-03-04 12:45  春告鳥  阅读(248)  评论(0编辑  收藏  举报