vulnhuntr浅析

#1024程序员节 | 征文#

今天有个开源项目vulnhuntr很火:GitHub - protectai/vulnhuntr: Zero shot vulnerability discovery using LLMs

它给自己的定义是:全球首个由ai自动化发现0day漏洞的工具,结合了LLM(大模型)的Python静态代码分析器。由于github中列出了它们用ai发现的0day漏洞列表,所以看起来有一定的说服力。跟进看一下它们发现漏洞的博客文章:Vulnhuntr: Autonomous AI Finds First 0-Day Vulnerabilities in Wild

现在市面上流行的大模型包括GPT-4o、Claude 3.5等。而vulnhuntr也借助了这俩模型的功能。最早使用过ChatGPT的都知道它有一个上下文长度的问题,也叫Context windows。它最开始只能识别1024个token(token包含特殊字符、数字、字母等),也就是4000个字符,很难理解上下文,想要识别漏洞也只能在单个文件中。Claude 3.5包括现在的GPT-4o则大大增加了这个Context windows,大概是原来的200倍。这也给ai自动挖掘0day漏洞带来了可能。因为代码文件之间是存在逻辑关联的,漏洞往往从一个文件输入数据后,要经多个文件中的多个函数来传递。而Context windows长度就会对此有很大影响。这个团队最初尝试用RAG和微调来解决Context windows的影响,但是效果不尽如人意。

为什么采用静态分析

Retrieval Augmented Generatio(RAG)看起来很有希望将漏洞调用链放在一起。这是一种将大量文本直接解析为token的技术,将它们存储在数据库中,然后允许使用LLM查询数据库。但是对于多个不同类具有相同方法名的场景,如Class1.run(), Class2.run(),识别起来不准确,容易将非实际调用的函数添加到LLM。而在微调方面,vulnhuntr团队收集了这些数百个补丁前后的代码,并将它们与易受攻击的代码数据库(如CVEFixes)结合在一起,但是实际效果也不好。所以最终还是结合了静态代码分析。静态分析解析Python这种动态类型语言有很多问题,但是vulnhuntr选择了表现相对较好的Jedi作为静态解析器。结合静态分析核心还是为了解决Context windows限制的问题,静态分析手动解析代码,只将用户输入到服务器输出的调用链代码传入。如果未来Context windows能到数百万甚至无限大,也就不需要再配合静态分析。

后来为了解决这个问题,这个团队开发Vulnhuntr时采用了一个巧妙的方式:将代码分解成易于管理的小块,而不是直接将多个代码文件传给LLM。它只将与请求代码相关的部分传入。首先它会自动在项目文件中搜索可能优先处理用户输入的文件,然后从文件中提取潜在的所有漏洞,对这些漏洞跟进相关的function/class,最终完成函数调用链。当它请求函数、类或其他确认或拒绝漏洞所需的相关代码片段时,它会在循环中进行分析和重新分析。这种情况一直持续到Vulnhuntr看到足够的代码来映射出从用户输入到服务器输出的完整路径为止。

完成分析后会指出问题点、提供poc、提供每个漏洞的置信度评级。它无法100%确定存在漏洞,但会明确告诉用户需要关注什么区域的代码。另外,由于LLM具有不确定性,因此可以在完全相同的项目上多次运行该工具并得到不同的结果进行综合评定。置信度评级得分在7及以上的才可能是漏洞。

官方架构图如下

代码主要有四个LLMs.py、__main__.py、prompts.py、symbol_finder.py。LLMs.py主要是用于调用GPT-4o、Claude 3.5的接口并处理常见异常等。

Prompts

由于这个工具是借助GPT-4o、Claude 3.5,与LLM(大模型)进行交互,就涉及到Prompt(提示词),提示词对LLM的引导有很大作用。它参考提示词工程的最佳实践指引,使用思维链(如何理解Chain of thought,所谓的思维链简单来说就是在提示词阶段说明第一步干什么,第二步干什么),prompts采用xml格式,输出也定义了标准格式。

漏洞类型关注点提示

查看vulnhuntr的prompts.py。结构都差不多,拿SSRF漏洞类型来说

SSRF_TEMPLATE = """
Combine the code in <file_code> and <context_code> tags then analyze for remotely-exploitable Server-Side Request Forgery (SSRF) vulnerabilities by following the remote user-input call chain of code.

SSRF-Specific Focus Areas:
1. High-Risk Functions and Methods:
   - requests.get(), urllib.request.urlopen()
   - Custom HTTP clients
   - API calls to external services

2. URL Parsing and Validation:
   - URL parsing libraries usage
   - Custom URL validation routines

3. Indirect SSRF Vectors:
   - File inclusion functions (e.g., reading from URLs)
   - XML parsers with external entity processing
   - PDF generators, image processors using remote resources

4. Cloud Metadata Access:
   - Requests to cloud provider metadata URLs

5. Example SSRF-Specific Bypass Techniques are provided in <example_bypasses></example_bypasses> tags.

When analyzing, consider:
- How user input influences outgoing network requests
- Effectiveness of URL validation and whitelisting approaches
- Potential for DNS rebinding or time-of-check to time-of-use attacks
"""

