Wie in unserem vorherigen Artikel hervorgehoben, weisen über 30 % der Projekte im Awesome Uniswap v4 Hooks-Repository[1] Schwachstellen auf. Es ist erwähnenswert, dass die hier von uns genannten Schwachstellen spezifisch für Uniswap v4-Interaktionen sind. Entsprechend werden wir in diesem Artikel die sichere Hook-Interaktionslogik aus den folgenden zwei Perspektiven untersuchen:
- Fehlerhafte Zugriffskontrolle
- Unsachgemäße Eingabevalidierung
Für jede Kategorie beginnen wir mit einer Analyse der Schwachstelle und demonstrieren ihre potenzielle Ausnutzung, indem wir den entsprechenden Proof-of-Concept (PoC) bereitstellen. Darauf folgt eine Diskussion möglicher Minderungsstrategien.
Fehlerhafte Zugriffskontrolle
Im Allgemeinen können Interaktionen im Zusammenhang mit Uniswap v4-Hooks danach klassifiziert werden, ob der Hook als Locker fungiert und eine Sperre im PoolManager erwirbt, um Operationen in Pools durchzuführen. Zwei primäre Interaktionsszenarien erfordern eine ordnungsgemäße Zugriffskontrolle:
- Hook-PoolManager-Interaktion: Dies umfasst Interaktionen zwischen den offiziellen Callback-Funktionen und dem
PoolManager. Die Callback-Funktionen umfassen acht Pool-Action-Callbacks (d. h.initialize,modifyPosition,swapunddonate) und den Lock-Callback (d. h.lockAcquired). - Hook-intern-Interaktion: Dies bezieht sich auf Interaktionen, die innerhalb des Hook-Vertrags (der als Locker fungiert) stattfinden.
Hook-PoolManager-Interaktionen sind relativ unkompliziert. Hier fungiert der Hook ausschließlich als Hook und nimmt die acht Pool-Action-Callbacks entgegen. Die Logik im Hook beeinflusst die zugehörigen Pools nicht, was bedeutet, dass es keine Geldflüsse zwischen dem Hook und den Pools gibt. Die von den Callback-Funktionen bereitgestellten Parameter werden verwendet, um notwendige Speicher zu ändern oder als wichtige Funktionsparameter zu dienen. Die wichtigste Überlegung ist, ob die Callback-Parameter manipuliert werden können.
Hook-interne Interaktionen sind etwas komplexer. In der Praxis tun viele Hook-Prototypen mehr, als sie als reine Hooks fungieren. Einige Entwickler erlauben den Hooks, für ihre Benutzer Funktionen zur Geldverwaltung bereitzustellen. Diese Funktionen sind möglicherweise nicht in den Hook-Verträgen implementiert, aber wir können sie in diesem Kontext immer noch kollektiv als Hooks betrachten. In diesen Fällen nimmt ein Hook Benutzergelder entgegen und führt Pool-Operationen wie Liquiditätsverwaltung oder Swaps durch. Das bedeutet, dass der Vertrag eine Sperre vom PoolManager erwerben muss, wodurch der Hook zu einem Locker wird.
Die Uniswap Foundation hat diese Situation berücksichtigt und eine Funktion in ihre Hook-Vorlage integriert. Insbesondere stellt die BaseHook-Vorlage die lockAcquired-Funktion als Lock-Callback bereit, wie folgt:
function lockAcquired(bytes calldata data) external virtual poolManagerOnly
returns (bytes memory) {
(bool success, bytes memory returnData) = address(this).call(data);
if (success) return returnData;
if (returnData.length == 0) revert LockFailure();
// if the call failed, bubble up the reason
/// @solidity memory-safe-assembly
assembly {
revert(add(returnData, 32), mload(returnData))
}
}
Um benutzerdefinierte Logik auszuführen, nimmt lockAcquired data-Bytes entgegen und führt einen Low-Level-Aufruf an sich selbst mit diesen data durch. Die data hängt von der Geschäftslogik des Hooks ab und kann von Benutzern manipuliert werden, was potenziell zu Sicherheitsproblemen aufgrund von Hook-internen Interaktionen führt, die durch lockAcquired ausgelöst werden. Beachten Sie, dass das Hook-Design so flexibel ist, dass wir in dieser Situation nicht alle möglichen Szenarien abdecken können. Unser Hauptaugenmerk liegt hier auf dem Hook, der eine Sperre erwirbt, und seinen anschließenden internen Interaktionen. Die Untersuchung anderer potenzieller Geschäftslogiken würde die Situation für diese Diskussion zu komplex machen.
In beiden Szenarien ist es vorrangig, alle fehlerhaften Zugriffskontrollen zu beheben, die potenziell zur Ausnutzung führen könnten, da diese Funktionen klare Interaktionsentitäten haben. In den folgenden Unterabschnitten werden wir jedes Szenario sequenziell untersuchen und die notwendigen Zugriffskontrollen diskutieren, um sicherere Interaktionslogik zu gewährleisten.
Schwachstellenanalyse
Zugriffskontrollen dienen als sehr effiziente und unkomplizierte Sicherheitslösungen für viele Projekte. Wenn eine Funktion für die Aufrufe durch bestimmte Entitäten konzipiert ist, sollte sie eine Zugriffskontrolle beinhalten. Das bekannteste Beispiel für Zugriffskontrolle ist der Ownable-Vertrag der OpenZeppelin-Bibliothek, der erfordert, dass privilegierte Funktionen nur vom Eigentümer des Vertrags aufgerufen werden. Es ist klar, dass die beiden oben diskutierten Szenarien geeignete Fälle für diese Art von Kontrolle sind.
Hook-PoolManager-Interaktion: Für sichere Interaktionen mit dem PoolManager sollten Hooks die notwendige Zugriffskontrolle auf diese Callback-Funktionen erzwingen. Insbesondere sollten diese Callbacks ausschließlich vom PoolManager und keinen anderen Konten aufrufbar sein. Das Versäumnis, solche Kontrollen einzurichten, kann diese sensiblen Schnittstellen potenzieller Ausnutzung durch böswillige Akteure aussetzen.
Hook-interne Interaktion: Die an den Hook-internen Interaktionen beteiligten Funktionen sind ebenfalls so konzipiert, dass sie von bestimmten Aufrufern aufgerufen werden. Wie bereits erwähnt, enthält dieses Szenario zwei Phasen. Erstens wird die lockAcquired-Funktion des Lockers vom PoolManager aufgerufen, was darauf hindeutet, dass die Funktion msg.sender als PoolManager erfordern sollte. Zweitens leitet der Hook den Funktionsaufruf entsprechend weiter. Basierend auf dem Design von BaseHook wird dies durch Low-Level-Aufrufe an den Hook selbst implementiert. Dies bedeutet, dass diese Funktionen als external definiert sein müssen und den Aufrufer auf die Adresse des Hooks beschränken müssen.
Nehmen wir eines der Beispiele aus dem Awesome Uniswap v4 Hooks-Repository, d.h. Stop Loss Order als Beispiel[2]:
Stop-Loss-Orders sind direkt in die Uniswap V4-Pools integriert, werden on-chain platziert und über den afterSwap()-Hook ausgeführt. Es sind keine externen Bots oder Akteure erforderlich, um die Ausführung zu gewährleisten.
Betrachten wir seine afterSwap-Callback-Funktion:

