
WTF Solidity 合约安全: S08. 绕过合约检查
我最近在重新学solidity,巩固一下细节,也写一个“WTF Solidity极简入门”,供小白们使用(编程大佬可以另找教程),每周更新1-3讲。 推特:@0xAA_Science|@WTFAcademy_ 社区:Discord|微信群|官网 wtf.academy 所有代码和教程开源在github: github.com/AmazingAng/WTFSolidity这一讲,我们将介绍绕过合约长度检查,并介绍预防的方法。绕过合约检查很多 freemint 的项目为了限制科学家(程序员)会用到 isContract() 方法,希望将调用者 msg.sender 限制为外部账户(EOA),而非合约。这个函数利用 extcodesize 获取该地址所存储的 bytecode 长度(runtime),若大于0,则判断为合约,否则就是EOA(用户)。 // 利用 extcodesize 检查是否为合约 function isContract(address account) public view returns (bool) { // extcodesize > 0 的地址一定是合约...

WTF Solidity 合约安全: S09. 拒绝服务
我最近在重新学solidity,巩固一下细节,也写一个“WTF Solidity极简入门”,供小白们使用(编程大佬可以另找教程),每周更新1-3讲。 推特:@0xAA_Science|@WTFAcademy_ 社区:Discord|微信群|官网 wtf.academy 所有代码和教程开源在github: github.com/AmazingAng/WTFSolidity这一讲,我们将介绍智能合约的拒绝服务(Denial of Service, DoS)漏洞,并介绍预防的方法。NFT项目 Akutar 曾因为 DoS 漏洞损失 11,539 ETH,当时价值 3400 万美元。DoS在 Web2 中,拒绝服务攻击(DoS)是指通过向服务器发送大量垃圾信息或干扰信息的方式,导致服务器无法向正常用户提供服务的现象。而在 Web3,它指的是利用漏洞使得智能合约无法正常提供服务。 在2022年4月,一个很火的 NFT 项目名为 Akutar,他们使用荷兰拍卖进行公开发行,筹集了 11,539.5 ETH,非常成功。之前持有他们社区Pass的参与者会得到 0.5 ETH的退款,但是他们处理...

WTF Solidity 合约安全 S06. 签名重放
我最近在重新学solidity,巩固一下细节,也写一个“WTF Solidity极简入门”,供小白们使用(编程大佬可以另找教程),每周更新1-3讲。 推特:@0xAA_Science|@WTFAcademy_ 社区:Discord|微信群|官网 wtf.academy 所有代码和教程开源在github: github.com/AmazingAng/WTFSolidity这一讲,我们将介绍智能合约的签名重放(Signature Replay)攻击和预防方法,它曾间接导致了著名做市商 Wintermute 被盗2000万枚 $OP。签名重放上学的时候,老师经常会让家长签字,有时候家长很忙,我就会很“贴心”照着以前的签字抄一遍。某种意义上来说,这就是签名重放。 在区块链中,数字签名可以用于识别数据签名者和验证数据完整性。发送交易时,用户使用私钥签名交易,使得其他人可以验证交易是由相应账户发出的。智能合约也能利用 ECDSA 算法验证用户将在链下创建的签名,然后执行铸造或转账等逻辑。更多关于数字签名的介绍请见WTF Solidity第37讲:数字签名。 数字签名一般有两种常见的重放攻击...
WTF Academy: wtf.academy

WTF Solidity 合约安全: S08. 绕过合约检查
我最近在重新学solidity,巩固一下细节,也写一个“WTF Solidity极简入门”,供小白们使用(编程大佬可以另找教程),每周更新1-3讲。 推特:@0xAA_Science|@WTFAcademy_ 社区:Discord|微信群|官网 wtf.academy 所有代码和教程开源在github: github.com/AmazingAng/WTFSolidity这一讲,我们将介绍绕过合约长度检查,并介绍预防的方法。绕过合约检查很多 freemint 的项目为了限制科学家(程序员)会用到 isContract() 方法,希望将调用者 msg.sender 限制为外部账户(EOA),而非合约。这个函数利用 extcodesize 获取该地址所存储的 bytecode 长度(runtime),若大于0,则判断为合约,否则就是EOA(用户)。 // 利用 extcodesize 检查是否为合约 function isContract(address account) public view returns (bool) { // extcodesize > 0 的地址一定是合约...

