安全静态分析(2)- 基于AST编写自动化漏洞检测

admin 2023年12月22日07:55:47评论14 views字数 5316阅读17分43秒阅读模式

本篇为安全静态分析第二篇,简单介绍了AST是什么、AST语法、以及基于AST来做SQL注入检测的原理及代码实现

了解AST

AST 是一个树状结构,其中每个节点表示源代码中的一个语法结构,如表达式、语句、函数声明等

源代码首先经过词法分析器(Lexer)生成一系列令牌(tokens),然后经过语法分析器(Parser)构建出 AST。AST 表示了源代码的语法结构,但不包含冗余的细节,如空格、注释等

以简单的输出Hello world来举例看下AST解析出来之后的结果

Java                  
print('Hello world')

https://astexplorer.net/

安全静态分析(2)- 基于AST编写自动化漏洞检测

https://esprima.org/demo/parse.html#    

安全静态分析(2)- 基于AST编写自动化漏洞检测

          

作为对这块没有了解的同学,看到上边的AST语义树会一脸懵逼,那么先介绍下两个编程语言中最基本的概念

表达式(Expression|expr):

  • 定义:表达式是由操作数(operands)和运算符(operators)组成的代码片段,它产生一个值。表达式可以包含变量、常量、函数调用等。

  • 特点:表达式的主要目的是计算和生成一个值。它可以嵌套,由更简单的表达式构成。

  • 例子:在 Python 中,2 + 3 就是一个表达式,它的值为 5。函数调用、赋值语句右侧也可以是表达式。

Python                  
pythonCopy code                  
x = 2 + 3  # 表达式 2 + 3 在赋值语句中 y = x * 4  # 表达式 x * 4 在赋值语句中

语句(Statement|stmt):

  • 定义:语句是一条执行操作的独立单元,它表示一种动作或控制结构。语句执行后通常会产生副作用,如修改变量的值、控制程序的流程等。

  • 特点:语句通常包含关键字(如 ifforwhilereturn)和表达式,它们是程序的基本构建块。    

  • 例子:在 Python 中,if 语句、for 循环语句、赋值语句都是语句。

Python                  
pythonCopy code                  
if x > 0:  # if 语句是一个语句块 print("x is positive")  # 这是一个表达式语句 for i inrange(3):  # for 循环语句 print(i)  # 这是一个表达式语句

          

AST语法

在 Python 中,ast 模块提供了用于生成和操作 AST 的工具。以下是一些 Python AST 的节点类型:

  • Module(模块):代表整个 Python 模块,包含了一系列语句。

  • FunctionDef(函数定义):代表函数定义。

  • ClassDef(类定义):代表类定义。

  • Assign(赋值):代表赋值语句。

  • If(条件语句):代表 if 语句。

  • While(循环语句):代表 while 语句。

  • Expr(表达式语句):代表一个表达式语句。

详细的语法请参考https://docs.python.org/zh-cn/3/library/ast.html    

安全静态分析(2)- 基于AST编写自动化漏洞检测

使用python ast模块来实现一个简单的代码解析

Java                  
import ast                  
                 
code = "2 + 3 * 4"                  
parsed_ast = ast.parse(code)                  
                 
# 输出 AST                  
print(ast.dump(parsed_ast))

输出结果为

Java                  
Module(body=[Expr(value=BinOp(left=Constant(value=2, kind=None), op=Add(), right=BinOp(left=Constant(value=3, kind=None), op=Mult(), right=Constant(value=4, kind=None))))], type_ignores=[])        

          

使用AST来检测SQL注入

检测的逻辑为

  • 根据sql执行的代码函数进行初步定位

  • 解析SQL函数里边的数据内容(参数),根据数据内容(参数)是否存在拼凑来判断风险

          

对此详细分析下

第一步 检测特征函数

要检测SQL注入,此处以常用的SQL代码语句为例

Java                  
cursor.execute()

那么其在AST中解析为

Java                  
Module(body=[Expr(value=Call(func=Attribute(value=Name(id='cursor', ctx=Load()), attr='execute', ctx=Load()), args=[], keywords=[]))], type_ignores=[])

那么根据这个特征结合AST解析出来的结果来写代码进行匹配

使用isinstance()函数判断对象是否为某一类,判断当前节点函数为Attribute类、且值为Name类型,对应的值id为cursor、attr为execute,那么就为检测到cursor.execute,其实就是把代码语句正向使用ast解析出来后,根据其ast结构写代码来匹配特征即可