Offensichtlich ist die obige Funktion für sensible Operationen ausgelegt. Aufgrund fehlerhafter Zugriffskontrollen könnte sie jedoch von böswilligen Akteuren ausgenutzt werden, die die Argumente (z. B. den key und die params) manipulieren, was zu unerwartetem Verhalten führt. Zum Beispiel könnte der afterSwap-Callback davon ausgehen, dass der Swap bereits im PoolManager stattgefunden hat. Danach könnte er Aktionen initiieren, um wesentliche Zustandsinformationen aufzuzeichnen, wie z. B. den aktuellen Preis oder gesammelte Swap-Gebühren. Wenn afterSwap seine Aufrufe jedoch nicht streng auf den PoolManager beschränkt, könnten böswillige Akteure den params-Parameter fälschen, was zu verzerrten aufgezeichneten Zuständen führt.
Exploit & PoC
Der Einfachheit halber verwenden wir einen einfachen PoC, um dieses Zugriffskontrollproblem zu veranschaulichen. Im Allgemeinen akzeptiert der beforeInitialize-Hook des Hooks einen Parameter vom Typ PoolKey, der die Hook-Adresse in seinem hooks-Feld enthalten muss (da der PoolManager dieses Feld verwendet, um die aufzurufende Hook-Adresse zu ermitteln).
Der Screenshot zeigt einen PoC, der die Ausnutzung eines Hooks mit fehlerhafter Zugriffskontrolle demonstriert, wie in DiamondHookPoC [3] zu sehen ist.
In Abwesenheit von Zugriffsbeschränkungen für die beforeInitialize-Callback-Funktion können böswillige Akteure dieser Funktion einen beliebigen poolKey übergeben. Der Hook überprüft nicht, ob der Hook dieses poolKey mit der aktuellen Hook-Adresse übereinstimmt.