WTF Solidity 合约安全: S09. 拒绝服务
我最近在重新学solidity,巩固一下细节,也写一个“WTF Solidity极简入门”,供小白们使用(编程大佬可以另找教程),每周更新1-3讲。 推特:@0xAA_Science|@WTFAcademy_ 社区:Discord|微信群|官网 wtf.academy 所有代码和教程开源在github: github.com/AmazingAng/WTFSolidity这一讲,我们将介绍智能合约的拒绝服务(Denial of Service, DoS)漏洞,并介绍预防的方法。NFT项目 Akutar 曾因为 DoS 漏洞损失 11,539 ETH,当时价值 3400 万美元。DoS在 Web2 中,拒绝服务攻击(DoS)是指通过向服务器发送大量垃圾信息或干扰信息的方式,导致服务器无法向正常用户提供服务的现象。而在 Web3,它指的是利用漏洞使得智能合约无法正常提供服务。 在2022年4月,一个很火的 NFT 项目名为 Akutar,他们使用荷兰拍卖进行公开发行,筹集了 11,539.5 ETH,非常成功。之前持有他们社区Pass的参与者会得到 0.5 ETH的退款,但是他们处理...

WTF Solidity 合约安全 S06. 签名重放
我最近在重新学solidity,巩固一下细节,也写一个“WTF Solidity极简入门”,供小白们使用(编程大佬可以另找教程),每周更新1-3讲。 推特:@0xAA_Science|@WTFAcademy_ 社区:Discord|微信群|官网 wtf.academy 所有代码和教程开源在github: github.com/AmazingAng/WTFSolidity这一讲,我们将介绍智能合约的签名重放(Signature Replay)攻击和预防方法,它曾间接导致了著名做市商 Wintermute 被盗2000万枚 $OP。签名重放上学的时候,老师经常会让家长签字,有时候家长很忙,我就会很“贴心”照着以前的签字抄一遍。某种意义上来说,这就是签名重放。 在区块链中,数字签名可以用于识别数据签名者和验证数据完整性。发送交易时,用户使用私钥签名交易,使得其他人可以验证交易是由相应账户发出的。智能合约也能利用 ECDSA 算法验证用户将在链下创建的签名,然后执行铸造或转账等逻辑。更多关于数字签名的介绍请见WTF Solidity第37讲:数字签名。 数字签名一般有两种常见的重放攻击...
WTF Academy: wtf.academy

Subscribe to 0xAA

Subscribe to 0xAA
Share Dialog
Share Dialog
>100 subscribers
>100 subscribers


我最近在重新学solidity,巩固一下细节,也写一个“WTF Solidity极简入门”,供小白们使用(编程大佬可以另找教程),每周更新1-3讲。
所有代码和教程开源在github: github.com/AmazingAng/WTFSolidity
这一讲,我们将介绍代理合约中选择器冲突(Selector Clash)的另一个解决办法:通用可升级代理(UUPS,universal upgradeable proxy standard)。教学代码由OpenZepplin的UUPSUpgradeable简化而成,不应用于生产。
我们在上一讲已经学习了"选择器冲突"(Selector Clash),即合约存在两个选择器相同的函数,可能会造成严重后果。作为透明代理的替代方案,UUPS也能解决这一问题。
UUPS(universal upgradeable proxy standard,通用可升级代理)将升级函数放在逻辑合约中。这样一来,如果有其它函数与升级函数存在“选择器冲突”,编译时就会报错。
下表中概括了普通可升级合约,透明代理,和UUPS的不同点:

首先我们要复习一下WTF Solidity极简教程第23讲:Delegatecall。如果用户A通过合约B(代理合约)去delegatecall合约C(逻辑合约),语境仍是合约B的语境,msg.sender仍是用户A而不是合约B。因此,UUPS合约可以将升级函数放在逻辑合约中,并检查调用者是否为管理员。

UUPS的代理合约看起来像是个不可升级的代理合约,非常简单,因为升级函数被放在了逻辑合约中。它包含3个变量:
implementation:逻辑合约地址。
admin:admin地址。
words:字符串,可以通过逻辑合约的函数改变。
它包含2个函数
构造函数:初始化admin和逻辑合约地址。
fallback():回调函数,将调用委托给逻辑合约。
contract UUPSProxy {
address public implementation; // 逻辑合约地址
address public admin; // admin地址
string public words; // 字符串,可以通过逻辑合约的函数改变
// 构造函数,初始化admin和逻辑合约地址
constructor(address _implementation){
admin = msg.sender;
implementation = _implementation;
}
// fallback函数,将调用委托给逻辑合约
fallback() external payable {
(bool success, bytes memory data) = implementation.delegatecall(msg.data);
}
}
UUPS的逻辑合约与第47讲中的不同是多了个升级函数。UUPS逻辑合约包含3个状态变量,与保持代理合约一致,防止插槽冲突。它包含2个
upgrade():升级函数,将改变逻辑合约地址implementation,只能由admin调用。
foo():旧UUPS逻辑合约会将words的值改为"old",新的会改为"new"。
// UUPS逻辑合约(升级函数写在逻辑合约内)
contract UUPS1{
// 状态变量和proxy合约一致,防止插槽冲突
address public implementation;
address public admin;
string public words; // 字符串,可以通过逻辑合约的函数改变
// 改变proxy中状态变量,选择器: 0xc2985578
function foo() public{
words = "old";
}
// 升级函数,改变逻辑合约地址,只能由admin调用。选择器:0x0900f010
// UUPS中,逻辑函数中必须包含升级函数,不然就不能再升级了。
function upgrade(address newImplementation) external {
require(msg.sender == admin);
implementation = newImplementation;
}
}
// 新的UUPS逻辑合约
contract UUPS2{
// 状态变量和proxy合约一致,防止插槽冲突
address public implementation;
address public admin;
string public words; // 字符串,可以通过逻辑合约的函数改变
// 改变proxy中状态变量,选择器: 0xc2985578
function foo() public{
words = "new";
}
// 升级函数,改变逻辑合约地址,只能由admin调用。选择器:0x0900f010
// UUPS中,逻辑函数中必须包含升级函数,不然就不能再升级了。
function upgrade(address newImplementation) external {
require(msg.sender == admin);
implementation = newImplementation;
}
}
部署UUPS新旧逻辑合约UUPS1和UUPS2。
部署UUPS代理合约UUPSProxy,将implementation地址指向把旧逻辑合约UUPS1。
利用选择器0xc2985578,在代理合约中调用旧逻辑合约UUPS1的foo()函数,将words的值改为"old"。
利用在线ABI编码器(HashEx)[https://abi.hashex.org/]获得二进制编码,调用升级函数upgrade(),将implementation地址指向新逻辑合约UUPS2。

5. 利用选择器0xc2985578,在代理合约中调用新逻辑合约UUPS2的foo()函数,将words的值改为"new"。
这一讲,我们介绍了代理合约“选择器冲突”的另一个解决方案:UUPS。与透明代理不同,UUPS将升级函数放在了逻辑合约中,从而使得"选择器冲突"不能通过编译。相比透明代理,UUPS更复杂,但是也更省gas。
我最近在重新学solidity,巩固一下细节,也写一个“WTF Solidity极简入门”,供小白们使用(编程大佬可以另找教程),每周更新1-3讲。
所有代码和教程开源在github: github.com/AmazingAng/WTFSolidity
这一讲,我们将介绍代理合约中选择器冲突(Selector Clash)的另一个解决办法:通用可升级代理(UUPS,universal upgradeable proxy standard)。教学代码由OpenZepplin的UUPSUpgradeable简化而成,不应用于生产。
我们在上一讲已经学习了"选择器冲突"(Selector Clash),即合约存在两个选择器相同的函数,可能会造成严重后果。作为透明代理的替代方案,UUPS也能解决这一问题。
UUPS(universal upgradeable proxy standard,通用可升级代理)将升级函数放在逻辑合约中。这样一来,如果有其它函数与升级函数存在“选择器冲突”,编译时就会报错。
下表中概括了普通可升级合约,透明代理,和UUPS的不同点:

首先我们要复习一下WTF Solidity极简教程第23讲:Delegatecall。如果用户A通过合约B(代理合约)去delegatecall合约C(逻辑合约),语境仍是合约B的语境,msg.sender仍是用户A而不是合约B。因此,UUPS合约可以将升级函数放在逻辑合约中,并检查调用者是否为管理员。

UUPS的代理合约看起来像是个不可升级的代理合约,非常简单,因为升级函数被放在了逻辑合约中。它包含3个变量:
implementation:逻辑合约地址。
admin:admin地址。
words:字符串,可以通过逻辑合约的函数改变。
它包含2个函数
构造函数:初始化admin和逻辑合约地址。
fallback():回调函数,将调用委托给逻辑合约。
contract UUPSProxy {
address public implementation; // 逻辑合约地址
address public admin; // admin地址
string public words; // 字符串,可以通过逻辑合约的函数改变
// 构造函数,初始化admin和逻辑合约地址
constructor(address _implementation){
admin = msg.sender;
implementation = _implementation;
}
// fallback函数,将调用委托给逻辑合约
fallback() external payable {
(bool success, bytes memory data) = implementation.delegatecall(msg.data);
}
}
UUPS的逻辑合约与第47讲中的不同是多了个升级函数。UUPS逻辑合约包含3个状态变量,与保持代理合约一致,防止插槽冲突。它包含2个
upgrade():升级函数,将改变逻辑合约地址implementation,只能由admin调用。
foo():旧UUPS逻辑合约会将words的值改为"old",新的会改为"new"。
// UUPS逻辑合约(升级函数写在逻辑合约内)
contract UUPS1{
// 状态变量和proxy合约一致,防止插槽冲突
address public implementation;
address public admin;
string public words; // 字符串,可以通过逻辑合约的函数改变
// 改变proxy中状态变量,选择器: 0xc2985578
function foo() public{
words = "old";
}
// 升级函数,改变逻辑合约地址,只能由admin调用。选择器:0x0900f010
// UUPS中,逻辑函数中必须包含升级函数,不然就不能再升级了。
function upgrade(address newImplementation) external {
require(msg.sender == admin);
implementation = newImplementation;
}
}
// 新的UUPS逻辑合约
contract UUPS2{
// 状态变量和proxy合约一致,防止插槽冲突
address public implementation;
address public admin;
string public words; // 字符串,可以通过逻辑合约的函数改变
// 改变proxy中状态变量,选择器: 0xc2985578
function foo() public{
words = "new";
}
// 升级函数,改变逻辑合约地址,只能由admin调用。选择器:0x0900f010
// UUPS中,逻辑函数中必须包含升级函数,不然就不能再升级了。
function upgrade(address newImplementation) external {
require(msg.sender == admin);
implementation = newImplementation;
}
}
部署UUPS新旧逻辑合约UUPS1和UUPS2。
部署UUPS代理合约UUPSProxy,将implementation地址指向把旧逻辑合约UUPS1。
利用选择器0xc2985578,在代理合约中调用旧逻辑合约UUPS1的foo()函数,将words的值改为"old"。
利用在线ABI编码器(HashEx)[https://abi.hashex.org/]获得二进制编码,调用升级函数upgrade(),将implementation地址指向新逻辑合约UUPS2。

5. 利用选择器0xc2985578,在代理合约中调用新逻辑合约UUPS2的foo()函数,将words的值改为"new"。
这一讲,我们介绍了代理合约“选择器冲突”的另一个解决方案:UUPS。与透明代理不同,UUPS将升级函数放在了逻辑合约中,从而使得"选择器冲突"不能通过编译。相比透明代理,UUPS更复杂,但是也更省gas。
No activity yet