Analyse de vulnérabilité de l'équilibreur

arrière-plan

Le 22 août, Balancer a officiellement annoncé avoir reçu de graves rapports de vulnérabilité affectant plusieurs pools V2 Boost. Seuls 1,4 % de TVL ont été affectés. Plusieurs pools ont été suspendus et les utilisateurs ont été invités à retirer leurs liquidités LP dès que possible. [1] [2]

Le 27 août, le système SlowMist MistEye a découvert une transaction d'attaque soupçonnée d'exploiter la vulnérabilité Balancer. [3]

Étant donné que le pool ne peut pas être suspendu et que certains fonds sont toujours affectés par l'attaque, les responsables de Balancer rappellent une fois de plus aux utilisateurs de récupérer le LP dans le pool concerné. [4] Par la suite, Balancer a officiellement publié les détails de la vulnérabilité divulguée en août sur Medium. [5] , l'équipe de sécurité de SlowMist l'a examiné, les détails sont les suivants :

Introduction

Les responsables de l'équilibreur ont simplement souligné dans leur divulgation que le problème cette fois est que l'arrondi à la baisse du pool linéaire et l'offre virtuelle du pool combinable ont fait que bptSupply était à 0. Tout d’abord, comprenons brièvement le contenu du protocole Balancer lié à cette vulnérabilité.

Coffre-fort Balancer V2

Équilibreur V2 [6] Le protocole est un protocole de teneur de marché automatisé décentralisé (AMM) basé sur Ethereum qui représente un élément de base flexible pour la liquidité programmable. Son composant principal est le contrat Vault, qui conserve des enregistrements de tous les pools et gère la comptabilité et le transfert des jetons, y compris même l'empaquetage et le déballage de l'ETH natif. En d’autres termes, Vault est implémenté pour séparer la comptabilité et la gestion des jetons de la logique du pool.