Java                  
isinstance(node.func, ast.Attribute) and isinstance(node.func.value, ast.Name) and node.func.value.id == 'cursor' and node.func.attr == 'execute'

              

第二步 检测特征参数

检测特征函数,只是帮助我们快速定位SQL注入可能发生的点,但无法帮助我们检测是否存在风险

要想知道是否存在风险,要看具体的SQL语句编写方式,如果存在拼凑则一般就存在SQL注入

Java                  
code_with_sql_injection = """                  
cursor.execute("SELECT * FROM Users WHERE username = '" + userInput + "';")                  
"""                  
                 
code_without_sql_injection = """                  
cursor.execute("SELECT * FROM Users WHERE username = %s;", (userInput,))                  
"""

通过我们对存在漏洞以及安全规范写法的差异来区别

1参数数量不同,存在漏洞的前者仅1个、后者大于1个

1参数表达式不同,存在漏洞的前者使用operator add来拼接字符串,后者没有

因此我们可以通过这种不同来检测

Java                  
第一种方案:                 
if len(node.args)>1:                  
   return is_safe                  
else:                  
    return is_vuln                  
                     
第二种方式:基于拼凑字符串+                  
if '+' in node.args[0]:                  
     return is_vuln                  
else                  
     return is_safe                  
                      
第三种方式:基于ast oprator add                  
if instance(node.args[0].op,ast.Add):                  
     return is_vuln                  
else:                  
    return is_safe
       

          

第三步  实现检测SQL注入Demo代码

将前两步串联起来,再结合python ast的规范使用,就可以基本上得到ast检测sql注入的demo

Java                  
#!/usr/bin/env python                  
# -*- coding: UTF-8 -*-                  
'''                                  
AST检测                  
'''                  
import ast                  
import astor                  
                 
class SQLInjectionAnalyzer(ast.NodeVisitor):                  
    def __init__(self):                  
        self.sql_injection_detected = False                  
                 
    def visit_Call(self, node):                  
        # 检查是否调用了 SQL 查询函数,这里简单地以 execute 为例                  
        if isinstance(node.func, ast.Attribute) and isinstance(node.func.value, ast.Name) and node.func.value.id == 'cursor' and node.func.attr == 'execute':                  
            # 获取 SQL 查询字符串                  
            sql_query = astor.to_source(node.args[0]) if node.args else None                  
                 
            # 简单的正则表达式检测可能的 SQL 注入                  
            # sql_injection_pattern = r'(bSELECTb.*bFROMb)|(bDELETEb.*bFROMb)|(bUPDATEb.*bSETb)|(bINSERT INTOb)'                  
            if sql_query and any(keyword in sql_query for keyword in ['SELECT', 'DELETE', 'UPDATE', 'INSERT INTO']) and '+' in sql_query:                  
                self.sql_injection_detected = True                  
                print('Potential SQL injection detected. Review the following code snippets:')                  
                print(sql_query)                  
                 
        self.generic_visit(node)                  
                 
# 示例用法                  
code_with_sql_injection = """                  
cursor.execute("SELECT * FROM Users WHERE username = '" + userInput + "';")                  
"""                  
                 
code_without_sql_injection = """                  
cursor.execute("SELECT * FROM Users WHERE username = %s;", (userInput,))                  
"""                  
                 
# 使用 ast 模块解析代码并进行分析                  
analyzer_with_injection = SQLInjectionAnalyzer()                  
ast_with_injection = ast.parse(code_with_sql_injection)                  
analyzer_with_injection.visit(ast_with_injection)                  
if not analyzer_with_injection.sql_injection_detected:                  
    print('No SQL injection risk detected in the code.')                  
                 
analyzer_without_injection = SQLInjectionAnalyzer()                  
ast_without_injection = ast.parse(code_without_sql_injection)                  
analyzer_without_injection.visit(ast_without_injection)                  
if not analyzer_without_injection.sql_injection_detected:                  
    print('No SQL injection risk detected in the code.')
       

结果为

安全静态分析(2)- 基于AST编写自动化漏洞检测

              

原文始发于微信公众号(暴暴的皮卡丘):安全静态分析(2)- 基于AST编写自动化漏洞检测

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年12月22日07:55:47
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   安全静态分析(2)- 基于AST编写自动化漏洞检测http://cn-sec.com/archives/2325750.html

发表评论

匿名网友 填写信息