Как‑то раз мы про­води­ли раз­ведку по пулу из ~9000 IP-адре­сов круп­ной рос­сий­ской IT-ком­пании и наш­ли заб­рошен­ный домен и работа­ющий на нем сер­вис. Он ока­зал­ся уяз­вимым, и вско­ре мы смог­ли зах­ватить веб‑сер­вер и про­ник­нуть во внут­реннюю сеть пред­при­ятия. Как это было сде­лано, я рас­ска­жу в этой статье.

Pentest Award

Этот текст получил пятое мес­то на пре­мии Pentest Award 2024 в катего­рии «Про­бив web». Наг­ражде­ние еже­год­но про­водит ком­пания Awilix.

Для поис­ка ста­рых и забытых ресур­сов мы исполь­зовали тех­нику Passive DNS. Дан­ные мы взя­ли из VirusTotal: написа­ли неболь­шой скрипт, который отправ­лял зап­росы к API с IP-адре­сами из нашего пула. Это поз­волило соб­рать мно­жес­тво инте­рес­ных доменов, боль­шинс­тво из которых вхо­дили в наш ско­уп. Так и нашел­ся тот самый «забытый» веб‑сер­вис, который впос­ледс­твии ока­зал­ся уяз­вимым.

До­мен был неп­ростой, что‑то вро­де web1.smth.smth.companyname.com. Там был раз­вернут самопис­ный сер­вис монито­рин­га веб‑при­ложе­ний, на который, судя по логам, пос­ледний раз заходи­ли око­ло месяца назад. Дос­туп к нему мы получи­ли с помощью дефол­тных учет­ных дан­ных (admin:admin), соз­дали отдель­ную учет­ку адми­нис­тра­тора для даль­нейше­го тес­тирова­ния и, конеч­но же, как нас­тоящие этич­ные хакеры, опо­вес­тили отдел ИБ заказ­чика об уяз­вимос­ти.

На сле­дующий день ИБ‑отдел зак­рыл ресурс, хотя мы про­сили дать воз­можность про­дол­жать тес­тирова­ние. Ребята из отде­ла ИБ объ­ясни­ли решение тем, что «там нет ничего инте­рес­ного».

Од­нако я опус­тил один момент: перед тем как сооб­щать об уяз­вимос­ти, мы спар­сили все учет­ные дан­ные поль­зовате­лей (для про­ектных целей и даль­нейше­го тес­тирова­ния), которые хра­нились в откры­том виде пря­мо в раз­метке HTML. Естес­твен­но, сре­ди них были и учет­ки адми­нис­тра­торов.

Кусочек списка пользователей
Ку­сочек спис­ка поль­зовате­лей
Пароль
Па­роль

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

Спус­тя две недели работы над осталь­ным ско­упом я парал­лель­но занимал­ся раз­ведкой и работой с вебом. В какой‑то момент я взял наш прош­лый хост — *.smth.smth.companyname.com, закинул на перебор в gobuster и... вуаля! Нашел­ся тот же самый ресурс, но уже с дру­гим али­асом — пусть он будет называть­ся vulnerable.smth.smth.companyname.com.

Здесь уже не было дефол­тных учет­ных дан­ных адми­на, как рань­ше, поэто­му нам и при­годи­лась учет­ка адми­нис­тра­тора из дам­па ресур­са web1.smth.smth.companyname.com.

По­луча­ем дос­туп, ковыря­ем при­ложе­ние и находим воз­можнос­ти для самых раз­ных SQL-инъ­екций. Нам уда­лось реали­зовать error-based SQLi, blind (time-based) SQLi, а так­же мы мог­ли читать локаль­ные фай­лы с помощью встро­енной фун­кции MS SQL — OpenRowset (она поз­воля­ет обра­щать­ся к локаль­ным фай­лам).

Это было вечером, а к поз­дней ночи я док­рутил инъ­екцию в MS SQL до Out-of-Band.

Полезная нагрузка для эксплуатации OOB
По­лез­ная наг­рузка для экс­плу­ата­ции OOB

В прин­ципе, ничего сверхъ­естес­твен­ного: в одну строч­ку наз­нача­ем про­изволь­ную перемен­ную, прис­ваиваем перемен­ной hostname уда­лен­ного сер­вера, с помощью фун­кции exec отправ­ляем на исполне­ние. Приш­лось нем­ного пошама­нить с hostname, ско­рее все­го, WAF бло­киро­вал зап­росы к **.oastify.com. Но с помощью нехит­рых манипу­ляций — раз­деления FQDN на час­ти и кон­катена­ции строк — мне уда­лось получить отстук на уда­лен­ный сер­вер.

Но из‑за это­го воз­никло нес­коль­ко проб­лем:

В целом с пер­вым воп­росом мы разоб­рались доволь­но быс­тро: с помощью БД master перенас­тро­или исполь­зование xp_cmdshell (нас­коль­ко пом­ню, у нас была сер­висная учет­ка sa, у которой по дефол­ту есть пра­ва для исполь­зования БД master):

use master; sp_configure 'xp_cmdshell', '1'; RECONFIGURE

Ре­кон­фиг про­шел успешно, теперь мы можем уда­лен­но исполнять код.

Те­перь оста­лось решить воп­рос с получе­нием сес­сии и зак­репле­нием. Динами­чес­кий реверс‑шелл, к сожале­нию, получить не уда­лось из‑за отсутс­твия обратной свя­зи по TCP. Поэто­му мы обра­тились за помощью к нашему небезыз­вес­тно­му кол­леге и товари­щу s0i37. Очень при­годи­лась его статья про спо­собы переда­чи фай­лов через DNS, а так­же сами кли­ент и сер­вер для DNS-шел­ла.

Ос­талось понять, каким обра­зом мы можем передать VBS-кли­ент на веб‑сер­вер. Пораз­мыслив, мы наш­ли толь­ко один вари­ант — закоди­ровать скрипт в Base64 и пос­троч­но передать в файл через Burp Intruder, а потом с помощью certutil декоди­ровать файл и получить рабочий DNS-кли­ент.

Построчная передача файла
Пос­троч­ная переда­ча фай­ла
Декодируем клиент DNS на сервере
Де­коди­руем кли­ент DNS на сер­вере
Фрагмент декодированного клиента
Фраг­мент декоди­рован­ного кли­ента

Мы получи­ли сес­сию с помощью DNS-шел­ла и по навод­ке наших инфраструк­турщи­ков про­вери­ли сетевой дос­туп со ском­про­мети­рован­ной машины до дата‑цен­тра. Вза­имо­дей­ствие по SMB откры­то.

Де­ло за малым — оста­лось локаль­но повысить при­виле­гии и передать машину кол­легам — спе­циалис­там по тес­тирова­нию внут­ренней инфраструк­туры.

В локаль­ном повыше­нии нам поможет вклю­чен­ная SeImpersonatePrivilege. Берем экс­пло­ит из семей­ства «кар­тошек» или PrintSpoofer, повыша­ем при­виле­гии и переда­ем машин­ку кол­легам.

Полная схема пробива веб-приложения
Пол­ная схе­ма про­бива веб‑при­ложе­ния

Выводы

Вот как в ито­ге выг­лядит цепоч­ка уяз­вимос­тей:

  1. Ис­поль­зование сло­вар­ных паролей.
  2. Не­безо­пас­ное хра­нение паролей.
  3. Error-based SQL-инъ­екция.
  4. Вклю­чен­ная при­виле­гия SeImpersonatePrivilege у сер­висной учет­ной записи.

За­каз­чику мы выдали сле­дующие рекомен­дации: