Hackquest 2015: Megatask.exe solution

Hello everyone!

Вот и закончился ежегодный Hackquest, который проводился с 11 по 17 мая 2015 года. Было множество интересных и трудных заданий, чтобы немного размять свои мозги и настроиться на волну форума Positive Hack Days V. К сожалению, с заданием megatask справилась только команда RDot.org. И о решении этого задания я хочу сегодня вам рассказать. Так сказать помахать немного кулаками после драки. Приступим.

Помним, что тема прошедшего Hackquest’а, была Web. А значит большая часть заданий, так или иначе,  была связана с уязвимостями веб-приложений. Вот и megatask затрагивает проблемы безопасности плагинов для самой популярной CMS WordPress, веб-интерфейсов роутеров, а так же получение информации из публичных источников.

Открываем описание задачи и смотрим, что от нас требуется:

http://megatask.mcdir.ru/
Бесстыжый пользователь SuperHacker1337 оставил обидный коммент в посте Marty McFly, нужно узнать:
1) Имя пользователя
2) Где он живет
3) Как выглядит
На каждом этапе поиска найдем часть флага.
flag = flag1.flag2.flag3
Вторая часть флага — ник в instagram. Но не все так очевидно

Перейдем непосредственно к решению задачи.

Первое, что я сделал это немного потискал сам сайт. Обычный WordPress блог с небольшим количеством плагинов и псевдо-тестовыми постами. В одном из этих постов и оставил непотребный комментарий SuperHacker1337.

Те, кто-более менее знает как работает WP, ведет на нем блоги или ломал сайты на этой CMS вкурсе, что IP адреса комментаторов сохраняются в таблице wp_comments. Поэтому, один из самых очевидных способов узнать какую-то информацию о том, кто оставил коммент, это получить доступ к БД и выудить ее из этой таблицы.

Начнем с программы WPScan — самого продуктивного блэк-бокс сканера для WordPress. Вот, что ему удалось разнюхать:


[+] robots.txt available under: ‘http://megatask.mcdir.ru/robots.txt’
[+] Interesting entry from robots.txt: http://megatask.mcdir.ru/wp-content/*
[+] Interesting entry from robots.txt: http://megatask.mcdir.ru/cgi-bin/*
[+] Interesting entry from robots.txt: http://megatask.mcdir.ru/plugins.log
[+] Interesting entry from robots.txt: http://megatask.mcdir.ru/wpadmin.html
[!] The WordPress ‘http://megatask.mcdir.ru/readme.html’ file exists exposing a version number
[!] Full Path Disclosure (FPD) in: ‘http://megatask.mcdir.ru/wp-includes/rss-functions.php’

[+] WordPress version 4.2.2 identified from advanced fingerprinting
[+] WordPress theme in use: modern – v1.4.1
[+] Name: modern – v1.4.1
| Location: http://megatask.mcdir.ru/wp-content/themes/modern/
| Style URL: http://megatask.mcdir.ru/wp-content/themes/modern/style.css
| Description:
[+] Enumerating installed plugins (only vulnerable ones) …
[+] We found 1 plugins:
[+] Name: wordfence – v5.3.12
| Location: http://megatask.mcdir.ru/wp-content/plugins/wordfence/
| Readme: http://megatask.mcdir.ru/wp-content/plugins/wordfence/readme.txt

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

Стоит обратить внимание на интересное в файле robots.txt, а именно на строку:

[+] Interesting entry from robots.txt: http://megatask.mcdir.ru/plugins.log

Глянем содержимое этого файла повнимательнее. Да это же лог установки плагинов на WP! С указанием дат и мест, откуда плагин устанавливался. Отлично, посмотрим какие плагины были установленны, может быть есть что-то, чего не нашел WPScan. Обратим внимание, что названия плагинов соответсвуют папкам в которых они расположены на сайте.

Через несколько минут нахожу плагин WP-Logging, который был установлен из git-репозитория.


Beginning installation of ‘WP-Logging’ plugin
Successfully installed ‘WP-Logging’ plugin from remote Git repository

Стоит посмотреть, существует ли еще этот плагин и имеется ли папка .git со служебными файлы репозитория, напимер index. http://megatask.mcdir.ru/wp-content/plugins/WP-Logging/.git/index.

Очень хорошо, файл на месте. Теперь можно попробовать восстановить структуру репозитория и файлов или проверить наличие config файла, чтобы узнать адрес репозитория. Пробуем последний вариант, так как он гораздо быстрее — http://megatask.mcdir.ru/wp-content/plugins/WP-Logging/.git/config:


