背景信息

2024 年 9月 3日,Penpie 合约遭受重入攻击,攻击者在重入阶段向合约添加流动性来冒充奖励金额,从而获取合约内原有的奖励代币。资产损失高达 2734 万美元。

2024 年 5月,Penpie 平台新增了推出了无需许可的资产池功能,即允许 Pendle 上的用户可以在该平台上自建任何 PT 或 YT 代币的 LP 资金池,用户在 Penpie 平台上存入 LP 后,可以额外多获得一份代币奖励。

Trace 分析

攻击者首先在前置交易中新建了一个 market,并将 SY 地址设置为攻击合约 0x4af4

随后在攻击交易中,攻击者闪电贷了四种资产(由于四种资产的操作类似,我们选择其中一种资产 wstETH 进行分析)

闪电贷内进行了这几类操作

batchHarvestMarketRewards 函数中进行了重入攻击

代币流向分析

  1. 0x6e79.batchHarvestMarketRewards:

    1. redeemRewards:

      1. [Reentrancy] addLiquiditySingleTokenKeepYt:deposit 16010 wstETH to [Pendle: RouterV4], get 8860 [MarketToken]
      2. [Reentrancy] depositMarket:deposite 1751 [MarketToken] to [0x6e79], received 1751 [StakingToken]
    2. queueNewRewards:Claim 1751 [MarketToken] to 0xd128
  2. multiclaim:Claim 1751 [MarketToken] from 0xd128
  3. withdrawMarket:burn 1715 [StakingToken], get 1715 [MarketToken]
  4. removeLiquiditySingleToken:burn 10611 [MarketToken], get 18733 wstETH
  5. transfer:Repay flashloan

漏洞分析

CreatePool

任何用户都可以在 Pendle 上注册 Pool(market)

https://etherscan.io/address/0x588f5e5d85c85cac5de4a1616352778ecd9110d3#code

其中的 onlyVerifiedMarket 检查,会检查 pool 地址是否在 allMarkets 中。而任何人都可以创建池子,绕过这个限制。

https://vscode.blockscan.com/ethereum/0x45cF29F501d218Ad045EB8d622B69968E2d4Ef5C

batchHarvestMarketRewards

batchHarvestMarketRewards 函数中,通过计算调用 market.redeemRewards 函数前后的 MarketToken 数量差值,来得到作为奖励代币的 wstETH 数量。

攻击者利用这个设计缺陷,调用 0x6e79.batchHarvestMarketRewards 函数触发重入攻击,使得 bounsTokens 的值增大。

https://vscode.blockscan.com/ethereum/0xff51c6b493c1e4df4e491865352353eadff0f9f8

batchHarvestMarketRewards(Part1)

redeemRewards

由于 market 合约为攻击者创建的合约,其 SY 在创建时被设为了攻击合约的地址。

https://vscode.blockscan.com/ethereum/0x40789E8536C668c6A249aF61c81b9dfaC3EB8F32

函数调用流程

redeemRewards -> _redeemRewards -> _updateAndDistributeRewards -> _updateAndDistributeRewardsForTwo -> _updateRewardIndex -> _redeemExternalReward -> StandardizedYield.claimRewards

SY 为攻击合约地址,在 claimRewards 函数中进行重入。

[Reentrancy] addLiquiditySingleTokenKeepYt & depositMarket

[Reentrancy] addLiquiditySingleTokenKeepYt:deposit 16010 wstETH to [Pendle: SY-stETH Token], get 8860 [MarketToken][Reentrancy] depositMarket:deposite 1751 [MarketToken] to [StakingToken], received 1751 [StakingToken]

重入攻击 trace,通过 addLiquiditySingleTokenKeepYtdepositMarket 操作将 wstETH 转换为 MarketToken ,并质押到 0x6e79合约中。

batchHarvestMarketRewards(Part2)

通过重入攻击,使得合约在计算 originalBonusBalance 奖励数量时误以为获得了 1751 的奖励(实际上并没有获得任何奖励,余额多出来的部分是重入的时候添加流动性那部分)。

originalBonusBalanceleftBonusBalance 的值会按照 _harvestBatchMarketRewards -> _sendRewards -> _queueRewarder 的调用路径传递到 _queueRewarder 函数中。

此时合约会向 0xd128 地址发送 1751 _rewardToken

攻击者在通过重入添加流动性时,所添加的代币数量 1751 等于 0x6e79 合约中代币余额的数量 1751,其目的是构造“新增奖励”的数量等于“账户余额”,使得接下来的 queueRewarder 函数将 0x6e79 合约中的所有代币转移到 0xd128。

queueNewRewards

queueNewRewards:Claim 1751 [MarketToken] to 0xd128

0xd128 从 0x6e79 处转移奖励代币。其中0xd128是rewardPool合约。

multiclaim

multiclaim:get 1751 [MarketToken] from 0xd128

攻击者从 0xd128 合约中领取奖励(完成获利,这笔资金的来源是 0x6e79 合约的余额)

withdrawMarket

withdrawMarket:burn 1751 [StakingToken], get 1751 [MarketToken]

取回在重入中通过 depositMarket 存入的 1751 [MarketToken]

removeLiquiditySingleToken

removeLiquiditySingleToken:burn 10611 [MarketToken], get 18733 wstETH

此时攻击者手里持有原来的 8860 ,加上攻击所得 1751,一共持有 10611 [MarketToken]

最终攻击者移除 10611 流动性,获得 18733 的 wstETH。

Repay flashloan

向闪电贷归还 16010 wstETH,获利 2723 wstETH。

后记

这次的攻击事件影响挺大的,涉及的金额也是巨大。在事件发生以后,许多安全从业人员都对这件事情进行了分析,我也第一时间尝试着从 trace 去分析这个攻击事件。由于当时事发不久,还没有厂商公布详细的漏洞分析结果,再加上个人叛逆的心态想着难道不参考别人的分析报告我就分析不出来了吗,这次的攻击事件分析是在一天的时间内硬啃 trace 分析得来的。这样的分析对我来说进行得并不容易,且最终输出的分析文档,也会缺乏了一些对项目架构与设计的理解,有骨没肉,读起来很干巴。我反思了一下我为何会落入如此窘境,归根究底还是对项目的不熟悉。在不熟悉项目的前提下做的攻击分析,有形无意,味如嚼蜡。这是一个不可忽视的问题,我需要想想办法。

