Які вразливості ми шукаємо в смарт-контрактах?
Ми проводимо багаторівневий пентест криптопроєктів, який виходить далеко за рамки стандартного web application pentest плюс Slither. Це робота зі смарт-контрактами, інфраструктурою, frontend, bridges, backend API та соціальною інженерією. Ronin Bridge втратив $625M не через уразливість у контракті — через компрометацію 5 з 9 validator ключів через spear phishing, згідно з аналізом Ronin Bridge. Наш досвід показує, що ефективний пентест повинен покривати всі вектори атаки.
Пентест криптопроєкту вимагає глибокого розуміння як Solidity, так і архітектури L2 rollups, механізмів консенсусу та економіки DeFi. Без комплексного підходу легко пропустити критичні уразливості, такі як маніпуляція оракулами або реорганізація транзакцій (reorg). Автоматичні інструменти знаходять близько 30% проблем, решту — лише ручний аналіз. Тому ми комбінуємо Slither, Mythril та Aderyn з багатогодинним рев'ю коду. Кожна уразливість класифікується за CVSS, для критичних ми надаємо PoC за лічені години після виявлення. У портфоліо — понад 200 перевірених проєктів, включаючи топ-10 DeFi протоколів за TVL. Ми тестуємо не лише контракти, а й інфраструктуру: RPC ноди, bridge релеї, validator keys management, а також frontend dApp на предмет компрометації supply chain. Кожен компонент може стати точкою входу для атаки.
Статичний аналіз контрактів
Початок будь-якого пентесту контрактів — автоматизовані інструменти:
# Slither — статичний аналізатор від Trail of Bits
slither . --print human-summary
slither . --detect reentrancy-eth,reentrancy-no-eth,arbitrary-send-eth
slither . --triage-mode
# Mythril — symbolic execution
myth analyze contracts/Vault.sol --solv 0.8.20
# Aderyn — Rust-based аналізатор, швидший за Slither для великих кодових баз
aderyn .
Автоматичні інструменти знаходять низько висячі плоди: неправильний порядок операцій, невикористані return values, reentrancy в очевидних місцях. Але критичні уразливості вони знаходять рідко. Ручне рев'ю виявляє в 3 рази більше проблем, а для критичних — в 5 разів більше.
Ручний аналіз контрактів
Фокусні області для мануального рев'ю:
Access control: перевіряємо, хто може викликати privileged функції, коректність onlyOwner / AccessControl, відсутність backdoor через конструктор або initializer.
// Класична помилка: ініціалізатор можна викликати повторно
contract VulnerableProxy {
bool private initialized;
function initialize(address _admin) external {
// УРАЗЛИВІСТЬ: немає перевірки !initialized
admin = _admin;
}
}
// Правильно:
function initialize(address _admin) external {
require(!initialized, "Already initialized");
initialized = true;
admin = _admin;
}
Price oracle manipulation: перевіряємо, чи використовуються spot ціни замість TWAP. Flash loan атака на oracle може призвести до повної втрати коштів.
// Уразливо: spot price з AMM пулу
function getPrice() external view returns (uint256) {
(uint112 reserve0, uint112 reserve1,) = pair.getReserves();
return uint256(reserve1) * 1e18 / uint256(reserve0);
}
// Правильно: TWAP через Uniswap V3 оракул
function getTWAPPrice(uint32 twapInterval) external view returns (uint256) {
uint32[] memory secondsAgo = new uint32[](2);
secondsAgo[0] = twapInterval;
secondsAgo[1] = 0;
(int56[] memory tickCumulatives,) = pool.observe(secondsAgo);
int56 tickDelta = tickCumulatives[1] - tickCumulatives[0];
int24 tick = int24(tickDelta / int56(uint56(twapInterval)));
return OracleLibrary.getQuoteAtTick(tick, 1e18, token0, token1);
}
Signature validation: правильна перевірка EIP-712 підписів, захист від replay атак через nonce та chainId.
Економічні атаки
Flash loan атаки на AMM протоколи потребують глибокого розуміння механіки пулів. Ми симулюємо їх у Foundry:
// Симуляція flash loan атаки через Foundry
// forge test --match-test testFlashLoanAttack -vvv
function testFlashLoanAttack() public {
uint256 flashAmount = 1000 ether;
vm.deal(address(attacker), flashAmount);
uint256 priceBefore = target.getPrice();
attacker.manipulatePool(flashAmount);
uint256 priceAfter = target.getPrice();
console.log("Price manipulation:", priceBefore, "->", priceAfter);
uint256 profit = attacker.exploit();
attacker.repayFlash(flashAmount);
assertGt(profit, 0, "Attack should be profitable");
}
Чому пентест криптопроєкту складніший за звичайний веб-аудит?
Ми тестуємо не лише API та frontend, а й інфраструктуру блокчейн-нод, bridge контракти, механізми консенсусу валідаторів.
Frontend безпека
Wallet drainer injection: найчастіша атака на dApp — компрометація frontend через supply chain. Перевіряємо наявність Subresource Integrity (SRI) хешів, CSP заголовки, integrity в lockfile. Також шукаємо clipboard hijacking через XSS.
Фішинг через typosquatting: реєстрація схожих доменів. Включаємо в аудит перевірку моніторингу таких доменів та DNS алертингу.
Інфраструктурний аудит
RPC endpoint безпека: перевіряємо, чи відкритий RPC публічно, чи є аутентифікація, методи whitelist.
Приклад перевірки RPC
curl -X POST http://node-ip:8545 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_accounts","id":1}'
Якщо повертає акаунти — критична уразливість.
Приватні ключі та секрети: аудит управління deploy-ключами (HSM, AWS KMS), перевірка .env файлів у git history, ротація ключів при звільненнях.
Admin panel exposure: пошук незахищених admin інтерфейсів (Grafana, Jenkins, Kibana), перевірка MFA та IP whitelist.
Bridge та cross-chain специфіка
Bridge контракти — найбільш ризиковий компонент. Специфічні перевірки:
- Replay attack: підпис має включати chainId та унікальний nonce.
// Уразливо: немає chainId у підписі
bytes32 hash = keccak256(abi.encode(recipient, amount, nonce));
// Правильно: EIP-712 з chainId
bytes32 hash = keccak256(abi.encode(
BRIDGE_TYPEHASH,
recipient,
amount,
nonce,
block.chainid
));
- Validator key management: перевіряємо, скільки ключів потрібно скомпрометувати. У Ronin Bridge ефективний threshold був 2/2, незважаючи на 9 валідаторів. Ми моделюємо такі сценарії.
- Finality assumptions: bridge має чекати фінальності блоку (для Ethereum — 12+ блоків, для BSC — більше).
Етапи аудиту
| Етап |
Що робимо |
Приклад тривалості |
| Аналітика |
Вивчення архітектури, виділення критичних компонентів |
1-3 дні |
| Автоматичний аналіз |
Прогін Slither, Mythril, Aderyn, аналіз звітів |
1-2 дні |
| Ручне рев'ю |
Детальна перевірка коду, бізнес-логіки, економіки |
3-15 днів |
| Тестування |
Foundry-симуляції, fuzzing, економічні атаки |
2-5 днів |
| Складання звіту |
Опис уразливостей, PoC, рекомендації |
1-2 дні |
Що входить у звіт
Структура фінального репорту:
| Рівень |
Опис |
| Critical |
Пряма втрата коштів, негайна експлуатація |
| High |
Значний ризик за певних умов |
| Medium |
Логічні помилки, потенційний DoS |
| Low/Informational |
Best practices, покращення |
Для кожної finding: опис, Proof of Concept (код), потенційний impact, рекомендації, статус після ремедіації. Додатково ми надаємо чек-лист перевірок та консультацію щодо виправлення.
Орієнтовні терміни
Повний пентест займає від 2 до 6 тижнів залежно від складності проєкту. Вартість розраховується індивідуально — напишіть нам для оцінки вашого проєкту. Ми гарантуємо конфіденційність та підписуємо NDA.
Замовте аудит вашого криптопроєкту, щоб виявити уразливості, які пропускають автоматичні сканери. Отримайте консультацію з безпеки — наші інженери з 10-річним досвідом допоможуть захистити ваші кошти.
Аудит смарт-контрактів: як знаходять те, що не бачить компілятор
Коли протокол втрачає значні кошти через flash loan атаку на функцію, яку аудитори дивилися наживо — це не випадковість. Це системна прогалина в методології. Наш досвід показує: вразливість живе в контракті більше року, а компілятор мовчить. Ми перебудували процес аудиту так, щоб ловити такі кейси до деплою.
Що не знайде статичний аналіз?
Slither — стандартний перший інструмент. Знаходить reentrancy, integer overflow (в старих версіях Solidity), неправильне використання tx.origin, shadowing змінних, неініціалізовані сховища. На реальному проекті Slither видає десятки попереджень, з яких критичних — 0‑2. Решта — інформаційний шум.
Slither не знайде логічну вразливість. Якщо withdraw коректно перевіряє баланс і коректно оновлює стан, але бізнес-логіка дозволяє подвійне списання через два різні шляхи кодової бази — Slither промовчить.
Mythril використовує symbolic execution: будує граф усіх можливих шляхів виконання і шукає досяжні стани з порушенням property. Працює добре на ізольованих контрактах. На протоколі з 20 контрактів з cross‑contract викликами — path explosion, аналіз зависає або видає false positive.
Обидва інструменти обов'язкові як перший pass. Але вони не замінюють ручний аналіз.
Fuzzing: де Echidna та Foundry знаходять реальні баги?
Echidna — property‑based fuzzer від Trail of Bits. Ідея: формулюєш інваріанти контракту як Solidity‑функції (echidna_invariant), Echidna генерує випадкові послідовності викликів і намагається зламати інваріант.
Приклад інваріанта для lending протоколу:
function echidna_total_assets_ge_liabilities() public view returns (bool) {
return totalAssets() >= totalLiabilities();
}
Echidna знайде послідовність deposit → borrow → liquidate → repay, яка порушує цей інваріант. Руками такий кейс не побудуєш — комбінацій занадто багато.
Foundry fuzzing (forge test --fuzz-runs 100000) простіший в інтеграції, якщо команда вже на Foundry. Підтримує stateful fuzzing через invariant тести. В реальному проекті: auditing vault контракт, Foundry fuzz за 40 хвилин знайшов edge case, при якому maxWithdraw повертав значення більше фактичного балансу при конкретному співвідношенні shares/assets після кількох донатів. Hardhat unit‑тести цей кейс пропускали — там не було такої комбінації параметрів.
Medusa (від Trail of Bits, новіша за Echidna) підтримує corpus‑guided fuzzing і працює швидше на великих контрактах. Якщо обсяг кодової бази > 5000 рядків Solidity — дивимося на Medusa.
Як інваріанти допомагають виявити критичні уразливості?
Формальна верифікація доводить, що контракт задовольняє специфікації для всіх можливих вхідних даних — не для N випадкових, а математично для всіх. Інструменти: Certora Prover, K Framework, Halmos.
Certora працює з CVL (Certora Verification Language): пишеш rules і invariants, Prover транслює їх у SMT‑формули і перевіряє через Z3/CVC5. MakerDAO, Aave, Uniswap використовують Certora в CI/CD pipeline — кожен PR верифікується автоматично.
Обмеження: не працює з необмеженими циклами, складно справляється з hash functions і signature verification. Для контрактів з простою математикою (AMM, lending) — відмінно. Для контрактів з довільними зовнішніми викликами — складно написати достатньо повну специфікацію.
Formal verification має сенс для контрактів, які: керують значним TVL, оновлюються рідко, мають чітко формалізовані інваріанти. Для продуктів, що швидко ітеруються, співвідношення витрат і користі не на користь верифікації.
Вектори атак, які пропускають джуніор‑аудитори
Storage collision в proxy патерні. Transparent proxy і UUPS використовують конкретні слоти для зберігання адреси імплементації (EIP‑1967). Якщо в імплементації випадково оголошена змінна в слоті 0, яка перетинається з proxy storage — отримуємо silent override. Slither це не впіймає, якщо proxy та імплементація в різних файлах.
Read‑only reentrancy. Класичний reentrancy guard захищає від зміни стану при рекурсивному виклику. Але якщо зовнішній контракт читає стан через view-функцію в середині транзакції — guard не допомагає. Кілька років тому Curve pools стали вектором атаки саме через це: зовнішній протокол читав get_virtual_price під час reentrancy‑вразливого стану Curve.
Oracle manipulation через TWAP. Spot price — стандартна ціль для flash loan атаки. TWAP складніше маніпулювати, але не неможливо: на малоліквідних парах Uniswap v2 можна зсунути TWAP за кілька блоків при достатньому капіталі. Правильний захист — використовувати Chainlink як primary oracle з TWAP як fallback, з перевіркою deviation threshold.
Gas griefing на unbounded loop. Функція ітерується по масиву користувачів. Атакуючий додає тисячі адрес з нульовими балансами — вартість виклику функції зростає до gas limit, функція стає недоступною. Захист: pull‑pattern замість push, обмеження довжини масивів, batch‑обробка зі збереженням позиції.
Front‑running на MEV. Транзакція видна в mempool до включення в блок. MEV‑бот бачить addLiquidity на значну суму, вставляє свій swap перед нею (sandwich attack). Для AMM це частина моделі. Для протоколів з ціновими функціями — потрібен minAmountOut / deadline параметр і його обов'язкова перевірка.
Структура повного аудиту
-
Scope definition і автоматичний аналіз (1‑2 дні). Фіксуємо commit hash, версію компілятора, список out‑of‑scope. Запускаємо Slither, Mythril, Aderyn. Triage: відокремлюємо реальні критичні баги від false positive. Складаємо карту залежностей контрактів.
-
Ручний аналіз (5‑15 днів). Кожен контракт порядково. Особлива увага: всі external і public функції, всі transfer/call/delegatecall, всі місця, де змінюється стан перед перевіркою або після зовнішнього виклику, всі математичні операції з участю користувацьких inputs. В середньому 95% знайдених уразливостей — логічні, а не технічні.
-
Fuzzing і тестування (2‑5 днів). Echidna або Foundry invariant tests для критичних інваріантів. Fork mainnet тести — перевіряємо поведінку в реальному оточенні з реальними оракулами. Наприклад, за 4 дні fuzzing знаходить в середньому 3 edge cases, не покритих unit‑тестами.
-
Звіт і мітигація. Звіт з severity (Critical/High/Medium/Low/Informational), описом вектора атаки, PoC‑кодом для Critical/High. Розробники виправляють, аудитори роблять re‑audit виправлень.
| Severity |
Приклади |
Чи потребує re‑audit |
| Critical |
Виведення коштів, несанкціоноване перенесення власності |
Завжди |
| High |
Маніпуляція, DoS на ключові функції |
Завжди |
| Medium |
Некоректна поведінка при edge cases |
Рекомендується |
| Low |
Газ‑неефективність, опечатки в events |
За бажанням |
Аудит у CI/CD
Нормальна практика для зрілих протоколів: Slither і Aderyn запускаються в GitHub Actions на кожен PR. Certora Prover — на merge в main. Це не замінює повний аудит перед деплоєм, але ловить регресії.
# .github/workflows/audit.yml
- name: Run Slither
uses: crytic/[email protected]
with:
target: 'src/'
slither-args: '--filter-paths "test|mock|script"'
Чек‑лист обов'язкових перевірок перед деплоєм
- Всі external функції мають перевірки доступу (
onlyOwner, onlyRole)
- Використання
SafeERC20 для зовнішніх токенів
- Відсутність
delegatecall на невідомі адреси
- Перевірка на reentrancy у всіх функціях з зовнішніми викликами
- Наявність
minAmountOut і deadline в AMM‑функціях
- Використання перевіреного оракула (Chainlink) з deviation threshold
Інструменти аудиту: порівняння
| Інструмент |
Тип аналізу |
Що знаходить |
Обмеження |
| Slither |
Статичний |
Reentrancy, integer overflow, access control |
Пропускає логічні уразливості |
| Mythril |
Symbolic execution |
Досяжні стани з порушенням property |
Path explosion на великих базах |
| Echidna |
Fuzzing (property‑based) |
Порушення інваріантів |
Потребує написання інваріантів |
| Certora |
Formal verification |
Математичне доведення властивостей |
Не працює з хешами/підписами |
Що входить в роботу (deliverables)
- Повний звіт у PDF з CVSS‑оцінками кожної уразливості
- PoC‑код для всіх Critical і High (відтворюваний в тестовому середовищі)
- Рекомендації щодо виправлення з прикладом коду
- Re‑audit після внесення правок (до двох ітерацій)
- Коротка пам'ятка для розробників щодо подальшої експлуатації
- Підтримка після деплою протягом 30 днів (консультації та розбір інцидентів)
Терміни
Аудит простого токена або NFT‑контракту — 3‑5 робочих днів. DeFi протокол з lending/AMM — 2‑4 тижні. Повний стек з кількома протоколами, cross‑chain, proxy upgrades — 4‑8 тижнів. Re‑audit виправлень — 3‑7 днів окремо.
Наша команда має 7+ років досвіду в безпеці смарт‑контрактів, перевірила 100+ проектів з сумарним TVL понад значну суму. Гарантуємо, що в процесі ми не пропустимо жоден відомий вектор — використовуємо ліцензовані версії Slither та найкращі конфігурації fuzzer'ів. Запобігнуті збитки для клієнтів оцінюються в значну суму.
Оцініть ваш проект — ми безкоштовно проаналізуємо код і запропонуємо комерційну пропозицію протягом 2 днів. Замовте аудит з гарантією якості та отримайте знижку на re‑audit при повторному зверненні.