Obwohl es wichtig ist zu beachten, dass der Exploit in diesem Szenario möglicherweise keine finanziellen Verluste für den Hook verursacht, verdeutlicht er dennoch dramatisch, wie der Zustand des Hooks durch ungeschützte Callback-Funktionen manipuliert werden kann.
Wie man sichert
Um die Sicherheit der Hook-PoolManager-Interaktionen zu gewährleisten, sollten sowohl die Hook-Callbacks als auch der Lock-Callback ihre Zugänglichkeit ausschließlich auf den PoolManager beschränken.
Glücklicherweise bietet Uniswap v4 Best Practices über die BaseHook in seinem v4-periphery-Repository[4].
Die BaseHook stellt den Modifikator poolManagerOnly bereit, um Aufrufe streng vom PoolManager zu beschränken:
/// @dev Only the pool manager may call this function
modifier poolManagerOnly() {
if (msg.sender != address(poolManager)) revert NotPoolManager();
_;
}
Dieser Modifikator kann effektiv eingesetzt werden, um eine ordnungsgemäße Zugriffskontrolle auf die sensiblen Hook- und Lock-Callbacks durchzusetzen.
Andererseits erfordert das Vorhandensein von Hook-internen Interaktionen, dass alle signifikanten Zustandsänderungsfunktionen, die über den lockAcquired-Callback aufgerufen werden, wie in BaseHook spezifiziert, nicht beliebig aufrufbar sein sollten.
Um diese Anforderung zu erfüllen, bietet die BaseHook einen selfOnly-Modifikator. Dieser Modifikator beschränkt die Zugänglichkeit der deklarierten Funktion auf den Hook selbst und verbietet externen Verträgen, diese sensiblen Funktionen zu bösartigen Zwecken direkt aufzurufen.
/// @dev Only this address may call this function
modifier selfOnly() {
if (msg.sender != address(this)) revert NotSelf();
_;
}
Zusammenfassend lässt sich sagen, dass benutzerdefinierte Hooks durch die Vererbung von BaseHook diese integrierten Zugriffskontrollmodifikatoren und Callbacks nutzen können, um eine ordnungsgemäße Zugriffskontrolle durchzusetzen.
Unsachgemäße Eingabevalidierung
Die BaseHook in v4-periphery[4] bietet eine Lösung für sicherere Interaktionslogik, die Entwickler von Hooks nutzen können. Wir beobachten jedoch weiterhin Fälle unsachgemäßer Verwendung, die neue Angriffsvektoren in bestehenden Hooks eröffnen.
Standardmäßig erlauben Hooks, dass jeder Pool über die initialize-Funktion im PoolManager registriert wird. Wenn ein Hook jedoch die zugrunde liegenden Vermögenswerte im registrierten Pool nicht validiert, könnten böswillige Benutzer einen Pool mit gefälschten Token registrieren, wodurch sie über die transfer-Funktion der Token erneut in den Hook eintreten könnten.
Diese Schwachstelle ist subtil, da der Hook selbst möglicherweise keine bösartige Logik ausführt. Wenn der Hook jedoch den PoolManager aufruft, könnten die Interaktionen zwischen dem PoolManager und den zugrunde liegenden Vermögenswerten eines bösartigen Pools potenziell den Kontrollfluss über die take-Funktion im PoolManager an einen Angreifer übergeben.
/// @inheritdoc IPoolManager
function take(Currency currency, address to, uint256 amount) external override
noDelegateCall onlyByLocker {
_accountDelta(currency, amount.toInt128());
reservesOf[currency] -= amount;
currency.transfer(to, amount);
}
Im Wesentlichen rührt die Schwachstelle von unsachgemäßen Validierungen des registrierten Pools her, mit dem Hook-Benutzer interagieren möchten. Wir werden uns diese Schwachstelle anhand eines konkreten Beispiels ansehen und mögliche Minderungsstrategien diskutieren.
Schwachstellenanalyse
Take Profits Hook[5] ist ein Hook, der von Awesome Uniswap v4 Hooks gelistet ist:
In diesem Beispiel erstellen wir einen Hook, der es Benutzern ermöglicht, 'Take-Profit'-Positionen zu platzieren. Beispielsweise könnte in einem ETH/DAI-Pool, wenn derzeit 1 ETH = 1500 DAI ist, eine Take-Profit-Order platziert werden wie "verkaufe mein gesamtes ETH, wenn 1 ETH = 2000 DAI", die automatisch ausgeführt wird.
Schauen wir uns die Funktion _handleSwap in diesem Hook an. Diese Funktion führt einen Swap aus, um Take-Profit-Orders nach Erhalt einer Sperre zu erfüllen.

