В этой статье мы прой­дем машину Cereal с пло­щад­ки Hack The Box. На ее при­мере я покажу, как обхо­дить X-Rate-Limit-Limit при ска­ниро­вании веб‑сай­та. Заод­но ты узна­ешь, как с помощью XSS в Markdown и десери­али­зации объ­екта С# получить уда­лен­ное выпол­нение кода. Пос­ле чего порабо­таем с тех­нологи­ей GraphQL и под­дела­ем учет­ную запись System в Windows при помощи при­виле­гии SeImpersonate Privilege.

warning

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

 

Разведка

 

Сканирование портов

IP машины — 10.10.10.217, добав­ляем его в /etc/hosts.

10.10.10.217 cereal.htb

И перехо­дим к ска­ниро­ванию пор­тов:

#!/bin/bash
ports=$(nmap -p- --min-rate=500 $1 | grep ^[0-9] | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)
nmap -p$ports -A $1
Результат работы скрипта
Ре­зуль­тат работы скрип­та

В резуль­тате работы скрип­та узна­ем спи­сок откры­тых пор­тов и работа­ющих служб:

  • порт 22 (TCP) — служ­ба SSH;
  • порт 80 (HTTP) — веб‑сер­вер Microsoft IIS/10.0;
  • порт 443 (HTTPS) — веб‑сер­вер Microsoft IIS/10.0.

SSH пока что про­пус­каем, пос­коль­ку учет­ных дан­ных у нас нет. Веб‑сер­вер с 80-го пор­та перенап­равля­ет на 443-й порт. При этом в SSL-сер­тифика­те ука­зано домен­ное имя source.cereal.htb — тоже добав­ляем его в /etc/hosts.

10.10.10.217 source.cereal.htb
 

Сканирование веба

Те­перь выпол­няем зап­рос в бра­узе­ре, и нас перено­сит с 80-го пор­та на 443-й, затем зас­тавля­ют под­твер­дить, что мы при­нима­ем рис­ки, свя­зан­ные с безопас­ностью. Опас­ность будет про­исхо­дить как раз от нас, так что без проб­лем под­твержда­ем. И попада­ем на фор­му авто­риза­ции.

Форма авторизации на сайте cereal.htb
Фор­ма авто­риза­ции на сай­те cereal.htb

Ес­ли же обра­тить­ся по най­ден­ному домен­ному име­ни, то получим ошиб­ку Server Error in '/' Application.

Ошибка на сайте source.cereal.htb
Ошиб­ка на сай­те source.cereal.htb

Нам не дос­тупно поч­ти ничего инте­рес­ного, а зна­чит, сле­дует при­бег­нуть к перебо­ру катало­гов. Для это­го есть такие прог­раммы, как dirsearch, DIRB и gobuster, который я и исполь­зую, пос­коль­ку он показал себя как самый быс­трый.

Вот коман­да, которую я исполь­зовал:

gobuster dir -t 128 -u https://cereal.htb/ -k -w /usr/share/seclists/Discovery/Web-Content/raft-large-words-lowercase.txt -x php,html,aspx --timeout 30s

А вот что озна­чает каж­дый параметр:

  • dir — ска­ниро­вание дирек­торий и фай­лов;
  • -t [] — количес­тво потоков;
  • -u [] — URL-адрес для ска­ниро­вания;
  • -k — не про­верять сер­тификат;
  • -w [] — сло­варь для перебо­ра;
  • --timeout [] — вре­мя ожи­дания отве­та;
  • -x [] — искать фай­лы со сле­дующи­ми рас­ширени­ями (ука­зыва­ем ASPX, пос­коль­ку целевой веб‑сер­вер работа­ет на Microsoft IIS).

Но при попыт­ке переб­рать скры­тые катало­ги на сай­те cereal.htb мы получа­ем бан, что может озна­чать наличие WAF. Давай про­верим, так ли это. Для это­го отпра­вим зап­рос на авто­риза­цию и перех­ватим его в Burp, пос­ле чего перенап­равим в Repeater (ком­бинация Ctrl + R) и пос­мотрим ответ.

Запрос в Burp Repeater
Зап­рос в Burp Repeater

В отве­те видим заголов­ки X-Rate-Limit-Limit и X-Rate-Limit-Remaining. Дан­ные заголов­ки сооб­щают, что на пять минут у нас оста­лось 149 зап­росов. Мож­но поп­робовать обой­ти огра­ниче­ние, вста­вив в зап­рос сле­дующие заголов­ки:

  • X-Originating-IP: 127.0.0.1
  • X-Forwarded-For: 127.0.0.1
  • X-Remote-IP: 127.0.0.1
  • X-Remote-Addr: 127.0.0.1
  • X-Client-IP: 127.0.0.1
  • X-Real-Ip: 127.0.0.
  • X-Host: 127.0.0.1
  • X-Forwared-Host: 127.0.0.1

Из это­го сра­ботал X-Real-Ip: если добавить этот заголо­вок, то в отве­те будут отсутс­тво­вать хедеры X-Rate-Limit-Limit и X-Rate-Limit-Remaining.

Запрос в Burp Repeater с использованием заголовка X-Real-Ip
Зап­рос в Burp Repeater с исполь­зовани­ем заголов­ка X-Real-Ip

Ска­ниру­ем пов­торно, уже с исполь­зовани­ем X-Real-Ip (опция -H), и стал­кива­емся с дру­гой проб­лемой: сер­вер на зап­росы несущес­тву­ющих стра­ниц не отве­чает кодом ошиб­ки, а радос­тно воз­вра­щает 200 («Успешный резуль­тат»).

