GPTScan:Detecting Logic Vulnerabilitiesi n SmartContracts by CombiningGPT with Program Analysis

综述

正如 45th_24_ICSE_Demystifying Exploitable Bugs in Smart Contracts 将漏洞分为26种。现在,可以将这 26 种漏洞分为三组。 第一组漏洞很难利用,不确定,或者与给定项目的功能不直接相关。第二组漏洞涉及到使用简单和通用的预言机,不需要深入理解代码语义。示例包括重入攻击和算术溢出。这样的漏洞可以通过数据流跟踪(例如,Slither ),静态符号执行(例如,Solidity SMT Checker 和 Mythril )以及其他静态分析工具来检测。第三组漏洞需要高级语义预言机来检测,并且与业务逻辑密切相关。这些漏洞中的大多数都无法通过现有的静态分析工具检测到。该组包括六种主要类型的漏洞:(S1)价格操纵,(S2)与ID相关的违规行为,(S3)错误的状态更新,(S4)原子性违规,(S5)权限提升,以及(S6)错误的会计处理。

困难

  • 静态分析无法理解高级语义信息,而 GPT 可能会忽略一些低级信息,可能导致召回率低和误报率高。结合这两种技术可以相互补充,提高检测性能。
  • 智能合约项目可能包含数十个 Solidity 文件,直接将所有文件输入 GPT 成本高昂。此外,非漏洞函数的占比过高,可能会影响 GPT 对漏洞函数的识别。综上两者,必须缩小提供给 GPT 的候选函数。
  • 现有的基于 GPT 的漏洞检测工作通过向 GPT 提供高级漏洞描述来进行漏洞匹配,这要么要求 GPT 具备高级推理能力(思维链过长会显著降低 GPT 的准确性),要么通过预训练/微调优化 GPT 在智能合约漏洞检测方面的表现(实际上不可行)。因此,应该转化思维,尽量让 GPT 作为通用的代码理解工具,直接从代码级语义识别漏洞类型,这叫要求我们必须做一些前置工作。

核心观点

  • 将漏洞 vulnerabilities 分解为 scenarios 场景properties 属性
    • 场景是对函数功能的描述,这有助于 GPTScan 进行候选函数的初步筛选,以减少不必要的后续扫描。
      • 例如 Front Running漏洞,场景描述为 “mint or vest(和transfer有什么区别) or collect token/liquidity/earning and assign them to
        the address recipient or to variable”。此类操作在之前的交易中被授权,允许攻击者提前执行某个函数并获得不公平的优势。
    • 属性是对函数行为的描述,例如缺乏安全检查和错误的会计顺序(accounting order)

次要观点

  • GPT 在漏洞检测中的应用。例如 GPT-3.5 这样的通用预训练变换器(GPT)模型是在庞大的文本语料库上训练的大型语言模型(LLMs),包括不同编程语言和漏洞的源代码描述。有了这些知识,GPT 可以理解和解释源代码,实现零样本学习,即不需要漏洞示例就能检测源代码中的漏洞。

  • 解决 GPT 模型有时会提供模糊的答案或难以解析的文本的问题:

    • 场景和属性匹配中,限定 GPT 只能回答“是/否“
    • 降低 GPT 模型的温度参数,使模型倾向于确定性。
    • 提出了一种称为“背景模仿”(mimic-in-the-background)的提示技巧,这受到了在零样本链式思考提示中“让我们一步步地思考。”的启发。如图4所示,我们使用一个GPT系统提示来指导模型在背景中模仿五次反问,并提供最常出现的答案以确保更大的一致性。
    • 在这里插入图片描述

工作

在这里插入图片描述

多维函数过滤