[remote “origin”]
fetch = +refs/heads/*:refs/remotes/origin/*
url = https://github.com/allyshka/WP-Logging.git

Проверяем, публичный ли данный репозиторий. Ура-ура, репозиторий публичный, поэтому клонирую его себе для дальнейшего изучения.

Для начала можно глянуть последние коммиты, возможно исправлялись какие-то критические баги. Так же стоит посмотреть, актуальная ли версия плагина установлена на сайте http://megatask.mcdir.ru/wp-content/plugins/WP-Logging/.git/packed-refs.

Latest commits
Latest commits

Версия последняя, хорошо. Тогда начинаем изучать исходники, благо кода не так много. При беглом осмотре бросаются в глаза методы чтения и записи log-файлов и функция file_get_contents(). Рассмотрим повнимательнее эти методы, возможно тут присутствует возможность чтения файлов на сервере.

Метод showLogFile читает файл, переданный ему в качестве параметра. Этот метод вызывается при создании экземпляра класса WP_Logger. Видно что, при передаче параметров show и log, в запросе к файлу плагина wp-logger.php, мы можем читать файлы с именем указанным в log:

Теперь рассмотрим повнимательнее метод showLogFile. Он читает файл из папки logs, которая находится в директории с плагином, но выйти из папки нам не дает функция basename(). Однако, в коде допущена банальная синтаксическая ошибка. Поэтому, при выполнении, скрипт пробует прочитать файл с именем свойства класса, которое содержится в переменной $file:

basename($this->$file));

Что из этого можно получить? Чтобы это узнать нужно повнимательнее рассмотреть последние коммиты от 13.04.2015 и 14.04.2015:

Первый коммит делает класс WP_Logger дочерним от WordPress-класса для работы с базой данных wpdb.

Второй коммит добавляет загрузку файла wp-config.php и всех необходимых для работы ядра wordpress файлов. Таким образом создается подключение к рабочей базе данных.

Третий коммит добавляет функцию cast() — она вызывается при создании класса и копирует все свойства wpdb в текущий экземпляр класса. Включая такие интересные свойства как: dbuser, dbpassword, dbname, dbhost:

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

Warning: file_get_contents(/home/httpd/vhosts/megatask.mcdir.ru/httpdocs/wp-content/plugins/WP-Logging/logs/a137299_1)…
Warning: file_get_contents(/home/httpd/vhosts/megatask.mcdir.ru/httpdocs/wp-content/plugins/WP-Logging/logs/erhv78sbu4iwGFB)…
Warning: file_get_contents(/home/httpd/vhosts/megatask.mcdir.ru/httpdocs/wp-content/plugins/WP-Logging/logs/a137299.mysql.mchost.ru)…

Видим, что сайт расположен на хостинге Макхост (mchost.ru) и там имеется phpMyAdmin для управления базой данных. Я просто загуглил его (mchost phpmyadmin) и, вуаля, вот адрес https://pma.mchost.ru/. Вводим туда добытые данные и попадаем в БД, тут же мы видем таблицу flagThree в которой единственное blob-поле. Похоже, это третья часть нашего флага, поэтому запишем её. flag3 = 28D6CEA3574248

Flag part three
Flag part three

Теперь переходим к таблице wp_comment. Делаем запрос, чтобы найти комментатора.

Commentator data
Commentator data

Тут мы видим IP-адрес (80.78.251.54) и фейковый url (http://fuck.off). Пропингуем этот IP:

Хорошо, хост живой. Теперь запустим сканер nmap по всем портам с определением сервисов.

На порту 7331 торчит веб-сервер RomPager, что вкупе с ZyXEL ZyWALL 2 намекает на то, что перед нами веб-интерфейс роутера. Заходим через браузер по адресу http://80.78.251.54:7331/ и видим окно авторизации, которое так же недвусмысленно дает понять, что это Wireless Zyxel ADSL Modem/Router. Верим ему на слово и попробуем несколько самых популярных эксплоитов для роутеров ZyXEL. Для этого я воспользуюсь программой Router Scan v2.51 by Stas’M, которая позволяет проверять роутеры на наличие известных уязвимостей в автоматическом режиме.

RouterScan results
RouterScan results

Теперь заходим в веб-интерфейс роутера с добытым логином и паролем — admin:EbS7P27

Примечание: уязвимость, с помощью которой программа узнала пароль, это банальное чтение бекапа конфигурационного файла роутера. Это возможно благодаря ошибке в авторизации роутера — http://80.78.251.54:7331/rom-0. Файл запакован по алгоритму Stac LZS, можно найти множество его реализаций на любых языках программирования. Немного больше об этом можете прочитать, например, тут — http://www.hakim.ws/huawei/rom-0/kender.html

Переходим на вкладку Client list и видим список устройств, которые в данный момент подключены к роутеру. Так же можно увидеть названия устройств, что однозначно дает понять, что тот человек, которого мы ищем женщина и зовут ее Дарья. Запомним это.

Devices list
Devices list

Немного оглядевшись, я нашел раздел Status->System log,  в котором находятся логи. На роутере ведется некий лог geoloction, было бы интересно на него взглянуть. К сожалению, прочитать из веб-интерфейса лог файлы не представляется возможным. Указан только путь по которому они расположены. Ок, тогда попробуем раздобыть читалку файлов.

Logs page
Logs page

Еще немного покопавшись, в разделе Maintenance->Diagnostics я наткнулся на возможность выполнить команду snmpwalk.

SNMPwalk test
SNMPwalk test

По всей видимости, параметры, которые поступают из формы в комманду, экранируются. Однако это не панацея и у нас все еще остается возможность добавлять или менять параметры запуска программы snmpwalk. Об этом можно прочитать здесь — http://lab.onsec.ru/2013/03/breaking-escapeshellarg-news.html. Я установил себе snmpwalk и вооружившись командой man начал изучать интересные флаги. Вот что я нашел: можно записывать лог выполнения команды в файл используя ключ -Lf /path/to/file. Это поможет нам залить шелл, если папка веб-сервера доступна для записи. Пробуем ее найти.

SNMPwalk find webdir
SNMPwalk find webdir

Тут я переделал Output format в текстовое поле, чтобы было нагляднее. А также использую флаг дебага -d в Community string чтобы вся информация выводилась в файл. Корень веб-сервера нашли с первой попытки.

Finded web root
Finded web root

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

Uploaded shell
Uploaded shell

Делаем листинг директории с логами /var/log/ и видим там файл geolocation.log. Читаем его содержимое:

Теперь у нас есть первая часть флага flag1=1fcf4803e8f1. Так же мы можем видеть список подключений к роутеру и координаты места откуда оно было сделано. Судя по координатам, роутер стоит на одном месте, скорее всего дома у Даши 😉 Посмотрим, где оно находится https://goo.gl/maps/QQZjY

Так, какая-то маложилая местность на севере России. Возможно, сначала в логе указана долгота, а затем широта. Пробуем поменять местами https://goo.gl/maps/NyBKv

Вот это уже более похоже на правду — г. Тюмень, жилой дом номер 3 по улице Елецкая.

Теперь у нас есть все данные чтобы попробовать найти фотографию девушки в инстаграмме. Для этого я воспользуюсь одним из сервисов по поиску фотографий в инстаграмм по координатам на карте http://www.gramfeed.com/instagram/map#/57.1539,65.5517/20/-. Не забываем о том, что искомую девушку зовут Дашей, поэтому, ищем что-то похожее. Имеются немало фотографий некой @dashaffox, видимо, это и есть искомый нами человек.

Instagram geo
Instagram geo

Вернемся к заданию, где сказано, что ник в инстаграм это и есть последняя часть флага. Как мы знаем, флаги — это md5 хэши, которые состоят из символов [0-9a-f]. Но в найденном нике содержатся символы и не подходящие под эту маску. Отбрасываем их и получаем flag2=daaff.

Соединяем все части флага вместе. flag=flag1.flag2.flag3=1fcf4803e8f1daaff28d6cea3574248. Хм, не хватает одного символа до валидного размера хэша. Вообще, можно не парится и просто его сбрутить, вариантов тут не особо много. Но это не наш метод 😉 Повнимательнее посмотрим на ник и на описание таска “…не все так очевидно…”. В логине присутствует буква “о” она очень похожа на цифру “0”, используем и её тоже и пробуем добавить флаг. Вуаля, флаг сработал, значит наше предположение было верным. Итак, наш флаг: flag=1fcf4803e8f1daaff028d6cea3574248. Задание выполнено.

 

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

Если вам было что-либо непонятно, то я прикладываю подрообное видео с решением данного таска. Оно должно снять все вопросы.

Всем спасибо за внимание. И до новых встреч!