所有漏洞类型的开头,都有这样一句:结合<file_code>和<context_code>标签中的代码,然后通过跟踪远程用户输入代码调用链来分析XX类型漏洞。

针对特定的漏洞类型,列出了需要关注的函数、可能存在漏洞的场景、分析时需要考虑的要素。

poc提示

并且在提示词文件中定义了VULN_SPECIFIC_BYPASSES_AND_PROMPTS,包含了漏洞类型的一些常见poc。

VULN_SPECIFIC_BYPASSES_AND_PROMPTS = {
    "LFI": {
        "prompt": LFI_TEMPLATE,
        "bypasses" : [
            "../../../../etc/passwd",
            "/proc/self/environ",
            "data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7Pz4=",
            "file:///etc/passwd",
            "C:\\win.ini"
            "/?../../../../../../../etc/passwd"
        ]
    },
    "RCE": {
        "prompt": RCE_TEMPLATE,
        "bypasses" : [
            "__import__('os').system('id')",
            "eval('__import__(\\'os\\').popen(\\'id\\').read()')",
            "exec('import subprocess;print(subprocess.check_output([\\'id\\']))')",
            "globals()['__builtins__'].__import__('os').system('id')",
            "getattr(__import__('os'), 'system')('id')",
            "$(touch${IFS}/tmp/mcinerney)",
            "import pickle; pickle.loads(b'cos\\nsystem\\n(S\"id\"\\ntR.')"
        ]
    },

分析过程提示

ANALYSIS_APPROACH_TEMPLATE定义了分析过程,挑选了部分关键点如下。

ANALYSIS_APPROACH_TEMPLATE = """
2. Vulnerability Scanning:
   - You only care about remotely exploitable network related components and remote user input handlers.
   - Identify potential entry points for vulnerabilities.
   - Consider non-obvious attack vectors and edge cases.

3. Code Path Analysis:
   - Very important: trace the flow of user input from remote request source to function sink.
   - Examine input validation, sanitization, and encoding practices.
   - Analyze how data is processed, stored, and output.

4. Security Control Analysis:
   - Evaluate each security measure's implementation and effectiveness.
   - Formulate potential bypass techniques, considering latest exploit methods.

2. 漏洞扫描阶段要识别漏洞的潜在入口、考虑不明显的攻击向量。3. 代码路径分析要从用户输入的请求源头跟踪;分析数据的处理、存储和输出方式 4. 安全控制分析评估每项安全措施的有效性(例如黑白名单是否有效等),考虑最新的攻击方法,指定潜在的绕过技术。这些给LLM提出的要求,以便分析过程能够符合漏洞挖掘注意事项。

静态分析

查看symbol_finder.py。结合jedi库(用于解析Python代码并搜索符号的定义),从代码仓库中查找并提取指定符号的定义和信息。相当于一个符号提取器。同时定义了一些不需要搜索的目录,例如`/test`、`/docs`等,这些路径下的内容不做提取。extract方法根据给定的符号名(如类名、方法名等)、代码行等找到对应的符号。

程序逻辑

查看__main__.py,首先程序在初始化阶段整合了常见的python框架路由写法,部分如下。

从上面这些正则匹配到的文件作为入口文件进行分析。真正执行分析的代码如下。

如果初步检测到了漏洞,就进一步对每个漏洞类型进行深度分析,对检测到的漏洞类型,检测是否有上下文代码需要提取(函数或类定义等)。如果需要就调用静态分析中的extract方法。将提取到的代码定义加入到新的用户输入的prompt中,构建一个新的请求。如果没有新的上下文代码加入到分析中,就停止程序。

ThreadLocal 是 Java 中的一个类,它提供了一种线程局部变量的机制。线程局部变量是指每个线程都有自己的变量副本,每个线程对该变量的访问都是独立的,互不影响。 ThreadLocal 主要用于解决多线程并发访问共享变量时的线程安全问题。在多线程环境下,如果多个线程共同访问同一个变量,可能会出现竞争条件,导致数据不一致或者出现线程安全问题。通过使用 ThreadLocal,可以为每个线程提供独立的副本,从而避免了线程安全问题。 ThreadLocal 的工作原理是,每个 Thread 对象内部都维护了一个 ThreadLocalMap 对象,ThreadLocalMap 是一个 key-value 结构,其中 key 是 ThreadLocal 对象,value 是该线程对应的变量副本。当访问 ThreadLocal 的 get() 方法时,会根据当前线程获取到对应的 ThreadLocalMap 对象,并从中查找到与 ThreadLocal 对象对应的值。如果当前线程尚未设置该 ThreadLocal 对象的值,则会通过 initialValue() 方法初始化一个值,并将其存入 ThreadLocalMap 中。当访问 ThreadLocal 的 set() 方法时,会将指定的值存入当前线程对应的 ThreadLocalMap 中。 需要注意的是,ThreadLocal 并不能解决共享资源的并发访问问题,它只是提供了一种线程内部的隔离机制。在使用 ThreadLocal 时,需要注意合理地使用,避免出现内存泄漏或者数据不一致的情况。另外,由于 ThreadLocal 使用了线程的 ThreadLocalMap,因此在使用完 ThreadLocal 后,需要手动调用 remove() 方法清理对应的变量副本,以防止内存泄漏。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值