在这里插入图片描述

  • 项目级文件过滤:排除非 Solidity 文件,例如在“node_modules”目录下的文件、“test”目录中的测试文件、以及第三方库文件(例如“openzeppelin”)。
  • 过滤 OZ 库函数:虽然过滤掉了作为库导入的 OpenZeppelin 合约,但 OZ 函数经常直接复制到许多开发者的合约代码中,使得我们的项目级文件过滤无效。为了解决这个问题,我们首先对 OZ 的源代码进行离线分析,提取其所有 API 函数签名作为白名单。白名单中的每个函数签名包括访问控制修饰符、类名(子合约名)、函数名、返回值类型和参数类型。例如,ERC20 合约中转账函数的签名是公开的ERC20.transfer(address,uint256)。接下来,GPTScan 生成所有候选函数的签名,并与白名单中的签名进行比较。请注意,候选函数的签名是使用类名和继承类的名称生成的,因为开发者可能会实现继承类。通过进行这种比较,GPTScan 排除了与白名单中函数签名相同的函数,我们认为这在本文中是安全的。将来,我们将增加基于克隆的过滤,以覆盖函数体。
    在这里插入图片描述
  • Vulnerability-specific function filtering 特定漏洞函数过滤:设计了一个基于YAML的过滤规则规范,以支持以下过滤规则:
    • FNK:函数名称应至少包含一个关键词。
    • FCE:函数内容应至少包含一个表达式。
    • FCNE:函数内容不应包含任何表达式。
    • FCCE:函数内容应至少包含一组给定的表达式。
    • FCNCE:函数内容不应包含任何给定表达式的组合。
    • FPT:函数参数应与给定类型匹配。
    • FPNC:函数应为公共的,我们将不对其调用者进行分析。
    • FNM:函数不应包含具有访问控制修饰符(例如,onlyOwner)的修饰符。
    • CFN:此函数的调用者将不会被分析。

例如:
Price Manipulation by AMM is related to the calculation of token prices. In this rule, we used the FNK rule to select functions
related to price calculation, and the FCE rule to select functions that contain the keywords “price,” “value,” and “liquidity.”

这里说反了,实质上,FNK 才是关键词,FCE 是表达式

可达性分析

多维过滤后,我们调用图分析以确定候选功能的可达性。本文使用 ANTLR,一个解析器和生成器,来解析源代码。a lexer and parser generator, to parse the source code of the smart contract project and generate an abstract syntax tree (AST).

Solidity 中,有四种类型的访问控制注释:public, external, internal private。

  • 标记为 public、external 的内置函数可以被任何人调用,使它们对潜在攻击者直接可达。
  • 标记为 internal、private 的函数可能会被其他可达函数调用。
  • 标记为 OnlyOwner 的函数只允许所有者调用,我们认为这是不可达的。被认为是不可达的函数不再进行后续基于 GPT 的匹配。

迈向静态分析

在这里插入图片描述

尽管候选函数通过了场景和属性的GPT匹配、可达性分析,但GPT并不总是关注语法细节,如条件语句、要求语句、断言语句、回滚语句等。

静态分析工具通常关注特定的变量或语句,而我们当前的输入仍然是函数。

这时候用 GPT 来提取与 prompt 中描述相符的特定业务逻辑相关的变量和语句的地方。
有了这些变量和语句,我们可以使用静态分析来确认是否存在漏洞。

// prompt
In this function, which variable or function holds the total supply/liquidity AND is used by the conditional branch to
determine the supply/liquidity is 0? Please answer in asection starts with "VariableB:".

In this function, which variable or function holds the value of the deposit/mint/add amount? Please answer in a section starts with "VariableC:".

Please answer in the following json format:
{"VariableA":{"Variable name":"Description"}, "VariableB":{"Variable name":"Description"}, "VariableC":{"Variable name":"Description"}}
[%CODE%]

静态分析

设计了以下四种主要的静态分析类型来验证分类表中列出的常见逻辑漏洞:

  • 静态数据流跟踪(DF):这种方法追踪程序中变量的数据流,静态分析确定GPT提供的两个变量或表达式是否有数据依赖性。例如,如下代码显示了需要静态分析来 determine whether the share is calculated directly with the deposit amount in the Risky First Deposit vulnerability
