AI Agent可被操纵将钱转到错误地址——Freysa事件中攻击者用语义误导让Agent转出47K美元;支付路径必须在模型外部加地址风险校验,而非依赖prompt规则。
LLM 通过文本决定做什么,而其中部分文本来自你无法控制的人:网页、邮件、工具返回结果、其他智能体。写这些文本的人可以引导智能体的行为。
最著名的公开演示是 Freysa,这是 2024 年 11 月发布的一个智能体,规则只有一条:永远不要批准提取奖金池。经过 481 次失败尝试后,第 482 条消息让它做了完全相反的事。攻击者说服它 approveTransfer 是处理入账的,然后宣布了一笔虚假存款。智能体转出了全部余额,13.19 ETH(约 47,000 美元)。
教训并非这个模型很弱。写在 prompt 里的规则只能由模型本身来执行,而模型是可以被说服的。如果付款路径上没有模型之外的检查,足够好的注入就能通过。
地址是 40 位十六进制字符,人(和智能体)会从交易历史中复制它们。攻击者生成一个与你曾用过的地址首尾字符相同的仿冒地址,然后从该地址向你发送一笔极小或零值的转账,使其出现在你的历史记录中。
2024 年 5 月 3 日,一位持有者将 1,155 WBTC(约 6800 万美元)转到了一个仿冒地址,该地址与你目标收款人的前六位和后六位字符匹配。资金后来经过谈判被退回,这种情况很罕见。
这并非个案。USENIX Security 2025 论文《区块链地址投毒》测量了以太坊和 BSC 上两年的数据:2.7 亿次攻击尝试针对 1700 万受害者,6633 起事件中损失至少 8380 万美元。
一个从历史记录或日志中取"上次付款地址"的智能体,正是这种攻击的目标用户。它也不会感觉到有什么不对劲。
OFAC 的 SDN 名单包含加密货币地址。例如,2022 年 4 月 14 日的更新添加了 Lazarus 小组的一个以太坊地址:0x098B716B8Aaf21512996dC57EB0615e2383E2f96。0xB10C/ofac-sanctioned-digital-currency-addresses 项目每晚从财政部的 XML 中提取这些地址。
这里不需要任何东西来欺骗你的智能体。地址完全有效,付款可以完成,但你仍然支付了你可能依法被禁止支付的对方。这是否适用于你需要咨询律师,但检查这个名单几乎不需要什么成本。
x402 付款是一种 EIP-3009 授权,由 facilitator 在链上结算。USDC 转账是一笔交易。一旦其中任何一个被签名并广播,你就无法撤回。付款后的警报只是告诉你损失了什么。
所以检查必须运行在签名路径中:
payTo 或 to,而非模型收到的名称verdix-guard(MIT,源码)钩入两个位置:
@x402 客户端上注册 onBeforePaymentCreation 钩子,在创建付款负载之前筛查 payTonpm install verdix-guard @x402/fetch @x402/evm viem
import { ExactEvmScheme } from '@x402/evm';
import { wrapFetchWithPayment, x402Client } from '@x402/fetch';
import { privateKeyToAccount } from 'viem/accounts';
import { createVerdixGuard, guardX402Client } from 'verdix-guard';
const account = privateKeyToAccount(process.env.AGENT_KEY as `0x${string}`);
const client = new x402Client().register('eip155:8453', new ExactEvmScheme(account));
guardX402Client(client, createVerdixGuard({ account }));
const fetchWithPayment = wrapFetchWithPayment(fetch, client);
const response = await fetchWithPayment('https://seller.example/paid-endpoint');
直接用 viem 转账:
import { createVerdixGuard, guardAccount, VerdixGuardBlockedError } from 'verdix-guard';
const guard = createVerdixGuard({ account });
const guardedAccount = guardAccount(account, guard); // use it in your walletClient
try {
await guardedAccount.signTransaction(tx);
} catch (error) {
if (error instanceof VerdixGuardBlockedError) {
console.log(error.decision.reason, error.decision.verdict);
}
}
被阻止的 x402 请求抛出一个包含 verdix-guard: <reason> 的错误。被守护的账户抛出 VerdixGuardBlockedError。两种情况下都没有签名产生。
不需要 API key。每次检查本身就是一个 x402 付款:在默认 lite 层级上为 Base 上的 0.01 美元 USDC,从你选择的钱包支付。答案按地址缓存 24 小时,每天的检查支出有上限(默认 1 美元)。低于 minAmountUsd(默认 0.10 美元)的 USDC 付款不检查。守卫无法以美元定价的金额(如 ETH 或其他代币)始终会检查。
为了端到端测试这个已发布的包,我将 verdix-guard@0.1.0 从 npm 安装到一个空目录。我用 guardAccount 包装了一个测试钱包,使用 tier: 'lite'、minAmountUsd: 0 和 dailyBudgetUsd: 0.01,然后让它签署一笔 0.20 USDC 转账到 0x32fb1bedd95bf78ca2c6943ae5aeaeaafc0d97c1。那是 Base 上的一个漏洞合约,在 DeFiHackLabs 中列为 CloberDEX(2024-12)。
Verdix 返回 danger,原因为 deployer_flagged。
守卫抛出 VerdixGuardBlockedError,没有产生签名。
send 函数是一个本地存根,只在签名成功后调用。它从未被调用,测试钱包在 Base 上的 nonce 保持为 0。
唯一移动的资金是检查本身:0.01 USDC,交易 0xd3c4a29f…eb54a0(Base 区块 52220766)。
如果你要复现这个,有一个实际的注意事项:viem 钱包客户端可能在签名之前调用其传输层(例如 eth_fillTransaction)。为了证明没有任何东西离开机器,直接用被守护的账户签名,而不是通过钱包客户端。
safe 不是保证。它意味着运行的检查没有发现问题。一个全新的、不在任何名单上且没有链上历史记录的新骗局地址会通过。继续让人确认大额或异常付款。
lite 永远不会说 safe。默认层级检查制裁、诈骗和钓鱼名单、地址投毒仿冒地址、销毁地址、钓鱼代币和被标记的部署者。它跳过地址年龄和部分合约检查。干净的结果是 no_known_risk,这比 safe 说的更少。如果你需要 safe,使用 tier: 'quick'(0.02 美元)。
失败关闭是有代价的。使用默认的 failMode: 'block',当 Verdix 或其数据源不可用时,你的智能体会停止付款。failMode: 'allow' 让它继续运行,但这些付款会未经检查通过。已经是 danger 的答案在两种模式下都会被阻止。
仅限 Base。其他网络上的付款默认未经检查通过(otherNetworks: 'allow')。
并非所有内容都会被解码。原始的 sign / signMessage、EIP-7702 授权以及不是已知转账或审批的合约调用会原样通过。
状态在内存中。缓存和每日预算在进程重启时重置。
它不能修复 prompt 注入。它缩小了被注入的智能体对资金能做的事。保持你的其他控制:对已知收款人的白名单、钱包中每笔付款的上限,以及超过阈值时的人工审批步骤。
同样的检查也可以作为智能体工具使用。以下每个包的 lite 工具(0.01 美元)的干净答案是 no_known_risk,而不是 safe:
verdix-mcp(npm、GitHub),用 npx -y verdix-mcp 运行。Claude Desktop 的单文件 .mcpb 捆绑包附在最新 GitHub release 上。verdix-ai-sdk(npm、GitHub)。它还有 verdixNeedsApproval,这是一个用于你自己的转账工具的 needsApproval,只在 Verdix 发现风险时才询问用户。langchain-verdix(PyPI、GitHub)。如果你的智能体可以签署付款,在决策和签名之间放置一个检查:一个模型无法跳过的、无法获得答案时会阻止的检查。上面的包是实现这一点的一种方式。无论你用什么,在攻击者替你做之前,用一个你知道是坏人的地址测试付款。