0%

blockchain - 2024合约漏洞类型汇总(更新中)

汇总一下迄今为止所有的智能合约漏洞

按照 2023 年的这篇 A survey on smart contract vulnerabilities Data sources, detection and repair,将漏洞类型分为三类:solidity 语言漏洞、EVM漏洞和区块链漏洞

[0] 术语规范

由于中英互译带来的一系列问题,笔者在本处规范一些术语的常见用法,便于读者查看。

  • 被攻击合约 Victim

  • 攻击合约 Attacker

[1] solidity 语言漏洞

[1-1] Re-entrancy

原理

从最著名的重入攻击开始。首先,我们先铺垫一些基础知识。任何合约如果要在部署后接收以太币,都会调用 receive 函数或者 fallback 函数来处理,如果一个合约既没有 receive 函数,也没有 fallback 函数,那么就无法接收 ether 了。

而重入漏洞的本质是:Victim 先处理了真币,再处理合约账本上记录的内容。为什么这样的顺序会导致漏洞呢?

比如 attacker 调用 victim 向自己转 0.001 wei,在 victim transfer 之后,attacker 收到了 0.001 wei,但同时 attacker 也会调用 fallback 函数。而恶意攻击者可以在 attacker 的 fallback 函数中再次调用 victim 转账函数。

这样,victim 账本记录的内容并没有发生改变,但该合约下存储的 ether 就会一点一点被转干净。

样例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;

import "openzeppelin-contracts-06/math/SafeMath.sol";

contract Reentrance {
using SafeMath for uint256;

mapping(address => uint256) public balances;

function donate(address _to) public payable {
balances[_to] = balances[_to].add(msg.value);
}

function balanceOf(address _who) public view returns (uint256 balance) {
return balances[_who];
}

function withdraw(uint256 _amount) public {
if (balances[msg.sender] >= _amount) {
(bool result,) = msg.sender.call{value: _amount}("");
if (result) {
_amount;
}
balances[msg.sender] -= _amount;
}
}

receive() external payable {}
}

[1-2] 错别字漏洞

今天跑完步回来比较晚了。。摸个鱼写个简单一点的漏洞。

这种漏洞显而易见,就是重要的函数的名称写的不对,比如 constructor 写成 construct0r,fallback 写成 fal1back(当然很扯但不是不可能。。)

如果要检测的话可以扫描合约,然后查找里面是否有’constr’/‘constru’/‘onstruc’/‘nstruct’/…这样的字符片段,然后提取出来与正常函数名对比。

总之可以在这里记录一下重要的变量名或者函数

constructor,receive,fallback,transfer,…

[1-3] ABI encode

Attacker 在使用 call,send 等函数调用 Victim 时,会使用 data 传输函数标识符和参数等。如果我们精心构造 data 的编码,就可以

-------------文章就到这里啦!感谢您的阅读XD-------------