Trojan Tactics:合约恶意代码隐藏与蜜罐
你看到的代码不一定是你执行的代码
今日聊聊怎么隐藏恶意代码以及怎么利用这种手段来制造蜜罐
一、利用外部合约隐藏恶意代码(Trojan)
Solidity 中,任何地址都可以被“强制类型转换”为任意合约类型。你以为在执行某个合约的函数,实际上却在执行另一个地址上的代码。
看似无辜的代码
1 | contract Foo { |
Foo.callBar() 看似安全,他调用Bar合约的log函数来记录日志
但是bar对象来源于外部传入的地址,而不是内部使用new方法来实例化
恶意代码:
攻击者部署Foo合约的时候,实际传给constructor的地址的并不是 Bar 的地址,而是另一个我们看不到的合约 —— Mal:
1 | contract Mal { |
所以当你调用 Foo.callBar(),执行的是Mal合约中伪造的log()函数
Tips:
- 避免依赖外部传入的合约地址
- 将
construtor设置为public,以便审查传入的地址 - 更安全的做法:在构造函数中直接创建依赖合约
bar = new Bar()
二、蜜罐(Honey Pot)
Background:
当攻击者审到这段代码
1 | function withdraw(uint _amount) public { |
一眼重入,鉴定为状态更新写晚了,直接狠狠地进行一个调用的嵌套
Exploit:
攻击者部署如下合约尝试攻击:
1 | function attack() public payable { |
存入 1 ether,调用 withdraw(),妄图重入攻击榨干合约
蜜罐
但实际上,Bank 合约调用的 logger.log() 实际上并不是 Logger,而是利用上述Trojan技巧隐藏的蜜罐:
1 | contract HoneyPot { |
只要是尝试Withdraw行为,蜜罐就会 revert(),让整个交易失败,操作回滚
知识补充
为什么不直接把_action参数与Withdraw字符串进行比较
因为_action是string类型,本质是bytes的动态数组,存放在memory中,而solidity的==操作符默认不允许用于动态内存数组的比较
否则编译时会报错:TypeError: Operator == not compatible with types string memory and string literal.
所以这里使用keccak256(abi.encodePacked(...))讲动态长度的string压缩成定长的bytes32 hash
通过隐藏代码欺骗用户,通过蜜罐欺骗攻击者,唉唉,欺骗的艺术
- Title: Trojan Tactics:合约恶意代码隐藏与蜜罐
- Author: Chiu
- Created at : 2025-04-23 21:55:53
- Updated at : 2025-07-15 13:01:35
- Link: https://github.com/Idealist17/github.io/2025/04/23/合约恶意代码隐藏与蜜罐/
- License: This work is licensed under CC BY-NC-SA 4.0.