【漏洞分析】Penpie 攻击事件:重入攻击构造奖励金额的更多相关文章

  1. 使用timer定时器,防止事件重入

    首先简单介绍一下timer,这里所说的timer是指的System.Timers.timer,顾名思义,就是可以在指定的间隔是引发事件.官方介绍在这里,摘抄如下: 1 2 Timer 组件是基于服务器 ...

  2. ACE handle_timeout 事件重入

    当多线程运行反应器事件时, 注意handle_timeout会重入,单独线程不存在下列问题! 1. 一个timer事件 // test_ace_timer.cpp : Defines the entr ...

  3. 新浪微博XSS攻击事件

    http://blog.csdn.net/terryzero/article/details/6575078 6月28日20时14分左右开始,新浪微博出现了一次比较大的XSS攻击事件.大量用户自动发送 ...

  4. 【漏洞三】跨站点脚本(XSS)攻击

    [漏洞] 跨站点脚本(XSS)攻击 [原因] 跨站点脚本(也称为xss)是一个漏洞,攻击者可以发送恶意代码(通常在(Javascript的形式)给另一个用户.因为浏览器无法知道脚本是否值得信任,所以它 ...

  5. 风炫安全web安全学习第三十三节课 文件包含漏洞基础以及利用伪协议进行攻击

    风炫安全web安全学习第三十三节课 文件包含漏洞基础以及利用伪协议进行攻击 文件包含漏洞 参考文章:https://chybeta.github.io/2017/10/08/php文件包含漏洞/ 分类 ...

  6. Java 重入锁 ReentrantLock 原理分析

    1.简介 可重入锁ReentrantLock自 JDK 1.5 被引入,功能上与synchronized关键字类似.所谓的可重入是指,线程可对同一把锁进行重复加锁,而不会被阻塞住,这样可避免死锁的产生 ...

  7. 可重入读写锁ReentrantReadWriteLock基本原理分析

    前言 本篇适用于了解ReentrantLock或ReentrantReadWriteLock的使用,但想要进一步了解原理的读者.见于之前的分析都是借鉴大量的JDK源码,这次以流程图的形式代替源码,希望 ...

  8. malloc的可重入性和线程安全分析

    malloc函数是一个我们经常使用的函数,如果不对会造成一些潜在的问题.下面就malloc函数的线程安全性和可重入性做一些分析. 我们知道一个函数要做到线程安全,需要解决多个线程调用函数时访问共享资源 ...

  9. 详解DNS重绑定攻击

    0x00 前言 DNS重绑定攻击的用法有很多种,这篇文章主要理解DNS重绑定攻击的原理,并介绍如何通过DNS重绑定来攻击内网设备.为了更好的理解DNS重绑定攻击,我们先从Web浏览器的同源策略开始介绍 ...

  10. Redisson分布式锁学习总结:可重入锁 RedissonLock#lock 获取锁源码分析

    原文:Redisson分布式锁学习总结:可重入锁 RedissonLock#lock 获取锁源码分析 一.RedissonLock#lock 源码分析 1.根据锁key计算出 slot,一个slot对 ...

随机推荐

  1. .NET 9 预览版6发布

    微软发布了 .NET 9 的第 6 个预览版,此版本包括对运行时.SDK..NET MAUI.ASP.NET Core 和 C# 的更新,预览版没有包含太多新的主要功能或特性,因为已接近 .NET 9 ...

  2. Memcache 与 Memcached 的区别

    Memcached 从0.2.0开始,要求PHP版本>=5.2.0,Memcache 要求PHP版本>=4.3. Memcached 最后发布时间为2018-12-24,Memcache ...

  3. AT_abc178_d 题解

    洛谷链接&Atcoder 链接 本篇题解为此题较简单做法及较少码量,并且码风优良,请放心阅读. 题目简述 给定一个正整数 \(S\),问有多少个数满足以下条件: 序列中必须为 \(\ge 3\ ...

  4. Groovy 基于Groovy实现DES加解密

    groovy 3.0.7 DES加密简介 加密分为对称加密和非对称加密.非对称加密,加解密使用不同的密钥,如RSA:对称加密,加解密使用相同的密钥,如DES(Data Encryption Stand ...

  5. 一文全解:LVM(逻辑卷管理器)

    前两篇文章已经讲了关于磁盘分区和磁盘阵列的相关内容: 一文全懂:Linux磁盘分区 一文全懂:独立冗余磁盘阵列(RAID) 但是磁盘分区完后再想扩容或者缩容就比较麻烦了,甚至很多时候不能扩容或者缩容, ...

  6. 使用nvm管理node

    安装包在阿里云盘 安装时,记录安装位置 安装后,在安装目录的setting添加镜像地址 node_mirror:npm.taobao.org/mirrors/node/ npm_mirror:npm. ...

  7. docker nginx容器的均衡负载

    创建三个docker容器以实现nginx的负载均衡 编写nginx的dockfile [root@docker nginx]# cat Dockerfile FROM nginx RUN echo ' ...

  8. 内存溢出+CPU占用过高:问题排查+解决方案+复盘(超详细分析教程)

    内存溢出+CPU占用过高:问题排查+解决方案+复盘(超详细分析教程) 原文地址 https://zhanghan.blog.csdn.net/article/details/109255980 前言 ...

  9. 【Mybatis】01 概述 & 快速入门Part1

    什么是 MyBatis? MyBatis 是一款优秀的持久层框架,它支持自定义 SQL.存储过程以及高级映射. MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作. MyB ...

  10. 【Docker】09 部署挂载本地目录的Redis

    1.拉取Redis镜像: docker pull redis:6.0.6 2.执行挂载命令: docker run -d \ --name=redis \ --restart=always \ --p ...