Sie werden vielleicht feststellen, dass diese Funktion durch keinen Zugriffskontrollmodifikator geschützt ist. Zeile 250 schränkt den Zugriff jedoch effektiv ein, so dass diese Funktion nur nach Erwerb einer Sperre vom PoolManager aufgerufen werden kann. Andernfalls schlägt der poolManager.swap fehl, da der Operator nicht der letzte Locker ist. Mit anderen Worten, _handleSwap muss in einer bestimmten Reihenfolge aufgerufen werden, vorausgesetzt, die registrierten Pools werden validiert. Leider implementiert der Hook keine solche Validierung.
Aufgrund dieser fehlerhaften Implementierung ist der Hook anfällig für einen Reentrancy-Angriff. Diese Schwachstelle könnte es Angreifern ermöglichen, beliebige Swaps mit vom Benutzer eingezahlten Geldern zu erzwingen.
Exploit & PoC
Konkret kann der Angriff durch die folgenden Schritte gestartet werden:
- Der Angreifer registriert einen bösartigen Pool mit gefälschten Token und gibt den Take Profits Hook als Hook des Pools an.
- Der Angreifer platziert eine Stop-Profit-Order in dem bösartigen Pool über den Hook.
- Der Angreifer führt einen Swap in dem bösartigen Pool durch, der den
fillOrderimafterSwap-Callback auslöst, um die Stop-Profit-Order des Angreifers zu erfüllen. - Der Hook ruft die
lock-Funktion desPoolManagerauf, um eine Sperre anzufordern, und ruft die Funktion_handleSwapimlockAcquired-Callback auf. - In der Funktion
_handleSwaplösen die Übertragungen von Token böswillige Logik im gefälschten Token-Vertrag aus, die die Funktion_handleSwaperneut aufruft. Dies ist möglich, da_handleSwapeine externe Funktion ohne Zugriffsbeschränkungen ist. Da die Sperre bereits erworben wurde, kann der Angreifer den Hook zwingen, beliebige Swaps auf jedem Pool auszuführen, solange der Hook über ausreichende zugrunde liegende Vermögenswerte verfügt. Der Angreifer kann dann die Swaps verpacken, um auf Kosten anderer Benutzer Gewinne zu erzielen.
Das folgende detaillierte Diagramm veranschaulicht den Angriffsfluss.

