Fallback
receive 函数能够在合约账户接收以太币的时候触发 fallback,所以只要向合约发出带有以太币的交易就可以触发这个函数转移 owner
Fallout
拼写错误
Coin Flip
Telephone
- tx.origin:交易发送方
- msg.sender:消息发送方
比如 用户账户 A → 合约账户 B → 合约账户 C,那么 C 获取到的 tx.origin 为 A,msg.sender 为 B。所以写个合约调用就行。
Token
整型下溢就行
Delegation
https://www.wtf.academy/solidity-advanced/Delegatecall/
当用户 A 通过合约 B 来 delegatecall 合约 C 的时候,执行的是合约 C 的函数,但是语境仍是合约 B 的:msg.sender 是 A 的地址,并且如果函数改变一些状态变量,产生的效果会作用于合约 B 的变量上。
Force
selfdestruct
强制转账。
Vault
存储的所有状态变量都是上链的
King
可以看到题目合约在 receive 时会向前一个 king transfer,所以自己恶意合约的 receive 函数 revert 就行。
Re-entrancy
重入攻击,可以发现 withdraw
中直接调用的 msg.sender.call{value:_amount}("")
并且是先转账再改变状态,所以可以在转账的时候再次调用 withdraw
达到重入的效果。
Elevator
意思挺简单的,就是让一个函数第一次返回 false 第二次返回 true:
hack 合约
Privacy
这里考了 solidity storage 的布局以及 arg encoding。具体内容可以参考 ctf-wiki 进行学习。
可以知道 key 在 slot[5]
并且对于 byte32 取 byte16 即相当于取它的前 16 个字节。
Gatekeeper One
gateOne
: require(msg.sender != tx.origin);
,使用合约调用即可。
gateThree
: 一系列类型转换和截断,总之这样构造就行:
主要是 gateTwo
计算 gas 实现 require(gasleft() % 8191 == 0);
这一步有点折磨。
观察到 etherscan 上有环境的 compiler version 为 0.8.12,所以尝试利用此版本本地编译合约后使用 forge debug
的方式精确找到 gas:
不过这里奇怪的问题是 forge test
能够通过:
但是远程测试网里会失败emmm,似乎字节码还是没对上。
所以还是改成了暴力枚举…
Gatekeeper Two
gateTwo
考察的是 solidity 创建 contract 过程,在 constructor
中执行时 extcodesize
会返回 0,其余两个 gate 不用太多解释了。
Naught Coin
ERC20 可以 approve
然后 transferFrom
转钱。