Sommario di gestione
Il team di ZenPool ha contattato Sayfer Security per eseguire un audit di sicurezza completo per tutti i loro contratti.
Prima di valutare questi servizi, abbiamo tenuto un kickoff meeting con il team tecnico di ZenPool e abbiamo ricevuto una panoramica del sistema e gli obiettivi di questa valutazione.
Il completamento dell'audit successivo ha richiesto 20 giorni/uomo, tutti i contratti on-chain sono stati esaminati linea per linea con almeno 2 revisori per contratto. A causa dei limiti di tempo dal lato client, abbiamo esaminato i componenti off-chain e abbiamo adottato un approccio del massimo sforzo per assicurarci che non contenessero vulnerabilità critiche.
Abbiamo trovato un totale di 9 risultati, 3 dei quali sono stati classificati come vulnerabilità ad “alto” rischio e potrebbero essere sfruttati da malintenzionati, che potrebbero svuotare completamente i fondi del pool.
Abbiamo documentato il nostro processo ei nostri suggerimenti su come risolvere ogni vulnerabilità. Il team di ZenPool ha implementato le correzioni documentate in ogni sezione relativa alle vulnerabilità.
panoramica del sistema
ZenPool è un token open source, non detentivo e un protocollo di mercato dei prestiti.
Gli utenti possono depositare le proprie risorse crittografiche per guadagnare interessi o prendere in prestito altri token per pagare interessi nel mercato di ZenPool. ZenPool ha il suo token chiamato ZEN.
Lo ZEN è una valuta a fluttuazione libera sostenuta dalla fornitura di tesoreria della moneta stabile BUSD. I token ZEN possono essere coniati e bruciati solo dal protocollo, solo in risposta al prezzo il protocollo lo fa. Ogni ZEN è supportato da almeno un BUSD.
Se il prezzo di ZEN scende al di sotto di 1 BUSD, il protocollo acquista e brucia ZEN, spingendo il prezzo fino a 1 BUSD.
ZenPool supporta anche le obbligazioni che sono un altro modo per aumentare il proprio tesoro. Il protocollo vende obbligazioni in cambio di vari beni e, in cambio, l'acquirente riceve ZEN a un prezzo significativamente ridotto. Ciò aumenta la tesoreria e consente a ZeenPool di fornire rendimenti incredibili ai propri clienti.
Infine, una parte di tutte le tariffe del prodotto ZenPool verrà utilizzata come supporto aggiuntivo, fornendo potenzialmente al token di ZenPool una pista infinita per lo staking dei premi.
Vulnerabilità per rischio
Alta
- Transazione DoS
- perdita diretta di fondi
- congelamento permanente dei fondi
Medio
- Attacchi contro thin client
- Parzialmente DoS
- Attacchi di gas
Basso
- Buone pratiche
Informativo
- Non danneggia il sistema e non disponiamo di dati o conoscenze sufficienti per dimostrare che non farà mai del male, tuttavia è importante condividere il nostro
Ambito e Contratti
Nell'ambito della definizione dell'ambito del progetto e per comprendere l'attenzione e le esigenze dei nostri clienti, abbiamo definito i contratti che dovremmo testare in questo audit. Insieme abbiamo trovato il miglior equilibrio che si adatta a questo specifico progetto.
L'ambito è un ambito flessibile per definizione, il che significa che potremmo testare in un ambiente locale altri contratti che potrebbero interagire con il contratto definito come ambito dell'audit. Questo ci offre la flessibilità necessaria per individuare problemi di sicurezza che altrimenti potrebbero essere trascurati.
L'elenco dei contratti e il loro Github commit:
Conferma hash
Conferma hash | Contratto |
5d253cb3c544a17399e7d2f4c381a1065bcfa0a0 | https://github.com/zenpoolproject/zenpool/tree/master/contracts/createGovernor.sol |
3887cd307163d130477d4805e74ade11f84d7a4d | https://github.com/zenpoolproject/zenpool/tree/master/contracts/ZenPoolUserManager.sol |
a50062dfc63a0e82ec43de98865420193270236a | https://github.com/zenpoolproject/zenpool/tree/master/contracts/ZenPoolManager.sol |
c4d0db2f9fbfc3146a1a1a797e55c3f3d7a19899 | https://github.com/zenpoolproject/zenpool/tree/master/contracts/ZenTickets.sol |
844132ae5210cb27101bf4a415d9c16e201e1afc | https://github.com/zenpoolproject/zenpool/tree/master/contracts/ZenPoolFunds.sol |
a70146c5d276f933a79c567d2e96512302a6a940 | https://github.com/zenpoolproject/zenpool/tree/master/contracts/ZenGovernorAlpha.sol |
d476137af592fe71cae2578a62e2f8f92b335d8c | https://github.com/zenpoolproject/zenpool/tree/master/contracts/ZenLendingPool.sol |
8c5d858ad7fbfe856cd23160fd8810997f3fdf70 | https://github.com/zenpoolproject/zenpool/tree/master/contracts/ZenGovernorAlpha.sol |
Controllo dell'ordine da Sayfer
Risultati dell'audit del contratto intelligente
Gli utenti possono prelevare fondi da altri saldi fino a quando il pool non è vuoto
Contratto | contratti/ZenPoolFunds.sol |
Rischio | Alta |
Fissa | Sì |
Trovato da | Test manuale |
Descrizione
I withdrawFunds
la funzione convalida che l'utente può prelevare solo l'importo massimo nel suo saldo, controllato dal suo indirizzo e successivamente confrontato all'interno del pool di token.
Successivamente l'importo prelevato viene detratto dal prestito massimo corrente dell'utente al prezzo corrente. Se l'importo totale dei fondi presi in prestito dall'utente supera il nuovo prestito massimo, il metodo fallisce perché l'utente non ha più garanzie sufficienti per sostenere la propria posizione di prestito. Questo requisito, tuttavia, viene verificato solo se l'utente non è già sovraindebitato:
Un utente malintenzionato potrebbe utilizzare questa funzionalità per sfruttare il withdrawFunds
funzione per prelevare più dell'importo massimo del prestito.
Poteva farlo perché il getMaxBorrow
verrà controllato solo se l'utente prende in prestito meno dell'importo massimo consentito, in una varietà di scenari l'attaccante potrebbe abusare del flusso per saltare questo specifico require
, permettendogli di chiamare il withdrawFund
più volte fino a quando la piscina non si sarà svuotata.
Mitigazione
Cambiare il require
essere chiamato prima della riga 145 in modo che non dipenda dall'istruzione if. In questo modo il require
sarà sempre eseguito.
Autodistruzione non sicura in Proxy Contract
Contratto | contratti/ZenTickets.sol contratti/ZenPoolManager.sol |
Rischio | Alta |
Fissa | Sì |
Trovato da | Test manuale |
Descrizione
Quando il file main ZenPoolManager
viene distribuito, distribuisce anche più contratti.
Uno di questi è ZenTickets
contratto che è per lo più garantito, ad eccezione del destoryContract
funzione che non ha alcun meccanismo ACL in atto come il resto delle funzioni:
Sfruttando questa funzionalità, un utente malintenzionato non autenticato potrebbe chiamare il file destoryContract
funzione e scegliere dove trasferire l'ETH del contratto. Questo è facile da sfruttare dal punto di vista dell'attaccante.
Mitigazione
Usa il onlyOwner
modificatore come se fosse usato nel reset delle funzioni.
Un'altra soluzione più specifica sarebbe quella di utilizzare un meccanismo ACL personalizzato come l'ACL dei ruoli di openzeppelin che consentirà solo a ruoli specifici di accedere al destoryContract
funzioni
I pool dell'utente sono soggetti ad attacchi MEV
Contratto | contratti/ZenPoolUserManager.sol |
Rischio | Alta |
Fissa | No - Rischio corso |
Trovato da | Test manuale |
Descrizione
Il principale ZenPoolUserManager
sta gestendo pool di utenti personalizzati che sono stati creati nel sistema. L'utente può chiamare diverse azioni su questi pool se dispone delle autorizzazioni appropriate per farlo, un utente può anche concedere l'accesso tramite un meccanismo di ruoli basato sui contratti di controllo degli accessi di OpenZeppelin
I ZenPoolUserManager
ha un vero e proprio meccanismo ACL, ma allo stesso tempo ha 2 funzioni soggette a front/back running o ad eventuali attacchi MEV.
Mentre la logica aziendale stessa è corretta e sembra che non possa fare molto male poiché ha un ACL adeguato, un utente con lo stesso ruolo potrebbe sfruttarlo tramite attacchi MEV.
Mitigazione
Usa il onlyOwner
modificatore come se fosse usato nel reset delle funzioni.
Un'altra soluzione più specifica sarebbe quella di utilizzare un meccanismo ACL personalizzato come l'ACL dei ruoli di openzeppelin che consentirà solo a ruoli specifici di accedere al destoryContract
funzioni.
Mancanza di attenuazione del tutore
Contratto | contratti/ZenGovernorAlpha.sol |
Rischio | Medio |
Fissa | Fissa |
Trovato da | Analisi del codice legacy e test manuali |
Descrizione
Durante il nostro audit abbiamo eseguito l'analisi del codice originale e abbiamo confrontato l'attuale codebase del cliente con il
progetti open-source che il cliente ha utilizzato per sviluppare il codice. Se abbiamo scoperto che sono state apportate modifiche al codice open source, le abbiamo esaminate più a fondo per assicurarci che siano state eseguite con saggezza.
La maggior parte dei clienti considera sicuri i progetti open source di terze parti perché provengono dai principali fornitori (ad esempio Uniswap pool). Questa ipotesi è per lo più vera. Tuttavia, i principali bug di sicurezza si verificano quando i client eseguono modifiche minori nel codice e presumono che queste modifiche non influiscano sulla sicurezza complessiva del prodotto.
Durante la nostra analisi del codice originale, abbiamo scoperto che ZenPool utilizza il codice del protocollo STRIKE per creare token di governance, il codice originale del repository STRIKE è:
Mentre il codice in ZenGovernorAlpha è:
La seguente dichiarazione è stata rimossa msg.sender == guardian
, ciò significa che il tutore non può annullare gli ordini se viene raggiunta una soglia. L'eliminazione del potere dei guardiani può causare situazioni molto pericolose. Ad esempio, se è stata eseguita una proposta compromessa che trasferisce tutto il denaro del contratto a un attore malintenzionato (ad esempio tramite un hack della chiave privata), il meccanismo di protezione della soglia è ridondante e non può aiutare, perché non esiste un tutore che possa fermare la proposta dall'accadere.
Mitigazione
Aggiungi msg.sender == guardian
Vai all’email require
.
Mancanza di Reentrancy Guard
Contratto | contratti/ZenLendingPool.sol |
Rischio | Medio |
Fissa | Fissa |
Trovato da | Slither e test manuali |
Descrizione
La seguente funzione di prestito non contiene una guardia di rientro né il pattern checks-effects-interactions, questo non è attualmente sfruttabile in quanto il pool supporta solo i token ERC20, ma se in futuro verrà aggiunto un nuovo ERC-777 potrebbe verificarsi un exploit di rientro e causare il pieno prelievo dei fondi del contratto.
Inoltre, il codice non segue il modello checks-effects-interactions. In questo caso specifico, il require
viene controllato dopo che alcune logiche di business sono già state implementate, ciò potrebbe causare bug di rientro se gli effetti collaterali verranno aggiunti prima del require
.
Mitigazione
Aggiungi una guardia di rientro per il rientro della stessa funzione e usa anche il modello controlli-effetti-interazioni per il rientro complesso tra funzioni.
Non consentire trasferimenti di importo zero
Contratto | contratti/ZenPoolFunds.sol |
Rischio | Basso |
Fissa | Sì |
Trovato da | Test manuale |
Descrizione
I ZenPoolFunds.sol
il contratto consente trasferimenti di importo pari a zero tra gli utenti. Anche se di per sé non è una vulnerabilità di sicurezza, questo è un esempio di codice che può espandere il vettore di attacco di un utente malintenzionato.
La vulnerabilità esiste perché transfer
su ZenPool emetti eventi, facendo importo zero transfer
un utente malintenzionato potrebbe emettere più eventi che in alcuni scenari possono attivare la logica aziendale off-chain, senza nemmeno tenere alcun token nel pool.
Mitigazione
È necessaria una comprensione più profonda del business. Se possibile, rimuovere questa funzionalità.
Se ci sono casi d'uso in cui ha senso utilizzare trasferimenti di importo pari a zero, implementare un altro livello di controlli per limitare l'utilizzo agli utenti che fanno parte di questo gruppo.
Registrazione insufficiente per le funzioni privilegiate
Contratto | contratti/ZenGovernorAlpha.sol |
Rischio | Info |
Fissa | Sì |
Trovato da | Test manuale |
Descrizione
I seguenti privilegiati onlyOwner
la funzione non emette eventi.
Gli eventi sono una pratica comune per le funzioni privilegiate per annunciare al pubblico che è stata apportata una modifica. Senza eventi, le modifiche sono più difficili da rilevare e gli utenti rimangono sorpresi dal comportamento del contratto.
Il proprietario può chiamare il chainId
eseguendo setChainId()
e modificando il chaindId
parametro senza che venga emesso alcun evento nel processo.
Mitigazione
Aggiungi codice che emette eventi.
Controllo ridondante delle condizioni
Contratto | contratti/createGovernor.sol |
Rischio | Info |
Fissa | Sì |
Trovato da | Test manuale |
Descrizione
Quando si crea un nuovo token governatore, il _timelockDelay
parametro viene convalidato due volte. once in all controlla quando la funzione inizia e poi come parte di un'altra require
, è ridondante e deve essere rimosso.
Il codice duplicato richiede più gas per essere eseguito e può portare a bug futuri quando la logica aziendale cambia.
Mitigazione
Rimuovere il secondo ridondante require
.
Denominazione incoerenza
Contratto | – Multiplo – |
Rischio | Info |
Fissa | Rischio preso |
Trovato da | Slither e test manuali |
Descrizione
Ci sono più occorrenze di diversi stili di maiuscole (caso cammello inferiore, caso serpente ecc.).
Mitigazione
Esaminare il codice per lo stile del codice.
Aggiungi una guida allo stile del codice e applicala utilizzando un'attività CI.