自动化代码审计
程序员文章站
2022-07-01 08:40:23
0x00 前言
大概去年的时候就想写这样一篇文章,最近看到老外写的一个ppt,于是就有了这篇文章,随便看看就好,价值不是很大。
测试一个应用程序是否存在漏洞...
0x00 前言
大概去年的时候就想写这样一篇文章,最近看到老外写的一个ppt,于是就有了这篇文章,随便看看就好,价值不是很大。
测试一个应用程序是否存在漏洞的思路总结起来无非 黑盒 和白盒 ,或者两者结合。黑盒的思路简单概括即是 通过异常输入判断程序是否存在漏洞,白盒即是 通过审阅程序代码来了解程序逻辑来判断漏洞。我这里想说的思路是结合两者的特性来完成整个过程的自动化。
如果要你用一句话来概括一下白盒的过程改怎么评价,如果让我来评价就是“了解逻辑a 在到达 逻辑 b的中间发生了什么。”打个比方, 变量$s 在到达 get_one 函数之前发生了什么。
上面的代码转换成黑盒的方式就是 https://xxx.com/1.php?=[fuzz] 通过程序对于输入的反馈来判断。整个过程我们可以变换成以下形式,打个比方 我们输入 id=23,那么程序的执行过程就是。
这篇文章我们简单谈论一下,将输入映射到程序逻辑这个过程的自动化。
0x01 编程语言的解析
你思考过解释器或者编译器如何理解程序的逻辑的吗,程序会先经过词法分析解析,根据语法将程序分割。然后进行语法分析,根据程序的逻辑过程形成树的形式。之后是 语义分析 ,对程序的数据类型与一些语法进行校验。一个例子
现在读者应该对编程语言的解析有了一些概念,我们的输入的字符串最终就是通过这样一种树的形式,经过一道一道的逻辑。
0x02 有限状态机
思考一下,我们的程序从输入开始,经由一个逻辑 转移到下一个不同的逻辑,其中会经过许多变换最终可能变成一种完全不同的形式,是不是就像一个有限状态机的过程?我们现在要做的就是将源代码最终表示为有限状态机的形式。我们要做的简单来说,或者更符合直觉的解释就是,将我们想搞清楚的 逻辑a 到 逻辑b 的过程 抽象成类似一个流水线一样的东西,我们可以将字符串丢进去,然后工人会不停的判断,修改,最终得到我想要的。
0x03 简单的例子
现在我们会开始慢慢解析整个过程,首先需要的就是将 原生的程序的代码解析成语法树,下面是库调用。phply是一个老外写的python版本php解释器。
from phply.phpast import *
from phply import pythonast, phplex
import ast,sys,phply,pprint
我们简单的解析一个php代码
我们可以很简单的看出第一句是将字符串hello world分配给$s,接着第二行就是函数调用。其中有什么不同,难道只是字符串变长了?No~ 现在程序的操作都变成了一个个可操作的对象,我们又可以将它表示成有限状态机的形式。
我们来看另一个存在xss漏洞的例子
0x04 Bypass
是不是总觉得少些什么,挖掘漏洞的过程之中会遇到许多许多奇葩的逻辑,我们不能只是单纯的把他取出来,改下变量,然后看运气。我们需要一些更加复杂的东西。现在我举例一个真实的例子, 这是以前做代码审计的时候遇到的,代码我省略了很多。
打个比方,我们可以hook程序的 str_replace ,然后再写一个混淆规则 比如
if replace "*" to "":
* == 混淆字符
0x05 结语
最近比较忙,很多地方没办法写详细,以后再说吧。
大概去年的时候就想写这样一篇文章,最近看到老外写的一个ppt,于是就有了这篇文章,随便看看就好,价值不是很大。
测试一个应用程序是否存在漏洞的思路总结起来无非 黑盒 和白盒 ,或者两者结合。黑盒的思路简单概括即是 通过异常输入判断程序是否存在漏洞,白盒即是 通过审阅程序代码来了解程序逻辑来判断漏洞。我这里想说的思路是结合两者的特性来完成整个过程的自动化。
如果要你用一句话来概括一下白盒的过程改怎么评价,如果让我来评价就是“了解逻辑a 在到达 逻辑 b的中间发生了什么。”打个比方, 变量$s 在到达 get_one 函数之前发生了什么。
#1.php $s = $_GET['s']; if(is_numeric($s)){ echo get_one("select id from admin where id=".$s); }else{ echo "error"; }
上面的代码转换成黑盒的方式就是 https://xxx.com/1.php?=[fuzz] 通过程序对于输入的反馈来判断。整个过程我们可以变换成以下形式,打个比方 我们输入 id=23,那么程序的执行过程就是。
$s = 23; is_numeric($s)==True; echo get_one("select id from admin where id=23");
这篇文章我们简单谈论一下,将输入映射到程序逻辑这个过程的自动化。
0x01 编程语言的解析
你思考过解释器或者编译器如何理解程序的逻辑的吗,程序会先经过词法分析解析,根据语法将程序分割。然后进行语法分析,根据程序的逻辑过程形成树的形式。之后是 语义分析 ,对程序的数据类型与一些语法进行校验。一个例子
while b!=0 if a>b a:= a-b else b:= b-a return a
现在读者应该对编程语言的解析有了一些概念,我们的输入的字符串最终就是通过这样一种树的形式,经过一道一道的逻辑。
0x02 有限状态机
思考一下,我们的程序从输入开始,经由一个逻辑 转移到下一个不同的逻辑,其中会经过许多变换最终可能变成一种完全不同的形式,是不是就像一个有限状态机的过程?我们现在要做的就是将源代码最终表示为有限状态机的形式。我们要做的简单来说,或者更符合直觉的解释就是,将我们想搞清楚的 逻辑a 到 逻辑b 的过程 抽象成类似一个流水线一样的东西,我们可以将字符串丢进去,然后工人会不停的判断,修改,最终得到我想要的。
0x03 简单的例子
现在我们会开始慢慢解析整个过程,首先需要的就是将 原生的程序的代码解析成语法树,下面是库调用。phply是一个老外写的python版本php解释器。
from phply.phpparse import parser
from phply.phpast import *
from phply import pythonast, phplex
import ast,sys,phply,pprint
我们简单的解析一个php代码
example = "{C}" lexer = phplex.lexer.clone() output = parser.parse(example, lexer=lexer) for i in output: print i out: Assignment(Variable('$s'), 'hello world', False) Echo([Variable('$s')])
我们可以很简单的看出第一句是将字符串hello world分配给$s,接着第二行就是函数调用。其中有什么不同,难道只是字符串变长了?No~ 现在程序的操作都变成了一个个可操作的对象,我们又可以将它表示成有限状态机的形式。
我们来看另一个存在xss漏洞的例子
def tf(string): lexer = phplex.lexer.clone() output = parser.parse(string, lexer=lexer) return output example = "" for i in tf(example): print i out: Assignment(Variable('$a'), ArrayOffset(Variable('$_GET'), 's'), False) Echo([Variable('$a')])So,一切现在看起来清晰可见,我们接下来要做的是什么?表示成有限状态机?听起来是不是想打我,那换种说法,我们需要的是让变量能够转变成我们的输入,完成 https://xxx.com/1.php?=23 到 $s = $_GET['id'] 的映射,然后我们需要让程序一步步的执行。然后看最终执行到哪。那么,How? 刚才我们已经说过,现在的程序的每个逻辑都是我们可以操作的对象,所以只需要替换他就行了。
seq = [] for i in tf(example): if 'expr' in i.__dict__.keys(): if i.__dict__['expr'].__dict__['node']==Variable('$_GET'): seq.append(tf("")[0]) continue seq.append(i) for i in seq: print i Assignment(Variable('$a'), 'hello fuck', False) Echo([Variable('$a')])那么接下开我们需要做的就是执行他。我们会建立一个eval的函数,让python的解释器来执行php的代码。
def php_eval(nodes): body = [] for node in nodes: stmt = pythonast.to_stmt(pythonast.from_phpast(node)) body.append(stmt) code = ast.Module(body) comp = compile(code, '', mode='exec') eval(comp,globals())请注意,既然说了最终是由python的解释器来执行,那么像echo这种php有的函数python就没有,我们就需要自己实现一下,或者你也可以传递给php执行。因为我没转php所以就简单写一个实现。
def echo(*objs): for obj in objs: sys.stdout.write(str(obj))最后,执行。
php_eval(seq) out: hello fuck这样我们就可以用程序自动HOOK 逻辑a 和逻辑b,然后通过fuzz挖掘其是否存在漏洞。
0x04 Bypass
是不是总觉得少些什么,挖掘漏洞的过程之中会遇到许多许多奇葩的逻辑,我们不能只是单纯的把他取出来,改下变量,然后看运气。我们需要一些更加复杂的东西。现在我举例一个真实的例子, 这是以前做代码审计的时候遇到的,代码我省略了很多。
$string = $_POST['string']; $string = str_replace("select","\se\lect",$string) $string = str_replace("*","",$string) get_one($string);我们只需要 先注入一个 string , 去触发规则,然后再根据写好的混淆规则对sql做变形,那么就可以达到自动bypass的目的。
打个比方,我们可以hook程序的 str_replace ,然后再写一个混淆规则 比如
if replace "*" to "":
* == 混淆字符
0x05 结语
最近比较忙,很多地方没办法写详细,以后再说吧。