Введение
Всех приветствую. Думаю, абсолютно все здесь пристутсвующие, слышали и знают про такую уязвимость как SQL-инъекция. Ставшая, по сути классикой уязвимость, с которой начинают изучение веб-беза большинство новичков, уязвимость, которая у всех на слуху, и которая может пристуствовать не только в вебе, но и на десктопе. Со временем, об SQL-инъекциях узнали все, сейчас даже со стажеров и джунов на должность бекендера спрашивают умение не допускать эту уязвимость в коде. Сама же уязвимость, по сути потихоньку, но начинает уходить в раздел “устаревшие”. Да, до сих пор можно найти какие-то иньекции даже в серьезных проектах, но как правило эти иньекции достаточно сложные и далеко не дефолтные. Нельзя сказать что SQL-инъекции вымерли, но то что их стало гораздо меньше – это факт.
Но речь здесь пойдет все-таки не о них. С развитием технологий, стали развиваться и способы хранения данных. И помимо SQL баз данных (реляционные базы данных) появились еще NoSQL базы (нереляционные базы данных). Именно об уязвимостях в них и пойдет сегодня речь.
Начал статью я с SQL-inj, потому что многие ставят эти понятия рядом, думая что схожесть в названиях подразумевает и большую схожесть в процессе эксплуатации. Однако, это не совсем так, процесс атаки на SQL и NoSQL местами сильно различается, но, в каком-то смысле эти понятия действительно стоят рядом, и сказать что абсолютно ничего общего там нет - нельзя. Именно поэтому на протяжении всей статьи я буду стараться проводить аналогии с SQL-inj, так как по моему мнению, это поможет проще воспринимать информацию.
Но обо всем по порядку. Далее поговорим чем отличаются SQL и NoSQL, как происходит сама атака на вторые, что с этого можно получить, и какой инструмент поможет автоматизировать процесс. Вообщем, думаю, будет интересно
Примеры кода в данной статье буду приведены на языке Go. Да, я понимаю, что как правило проще это все показывать на php, но целью этой статьи являлось именно подача для новичка по этой теме в текущее время. Чистый php и указанные примеры в других статьях в итернете сейчас уже очень редко где встретишь и было интересно посмотреть, как дела обстоят сейчас с более интересными и перспективными технологиями, как можно допустить уязвимость там и т.д. И да, статья все же начального уровня, скорее как введение. Она рассчитана на людей, которых эта уязвимость обошла и они хотят понять что это и почему, плюс минус разобравшись еще и в смежных моментах. Более сложные аспекты здесь затрагиваться не будут, только основное. Статья итак получилась длинной, а если расписывать здесь же еще и более сложные кейсы – получилось бы вообще полотно. Поэтому, идем разбираться с начальным пониманием.
Отличия NoSQL и SQL
Для начала предлагаю как раз поговорить про отличия SQL от NoSQL, или реляционной модели баз данных от нереляционной. И заодно более менее разберемся как работает модель нереляционной БД. Сильно углубляться не будем, но основные моменты знать надо.
Для начала вспомним как вообще выглядят реляционные базы данных. По сути, каждая реляционная база – это набор таблиц, состоящих из строк и столбцов, в которых и хранятся данные. Тип данных в каждом столбце жестко структурирован – то есть, в поле типа integer (цлочисленный тип данных) мы не сможем положить строку, так же, как не сможем положить дату. Каждое поле имеет строго указаный тип данных, которые будут храниться в этом поле. Как работают реляционные БД достаточно просто понять, просто представив себе схему базы:
Процесс взаимодействия с данными происходит при помощи языка SQL, который позволяет обращаться к данным в определнных столбцах или строках, сортировать данные по определенным параметрам и т.д. Сам язык SQL досточно прост для восприятия, так как по сути, это просто английский, на котором вы просите базу выдать вам какую-либо информацию. Даже человек, не изучавший специально SQL, сможет интуитивно понять что делает запрос:
SELECT ALL IN “users” WHERE age=25
Ведь даже в переводе все звучит достаточно просто – выбрать все записи, в таблице пользователи, где возраст равен 25
Понятное дело, что в СУБД есть чуть более сложные моменты по типу связей и т.д., но в это мы углубляться не будем, у нас тут все-таки не лекция по SQL
Просто понимаем для себя, что SQL не самая сложная технология даже для неподготовленного пользователя, где все интуитивно понятно.
Принцип работы NoSQL же в корне отличается. Во первых, сама структура базы данных – она менее удобна для человека в плане структурирования информации .Если в случае с реляционными БД база является набором из таблиц, то в случае с нереляционными базами, база состоит не из таблиц, а из коллекций. И это не просто отличие в названиях, сама по себе коллекция по структуре отличается от таблицы. Если в случае с реляционными БД таблица состоит из строк и столбцов, то в случае с нереляционными БД коллекция состоит по сути из обычного JSON, с разделением на документы.
Однако, стоит всетаки заметить, что в нереляционных бд используется все же не совсем json. Там используется BSON – надмножество json, которое имеет некоторые свои плюшки, вроде регулярных выражений, автоматически генерируемых id и ряда других полезных функций, которые несколько увеличивают возможности БД.
Чтобы было понятнее что такое документы, вспомните строки в реляционных БД. Они, как правило содержат разную информацию об одном обьекте под определенным id. К примеру, у нас, в реляционной бд есть информация о человеке по имени Иван Иванов, содержащая некоторую информацию о возрасте и роде деятельности.
Вот как будет записана такая информация в реляционной БД:
А вот как будет выглядеть та же запись в нереляционной базе данных:
Каждый документ в базе имеет свой уникальный идентификатор (_id), который и позволяет обращаться к документу по запросу. Этот id является отдельным типом данных в bson.
Такой ID создается рандомно, в случае если он четко не указывается при создании документа. И в отличие от SQL здесь нельзя настроить автоинкремент.
Следующее отличие – язык запросов. В случае с реляционными бд языком запросов служит SQL. В случае с нереляционными бд все несколько иначе. Есть некий интерфейс управления (CLI идущая в комплекте с базой или драйверы для ЯП), при помощи которых и производятся операции. Как правило, все взаимодействие сводится к вызову методов, в которые передается тот самый BSON данные, на основе которых и производятся CRUD операции.
Если рассматривать самые популярные СУБД для обеих технологий, то если в случае с SQL самой популярной системой является MySQL, то в случае с нереляционными БД мейнстримом является mongoDB.
MongoDB имеет cli для управления базой из консоли и графический интерфейс под названием compass для упрощения работы, который и будет использован далее в примерах.
Так чем же вообще NoSQL выгоднее чем SQL. Казалось бы – классические реляционные базы куда более понятны, логичны да и популярнее. А здесь все просто – NoSQL, хоть и не имеет большого количества фишек SQL, таких как связи или автоинкременты, например, именно благодаря своей относительной “простоте” гораздо лучше работает с большими обьемами данных. Поэтому NoSQL используются в основном с высоконагруженными сервисами и там, где замешана “big data”.
Атаки
А теперь плавно переходим к самой атаке. Представим что у нас есть приложение, работающее как раз с нереляционной базой данных. В качестве примера такого приложения возьмем небольшой сайтик, фронтовая и бэк части которого общаются между собой посредством http-запросов, содержащих в себе json-данные. И, дабы подчернуть актуальность уязвимости и не описывать все примеры уязвимого кода на чистом php, в качестве бэкенда у нас будет выступать небольшое REST api на golang, которое и будет принимать все данные с фронта, обрабатывать и возвращать ответ.
В отличие от SQL-иньекций, NoSQL иньекции не ограничиваются одним типом внедрения. То есть, если в случае с SQL-иньекциями подразумевается только внедрение SQL-кода в запрос (речь не про результат, а про процесс), то в случае с NoSQL-иньекциями подразумевается целый блок разных уязвимостей. И в отличие от тех же SQL-inj здесь можно пойти далеко не одним путем. Однако, желаемый результат, ради которого применяется та или иная атака, может быть довольно схож в некоторых моментах с желаемым результатом в SQL-инъекциях. Поэтому, опять же, чтобы вам было проще разобраться, в случае если вы имеете представление о том, как работают SQL-inj, я буду проводить аналогии с некоторыми атаками на SQL. Давайте посмотрим какие виды NoSQL-иньекций существуют:
JSON-иньекции
Начать предлагаю с самой простой и в то же время имеющей место быть атаке. Здесь все достаточно логично. Проблема, как и в sql иньекциях здесь заключается в отсутствии нормальной валидации пользовательских данных и составлении какаих-то частей запросов конкатенацией, что уже само по себе является проблемой (а для кого-то возможностью) в любом приложении.
Представим ситуацию. У нас есть форма авторизации, которая принимает на вход логин и пароль:
Посмотрим как это работает:
Форма получает на вход логин и пароль. Далее формирует запрос на наш backend под управлением go. Запрос идет по методу POST, имеет content-type
“application/json” и в теле содержит json-структуру, содержащую введеные пользователем данные для авторизации – логин и пароль:
Далее запрос приходит на бэкенд. Здесь мы парсим полученный в запросе json в структуру, которая и содержит два поля – логин и пароль сотвественно. А вот далее самое интересное. Как происходит процесс авторизации ? Мы создаем запрос к нашей MongoDB, в которой просим найти ее пользователя с введенными учетными данными. Т.е. программа обращается к коллекции users, где ищет документ с полями name и passwd, которые будут соответствовать введеным пользователю данными.
После того как запрос отправлен – база возвращает результат и сразу сохраняет его в структуру.
Если пользователь найден – в структуру будут записаны его данные. Конкретно нас интересуют login и Id. Эти данные по итогу и отправляются обратно фронтенд.
Если же пользователь не найден, структура после запроса окажется пустой. Ну и далее, наше бэкенд приложение, увидев что поля ID и Name пустые понимает что пользователь не найден и возвращает на фронт ошибку.
Вот так в теории и должен выглядеть весь процесс. Предположим у нас в базе есть пользователь с данными admin/admin:
Если пользователь введет верные учетные данные, он увидит сообщение об успешной авторизации:
Если же пользователь введет неверные креды, приложение сообщит ему что такого польователя нет:
Вот так все должно работать в идеале. Но все же код, взаимодействующий с бд на бэкенде не идеален. У него есть одна серьезная проблема – составление фильтра (того самого параметра, по которому отбираются данные в бд) конкатенацией – составляется строка, в которую напрямую подставляются введенные пользователем данные.
Рассмотрим код подобробнее, чтобы понять в чем здесь проблема:
Для начала получаем коллекцию и готовим структуру, в которую будем парсить полученные данные.
Сначала мы составляем тот самый фильтр, который должен имет следущий вид:
{“name”:”<name>”,”passwd”:”<passwd>”}
Здесь в коде это происходит следующим образом:
Далее получившуюся строку преобразуем в тип bson, необходимый для того, чтобы передать его в качестве фильтра в запрос:
Ну и после всего этого отправляем запрос к бд:
Думаю, многие уже заметили что проблема здесь сильно напоминает проблему sql-иньекций. Составление запроса конкатенацией позволяет пользователю изменить саму структуру фильтра. Подход тот же, отличаться будет только реализация.
Давайте представим что произойдет, если заместо валидного имени пользователь введет вот такую конструкцию:
admin”}//
В этом случае наша строка примет следующий вид:
{"name": "admin"}//","passwd":"123"}
Таким образом, сначала при помощи двойной кавычки “ мы закроем значение поля name, затем закроем документ и закоментируем оставшуюся строку (json с относительно недавних пор поддерживает комментарии.) И после этих манипуляций мы получим фильтр, который ищет только имя, без пароля:
А значит, теперь мы сможем авторизоваться без помощи пароля, зная только имя пользователя.
Причем, в нашем случае даже знаки коментриев не являются обязательными, иньекция сработает и без них:
Обратите внимание, что у bson есть несколько разных форматов, и таким образом строка, составляемая для фильтра может быть составленна по разному, а значит и сам пейлоад иньекции будет отличаться. Например, значение может быть заключено не в двойные кавычки, а в одинарные. Тогда иньекция будет иметь вид:
admin’}\\
В целом, думаю сама идея этого подвида уязвимости понятна. Она очень сильно напоминает SQL-inj и для знакомых с этой уязвимостью интуитивно понятна. Однако, этим NoSQL-inj не ограничиваются. Посмотрим, какие еще подвиды возможны. Едем дальше
Небольшое отступление – как стоило сделать:
Для более полного понимания картины предлагаю посмотреть на вариант того, как нужно было сделать. Я думаю, все уже поняли что проблема здесь исключительно в составлении фильтра. И здесь тоже все просто, для того, чтобы составить фильтр требуется не создавать запрос через обьединение строк, а использовать типы, встроенные в драйвер. Таким образом правильный код будет выглядеть вот так:
collection := ClientGlobal.Database("test").Collection("users")
//Создаем правильный фильтр
filter := bson.D{{"name", name}, {"passwd", passwd}}
//Ищем нужного пользователя в бд с нормальным фильтром
collection.FindOne(context.TODO(), filter).Decode(&user)
Мы создаем фильтр через пакет bsond, и внутрь передаем данные для фильтра – логин и пароль. Такой фильтр не нужно анмаршалить – сразу передаем его в качестве аргумента в метод FindOne и получаем безопасный и рабочий код.
Иньекции регулярных выражений:
Следующее, на что стоит обратить внимание – иньекции в регулярные выражения. Вот в чем суть такого типа атаки:
Давайте снова обратимся к сравнению. Как происходит запрос в SQL БД, когда нужно получить какие-то определенные данные ? Для обращения к определенным данным в SQL-языке используется ключевое слово WHERE, которое служит своеобразным фильтром по полям. К примеру запрос, который требует выбрать пользователя с определенным паролем, будет выглядеть вот так:
SELECT * FROM users WHERE password = qwerty;
В NoSQL базах нет таких операторов, как собственно и языка SQL, поэтому там в качестве дополнительного или основного фильтра данных зачастую используются регулярные выражения. (Для тех, кто не знает что это – регулярные выражения, это некий “шаблон” или “маска”, на основе которого и определяются данные, которые подходят под этот шаблон. Если вы сосовсем не знакомы с регулярками, советую хоть немножко погуглить и посмотреть что это такое, для более простого и полного понимания, как работает эта атака.)
Один из способов создать такой же запрос, который ищет пользователя с определенным паролем в шеле при помощи регулярного выражения, будет выглядеть вот так:
db.users.find({ password: { $regex: "^qwerty" } })
То есть, здесь мы ищем пару ключ:значение, где в качестве ключа будет указан password, а в качестве значения – наш пароль qwerty. Для того, чтобы подставить заместо определенного значения регулярку, достаточно указать оператор $regex:”регулярка”.
А теперь перейдем к нашему уязвимому приложению. Здесь все будет несколько сложнее в плане кода, в отличие от шелла, но кейс вполне реален. Но начнем издалека. Для начала давайте представим себе ситуацию – у нас есть страница поиска по новостям, которая ищет отправленный пользователем текст в заголовках в базе и выводит все, что подходит под него. Проблема реализации такого функционала заключается в том, что мы не можем захардкодить текст введный пользователем в запросе. Предположим пользователь введет в поиск слово “тест”, а у нас есть новости с заголовками тест “1 тест”, “2 тест”, “тест 3” и т.д. Если мы просто закинем в find слово test функция не найдет нам ни одной новости, не смотря на то, что заголовков, содержащих это слово несколько штук. И вот здесь нам и может пригодится регулярное выражение, так как именно оно может помочь выбрать не просто заголовок “тест”, а все заголовки, содержащие внутри себя слово “тест”:
db.users.find({ password: { $regex: "^тест*” } })
Теперь посмотрим как это выглядит в Go:
Алгоритм действий тот же, с единственным отличием – мы используем find, а не findOne так как новостей может быть несколько и нам должен вернутся специальный обьект, который будет содержать все подходящие записи. В дальнейшем их можно легко распарсить и вернуть пользователю. Фильтр составляется следующим образом:
Ну и далее все логично. Получаем коллекцию, фильтруя ее через наше регулярное выражение:
А теперь посмотрим в чем собственно проблема. Обратите внимание на строчку составления фильтра, а точнее регулярного выражения:
Само выражение составляется конкатенацией, что вприцнипе логично, так как нам надо взять шаблон текста от пользователя и добавить к нему символы ^ в начало, и * в конец, что в свою очередь позволит выполнить поиск шаблона в заголовках независмо от регистра (символ ^) и с возможным продолжением (символ *)
Однако, это может стать проблемой, и вот почему:
Вернемся к нашей странице поиска. И попробуем вбить в запрос не просто текст, а регулярное выражение, например [/s/S]. Под это выражение попадают все заголовки новостей:
Как видим, таким образом пользователь может контролировать само тело регулярного выражения. Это не есть хорошо, но в данном случае это не так опасно. Здесь явно проявляется отличие от SQL иньекций. Мы не можем при помощи метода find для определенной коллекции создать или изменить какую-то запись.
Но давайте представим ситуацию, которую приводят как классическую когда речь идет о иньекции регулярных выражений. Представим что такой подход с регулярными выражениями будет использован там, где это не надо. Например, разработчик по какой-то причине решить что использовать такой подход в авторизации – хорошая идея. И составит вот такой кусок кода с вот таким фильтром:
Как видим здесь проверка логина задана правильно, а вот проверка пароля идет по какой-то причине через регулярку. То есть, первым критерием отбора служит логин, а далее выбор пароля происходит через поиск шаблона, который и является введенным пользователем шаблоном.
Чем этот момент может быть опасен ? Да все просто, если мы попробуем подставить туда ту самую регулярку [/s/S]*, которую мы указали до этого. И наш фильтр примет следующий абстрактный вид:
{{name:name},{passwd:$regex{“[/s/S]*”}}}
После того как будет выполнен запрос, получится что логин является единственным критерием выбора, так как под регулярку, заданную паролем, подойдет любой пароль в бд. Вот и получается, что авторизоваться в таком случае можно зная только логин:
Те кто знакомы с SQL-иньекциями наверное уже заметили, что конкретно в данном случае, вектор схож с атакой, когда атакующий, при помощи атаки на SQL, добивается обхода авторизации. Это один из самых классических примеров SQL-иньекции, (‘or 1=1
. Желаемый результат конктретно в данном случае с NoSQL-иньекцией такой же.
Итоги
Наверное вы заметили что все примеры выше приводили только к одному результату - к обходу авторизации. И единственное действие, которое происходило с данными из набора CRUD – это Read, т.е. чтение.
И вот здесь и кроется одно из главных отличий от SQL-inj. (По крайней мере в нашем случае, при драйвере mongo под golang) Если в случае с SQL-иньекциями мы можем встраивать код, который будет выполнять какие-то операции с базой (CRUD), то в нашем случае с NoSQL-иньекциями все зависит от метода, который вызывается. Не получится создать запись, изменив фильтр в FindOne запросе, точно так же, как не получится удалить данные через функцию создания этих данных.
Поэтому, все зависит от функции, которую вы проверяете. Если это уязвимая авторизация – вы сможете попробовать авторизироваться под другим пользователем, если функция смены пароля в личном кабинете – можете попробовать изменить данные другого пользователя. Но например создать нового пользователя через иньекцию в авторизации не получится. Надеюсь ход мыслей ясен.
Автоматизация
После того, как мы разобрались как работает сам процесс NoSQL-injection, можно поговорить об автоматизации атак.
В случае с SQL-иньекциями, есть очень популярный и хорошо себя зарекомендовавший инструмент – sqlmap, по сути – боевая классика.
В случае же с NoSQL-инъекциями тоже есть подобный инструмент – nosqlmap. Он часто встречается в материале, описывающем уязвимость как пример а втоматизации. Вот так он выглядит:
И вот тут нас ждет первое разочаровние. Этот небольшой инструмент для наших кейсов не подойдет абсолютно. К сожалению, он уже во многом утратил свою актуальность, и его функционала не хватает.
Например, в нашем примере мы используем POST запрос с типом данных application/json который передает данные в теле в виде json-структуры. И это проблема, так как NoSQLMap в отличие от своего “большого брата sqlmap” работать с json в запросах не умеет.
Он может принять на вход обычные POST-параметры, но с json работать не умеет.
Однако, может он сможет хорошо проявить себя с обычными x-www-form-urlencoded. Немного модифицируем код, для того чтобы он принимал параметры не json а формой и запрос теперь будет выглядеть вот так:
Но и здесь нас ждет разочарование:
Инструмент элементарно не заточен под такие кейсы. Он пытается вбить в иньекцию комманды шела, js и прочее, но конкретно попробовать регулярку или закрытие – такого он не делает. Возможно, где-то он и будет полезен, но не в э том случае.
Да и к тому же у инструмента достаточно низкая стабильность, частенько вылетают необработтаные ошибки, косяки и т.д.
Автоматизация кейса c json при помощи python.
Так как скрипт на коленке и небольшой, какого-то сильного задела под дальнейшее и массовое использование не будет и многие параметры будут захардкодены. Если вы хоть немного умеете писать код и вам нужно будет что-то изменить (добавить параметр, изменить его имя, запрос и т.д.) - сделать это особого труда не составит.
Как и говорил выше – приведу пример как некоторые действия по подборке валидных пейлоадов можно автоматизировать при помощи просто скрипта. Как пример возьмем наш примеры из начала статьи, где данные передавались через json в теле запроса.
Здесь тоже все просто. Для начала набросаем план того, что мы хотим от программы:
Для начала создадим txt файл, который будет содержать полезные нагрузки. Так как я уже знаю что корректно, а что нет – внесу туда следующее:
При этом заранее известно что корректно должны отработать 2,3 и 5 иньекции
Так как каждый запрос будет отправляться с новыми данными – оборачиваем весь наш процесс в цикл для считывания файла. Таким образом мы будем считывать новую строку из файла и сразу же отправлять запрос.
Еще нам понадобится до цикла создать connection и заголовки запроса, так в процессе работы эти данные изменяться не будут.
Ну и после этого переходим к считыванию из файла. При помощи вот такой конструкции считываем каждую строку:
Дальше создадим две переменные- name и passwd, куда будем записывать начальные параметры, к которым и будет добавляться иньекция. В нашем случае мы хотим зайти под админом, поэтому в name указываем admin и далее конкатенируем ее с name’ом, таким образом создавая нужную нам полезную нагрузку, а passwd либо указываем рандомное значение, либо можно вообще оставить его пустым:
Ну и после этого, здесь же в цикле создаем структру, превращаем ее в json и Дальше просто отправляем запрос, указав в теле метод, путь, тело запроса и заголовки.
Для того чтобы понять успешно все прошло или нет – вспомним ответ сервера. Он возвращает нам структуру, в которой есть три ключа – Error, name и ID. В случае если все успешно – Error будет пустым, а остальные два содержать информацию. Соттветственно зайдем от Error – если оно пустое, значит нам удалось авторизоваться, а значит провести иньекцию, если нет – значит неудачно. Поэтому в коде, просто проверяем значение error и в зависимости от него выводим результат. Ну и добавим termcolor для красоты и чтобы сразу видеть что успешно, а что нет:
Вот и весь скрипт:
Дешево и сердито. И вполне себе работает, если нужно попробовать перебрать какие-то нагрузки:
Заключение
Что можно сказать в заключении ? NoSQL-inj имеют место быть, но сейчас уже сами технологии и их разработчики предусматривают этот момент, поэтому допустить такую уязвимость неосознанно вполне возможно. Сама уязвимость сильно зависит от кода приложения и библиотек и драйверов, которые используются там для работы с СУБД. Например, в случае с go – каждый метод работает отдельно от других, единственное что там можно встроить, это параметры фильтра, для изменения результатов запроса. Создать новый запрос там не получится. И это достаточно хороший механизм защиты с точки зрения разработчика.
Но, так бывает не всегда. В некоторых случаях в других языках все может быть куда опаснее. (Например JS и метод eval). Поэтому, все очень сильно зависит от реализации приложения. Где-то при помощи только одного поля – получится выкачать всю базу через иньекции комманд шелла, а где-то максимум на что вы сможете расчитывать – регулярка в строке поиска.
Ну а на этом я заканчиваю с рассмотрением NoSQL иньекций в golang’e и с вами прощаюсь. Удачных взломов!
© Urob0ros, специально для форума xss.pro
Всех приветствую. Думаю, абсолютно все здесь пристутсвующие, слышали и знают про такую уязвимость как SQL-инъекция. Ставшая, по сути классикой уязвимость, с которой начинают изучение веб-беза большинство новичков, уязвимость, которая у всех на слуху, и которая может пристуствовать не только в вебе, но и на десктопе. Со временем, об SQL-инъекциях узнали все, сейчас даже со стажеров и джунов на должность бекендера спрашивают умение не допускать эту уязвимость в коде. Сама же уязвимость, по сути потихоньку, но начинает уходить в раздел “устаревшие”. Да, до сих пор можно найти какие-то иньекции даже в серьезных проектах, но как правило эти иньекции достаточно сложные и далеко не дефолтные. Нельзя сказать что SQL-инъекции вымерли, но то что их стало гораздо меньше – это факт.
Но речь здесь пойдет все-таки не о них. С развитием технологий, стали развиваться и способы хранения данных. И помимо SQL баз данных (реляционные базы данных) появились еще NoSQL базы (нереляционные базы данных). Именно об уязвимостях в них и пойдет сегодня речь.
Начал статью я с SQL-inj, потому что многие ставят эти понятия рядом, думая что схожесть в названиях подразумевает и большую схожесть в процессе эксплуатации. Однако, это не совсем так, процесс атаки на SQL и NoSQL местами сильно различается, но, в каком-то смысле эти понятия действительно стоят рядом, и сказать что абсолютно ничего общего там нет - нельзя. Именно поэтому на протяжении всей статьи я буду стараться проводить аналогии с SQL-inj, так как по моему мнению, это поможет проще воспринимать информацию.
Но обо всем по порядку. Далее поговорим чем отличаются SQL и NoSQL, как происходит сама атака на вторые, что с этого можно получить, и какой инструмент поможет автоматизировать процесс. Вообщем, думаю, будет интересно
Примеры кода в данной статье буду приведены на языке Go. Да, я понимаю, что как правило проще это все показывать на php, но целью этой статьи являлось именно подача для новичка по этой теме в текущее время. Чистый php и указанные примеры в других статьях в итернете сейчас уже очень редко где встретишь и было интересно посмотреть, как дела обстоят сейчас с более интересными и перспективными технологиями, как можно допустить уязвимость там и т.д. И да, статья все же начального уровня, скорее как введение. Она рассчитана на людей, которых эта уязвимость обошла и они хотят понять что это и почему, плюс минус разобравшись еще и в смежных моментах. Более сложные аспекты здесь затрагиваться не будут, только основное. Статья итак получилась длинной, а если расписывать здесь же еще и более сложные кейсы – получилось бы вообще полотно. Поэтому, идем разбираться с начальным пониманием.
Отличия NoSQL и SQL
Для начала предлагаю как раз поговорить про отличия SQL от NoSQL, или реляционной модели баз данных от нереляционной. И заодно более менее разберемся как работает модель нереляционной БД. Сильно углубляться не будем, но основные моменты знать надо.
Для начала вспомним как вообще выглядят реляционные базы данных. По сути, каждая реляционная база – это набор таблиц, состоящих из строк и столбцов, в которых и хранятся данные. Тип данных в каждом столбце жестко структурирован – то есть, в поле типа integer (цлочисленный тип данных) мы не сможем положить строку, так же, как не сможем положить дату. Каждое поле имеет строго указаный тип данных, которые будут храниться в этом поле. Как работают реляционные БД достаточно просто понять, просто представив себе схему базы:
Процесс взаимодействия с данными происходит при помощи языка SQL, который позволяет обращаться к данным в определнных столбцах или строках, сортировать данные по определенным параметрам и т.д. Сам язык SQL досточно прост для восприятия, так как по сути, это просто английский, на котором вы просите базу выдать вам какую-либо информацию. Даже человек, не изучавший специально SQL, сможет интуитивно понять что делает запрос:
SELECT ALL IN “users” WHERE age=25
Ведь даже в переводе все звучит достаточно просто – выбрать все записи, в таблице пользователи, где возраст равен 25
Понятное дело, что в СУБД есть чуть более сложные моменты по типу связей и т.д., но в это мы углубляться не будем, у нас тут все-таки не лекция по SQL
Принцип работы NoSQL же в корне отличается. Во первых, сама структура базы данных – она менее удобна для человека в плане структурирования информации .Если в случае с реляционными БД база является набором из таблиц, то в случае с нереляционными базами, база состоит не из таблиц, а из коллекций. И это не просто отличие в названиях, сама по себе коллекция по структуре отличается от таблицы. Если в случае с реляционными БД таблица состоит из строк и столбцов, то в случае с нереляционными БД коллекция состоит по сути из обычного JSON, с разделением на документы.
Однако, стоит всетаки заметить, что в нереляционных бд используется все же не совсем json. Там используется BSON – надмножество json, которое имеет некоторые свои плюшки, вроде регулярных выражений, автоматически генерируемых id и ряда других полезных функций, которые несколько увеличивают возможности БД.
Чтобы было понятнее что такое документы, вспомните строки в реляционных БД. Они, как правило содержат разную информацию об одном обьекте под определенным id. К примеру, у нас, в реляционной бд есть информация о человеке по имени Иван Иванов, содержащая некоторую информацию о возрасте и роде деятельности.
Вот как будет записана такая информация в реляционной БД:
А вот как будет выглядеть та же запись в нереляционной базе данных:
Каждый документ в базе имеет свой уникальный идентификатор (_id), который и позволяет обращаться к документу по запросу. Этот id является отдельным типом данных в bson.
Такой ID создается рандомно, в случае если он четко не указывается при создании документа. И в отличие от SQL здесь нельзя настроить автоинкремент.
Следующее отличие – язык запросов. В случае с реляционными бд языком запросов служит SQL. В случае с нереляционными бд все несколько иначе. Есть некий интерфейс управления (CLI идущая в комплекте с базой или драйверы для ЯП), при помощи которых и производятся операции. Как правило, все взаимодействие сводится к вызову методов, в которые передается тот самый BSON данные, на основе которых и производятся CRUD операции.
Если рассматривать самые популярные СУБД для обеих технологий, то если в случае с SQL самой популярной системой является MySQL, то в случае с нереляционными БД мейнстримом является mongoDB.
MongoDB имеет cli для управления базой из консоли и графический интерфейс под названием compass для упрощения работы, который и будет использован далее в примерах.
Так чем же вообще NoSQL выгоднее чем SQL. Казалось бы – классические реляционные базы куда более понятны, логичны да и популярнее. А здесь все просто – NoSQL, хоть и не имеет большого количества фишек SQL, таких как связи или автоинкременты, например, именно благодаря своей относительной “простоте” гораздо лучше работает с большими обьемами данных. Поэтому NoSQL используются в основном с высоконагруженными сервисами и там, где замешана “big data”.
Атаки
А теперь плавно переходим к самой атаке. Представим что у нас есть приложение, работающее как раз с нереляционной базой данных. В качестве примера такого приложения возьмем небольшой сайтик, фронтовая и бэк части которого общаются между собой посредством http-запросов, содержащих в себе json-данные. И, дабы подчернуть актуальность уязвимости и не описывать все примеры уязвимого кода на чистом php, в качестве бэкенда у нас будет выступать небольшое REST api на golang, которое и будет принимать все данные с фронта, обрабатывать и возвращать ответ.
В отличие от SQL-иньекций, NoSQL иньекции не ограничиваются одним типом внедрения. То есть, если в случае с SQL-иньекциями подразумевается только внедрение SQL-кода в запрос (речь не про результат, а про процесс), то в случае с NoSQL-иньекциями подразумевается целый блок разных уязвимостей. И в отличие от тех же SQL-inj здесь можно пойти далеко не одним путем. Однако, желаемый результат, ради которого применяется та или иная атака, может быть довольно схож в некоторых моментах с желаемым результатом в SQL-инъекциях. Поэтому, опять же, чтобы вам было проще разобраться, в случае если вы имеете представление о том, как работают SQL-inj, я буду проводить аналогии с некоторыми атаками на SQL. Давайте посмотрим какие виды NoSQL-иньекций существуют:
JSON-иньекции
Начать предлагаю с самой простой и в то же время имеющей место быть атаке. Здесь все достаточно логично. Проблема, как и в sql иньекциях здесь заключается в отсутствии нормальной валидации пользовательских данных и составлении какаих-то частей запросов конкатенацией, что уже само по себе является проблемой (а для кого-то возможностью) в любом приложении.
Представим ситуацию. У нас есть форма авторизации, которая принимает на вход логин и пароль:
Посмотрим как это работает:
Форма получает на вход логин и пароль. Далее формирует запрос на наш backend под управлением go. Запрос идет по методу POST, имеет content-type
“application/json” и в теле содержит json-структуру, содержащую введеные пользователем данные для авторизации – логин и пароль:
Далее запрос приходит на бэкенд. Здесь мы парсим полученный в запросе json в структуру, которая и содержит два поля – логин и пароль сотвественно. А вот далее самое интересное. Как происходит процесс авторизации ? Мы создаем запрос к нашей MongoDB, в которой просим найти ее пользователя с введенными учетными данными. Т.е. программа обращается к коллекции users, где ищет документ с полями name и passwd, которые будут соответствовать введеным пользователю данными.
После того как запрос отправлен – база возвращает результат и сразу сохраняет его в структуру.
Если пользователь найден – в структуру будут записаны его данные. Конкретно нас интересуют login и Id. Эти данные по итогу и отправляются обратно фронтенд.
Если же пользователь не найден, структура после запроса окажется пустой. Ну и далее, наше бэкенд приложение, увидев что поля ID и Name пустые понимает что пользователь не найден и возвращает на фронт ошибку.
Вот так в теории и должен выглядеть весь процесс. Предположим у нас в базе есть пользователь с данными admin/admin:
Если пользователь введет верные учетные данные, он увидит сообщение об успешной авторизации:
Если же пользователь введет неверные креды, приложение сообщит ему что такого польователя нет:
Вот так все должно работать в идеале. Но все же код, взаимодействующий с бд на бэкенде не идеален. У него есть одна серьезная проблема – составление фильтра (того самого параметра, по которому отбираются данные в бд) конкатенацией – составляется строка, в которую напрямую подставляются введенные пользователем данные.
Рассмотрим код подобробнее, чтобы понять в чем здесь проблема:
Для начала получаем коллекцию и готовим структуру, в которую будем парсить полученные данные.
Код:
//Создаем структуру для пользователя
var user UserWithID
//Получаем коллекцию из бд
collection := ClientGlobal.Database("test").Collection("users")
Сначала мы составляем тот самый фильтр, который должен имет следущий вид:
{“name”:”<name>”,”passwd”:”<passwd>”}
Здесь в коде это происходит следующим образом:
Код:
//Создаем неправильный фильтр
doc := `{"name": "` + name + `","passwd":"` + passwd + `"}`
var filter interface{}
Далее получившуюся строку преобразуем в тип bson, необходимый для того, чтобы передать его в качестве фильтра в запрос:
Код:
//парсим полученную строку в bson формат для поиска по коллекции
bson.UnmarshalExtJSON([]byte(doc), false, &filter)
Ну и после всего этого отправляем запрос к бд:
Код:
//Ищем нужного пользователя в бд с помощью кривого фильтра
collection.FindOne(context.TODO(), filter).Decode(&user)
Думаю, многие уже заметили что проблема здесь сильно напоминает проблему sql-иньекций. Составление запроса конкатенацией позволяет пользователю изменить саму структуру фильтра. Подход тот же, отличаться будет только реализация.
Давайте представим что произойдет, если заместо валидного имени пользователь введет вот такую конструкцию:
admin”}//
В этом случае наша строка примет следующий вид:
{"name": "admin"}//","passwd":"123"}
Таким образом, сначала при помощи двойной кавычки “ мы закроем значение поля name, затем закроем документ и закоментируем оставшуюся строку (json с относительно недавних пор поддерживает комментарии.) И после этих манипуляций мы получим фильтр, который ищет только имя, без пароля:
А значит, теперь мы сможем авторизоваться без помощи пароля, зная только имя пользователя.
Причем, в нашем случае даже знаки коментриев не являются обязательными, иньекция сработает и без них:
Обратите внимание, что у bson есть несколько разных форматов, и таким образом строка, составляемая для фильтра может быть составленна по разному, а значит и сам пейлоад иньекции будет отличаться. Например, значение может быть заключено не в двойные кавычки, а в одинарные. Тогда иньекция будет иметь вид:
admin’}\\
В целом, думаю сама идея этого подвида уязвимости понятна. Она очень сильно напоминает SQL-inj и для знакомых с этой уязвимостью интуитивно понятна. Однако, этим NoSQL-inj не ограничиваются. Посмотрим, какие еще подвиды возможны. Едем дальше
Небольшое отступление – как стоило сделать:
Для более полного понимания картины предлагаю посмотреть на вариант того, как нужно было сделать. Я думаю, все уже поняли что проблема здесь исключительно в составлении фильтра. И здесь тоже все просто, для того, чтобы составить фильтр требуется не создавать запрос через обьединение строк, а использовать типы, встроенные в драйвер. Таким образом правильный код будет выглядеть вот так:
collection := ClientGlobal.Database("test").Collection("users")
//Создаем правильный фильтр
filter := bson.D{{"name", name}, {"passwd", passwd}}
//Ищем нужного пользователя в бд с нормальным фильтром
collection.FindOne(context.TODO(), filter).Decode(&user)
Мы создаем фильтр через пакет bsond, и внутрь передаем данные для фильтра – логин и пароль. Такой фильтр не нужно анмаршалить – сразу передаем его в качестве аргумента в метод FindOne и получаем безопасный и рабочий код.
Иньекции регулярных выражений:
Следующее, на что стоит обратить внимание – иньекции в регулярные выражения. Вот в чем суть такого типа атаки:
Давайте снова обратимся к сравнению. Как происходит запрос в SQL БД, когда нужно получить какие-то определенные данные ? Для обращения к определенным данным в SQL-языке используется ключевое слово WHERE, которое служит своеобразным фильтром по полям. К примеру запрос, который требует выбрать пользователя с определенным паролем, будет выглядеть вот так:
SELECT * FROM users WHERE password = qwerty;
В NoSQL базах нет таких операторов, как собственно и языка SQL, поэтому там в качестве дополнительного или основного фильтра данных зачастую используются регулярные выражения. (Для тех, кто не знает что это – регулярные выражения, это некий “шаблон” или “маска”, на основе которого и определяются данные, которые подходят под этот шаблон. Если вы сосовсем не знакомы с регулярками, советую хоть немножко погуглить и посмотреть что это такое, для более простого и полного понимания, как работает эта атака.)
Один из способов создать такой же запрос, который ищет пользователя с определенным паролем в шеле при помощи регулярного выражения, будет выглядеть вот так:
db.users.find({ password: { $regex: "^qwerty" } })
То есть, здесь мы ищем пару ключ:значение, где в качестве ключа будет указан password, а в качестве значения – наш пароль qwerty. Для того, чтобы подставить заместо определенного значения регулярку, достаточно указать оператор $regex:”регулярка”.
А теперь перейдем к нашему уязвимому приложению. Здесь все будет несколько сложнее в плане кода, в отличие от шелла, но кейс вполне реален. Но начнем издалека. Для начала давайте представим себе ситуацию – у нас есть страница поиска по новостям, которая ищет отправленный пользователем текст в заголовках в базе и выводит все, что подходит под него. Проблема реализации такого функционала заключается в том, что мы не можем захардкодить текст введный пользователем в запросе. Предположим пользователь введет в поиск слово “тест”, а у нас есть новости с заголовками тест “1 тест”, “2 тест”, “тест 3” и т.д. Если мы просто закинем в find слово test функция не найдет нам ни одной новости, не смотря на то, что заголовков, содержащих это слово несколько штук. И вот здесь нам и может пригодится регулярное выражение, так как именно оно может помочь выбрать не просто заголовок “тест”, а все заголовки, содержащие внутри себя слово “тест”:
db.users.find({ password: { $regex: "^тест*” } })
Теперь посмотрим как это выглядит в Go:
Алгоритм действий тот же, с единственным отличием – мы используем find, а не findOne так как новостей может быть несколько и нам должен вернутся специальный обьект, который будет содержать все подходящие записи. В дальнейшем их можно легко распарсить и вернуть пользователю. Фильтр составляется следующим образом:
filter3 := bson.D{{"title", primitive.Regex{Pattern: "^" + text+”*”, Options: ""}}}Ну и далее все логично. Получаем коллекцию, фильтруя ее через наше регулярное выражение:
cur, err := collection.Find(context.TODO(), filter3)А теперь посмотрим в чем собственно проблема. Обратите внимание на строчку составления фильтра, а точнее регулярного выражения:
Само выражение составляется конкатенацией, что вприцнипе логично, так как нам надо взять шаблон текста от пользователя и добавить к нему символы ^ в начало, и * в конец, что в свою очередь позволит выполнить поиск шаблона в заголовках независмо от регистра (символ ^) и с возможным продолжением (символ *)
Однако, это может стать проблемой, и вот почему:
Вернемся к нашей странице поиска. И попробуем вбить в запрос не просто текст, а регулярное выражение, например [/s/S]. Под это выражение попадают все заголовки новостей:
Как видим, таким образом пользователь может контролировать само тело регулярного выражения. Это не есть хорошо, но в данном случае это не так опасно. Здесь явно проявляется отличие от SQL иньекций. Мы не можем при помощи метода find для определенной коллекции создать или изменить какую-то запись.
Но давайте представим ситуацию, которую приводят как классическую когда речь идет о иньекции регулярных выражений. Представим что такой подход с регулярными выражениями будет использован там, где это не надо. Например, разработчик по какой-то причине решить что использовать такой подход в авторизации – хорошая идея. И составит вот такой кусок кода с вот таким фильтром:
filter := bson.D{{"name", name}, {"passwd", primitive.Regex{Pattern: passwd, Options: ""}}}Как видим здесь проверка логина задана правильно, а вот проверка пароля идет по какой-то причине через регулярку. То есть, первым критерием отбора служит логин, а далее выбор пароля происходит через поиск шаблона, который и является введенным пользователем шаблоном.
Чем этот момент может быть опасен ? Да все просто, если мы попробуем подставить туда ту самую регулярку [/s/S]*, которую мы указали до этого. И наш фильтр примет следующий абстрактный вид:
{{name:name},{passwd:$regex{“[/s/S]*”}}}
После того как будет выполнен запрос, получится что логин является единственным критерием выбора, так как под регулярку, заданную паролем, подойдет любой пароль в бд. Вот и получается, что авторизоваться в таком случае можно зная только логин:
Те кто знакомы с SQL-иньекциями наверное уже заметили, что конкретно в данном случае, вектор схож с атакой, когда атакующий, при помощи атаки на SQL, добивается обхода авторизации. Это один из самых классических примеров SQL-иньекции, (‘or 1=1
Итоги
Наверное вы заметили что все примеры выше приводили только к одному результату - к обходу авторизации. И единственное действие, которое происходило с данными из набора CRUD – это Read, т.е. чтение.
И вот здесь и кроется одно из главных отличий от SQL-inj. (По крайней мере в нашем случае, при драйвере mongo под golang) Если в случае с SQL-иньекциями мы можем встраивать код, который будет выполнять какие-то операции с базой (CRUD), то в нашем случае с NoSQL-иньекциями все зависит от метода, который вызывается. Не получится создать запись, изменив фильтр в FindOne запросе, точно так же, как не получится удалить данные через функцию создания этих данных.
Поэтому, все зависит от функции, которую вы проверяете. Если это уязвимая авторизация – вы сможете попробовать авторизироваться под другим пользователем, если функция смены пароля в личном кабинете – можете попробовать изменить данные другого пользователя. Но например создать нового пользователя через иньекцию в авторизации не получится. Надеюсь ход мыслей ясен.
Автоматизация
После того, как мы разобрались как работает сам процесс NoSQL-injection, можно поговорить об автоматизации атак.
В случае с SQL-иньекциями, есть очень популярный и хорошо себя зарекомендовавший инструмент – sqlmap, по сути – боевая классика.
В случае же с NoSQL-инъекциями тоже есть подобный инструмент – nosqlmap. Он часто встречается в материале, описывающем уязвимость как пример а втоматизации. Вот так он выглядит:
И вот тут нас ждет первое разочаровние. Этот небольшой инструмент для наших кейсов не подойдет абсолютно. К сожалению, он уже во многом утратил свою актуальность, и его функционала не хватает.
Например, в нашем примере мы используем POST запрос с типом данных application/json который передает данные в теле в виде json-структуры. И это проблема, так как NoSQLMap в отличие от своего “большого брата sqlmap” работать с json в запросах не умеет.
Он может принять на вход обычные POST-параметры, но с json работать не умеет.
Однако, может он сможет хорошо проявить себя с обычными x-www-form-urlencoded. Немного модифицируем код, для того чтобы он принимал параметры не json а формой и запрос теперь будет выглядеть вот так:
Но и здесь нас ждет разочарование:
Инструмент элементарно не заточен под такие кейсы. Он пытается вбить в иньекцию комманды шела, js и прочее, но конкретно попробовать регулярку или закрытие – такого он не делает. Возможно, где-то он и будет полезен, но не в э том случае.
Да и к тому же у инструмента достаточно низкая стабильность, частенько вылетают необработтаные ошибки, косяки и т.д.
Автоматизация кейса c json при помощи python.
Так как скрипт на коленке и небольшой, какого-то сильного задела под дальнейшее и массовое использование не будет и многие параметры будут захардкодены. Если вы хоть немного умеете писать код и вам нужно будет что-то изменить (добавить параметр, изменить его имя, запрос и т.д.) - сделать это особого труда не составит.
Как и говорил выше – приведу пример как некоторые действия по подборке валидных пейлоадов можно автоматизировать при помощи просто скрипта. Как пример возьмем наш примеры из начала статьи, где данные передавались через json в теле запроса.
Здесь тоже все просто. Для начала набросаем план того, что мы хотим от программы:
- Считываем из файла полезыне нагрузки, которые будут использованны для иньекций
- Добавляем по одной в тело запроса, заместо одного из параметров.
- В цикле отправляем запросы, заменяя каждую итерацию целелвой параметр на следующий, из файла.
Для начала создадим txt файл, который будет содержать полезные нагрузки. Так как я уже знаю что корректно, а что нет – внесу туда следующее:
При этом заранее известно что корректно должны отработать 2,3 и 5 иньекции
Так как каждый запрос будет отправляться с новыми данными – оборачиваем весь наш процесс в цикл для считывания файла. Таким образом мы будем считывать новую строку из файла и сразу же отправлять запрос.
Еще нам понадобится до цикла создать connection и заголовки запроса, так в процессе работы эти данные изменяться не будут.
Python:
connection = http.client.HTTPConnection('127.0.0.1:8181')
headers = {'Content-type': 'application/json'}
Ну и после этого переходим к считыванию из файла. При помощи вот такой конструкции считываем каждую строку:
Python:
with open('paylodas.txt', 'r') as f:
for line in f.readlines():
Дальше создадим две переменные- name и passwd, куда будем записывать начальные параметры, к которым и будет добавляться иньекция. В нашем случае мы хотим зайти под админом, поэтому в name указываем admin и далее конкатенируем ее с name’ом, таким образом создавая нужную нам полезную нагрузку, а passwd либо указываем рандомное значение, либо можно вообще оставить его пустым:
Python:
name = "admin"
passwd = "123"
name = name + line
Ну и после этого, здесь же в цикле создаем структру, превращаем ее в json и Дальше просто отправляем запрос, указав в теле метод, путь, тело запроса и заголовки.
Python:
foo = {'name': name,'passwd':passwd}
json_foo = json.dumps(foo)
connection.request('POST', '/auth', json_foo, headers)
response = connection.getresponse()
result = response.read().decode()
json_res = json.loads(result)
Для того чтобы понять успешно все прошло или нет – вспомним ответ сервера. Он возвращает нам структуру, в которой есть три ключа – Error, name и ID. В случае если все успешно – Error будет пустым, а остальные два содержать информацию. Соттветственно зайдем от Error – если оно пустое, значит нам удалось авторизоваться, а значит провести иньекцию, если нет – значит неудачно. Поэтому в коде, просто проверяем значение error и в зависимости от него выводим результат. Ну и добавим termcolor для красоты и чтобы сразу видеть что успешно, а что нет:
Код:
if json_res["Error"] == "":
print(name + colored(" Injection successful",'green'))
else:
print(name + colored(" Injection not work",'red'))
Вот и весь скрипт:
Python:
import http.client
import json
from termcolor import colored
connection = http.client.HTTPConnection('127.0.0.1:8181')
headers = {'Content-type': 'application/json'}
with open('paylodas.txt', 'r') as f:
for line in f.readlines():
name = "admin"
passwd = "123"
name = name + line
foo = {'name': name,'passwd':passwd}
json_foo = json.dumps(foo)
connection.request('POST', '/auth', json_foo, headers)
response = connection.getresponse()
result = response.read().decode()
json_res = json.loads(result)
if json_res["Error"] == "":
print(name + colored(" Injection successful",'green'))
else:
print(name + colored(" Injection not work",'red'))
Дешево и сердито. И вполне себе работает, если нужно попробовать перебрать какие-то нагрузки:
Заключение
Что можно сказать в заключении ? NoSQL-inj имеют место быть, но сейчас уже сами технологии и их разработчики предусматривают этот момент, поэтому допустить такую уязвимость неосознанно вполне возможно. Сама уязвимость сильно зависит от кода приложения и библиотек и драйверов, которые используются там для работы с СУБД. Например, в случае с go – каждый метод работает отдельно от других, единственное что там можно встроить, это параметры фильтра, для изменения результатов запроса. Создать новый запрос там не получится. И это достаточно хороший механизм защиты с точки зрения разработчика.
Но, так бывает не всегда. В некоторых случаях в других языках все может быть куда опаснее. (Например JS и метод eval). Поэтому, все очень сильно зависит от реализации приложения. Где-то при помощи только одного поля – получится выкачать всю базу через иньекции комманд шелла, а где-то максимум на что вы сможете расчитывать – регулярка в строке поиска.
Ну а на этом я заканчиваю с рассмотрением NoSQL иньекций в golang’e и с вами прощаюсь. Удачных взломов!
© Urob0ros, специально для форума xss.pro
Последнее редактирование: