Проект - 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, txHashfetchTransferFromL1Completeds - recipient, amount, deadlinefetchWithdrawalBondedsByTransferId - transferIdЗапросы уходят из браузера напрямую (нет backend-прокси). Атакующий перехватывает запрос через DevTools/proxy и подменяет параметры. Или подставляет вредоносные значения через URL-параметры.
Да, веб3 - это будущее, кричали несколько лет назад. Но тут еще веб 2 не защищен, а вы уже доверяете ему бабками управлять. Веб3 дырявая шляпа и самые интересные баги этого канала скорее всего будут оттуда.