Wie bereits erwähnt, führt der Hook selbst keine böswillige Logik aus. Der einzige Fehler ist, dass der Hook nicht verhindert, dass nicht vertrauenswürdige Token-Pools im PoolManager-Vertrag registriert werden. Indirekt wird die böswillige Logik im gefälschten Token-Vertrag über Token-Transfer-Operationen aufgerufen, was ebenfalls eine Art nicht vertrauenswürdiger externer Aufruf ist.
Wie man sichert
Es gibt drei praktikable Ansätze, um potenzielle Angriffe aufgrund unsachgemäßer Eingabevalidierung zu mindern:
-
Ordnungsgemäße Zugriffskontrolle. Durch die Nutzung von Bausteinen aus der
BaseHookkann ein Hook die Funktionszugänglichkeit streng verwalten. Dies verhindert, dass beliebige Konten sensible Funktionen aufrufen. -
Reentrancy Lock. In dem oben genannten Angriffsszenario kann dieser Ansatz zweifellos verhindern, dass die bösartige Token-Logik erneut in die sensiblen Funktionen eintritt. In einigen Fällen erfordert das Hook-Design jedoch, dass der Hook selbst re-enterable ist. Insbesondere wenn ein Hook Pool-Aktionen ausführen muss, sollte er dem
PoolManagererlauben, seine Callbacks erneut aufzurufen, um diese Aktionen abzuschließen. Ein Reentrancy Lock kann diese beabsichtigte Funktionalität unterbrechen. -
Whitelisting-Ansatz. Dies würde einen privilegierten Administrator erfordern, um genehmigte Pools in den Hooks auf die Whitelist zu setzen. Der Administrator stellt sicher, dass Whitelisted-Pools keine potenziellen Risiken darstellen. Die Einschränkung besteht jedoch darin, dass Hook-Benutzer nur Operationen auf einer begrenzten Anzahl von vom Administrator genehmigten Pools über den Hook ausführen könnten. Während der Whitelisting-Ansatz die Sicherheit verbessert, schränkt er die Funktionalität des Hooks stark ein.
Es ist schwierig, eine perfekte Lösung zu finden, die Sicherheit und Benutzerfreundlichkeit für Hooks ausbalanciert. Während wir mehrere Minderungsansätze diskutieren, müssen Entwickler Kompromisse bei ihrem Hook-Design sorgfältig abwägen. Ziel sollte es sein, potenzielle Risiken so weit wie möglich zu mindern und gleichzeitig die beabsichtigte Funktionalität beizubehalten. Darüber hinaus deckt unsere Diskussion nur Schwachstellen ab, die in den Interaktionen speziell im Zusammenhang mit Uniswap v4-Funktionen auftreten können. Praktische Anwendungen werden zweifellos umfassender sein. Stellen Sie immer sicher, dass Sie jede Zeile Ihrer Verträge verstehen und bleiben Sie SAFU!
Fazit
In diesem Artikel untersuchen wir die Schwachstellen, die während der Hook-Interaktionslogik auftreten, und konzentrieren uns insbesondere auf zwei Szenarien: fehlerhafte Zugriffskontrolle und unsachgemäße Eingabevalidierung. Wir präsentieren eine detaillierte Schwachstellenanalyse, illustrieren potenzielle Ausnutzungen zusammen mit ihren PoCs und diskutieren mögliche Minderungsstrategien. Wir glauben, dass diese Erkenntnisse zur sicheren Entwicklung und Nutzung von Hooks beitragen und zukünftige Bemühungen zur Schwachstellenentdeckung leiten können.
Referenz
[2] Stop Loss Order
[3] DiamondHookPoC
[4] v4-periphery
[5] Take Profits