Il existe quatre interfaces dans Vault, à savoir joinPool, exitPool, swap et batchSwap (rejoindre, quitter et échanger sont des appels distincts et il n'y a pas de combinaison dans un seul appel). L'une des fonctionnalités exceptionnelles est batchSwap, qui peut réaliser plusieurs échanges atomiques entre plusieurs pools et connecter la sortie d'un échange de pool à l'entrée d'un autre pool (GiveIn et GiveOut). Le système introduit également des échanges éclair [7] , similaire à un prêt flash interne.

Piscines linéaires Piscine linéaire

Afin d'améliorer l'efficacité du capital de LP et le problème des frais généraux de déformation et de non-déformation élevés, Balancer a lancé le pool linéaire comme solution dans la V2, introduisant ainsi le jeton BPT (ERC20 Balancer Pool Token).

piscine linéaire [8] Y compris le jeton principal (actif sous-jacent), le jeton enveloppé (jeton enveloppé) et le jeton BPT, les actifs et leurs contreparties enveloppées avec des rendements sont échangés via des taux de change connus. Plus la proportion de jetons enveloppés est élevée, plus le rendement et l’efficacité du capital du pool sont élevés. Au cours du processus de déformation, des facteurs d'échelle sont généralement utilisés pour garantir que les différents jetons sont calculés avec la même précision.

Piscines Composables Piscine Composable

Tous les pools Balancer sont des pools composables qui contiennent d'autres jetons et le pool lui-même possède également ses propres jetons. Parmi eux, la devise BPT fait référence au jeton de pool équilibré ERC20, qui constitue la base de tous les pools. Les utilisateurs peuvent librement combiner des jetons BPT dans d'autres pools pour les échanger. Le rachat implique toujours un pool et deux jetons : GiveIn et GiveOut. In signifie envoyer des jetons de composants et recevoir du BPT, tandis que Out signifie envoyer du BPT et recevoir des jetons de composants. Si le BPT lui-même était un jeton composant, il pourrait être échangé comme les autres jetons. Une telle implémentation constitue un simple chemin batchSwap entre les actifs sous-jacents et les jetons du pool externe. Les utilisateurs peuvent utiliser BPT pour échanger contre les actifs sous-jacents du pool linéaire. Il s'agit également du Balancer Boosted Pool. [9] Fondation.

Grâce à la combinaison ci-dessus, le pool combinable de Balancer est formé. Un pool stable composable bb-a-USD se compose de trois pools linéaires, tout en envoyant des liquidités inactives au protocole externe (Aave). Par exemple, bb-a-DAI est un pool linéaire contenant DAI et waDAI (aDAI encapsulé). Lorsque l'utilisateur a besoin d'un batchSwap (par exemple, en changeant USDT en DAI), l'exemple de chemin d'échange est le suivant :

  1. Dans le pool linéaire USDT, échangez l'USDT contre bb-a-USDT (entrez dans le pool linéaire USDT) ;

  2. Dans bb-a-USD, bb-a-USDT est échangé contre bb-a-DAI (échange entre BPT linéaire) ;

  3. Dans le pool linéaire DAI, bb-a-DAI est échangé contre DAI (quittez le pool linéaire DAI).

Après avoir brièvement compris les connaissances préalables ci-dessus, nous entrons dans le lien d'analyse de vulnérabilité.

analyser

Le 27 août, l'équipe de sécurité de SlowMist a reçu du système MistEye une identification indiquant qu'une vulnérabilité suspectée de Balancer avait été exploitée dans la nature. commerce [3] comme suit:

L’attaquant a d’abord emprunté 300 000 USDC à l’AAVE via un prêt flash. Ensuite, l'opération batchSwap de Vault est appelée pour effectuer le calcul d'échange de jetons BPT via le pool stable combinable bb-a-USD. Enfin, 94 508 USDC sont échangés contre 59 964 bb-a-USDC, 68 201 bb-a-DAI et 74 280. bb-a-USDT. Enfin, les jetons BPT obtenus quitteront le pool via l'exitPool du contrat Vault en échange d'actifs sous-jacents, rembourseront le prêt flash et quitteront le marché avec un bénéfice d'environ 108 843,7 $ US.

On peut voir que la clé de cette attaque réside dans batchSwap, et que s'est-il passé exactement dans batchSwap ? Regardons de plus près.

Pendant tout le processus batchSwap, l'attaquant a d'abord échangé l'USDC dans le pool bb-a-USDC, puis a échangé des jetons BPT, échangeant bb-a-USDC contre bb-a-DAI, bb-a-USDT et USDC. Enfin, le jeton principal sous-jacent USDC est échangé contre du bb-a-USDT. En d’autres termes, bb-a-USDC, en tant que jeton clé BPT, sert de jeton composant GiveOut et GiveIn.

Dans un premier temps, l'attaquant échange les jetons BPT contre les jetons principaux USDC dans le pool linéaire bb-a-USDC avec un facteur d'échelle fixe, et le montant augmenté est enregistré dans le bptBalance du pool. Mais après le deuxième échange onSwap, nous avons constaté que la valeur du montantOut de l'USDC échangé était de 0 au cours du même processus d'échange. Pourquoi est-ce?

En approfondissant la fonction onSwap, nous constatons que dans ce processus, la précision sera traitée au nominal et le facteur d'échelle du jeton correspondant sera calculé. Lorsque la fonction _downscaleDown est appelée ensuite, montantOut est arrondi à l'inférieur. Si montantOut et scalingFactors [indexOut] La différence entre les valeurs est grande et la valeur calculée de _downscaleDown est nulle.

C'est-à-dire que lorsque nous utilisons des jetons BPT pour racheter des jetons principaux, si montantOut est trop petit, la valeur de retour sera arrondie à zéro, et cette valeur est inférieure à 1e12 calculé par scalingFactors. Cependant, le montant de bb-a-USDC provenant de montantIn sera toujours ajouté au montant virtuel bptBalance, et cette opération augmentera le solde du pool bb-a-USDC, ce qui peut être considéré comme un ajout unilatéral de bb. -a-Liquidité USDC.

Ensuite, en utilisant les caractéristiques du pool stable combinable, grâce à la conversion mutuelle entre les jetons BPT, échangez d'abord bb-a-USDC contre d'autres jetons BPT. Pour suivre ce processus d'échange, vous pouvez combiner le chemin d'appel suivant du pool stable : bb-a-DAI onSwap -> _swapGivenIn -> _onSwapGivenIn. Tout d'abord, remplacez bb-a-USDC par bb-a-DAI et bb- a-USDT en séquence. Contrairement aux pools linéaires en ligne, les pools stables composables nécessitent des mises à jour en cache du taux de change avant les opérations onSwap. D'après le code, nous pouvons voir que dans le pool de combinaisons, onSwap déterminera d'abord si le taux de change des jetons mis en cache doit être mis à jour.

Après l'échange précédent, le montant de bb-a-USDC a changé et le montant total réel après nominalisation via _toNominal est totalBalance 994 010 000 000, et l'offre virtuelle de jetons BPT est de 20 000 000 000. On peut calculer que le taux de change mis à jour est près de 45 fois le taux de change du cache d'origine du pool linéaire précédent de 1 100 443 876 587 504 549, soit 49 700 500 000 000 000 000.

Par la suite, bb-a-USDC est échangé contre USDC dans le pool linéaire. Cependant, cet échange est le même que le deuxième échange, ce qui entraîne à nouveau l'arrondi de montantOut à 0, et le chemin d'échange est le même qu'auparavant.

L'échange suivant consiste à inverser l'USDC en bb-a-USDC, et le chemin d'échange est onSwap -> onSwapGivenIn -> _swapGivenMainIn. Au cours de ce processus, nous avons constaté que lors du calcul du montant à racheter, le calcul de l'offre virtuelle est basé sur la différence entre l'offre totale du jeton BPT racheté et le montant restant dans le pool, qui est 0.

En effet, bptSupply vaut 0 et la fonction _toNominal est directement appelée lors du calcul du BPT Out, et l'appel de ce chemin rend le rapport d'échange de l'USDC à bb-a-USDC proche de 1:1.

Résumer

batchSwap connecte la sortie de l'échange d'un pool à l'entrée d'un autre pool (tokenIn et tokenOut) via plusieurs échanges atomiques entre plusieurs pools et convertit l'USDC en jetons BPT. Dans ce batchSwap, aucun transfert de jeton réel n'a lieu, mais le montant final de l'échange est confirmé en enregistrant le montant transféré entrant et sortant. Et comme le pool linéaire est échangé via le jeton d'actif sous-jacent, la méthode d'échange consiste à calculer le taux via une offre virtuelle et un algorithme fixe. Par conséquent, il existe deux failles de sécurité dans batchSwap :

Le premier est le problème d'arrondi à la baisse des pools linéaires.L'attaquant ajoute unilatéralement des jetons principaux au pool par arrondi pour augmenter le ratio de jetons mis en cache, manipulant ainsi le taux de change des jetons dans le pool composable correspondant;

Deuxièmement, en raison des caractéristiques d'offre virtuelle du pool combinable, l'offre virtuelle est calculée en soustrayant le solde du pool du jeton BPT. Si GiveIn est un jeton BPT au moment du rachat, cette partie sera déduite du jeton suivant. supply. , l'attaquant n'a qu'à échanger BPT en tant que GiveIn, et manipuler d'abord son offre à 0, puis effectuer un échange inversé, c'est-à-dire que BPT est ensuite utilisé comme côté GiveOut. À ce stade, puisque l'offre est de 0, le L'algorithme sera proche de 1 : le ratio de 1 est inférieur au taux de rachat du pool linéaire pour le rachat réel, ce qui rend le nombre de jetons BPT de GiveOut indirectement manipulé.

Nous pouvons constater que la vulnérabilité 1 augmente le taux de change, tandis que la vulnérabilité 2 réduit le taux de change dans le sens inverse. L'attaquant a profité du double buff pour réaliser un profit et partir.

Lien de référence :

[1]

[2]

[3]

[4]

[5]

[6]

[7]

[8]

[9]

Voir l'original
Cette page peut inclure du contenu de tiers fourni à des fins d'information uniquement. Gate ne garantit ni l'exactitude ni la validité de ces contenus, n’endosse pas les opinions exprimées, et ne fournit aucun conseil financier ou professionnel à travers ces informations. Voir la section Avertissement pour plus de détails.
  • Récompense
  • Commentaire
  • Partager
Commentaire
0/400
Aucun commentaire
Trader les cryptos partout et à tout moment
qrCode
Scan pour télécharger Gate app
Communauté
Français (Afrique)
  • 简体中文
  • English
  • Tiếng Việt
  • 繁體中文
  • Español
  • Русский
  • Français (Afrique)
  • Português (Portugal)
  • Bahasa Indonesia
  • 日本語
  • بالعربية
  • Українська
  • Português (Brasil)