//  The Risky First Deposit (line 8-9) vulnerability.
function deposit ( uint256 _amount ) external returns ( uint256 ) {
	uint256 _pool = balance () ;
	uint256 _before = token . balanceOf ( address ( this ));
	token . safeTransferFrom ( msg . sender , address ( this ) , _amount );
    uint256 _after = token . balanceOf ( address ( this ));
	_amount = _after . sub ( _before ); // Additional check for deflationary tokens
	uint256 _shares = 0;
	if ( totalSupply () == 0) {
		_shares = _amount ;
	 } else {
		 _shares = ( _amount . mul ( totalSupply () )). div ( _pool );
	 }
	 _mint ( msg . sender , _shares );
	 }
  • 值比较检查(VC):该方法用于检查两个变量或表达式是否在条件语句中进行了比较,如require、assert、if。它用于确保在使用变量或表达式之前正确检查它们。在 Risky First Deposit 中,VC 用于检查代码是否将 share 与 amout 进行比较;类似地,在 unauthorized tranfer 中,VC 用于验证转账前是否已检查发送者。
  • 顺序检查(OC):该方法检查两个语句的执行顺序,其中静态分析由GPT提供的两个语句的顺序决定。如下代码显示了使用 OC 来验证执行转账和更新错误检查点顺序中的检查点的执行顺序。
function transfer ( address account , uint256 amount ) external override notPaused returns ( bool ) {
	require ( msg . sender != account , Error .SELF_TRANSFER_NOT_ALLOWED );
	require ( balances [ msg . sender ] >= amount , Error .INSUFFICIENT_BALANCE );
    // Initialize the ILiquidityPool pool variable
	pool . handleLpTokenTransfer ( msg . sender , account , amount );
	balances [ msg . sender ] -= amount ;
	balances [ account ] += amount ;
	address lpGauge = currentAddresses [ _LP_GAUGE ];
	if ( lpGauge != address (0) ) {
		ILpGauge ( lpGauge ). userCheckpoint ( msg . sender );
		ILpGauge ( lpGauge ). userCheckpoint ( account );
	 }
	emit Transfer ( msg . sender , account , amount );
	return true ;
    }
  • 函数调用参数检查 Function Call Argument Check(FA):该方法检查函数调用的参数是否
    • 可以被用户控制?或
    • 满足特定要求 specific requirements. 。
      具体来说,GPT 提供了一个函数调用和一个参数的索引( a function call and the index of an argument,),静态分析确定参数是否可以被用户控制或满足规则中描述的要求。在通过购买令牌进行价格操纵(Price Manipulation by Buying Tokens)的情况下,需要使用 FA 检查函数调用,因为一些敏感变量可能被用作参数并导致价格操纵。

评估

数据集

第一个数据集,称为Top200,包含了市值前 200名的智能合约。它包括了来自六个主流以太坊兼容链的 303 个开源合约项目。由于这些项目经过充分审计且广泛使用,可以合理假设它们不包含显著的漏洞。这个数据集主要用于测试 GPTScan 在审计合约中的误报率。

第二个数据集,称为 Web3Bugs,是从最近的 Web3Bugs 数据集中收集的,该数据集包括 100 个 Code4rena 审计的项目。在这 100 个项目中,我们包括了可以直接编译的72个项目。其余的项目要么错过了原始Web3Bugs仓库中的库依赖关系,要么配置文件缺失。这个数据集主要用于测试 GPTScan 在审计合约中的召回率。

第三个数据集,称为 De-fiHacks,来自著名的 DeFi Hacks 数据集,该数据集包含过去发生过攻击的易受攻击的令牌合约。我们包括了13个易受攻击的项目,这些项目肯定涵盖了我们十种类型中的漏洞。这些数据集中的真实漏洞包括那些已经报告的以及 GPTScan 新检测并得到社区确认的漏洞。这个数据集主要用于测试 GPTScan 在审计合约中的召回率。

问题

RQ1:当分析一个非易受攻击顶级合约的数据集时,GPTScan的假阳性率是多少?
RQ2:GPTScan在分析具有逻辑漏洞的真实世界数据集时的准确性如何,与现有工具相比效果如何?
RQ3:GPTScan的静态确认在提高GPTScan准确性方面的有效性如何?
RQ4:GPTScan的运行性能和财务成本是什么?

逻辑漏洞分类

在这里插入图片描述

在上述案例中,实现了一个存款(deposit)的操作。当流动性池的余额为0时,share 会被设置成存款总数。因此,第一个存款人可以存入很少的钱并占据所有的 share,之后转入一大笔使 share 价格通胀,从而获取不正当利益。对于静态分析的检测方法来说,难点在于如何判断业务场景并识别出关键变量。

在这里插入图片描述

漏洞描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值