背景
CertiKの前回の記事「連鎖する初心者ゲーム、大規模なRugPullの手口が明らかに」は、0xdf1aをターゲットにした初心者ボットを明らかにしました。大量退出詐欺自動収穫機のアドレス0xdf1aは、わずか2ヶ月ほどで200件以上の退出詐欺(以下、総称してRugPullと呼びます)を完了させていますが、このグループはRugPullの手法を1つだけ持っているわけではありません。
このアドレスの背後にいるグループのRugPull手法は、MUMIトークンを例として、以前の投稿で説明しました:トークンの総供給量を変更したり、転送イベントを送信したりすることなく、コードのバックドアを通じて直接、納税者アドレスのトークン残高を変更する。
今日の投稿では、グループによる別のRugPull戦術の例として、トークン「ZhongHua」を取り上げます:RugPullに使用できる転送関数を隠すために、複雑な税金関数ロジックを使用します。次に、「ZhongHua」トークンのケースを通して、アドレス0xdf1aにおける別のRugPullテクニックの詳細を分析します。
Deeper into the scam
このケースでは、プロジェクトは合計9,990億のZhongHuaを約5,884 WETHと交換し、プールの流動性を流出させました。プールの流動性を引き上げている。RugPull詐欺全体をより深く理解するために、最初から一連の出来事を見てみましょう。
Deploying tokens
1月18日午前1時40分(UTC)、攻撃者のアドレス(?0x74fc)がZhongHua(?0x71d7)という名前のERC20トークンをデプロイし、攻撃者アドレス(?0x74fcfc)に送信するために10億トークンを事前にマイニングしました。
事前に採掘されたトークンの数は、契約ソースコード内で定義された数と同じです。契約のソースコード内で定義されたトークンの数と一致します。
Add Liquidity.nbsp;流動性の追加
午後1時50分(トークン作成から10分後)、攻撃者アドレス(?0x74fc)は、流動性の追加に備えて、Uniswap V2ルーターにZhongHuaトークンの承認許可を与えます。
1分後、攻撃者アドレス(?0x74fc)がルーターのaddLiquidityETH関数を呼び出して流動性を追加し、ZhongHua-WETH流動性プール(?0x5c8b)を作成し、事前に採掘されたすべてのトークンと1.5ETHを流動性プールに追加し、最終的に約1.225LPトークンを獲得します。
上記のトークン送金ログから、次のことがわかります。攻撃者(?0x74fc)がZhongHuaトークンコントラクト自体に0トークンを送信した転送が1つあります。
この送金は流動性を付加する通常の送金ではありませんが、トークン契約のソースコードを見ると、_getAmount関数を実装しており、この関数が送金の送信元アドレスからお金を差し引き、課金される手数料を計算する役割を担っています。その後、トークンのアドレスに手数料を送信し、トークンのアドレスが手数料を受け取ったことを示すTransferイベントをトリガーします。
_getAmount関数は、送金の送信者が_ownerであるかどうかを判定します。getAmount関数は、送金の送信者が_ownerであるかどうかを判断し、_ownerである場合、手数料を0に設定します。_ownerは、Ownableコントラクトがデプロイされるときに、コンストラクタの入力パラメータとして与えられます。
そして、ZhongHuaトークンコントラクトは、Ownableコントラクトを継承しています。Ownableコントラクトを継承し、デプロイ時にデプロイ先のmsg.senderをOwnableコンストラクタの入力パラメータとして受け取ります。
したがって、攻撃者のアドレス(?0x74fc)はトークン契約の_ownerであり、流動性を追加する0トークンの転送は_getAmount関数を通じて送信されます。関数の中で呼び出されるからです。
Permanently locking liquidity
1時51分(流動性プールが作成されてから1分以内)に、攻撃者のアドレス(?)は、流動性を追加することで獲得したすべての1.225 LPトークンを直接0xdeadアドレスに送信し、LPトークンの永久ロックを完了します。
MUMIトークンのケースと同様に、LPがロックされると、攻撃者のアドレス(?0x74fc)はがロックされると、攻撃者のアドレス(?0x74fc)は理論上、流動性を除去してRugPullする能力を失います。そして、初心者ボットに対するアドレス0xdf1aが主導するRugPull詐欺の場合、このステップは主に初心者ボットの不正防止スクリプトを欺くために使用されます。
この時点では、事前にマイニングされたトークンはすべて流動性プールに追加するために使用され、異常は発生していないようにユーザーには見えました。
RugPull
午前2時10分(ZhongHuaトークンが作成されてから約30分後)に、攻撃者であるアドレス2(?0x5100)がRugPull専用の攻撃コントラクト(?0xc403)を展開しました。
MUMIトークンの場合と同様です。MUMIトークンの場合と同様に、プロジェクトはZhongHuaトークンコントラクトを展開するために使用されたのと同じ攻撃アドレスを使っておらず、RugPullのために使用された攻撃コントラクトはオープンソースではありませんでした。
午前7時46分(トークンコントラクトが作成されてから約6時間後)に、攻撃者アドレス2(?0x5100)はRugPullを実行しました。
彼は攻撃コントラクト(?0xc403)の「swapExactETHForTokens」メソッドを呼び出すことでこれを行い、彼は攻撃契約から9990億ZhongHuaトークンを約5.884ETHで変換し、プールの流動性のほとんどを流出させました。
攻撃契約(?0xc403)はオープンソースではないため、私たちはその攻撃契約を逆コンパイルしました。オープンソースではないため、そのバイトコードを逆コンパイルしたところ、次のような結果が得られました。
https://app.dedaub.com/ethereum/address/0xc40343c5d0e9744a7dfd8eb7cd3111e9cec49bd2e/decompiled
「」のコントラクト(?0xc403)を攻撃する。swapExactETHForTokens "関数の主な機能は、UniswapV2ルーターが最大数の中華トークン転送権限を付与することを承認することであり、その後、ルーターを通じて、呼び出し元は "xt "の数を指定します。次にルーターは、呼び出し元が「xt」として指定した数のZhongHuaトークン(攻撃契約(?0xc403)が所有)をETHに変換し、攻撃契約(?0xc403)で宣言された「_rescue」アドレスに送信します。
アドレス"_rescue"というアドレスが、攻撃契約(?0xc403)の正確な配備者のアドレス、攻撃者アドレス2(?0x5100)に対応していることがわかります。
結局、プロジェクト側は9,990億を使用しました。
前回記事のMUMIのケースと同様に、攻撃契約(?0xc403)のZhongHuaトークンの出所をまず確認する必要があります。前回の記事から、ZhongHuaトークンの総供給量は10億であることが分かっており、RugPull終了後、Block Explorerで照会したZhongHuaトークンの総供給量は10億のままですが、攻撃コントラクト(?0xc403)が販売したトークン数は9900億であり、9900億<となります。、これは契約によって記録された総供給量の999倍です。これらのはるかに多いトークンはどこから来たのでしょうか?
私たちはコントラクトのMUMIトークンのRugPullのケースと同様に、ZhongHuaトークンのケースの攻撃コントラクト(?0xc403)にもERC20トークンの移転イベントがないことがわかりました。
MUMIのケースでは、税契約のトークンは、ERC20トークンを直接修正したものです。トークン契約のBALANCEを直接変更することで、税務契約は総供給量よりもはるかに多くのトークンを直接所有することができます。MUMIのトークンコントラクトは、残高を変更するときにトークンのtotalSupplyを変更せず、Transferイベントをトリガーしないため、MUMIのケースでは、税契約からのトークンの転送を見ることができません。
中華のケースに戻ると、攻撃契約(?0xc403)の中華トークンも空中から出現しているので、中華トークン契約の「残高」というキーワードも検索してみる。"中華トークン契約 "の中で。
その結果、トークン契約全体で残高変数に変更があるのは、"_getAmount"、"_transferFrom"、"_transferBasic "の3つだけであることがわかりました。"_transferBasic "関数においてです。
"_getAmount "が送金手数料を請求するロジックを処理するために使用される場合、"_transferFrom "と"_transferFrom "と"_transferBasic "は送金ロジックを扱っており、以下のようにMUMIトークンで明らかなように、残高を直接修正するステートメントはありません。
より重大なのは、MUMIトークンのコントラクトが直接、以下のようなイベントを発生させないことです。Tranferイベントは、税契約のBalanceを変更するときにはトリガーされません。これが、ブロックブラウザで税契約のトークン移転イベントを確認できない理由ですが、税契約は大量のトークンを持つことができます。
しかし、ZhongHuaのトークン契約では、"_getAmount"、"_transferFrom"、または"_transferBasic "関数であろうと、それらはすべて、残高に変更を加えた後、正しくTransferイベントをトリガーしています。これは、攻撃契約(?0xc403)に関連するTransferイベントに関する以前のクエリで、トークンの転送を見つけることができなかったことと矛盾しています。Tranferイベントは競合しています。
無印良品の場合とは異なり、攻撃契約(?0xc403)のトークンは今回、本当に何もないところから現れたのでしょうか?
明らかになった手法
attack contractのトークンはどこから来るのか
attack contractのトークンはどこから来るのか?strong>
ケースを分析する過程で、ZhongHuaコントラクトの残高が変更されるたびにTransferイベントが正しくトリガーされることがわかりましたが、攻撃コントラクト(?0xc403)に関連するトークン転送レコードやTransferイベントが見つかりませんでした。転送イベントは正しくトリガーされましたが、攻撃契約(?0xc403)や転送イベントに関連するトークン転送レコードを見つけることができませんでした。
私たちは多くの転送記録を調べ、ある時、トークン契約内のトークンを売却する役割を担う、契約内の「performZhongSwap」関数を突破口として取り上げました。私たちが分析した他のRugPullイベントには、「RugPull」イベントがたくさんありました。私たちが分析した他のRugPullイベントでは、このような機能がRugPullのバックドアとして使われているケースが多くありました。
他の関数をチェックしたにもかかわらず、空振りに終わりました。攻撃者がどのようにRugPullを行ったとしても、「transfer」関数の実装ロジックには最も重要な情報が含まれているはずです。
致命的な転送関数。nbsp;致命的な転送
トークンコントラクトの「転送」関数は、「_transferFrom」関数を直接呼び出します。"関数を直接呼び出します。
「_transferFrom」関数を直接呼び出しているように見えます。transfer "関数はトークン転送を実行し、転送が完了するとTransferイベントをトリガーします。
しかし、トークン譲渡を行う前に、"transfer "関数は"_isNotTax "関数を使い、譲渡が完了したかどうかを判断します。もしそうでなければ、"_getAmount "関数を使用して税金を徴収します。もしそうでなければ、税金を徴収せず、トークンを直接受信者に送信します。
前述したように、「_getAmount」の実装では、トークン契約は送信者の残高をチェックし、送信者からお金を差し引き、トークン契約に料金を送ります。をトークンコントラクトに送ります。
そして問題は、"_getAmount "は送信者が非課税アドレスでない場合にのみ呼び出されるということです。
問題は、非課税アドレスが送金を行うために送信者として使用される場合、トークンコントラクトは送信者の残高が十分かどうかをチェックしません。送信者の残高から金額を引くことさえしません。これは、トークン契約によって定義された非課税アドレスである限り、どのような数のトークンでもどのようなアドレスにも送信できることを意味します。これが、攻撃契約(?0xc403)がトークンの総供給量の999倍を直接送金できる理由です。
調べてみると、トークン契約はコンストラクタ内で_taxReceiptを非課税アドレスに設定しているだけで、_taxReceiptに対応するアドレスは攻撃契約(?0xc403)であることがわかりました。
ZhongHuaトークンのRugPullはその後特定されました:攻撃者は特定のロジックを使って特権アドレスの残高チェックを回避し、特権アドレスが何もないところからトークンを転送できるようにし、RugPullを完成させました。
How to Profit
上記の脆弱性を悪用し、攻撃者アドレス2(?0x5100)が直接、"swapExactETHForTokens」関数では、攻撃契約(?0xc403)が Uniswap V2 Router トークンを付与します。攻撃契約(?0xc403)はUniswap V2 Routerにトークンを転送するためのアクセス権を付与した後、Routerのトークン交換機能を直接呼び出し、9,990億ZhongHuaトークンをプール内の5.88ETHと交換しました。
実際、上記のRugPullトランザクションに加えて、プロジェクトオーナーは攻撃契約(?0xc403)を使用して、RugPullトランザクションの最中にプール内の5.88ETHを転送しました。0xc403)は途中で11トークンを売却し、累積9.64ETHを獲得しました。最後のRugPullトランザクションで、合計15.52ETHを獲得しました。
このプロジェクトでも、トークンセールの途中で攻撃契約(?0xc403)を呼び出すために別のEOAアドレスを使用していました。
このプロジェクトは、トークンセールの途中で別のEOAアドレスを使って攻撃契約(?0xc403)を呼び出し、別のセンダーがトークンを販売しているように見せかけ、キャッシュアウトを続けるという真の意図を偽装していました。
概要
中華トークンRugPull事件全体を振り返って考えてみると、方法自体は非常にシンプルなのですが、特権アドレスのトークン残高チェックをキャンセルするだけであることがわかりました。しかし、このケースを分析すると、なぜうまくいかなかったのだろうか?
1. セキュリティ保護と攻撃は地平が違う
2.セキュリティ専門家にとって、コードのバランスチェックは、最も基本的なセキュリティが完了する必要があるため、ほとんどのセキュリティ専門家は、無意識のうちに、「転送」機能は、このような脆弱性の警戒を緩和するために、ユーザーのバランスチェックで完了する必要があると考えるでしょう(または、このタイプの脆弱性はあまりにも基本的であるため、攻撃者は)。は使わないだろう)。
しかし、攻撃者の視点から見ると、最も効果的な攻撃は最も初歩的なものであることが多いのです。効果的でありながら見過ごされやすいRugPullテクニックとして、残高をチェックしないことを使わない理由はありません。また、少なくともケースの特徴付けという点では、ZhongHuaトークン ケースのRugPull技法は最も少ない痕跡しか残さず、他のタイプのRugPullよりも追跡がはるかに難しく、最終的にはコードのバックドアを見つけるために手動でのコード監査が必要でした。
2. プロジェクト側は、バランスをチェックする必要のない特権アドレスのバックドア・コードを意識的に隠蔽しています。プロジェクト側は、非特権アドレスに対しても別途、税金の送金計算ロジック一式を実現し、トークン・アドレスの出金→再投資のロジックを実現し、トークンが複雑な送金ロジックを実現することが合理的に見えるようにしている。他のアドレスからの送金は通常の動作と変わりませんし、コードをよく見なければ何かを見抜くことは不可能です。
このチームのRugPullの手口と、MUMIトークンやZhongHuaトークンの手口を対比してみると、どちらも特権アドレスが大量のトークンにアクセスできるようにする、比較的隠密な方法で行われていました。
MUMIトークンRugPullのケースでは、totalSupplyを変更したり、Transferイベントをトリガーしたりすることなく、プロジェクトオーナーが直接残高を変更するため、ユーザーは特権アドレスがすでに大量のトークンを所有していると認識することができません。
ZhongHuaトークンのケースはさらに徹底しており、特権アドレスの残高をチェックしないことで、特権アドレスが無制限のトークンを持っていることを、ソースコードを見る以外の手段で見つけることを不可能にしています(balanceOfで問い合わせると、特権アドレスの残高は0を示します)。アドレスの残高は0を示すが、トークンは無制限に転送できる)。
ZhongHuaトークンのRugPullケースは、ERC20トークン標準の潜在的なセキュリティ問題を反映しています。攻撃者はしばしば、標準に準拠したビジネス・ロジックを実装しながら、検出が困難なバックドアを隠す。トークンの振る舞いを標準化することで、バックドアを隠す可能性は回避され、機能の柔軟性は低下しますが、より高いセキュリティが提供されます。