gobuster dir -t 128 -u https://cereal.htb/ -k -w /usr/share/seclists/Discovery/Web-Content/raft-large-words-lowercase.txt -x php,html,aspx --timeout 30s -H 'X-Real-Ip: 127.0.0.1' --wildcard

В таком слу­чае сто­ит выб­рать дру­гой кри­терий оцен­ки, к при­меру раз­мер отве­та в бай­тах — для сущес­тву­ющей и несущес­тву­ющей стра­ницы он будет раз­ным. Для ска­ниро­вания возь­мем ffuf, пос­коль­ку он уме­ет исклю­чать из вывода отве­ты в зависи­мос­ти от их раз­мера (опция -fs).

ffuf -H 'X-Real-Ip: 127.0.0.1' -w /usr/share/seclists/Discovery/Web-Content/raft-large-words-lowercase.txt -u https://cereal.htb/FUZZ -fs 1948
Результат сканирования каталогов с помощью ffuf
Ре­зуль­тат ска­ниро­вания катало­гов с помощью ffuf

На­ходим толь­ко одну стра­ницу, которая пред­положи­тель­но дол­жна при­нимать опре­делен­ные парамет­ры. Для ска­ниро­вания дру­гого сай­та, где нам встре­тилась ошиб­ка, исполь­зуем gobuster, бла­го там нет никаких бло­киров­щиков.

gobuster dir -t 128 -u https://source.cereal.htb/ -k -w /usr/share/seclists/Discovery/Web-Content/raft-large-words-lowercase.txt -x php,html,aspx --timeout 30s
Результат сканирования каталогов с помощью ffuf
Ре­зуль­тат ска­ниро­вания катало­гов с помощью ffuf

Нам попал­ся каталог .git, а это зна­чит, что мы можем попытать­ся ска­чать весь репози­торий.

 

Точка входа

Для заг­рузки репози­тори­ев я обыч­но исполь­зую пакет скрип­тов dvcs-ripper. Запус­каем rip-git со сле­дующи­ми аргу­мен­тами:

  • -s — не про­верять сер­тификат;
  • -v — вес­ти логиро­вание;
  • -u — URL репози­тория.
./rip-git.pl -s -v -u https://source.cereal.htb/.git/
Результат сканирования Git-репозитория
Ре­зуль­тат ска­ниро­вания Git-репози­тория

Да­вай гля­нем исто­рию ком­митов. Для ана­лиза и раз­бора репози­тори­ев Git я обыч­но исполь­зую Gitk. Прос­матри­вая код Services/UserService.cs, находим сек­рет JWT, а так­же дан­ные, из которых фор­миру­ется JWT, — это ID поль­зовате­ля и дата через семь дней.

Код Services/UserService.cs (ключ JWT)
Код Services/UserService.cs (ключ JWT)
Код Services/UserService.cs (формирование JWT)
Код Services/UserService.cs (фор­мирова­ние JWT)

JSON Web Token сос­тоит из трех час­тей: заголов­ка (header), полез­ной наг­рузки (payload) и под­писи. Заголо­вок и полез­ная наг­рузка пред­став­ляют собой объ­екты JSON, а наг­рузка может быть любой — это имен­но те кри­тичес­кие дан­ные, которые переда­ются при­ложе­нию. У заголов­ка есть сле­дующие поля:

  • alg — алго­ритм, исполь­зуемый для под­писи/шиф­рования. Явля­ется обя­затель­ным клю­чом;
  • typ — тип токена. Это поле дол­жно иметь зна­чение JWT;
  • cty — тип содер­жимого.

Тре­тий эле­мент вычис­ляет­ся на осно­вании пер­вых двух и зависит от выб­ранно­го алго­рит­ма. Токены могут быть переко­диро­ваны в ком­пак­тное пред­став­ление: к заголов­ку и полез­ной наг­рузке при­меня­ется алго­ритм кодиро­вания Base64-URL, пос­ле чего добав­ляет­ся под­пись и все три эле­мен­та раз­деля­ются точ­ками. Вот при­мер токена, взя­тый из Википе­дии.

Пример структуры JWT
При­мер струк­туры JWT

Поп­робу­ем сге­нери­ровать себе токен. Для это­го нам понадо­бит­ся либо при­ложе­ние jwt_tool, либо сер­вис jwt.io. Я исполь­зовал jwt_tool. Так как с заголов­ком все ясно, давай раз­берем­ся с дан­ными: ключ name будет содер­жать иден­тифика­тор 1, а ключ exp — текущую дату плюс семь дней.

date -d "+7 days" +%s
echo -n '{"name": "1", "exp":[дата]}' | base64 -w0 ; echo
echo -n '{"alg": "HS256", "typ":"JWT"}' | base64 -w0 ; echo
python3 jwt_tool.py -b -S hs256 -p 'secretlhfIH&FY*#oysuflkhskjfhefesf' [заголовок].[данные].
Генерирование JWT
Ге­нери­рова­ние JWT

У нас есть токен для дос­тупа, но стра­ница requests тре­бует парамет­ры в фор­мате JSON, о чем сви­детель­ству­ют класс Request в фай­ле Models/Request.cs и фун­кция requestCereal из фай­ла ClientApp/src/_services/request.service.js.

Класс Request
Класс Request
Функции requestCereal и getCerealRequests
Фун­кции requestCereal и getCerealRequests

Ана­лизи­руя исходни­ки далее, узна­ем и сами парамет­ры из фай­ла ClientApp/src/AdminPage/AdminPage.jsx.

Функция render
Фун­кция render

Продолжение доступно только участникам

Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее

Вариант 2. Открой один материал

Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.


  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    1 Комментарий
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии