10 月 22 日,CertiK Alert 标记了一起针对 BNB Chain 上 NEM 代币的攻击。慢雾随后核定损失约 821,920 美元,吴说区块链和 Foresight News 都在几小时内跟进了拆解,慢雾的被黑档案库收录了完整机制。

先说一个必须做的甄别:这个 NEM 是 BNB Chain 上的代币(合约 NemTokenV2),不是老牌 NEM/XEM 公链。被攻击的资产是 PancakeSwap 上的 USDT/NEM 流动性池。同名撞车在这种新闻里很常见,搜「NEM 被黑」会同时出现两条毫不相干的信息——这也是为什么买任何币之前应该先核对合约地址而不是名字。

漏洞长什么样

出问题的机制叫 pendingLpSellRecycle,一个「卖出追踪」功能:盯着流进流动性池的 NEM,把它们记作卖压——小项目很喜欢挂的这类「智能代币经济学」钩子,用来管理价格。

缺陷简单到近乎无聊:合约把每一笔转入池子的 NEM 都记成一次已完成的卖出,却从不验证这笔兑换是否真的结算过。「代币转进来」和「交易真的发生」是两件事,NemTokenV2 把它们混为一谈。

PancakeSwap 的池子会自己维护一份储备记录。当池子实际持有的代币超过这份记录时,任何人都可以调 skim() 把多出来的部分取走——这是 AMM 的正常自洁机制,前提是没有东西能人为制造这种「盈余」。

NemTokenV2 的漏洞干的就是这个。按慢雾的分析,攻击者的循环是三步:

  1. 转账把 NEM 转入 USDT/NEM 池子;
  2. 调 skim()——取走池子此刻实际余额超出内部记录的部分;
  3. 调 flushLpSellRecycle()——让合约的平行账本把这笔幽灵「卖出」记掉,账面跟着被抽低的池子一起往下走。

这个循环跑了 42 遍。每一轮都从池子里抽走真实价值、同步压低账面储备——副作用是 NEM 价格被不断推高。等池子被抽得差不多了,攻击者把剩余的 NEM 按虚高的价格卖出,套走约 821,920 USDT。据报道,赃款随后跨链转入了以太坊。

真正的教训:漏洞在哪一层

这案子最值得记住的一点是漏洞的位置。PancakeSwap 的池子全程没做错任何事,规则执行得一丝不苟。出问题的是代币合约伸手进池子机制、自己另记一套账。

这也不是孤例。事件后的安全报道指出,2026 年 BNB Chain 上已经连出了一串同族案件——Movie Token、ATM、LULA——共同点都是代币在转账税、卖出回收、奖励 redirect 这类自定义逻辑与 PancakeSwap 池子交互时,被自己的代码路径掏空。配方永远一样:合约靠「代币最后落在哪」去推断交易者的意图,而不是去验证「交易里实际发生了什么」。

对持币者和 LP 意味着什么

USDT/NEM 池子的 LP 承担的是直接损失——池子被抽干。对持币者,攻击窗口内的人造价格拉升是第二重陷阱:操纵期间的任何交易,成交的都是假价格。

对在 BNB Chain 上玩小市值代币的人,三条实在的结论:

  • **开源验证只是及格线,不是安全。**一份已验证、但挂了自定义池子交互逻辑的合约,照样可能带着这类 bug。关键是审计有没有专门覆盖「与池子交互的那部分逻辑」——多数时候没有。
  • **复杂代币经济学是攻击面。**每一个「自动卖出」「卖出回收」「税费重分配」功能,都是跑在你资金上的自定义代码。代币安全核查清单把能提前暴露这类风险的检查项过了一遍。
  • 同一套核查方法跨链通用。代币合约地址验证手册讲了每种验证方式在实践中怎么被绕过——包括像这次这样,随便一个 BSC 代币管自己叫 NEM 的同名撞车。

给开发者的话更直白:永远不要用「代币转入了某个地址」来推断一笔 swap。要么通过路由回调验证结算,要么就别做这个功能。NEM 的攻击者把同一个三步循环跑了 42 次,合约从头到尾没有问过一句:这笔交易真的发生了吗?

常见问题

被攻击的 NEM 是那个老牌 NEM(XEM)公链吗?

不是。出事的是 BNB Chain 上的代币合约 NemTokenV2,在 PancakeSwap 上和 USDT 配对交易,与老牌 NEM/XEM 公链没有任何关系。同名撞车本身就是一种混淆面——搜索「NEM 被黑」时会混出两条完全无关的新闻,买币前先核对合约地址再核对名字。

这次是 PancakeSwap 出漏洞了吗?

没有。PancakeSwap 的池子完全按规则运行。漏洞在 NEM 代币合约自己身上:pendingLpSellRecycle 机制把任何转入池子的代币都当作卖出记账,不去验证一笔真实兑换是否发生。攻击者只是批量制造了合约错误假定的那种条件。

一个记账漏洞怎么变成 82 万美元的?

每一轮循环:先把 NEM 转入 USDT/NEM 池子;调用 skim() 取走池子实际余额超出内部记录的部分;再调用 flushLpSellRecycle() 让合约的平行账本把这笔假「卖出」记掉。42 轮下来,真实储备被抽走、账面记录被同步做低,池子价格还被不断推高——最后攻击者把剩余 NEM 按虚高价格卖出,套走约 821,920 USDT。

持有带税费/卖出追踪机制的代币需要注意什么?

任何在合约里挂了自定义逻辑、直接和 AMM 池子交互的代币,都应视为高风险基础设施。加仓或提供流动性前:确认合约已开源验证、确认审计覆盖过「与池子交互的那部分逻辑」而不只是代币本身,并按代币安全核查清单过一遍。复杂的代币经济学是攻击面,不是卖点。