CPIMP 攻击是一种复杂的中间人攻击,攻击者通过抢跑交易篡改代理合约的实现,使恶意代码透明地存在于多个DeFi协议中。该攻击通过各种技术手段隐藏自身,包括欺骗区块链浏览器,并具备持久控制、灵活路由和规避检测的能力,对多个区块链上的知名协议构成威胁。在各方合作下,大部分潜在损失已成功避免。
YANNIS SMARAGDAKIS
[由 Dedaub 团队]
上周,针对多个区块链上几个著名 DeFi 协议的一次重大攻击(很大程度上)得到了成功缓解。该威胁可能(至少)影响数千万美元的总价值,然而攻击者还在等待更多价值,才准备采取行动!
该威胁在技术上最有趣的方面与感染方法无关,而是与攻击的隐蔽性有关:攻击合约已经隐藏在人们的眼皮子底下几周了,以自定义的方式渗透到多个协议中,同时确保它们对常规协议执行和 etherscan 上的合约浏览完全透明。
我们称该攻击向量为 CPIMP,即“Clandestine Proxy In the Middle of Proxy”(代理中的秘密代理),以令人难忘地捕捉其本质。
David Benchimol 来自 Venn,我们并不陌生。此前,他曾多次向我们提供了潜在的攻击向量,我们借助我们的工具,就确定可行性和影响进行了长期交流。
7 月 8 日下午,他确保让我们保持高度警惕,非常着急!
David 正在调查他的同事 Ruslan Kasheparov 提出的一个危险信号。他们发现有几个代理初始化显然被抢先交易了,以插入恶意实现。
这没什么新鲜的,对吧?任何未初始化的代理合约都可以被初始化函数的第一个调用者接管。
Clandestine Proxy In the Middle of Proxy (CPIMP) 的不同之处在于:
(将来,etherscan 将会更新以正确报告 CPIMP — 稍后会详细介绍。)
因此,CPIMP 确实是一个中间的秘密代理!
下面显示了一个抢先交易初始化交易的示例。
当然,这是由攻击者控制的代码。但请注意两个明显的 Upgraded
事件。
在此之后,受害者代理指向一个恶意的 CPIMP 作为其实现。然而,交易像往常一样进行。细心的观察者可以在任何交易浏览器中看到 CPIMP 的存在:
请注意,调度调用需要两个 delegatecall
指令,而不仅仅是一个!执行不是从代理委托到合法的实现,而是首先委托到 CPIMP,然后 CPIMP 委托到合法的实现。
攻击者只是在隐藏,可能在等待更大的鱼,然后才暴露他们的存在?
在联系我们时,David 已经知道这不是一个孤立的事件,而是影响了数十个合约。然而,我们谁也不知道威胁的程度。
在我们的数据库上起草正确的查询以确定所有受影响的合约并非易事。一个合理的第一个版本看起来像这样:
(如果运行此查询,请确保将持续时间设置为超过默认的“最近 24 小时”。)
在接下来的几个小时里,查询得到了很大的改进,以捕获多个网络上的所有受威胁合约,并且误报率非常低。但即使在早期,也出现了一个清晰的画面:存在许多面临风险的协议,并且完全评估威胁将需要数周甚至数月的时间!
已被 CPIMP 接管的合约(在不同的链上)属于 EtherFi、Pendle、Bera、Orderly Network、Origin、KIP Protocol、Myx 等项目,以及更多的代币、协议、预言机等。并非所有这些都同样容易受到攻击。在许多情况下,威胁很低。例如,Pendle 在三周前已成功从受感染的合约迁移,并确认它们不易受到攻击(尽管由于 CPIMP 采用的反恢复机制,它们在此过程中损失了一些少量金额)。
但是,由于已经感染了数十个合约,并且其中许多合约似乎具有重要的权限,因此我们必须采取行动,甚至在完全确定威胁的程度之前。
SEAL 911 及其无畏的领导者 @pcaversaccio 是运行任何战情室的绝对最佳人选,对于针对广泛的、多协议漏洞的战情室来说更是如此!
在接下来的 36 小时里,我们在评估受感染合约的威胁和寻找我们可以识别的所有受影响协议的联系人之间疯狂交替。
主要问题是缓解措施不能是原子性的,并且对一个协议的任何“修复”都有可能通知攻击者他们已被发现。这可能会导致对其他协议的迫在眉睫的攻击,可能在我们甚至意识到这些协议上的威胁程度之前。攻击者有几个月的时间来准备和估计他们可以窃取什么,而我们只有几个小时!
并且评估这种漏洞绝非易事。以 Orderly Network 在 BNB 链上的 CrossChainManager 为例。该合约显然可以执行操作(例如,deposit
),这些操作将通过 Layer Zero 被跨链接受。但是威胁有多严重?另一端是否有时间锁?是否有某些链下警报会触发并可以帮助缓解攻击?如果不检查大量的代码,一个人无法确定潜在攻击的严重性。
考虑到所有这些,在接下来的几个小时里,我们将我们可以找到的所有受影响协议的安全联系人带入了战情室。SEAL 的 @pcaversaccio 负责指挥,并以最小的信息泄漏的方式协调救援。每个解决方案都需要定制:在许多情况下,协议必须与 CPIMP 合作,后者必须被欺骗以批准他们的救援交易。此外,大多数救援必须在大致相同的时间运行,才能在攻击者做出反应之前。
最终结果并不完美,但对于如此广泛的漏洞来说,却非常成功。攻击仍在进行中,攻击者仍在试图从仍然容易受到攻击的受害者合约中获利。但是,绝大多数威胁已得到缓解。
David 的推文 是关注反应和后果的最佳起点。
各个协议此后发布了 [ 他们的] [ 自己的] [ 披露]。
CPIMP 的真正精妙之处在于检查其反编译的代码时显现出来,我们已经分析了其多个变体。这揭示了一种高度工程化的合约,旨在实现持久的统治、灵活性、逃避检测以及实现有针对性的资产盗取。以下是一个 以太坊变体 的简化摘要反编译,高亮显示了诸如签名触发的执行、精细路由和影子存储控制等隐藏机制:
// Manually reverse-engineered decompiled excerpt from malicious proxy (based
// on bytecode analysis)
// 手动逆向工程反编译的恶意代理摘录(基于字节码分析)
contract MaliciousProxy {
address private immutable backdoor =
0xa72df45a431b12ef4e37493d2bcf3d19af3d24fa;
address private owner; // Shadow owners possible via multiple slots
// 通过多个槽位,影子所有者是可能存在的
address private _implementation;
address private _admin;
mapping(bytes4 => uint8) private selectorModes;
// 0=normal, 1=blocked, 2=permissioned
// 0=正常,1=阻止,2=许可
mapping(bytes4 => address) private selectorToImpl;
mapping(bytes4 => mapping(address => address)) private perCallerRouting;
mapping(bytes4 => mapping(address => bool)) private permissions;
mapping(bytes4 => bool) private silentFlags; // Suppress events/logs
// 抑制事件/日志
mapping(address => bool) private whitelists;
uint256 private nonce; // Anti-replay in signatures
// 签名中的防重放
modifier backdoorOrOwner() {
if (msg.sender != backdoor && msg.sender != owner)
revert("Unauthorized");
_;
}
// ?
function drainAssets(address[] calldata tokens) external backdoorOrOwner {
// Bulk drain tokens, with special handling of ETH
// 批量耗尽代币,并特殊处理 ETH
}
function signedTakeover(bytes calldata data, uint8 v, bytes32 r,
bytes32 s) external {
// Off-chain triggered via ecrecover
// 通过 ecrecover 链下触发
bytes32 hash = keccak256(abi.encodePacked(
"\x19Ethereum Signed Message:\n", data.length, data));
address signer = ecrecover(hash, v, r, s);
require(signer == backdoor, "Invalid sig");
this.delegatecall(data); // Execute arbitrary payload
// 执行任意有效负载
}
function updateRouting(bytes4[] calldata selectors,
address[] calldata impls,
uint8[] calldata modes) external backdoorOrOwner {
// Granular routing updates
// 细粒度路由更新
for (uint i = 0; i < selectors.length; i++) {
selectorToImpl[selectors[i]] = impls[i];
selectorModes[selectors[i]] = modes[i];
}
}
// Complex Routing logic
// 复杂路由逻辑
function getImplementation(bytes4 selector) private returns (address) {
if (perSelectorRouting[selector][_implementation] != address(0)) {
return perSelectorRouting[selector][_implementation];
} else if (perSelectorRouting[selector][address(0)] != address(0)) {
return perSelectorRouting[selector][address(0)];
} else {
return _implementation;
}
}
// code to restore CPIMP in implementation slot(s)
// 用于在实现槽中恢复 CPIMP 的代码
function postDelegateReset() private {
// Slot integrity check/reset (prevents upgrades)
// 插槽完整性检查/重置(防止升级)
if (STORAGE[keccak256("eip1967.proxy.implementation") - 1] !=
_implementation) {
STORAGE[keccak256("eip1967.proxy.implementation") - 1] =
_implementation;
}
if (_admin != expectedAdmin) { // Similarly for admin/beacon slots
// 同样适用于 admin/beacon 插槽
_admin = expectedAdmin;
}
// Additional resets for owners, nonces if altered during call
// 如果在调用期间更改,则对所有者、nonce 进行额外重置
}
// Fallback delegates to routed implementation
// 回退委托给路由的实现
fallback() {
address impl = getImplementation(msg.sig);
(bool success, bytes memory ret) = impl.delegatecall(msg.data);
require(success);
postDelegateReset();
assembly { return(add(ret, 0x20), mload(ret)) }
}
// Additional: Direct storage writes, nonce for replays, etc.
// 其他:直接存储写入,用于重放的 nonce 等。
function updateManyStorageSlots(uint[] index, bytes32[] value)
external backdoorOrOwner {
// Updates multiple storage slots simultaneously
// 同时更新多个存储槽
}
}
尽管上面的逆向工程并不完整,但几个重要的要素很清楚。CPIMP 远远超出了简单的中继,它嵌入了一套用于劫持、持久性、逃避检测和启用有针对性的资产盗取的控制:
攻击者的投入闪耀:这不是机会主义 — 这是一个用于在时机到来时触发的自动化、弹性活动的框架。
CPIMP 攻击最引人注目的也许是其鬼祟性。攻击者一直在等待更大的鱼,并且为不同的受害者定制了他们不同的 CPIMP。每个 CPIMP 感染所需的手动工作量似乎很大。
也许这些措施中最有趣的是攻击者注意到不要被 etherscan 的 “作为代理读/写”功能 检测到。如果访问 受害者合约的页面,etherscan 不会报告 CPIMP 作为实现,而是列出合法的实现合约。
这并不奇怪,对吧?攻击者需要做的就是发出虚假事件,并且该服务会被愚弄。
嗯 … 不!
Etherscan 实现检测比这更复杂,并且攻击者花费了大量精力来规避它。具体来说,etherscan 正在查询代理合约中存储槽的值,以确定什么是实现。但是,由于没有单一的标准规定代理_在哪里_存储其实现的地址,因此,每种代理类型都有其自己的标准。在这种情况下,受感染的代理是 EIP-1967 代理。但是,攻击者在较旧的 OpenZeppelin 代理标准使用的槽中插入了错误的实现地址,欺骗 etherscan 报告_该_槽的内容作为实现!
SEAL 911 战情室除了受害者协议外,还引入了 etherscan 安全联系人。因此,etherscan 已迅速标记了我们调查确定的所有合约,并计划修复导致误导性实现报告的错误。
调查和缓解 CPIMP 攻击向量是一次非常有趣的体验:这是一次广泛的、高度复杂的中间人式劫持,已经感染了多个链(以太坊、币安、Arbitrum、Base、Bera、Scroll、Sonic)上的许多著名协议。
调查带来的肾上腺素激增令人难以置信,并且通过精心协调的努力防止了大部分潜在损失,这真是令人欣慰。David 总结得最好,因此我们将以他的消息结束:
在此漏洞上在此战情室工作是我在 web3 中最大的乐趣之一。
解剖和缓解如此精心执行和长期利用是一种荣幸。
我想再次感谢 @dedaub @pcaversaccio @seal_911 的所有帮助,并感谢 @etherscan 致力于获得…
— deebeez (@deeberiroz) 2025 年 7 月 9 日
- 原文链接: dedaub.com/blog/the-cpim...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!