Phishing
Definition
链上钓鱼行为(On-chain phishing)指的是攻击者通过特定手段诱导受害者将加密代币直接或间接地转移给攻击者的一种网络安全攻击形式。具体而言,此类行为可细分为直接转移(Direct Transfer)和间接转移(Indirect Transfer)。在直接转移的攻击模式中,受害者被诱导执行 transfer
操作,将代币直接从其钱包发送到攻击者的地址。间接转移涉及更为复杂的步骤,首先包括受害者在不完全了解情况的情况下对攻击者进行授权(approve
/permit
),允许攻击者代表他们执行transferFrom
操作。这一过程通常涵盖了对受害者的权限设置和可能的信任滥用。
Ice Phishing:Ice Phishing中,攻击者诱使用户通过授权操作,允许攻击者从用户账户中转移代币。一旦获得授权,攻击者随后执行transferFrom
操作,将用户的资产转移到攻击者控制的地址。
Address Poisoning Phishing:Address Poisoning Phishing涉及向用户发送微量的代币或不具价值的代币,使得攻击者的地址出现在用户的交易历史记录中。这一策略利用了用户可能会错误地将值钱的代币转移到先前交易中出现的地址的心理和操作失误,从而误将资产转给攻击者。
Detection
Ice Phishing Detection
定义2.1.1 定义一个交易 \(tx\) 中,所有的 \(transfer\) 行为为\(\text{TransferSet}(tx)\)。
定义2.1.2 定义一个交易 \(tx\) 中,一个账户 \(account\) 的余额变化为 \(\text{BalanceChange}(account)\)。
定义2.1.3 定义一个调用在一笔交易中的 \(\text{Participant}(action)\) (在RugPull检测中定义了)
定义2.1.4 考虑到钓鱼交易往往比较简单的特点,我们定义一个判断\(transfer\)是否简单的函数。判断依据主要有2个
- 钓鱼合约一般不打Log。
[!NOTE]
这个并不是很有依据。唯一发现的打Log的疑似钓鱼交易:https://app.blocksec.com/explorer/tx/eth/0x3f6d8a8384b3c8fdb676541b319ac6f7777e5ee418b3fa06b4ce60bc383c8208?line=0
- 钓鱼交易的资金流一般比较简单,受害者的资金单纯的减少。
定义2.1.5 给出判断一个transfer是否是ice phishing的方法:
- transfer.from 不是 tx.origin 。这说明transfer 不是用户主动发起的。
- transfer.from 是 EOA。
- Transfer的金额几乎是用户的余额。攻击者没有理由不把用户的余额取光
[!NOTE]
然而现实有一些疑似钓鱼的交易,留了很多钱: https://app.blocksec.com/explorer/tx/eth/0x32f17ac8b2d471d8a1461c7f32da32a1bb73e1948ee53bcc5346e8c77dcbf61b/?line=0
- 钓鱼交易比较简单(定义2.1.4)
-
transfer的Participant和recipient不在白名单中。由于一些交易实在难以区分,加入白名单机制。(~~或者结合历史交易?例如有资金往来?Approve和TransferFrom的间隔?有待考证~~)如果不加入,会有较多的FP。目前,导入了所有热钱包的地址,以及0x0和0xdead和部分合约为\(Whitelist\)。不加入白名单导致的FP如下。如果不看标签,我真会觉得可能是钓鱼。
-
e.g. 交易所热钱包 https://app.blocksec.com/explorer/tx/eth/0x7dc52028adacda75a5a90d25f2dc48f5a8282b2788aaba5a9783fa3c46cf8b3c
- e.g. 跨链桥 https://app.blocksec.com/explorer/tx/eth/0xf9db918627ad9774aa06929070cfdf7e65c7b2db093da600889b903ee6f9e414?line=5
[!NOTE]
事实上,我们无法判断用户的Approve行为,是否是受到诱骗后的行为。所以这里应该是 Potential Ice Phishing。
TransferFrom行为也分两种,一种是特权的TransferFrom,例如直接从EOA发起,或者调用有身份验证的特权合约。另一种是任何人都可以调用的TransferFrom,如Arbitrary Call,或者有问题的TransferFrom。现在也没有做任何判断。
Address Poisoning Phishing Detection
定义2.2.1 可以通过交易的存储变化来检测假代币。假Token为了欺骗用户,必然要加Log,大概是出于节省Gas的目的,观察到的假Token不会修改Storage。
定义2.2.2 将向用户account发送假Token,或者很少量真Token的所有地址记为
[!NOTE]
假Token Transfer实在是太多了,改成记录Tx试试。实际上假Token Transfer没有完成钓鱼,但是攻击检测爆出的是假Token Transfer的交易,所以入另一个库。
定义2.2.3 那么我们检测Address Poisoning Phishing就可以通过如下方式
Implementation
部署在k8s上运行了,运行在 Ethereum, Bsc, Arbitrum, Optimism上。结果写入Postgresql的 archive_phishing
和 archive_fake_token
表。
由于和Rugpull检测共享一些底层代码,目前组合成一个服务。RugPull写在archive_rugpull_info
表中。简单的过滤可以查表。
目前RugPull检测只能发现服务开始运行之后部署的Token的RugPull行为。因为对Token疑似特权账户的监控必须从Token部署开始。频繁的重启(现在基本没有因为Bug重启,主要是更新)会导致很多的遗漏。要不要单独将Token疑似特权账户的监控作为一个服务?
如果发现误报,可以将白名单地址加入Postgresql的 whitelist_phishing_participants
,然后POST http://host:7001/update_whitelist
。
Evaluation
对于降低攻击检测的FP的实验:
- 用攻击检测的共120个FP(筛选出了部分重复的,和打错标签的),做测试。
- Ice Phishing 有 6 个 FN(不止6比交易),
- 3个原因是转账的金额达不到设置的阈值(百分比)。但是降低阈值会提高FP,不宜修改。(3个FN包含很多条交易)(事实上可能也不是钓鱼,钓鱼为啥不把钱转光?)
- e.g. https://app.blocksec.com/explorer/tx/eth/0x32f17ac8b2d471d8a1461c7f32da32a1bb73e1948ee53bcc5346e8c77dcbf61b/?line=0
- 2个原因是被“钓鱼”的地址是合约。看了一下,应该是钓鱼者在转移赃款。
- e.g. https://app.blocksec.com/explorer/tx/eth/0xa48de887a2518869adfdee36f4353b5e8af438c00968f2d7a0dc2fb3b841b30e
- 1个原因是钓鱼合约打Log。看了一下,被“钓鱼”的地址都是同一个Funder,且只有Transfer In和Approve交易,感觉是在洗钱。
- e.g. https://app.blocksec.com/explorer/tx/eth/0x3f6d8a8384b3c8fdb676541b319ac6f7777e5ee418b3fa06b4ce60bc383c8208?line=0
- Payable Function 共3个。没有对Payable Function做检测,这三个其实更像没有池子的RugPull。但是可以使用特权地址的检测机制找出。
- Address Poisoning 没有发现 FN。
- 金额超过1M的都检测出来了。
攻击事件数据集,158个攻击,
- 5个FP:Arbitrary Call + TransferFrom
- e.g. Maestro Incident 攻击者有钓鱼标签
- e.g. BasketDao Incident 攻击者有钓鱼标签
- e.g. Bored Ape Incident 攻击者有钓鱼标签
- e.g. Seneca Incident 这个交易实在是和普通的钓鱼交易一模一样。
- e.g. Spectra Incident
- 1个没有被攻击检测报告的FP:
- e.g. https://app.blocksec.com/explorer/tx/bsc/0x5d42e47aade5892cd33f370bb7cd933fbe4d25ac35a29ed91c0dfe5d184cadb1/?line=5 没有校验Allowance的TransferFrom
陈卓学长的数据集:TODO
链上历史:TODO
Something Interesting
- 用池子来存储获利的高级钓鱼: https://app.blocksec.com/explorer/tx/bsc/0x385f13baa4fd2316780de7bba822b0b1e8187c0913b55bb4a6338f55fdf5ee0e/
- 貌似是钓鱼者归集赃款的交易,victim是合约:https://app.blocksec.com/explorer/tx/eth/0x7467e5d0e0d20943f931757c444f249a69ed59eea85bc503cded0c9a15958aea/?line=2
- 钓鱼+swap:https://app.blocksec.com/explorer/tx/bsc/0x898655eec538eeb3e76f7923f98aebe49c334ac6627832c37ba7a0daa4aa8a4e/?line=1
- 貌似是钓鱼,但是函数有一个让人迷惑的名字
swapExactTokensForETHSupportingFeeOnTransferTokens
:https://app.blocksec.com/explorer/tx/bsc/0x62222d99085079dd26de2631353bc8f7e926a30eb07531da5925ad352a07175f/ - 打Log的合约,是钓鱼吗:https://app.blocksec.com/explorer/tx/eth/0x3f6d8a8384b3c8fdb676541b319ac6f7777e5ee418b3fa06b4ce60bc383c8208?line=0 ?
- 有待商榷,理论上,有可能让用户approve给合约,再在合约中授权给自己,完成钓鱼。
- https://app.blocksec.com/explorer/tx/eth/0xda80640f973ac2eea54704455d448f3bfc70a1c05e60c516ed023676568ecb63/?line=6
- https://app.blocksec.com/explorer/tx/bsc/0x342b0d971c58c3d81257d4d6dc2ddee86327c2efca8d682dae07840a1b25a0f3/?line=4
- https://app.blocksec.com/explorer/tx/eth/0xf9230a08278f14811d48e050249a0472945d65559a28d20462544f485810bd80/?line=0
- 表面上谁都可以调用,实际上只有给出正确的Permit才能创造出正确合约,把钱转走。https://app.blocksec.com/explorer/tx/eth/0x31a222f52c0eb97bda383d846bd74c400e324027ec27f4db849723fe243c29fa/?line=5
- 谁都可以调用,但是获利者是写死的 https://app.blocksec.com/explorer/tx/eth/0xa85e64a86e4b4901a27bd4e5b74ad9004ea40e97656395c76fb8aa6c3994b938?line=0
还是想简单了,用uwin的工具调了几个case,发现两类反例:
- 特定参数:对于permit类型的钓鱼,发起transferFrom的钓鱼合约很可能是谁都可以调用的,关键是要传正确的permit参数。只用这个维度作为区分钓鱼和攻击的话, 会把这类钓鱼全都当成arbitrary call。
- 特定获利者:又或者是钓鱼合约谁都可以调用,但是会把获利发送到固定的地址。
在我只看了几个例子的情况下,就已经有这两类了。 这个维度肯定是有用的,但是作为单一维度还是不够, 可能得实现出来扫一遍才知道会怎么样,然后加更细粒度的规则。好麻烦。
区分Arbitrary Call, TODO
对TransferFrom的Participants依次进行替换
- TransferFrom的msg.sender替换后,执行:
- 成功:transferFrom可能没有校验Allowance
- 失败:
- Participants中的某msg.sender替换后,执行:
- 成功:这个invocation没有对msg.sender进行校验。
- 失败:这个invocation对msg.sender可能进行校验。
但是没有这么简单,校验可能发生在参数上。没有校验还可能是因为获利者写死了。
- 替换参数中的获利者。
- Permit的参数得识别出来。