ОРИГИНАЛЬНАЯ СТАТЬЯ
ПЕРЕВЕДЕНО СПЕЦИАЛЬНО ДЛЯ xss.pro
$600 на SSD для Jolah Milovski ---> 0x5B1f2Ac9cF5616D9d7F1819d1519912e85eb5C09 для поднятия ноды ETHEREUM и тестов
Мы уделяем особое внимание службе клиентского доступа (CAS)
CAS является основным обработчиком протоколов в Microsoft Exchange Server.
В официальной документации Microsoft также указано: "Серверы почтовых ящиков содержат службы клиентского доступа, которые принимают клиентские соединения для всех протоколов. Эти внешние службы отвечают за маршрутизацию или проксирование соединений к соответствующим внутренним
службам".
Служба клиентского доступа в IIS
Архитектура обмена
• Разбор входящих HTTP-запросов, применение настроек, заданных протоколом
• Приложения в Backend включают модуль BackendRehydrationModule
• Получение и заполнение HTTP-запросов от Frontend
• Приложения синхронизируют внутреннюю информацию между Frontend и Backend с помощью HTTP-заголовков
Можем ли мы получить доступ к Backend?
HTTP Header Blacklists
Код:
HTTP Header Blacklists
protected virtual bool ShouldCopyHeaderToServerRequest(string headerName) {
return !string.Equals(headerName, "X-CommonAccessToken", OrdinalIgnoreCase)
&& !string.Equals(headerName, "X-IsFromCafe", OrdinalIgnoreCase)
&& !string.Equals(headerName, "X-SourceCafeServer", OrdinalIgnoreCase)
&& !string.Equals(headerName, "msExchProxyUri", OrdinalIgnoreCase)
&& !string.Equals(headerName, "X-MSExchangeActivityCtx", OrdinalIgnoreCase)
&& !string.Equals(headerName, "return-client-request-id", OrdinalIgnoreCase)
&& !string.Equals(headerName, "X-Forwarded-For", OrdinalIgnoreCase)
&& (!headerName.StartsWith("X-Backend-Diag-", OrdinalIgnoreCase)
|| this.ClientRequest.GetHttpRequestBase().IsProbeRequest());
Клонирование идентификационных данных пользователя
Код:
if (this.ClientRequest.IsAuthenticated) {
CommonAccessToken commonAccessToken = AspNetHelper.FixupCommonAccessToken(
this.HttpContext, this.AnchoredRoutingTarget.BackEndServer.Version);
if (commonAccessToken != null) {
headers["X-CommonAccessToken"] = commonAccessToken.Serialize(
new int?(HttpProxySettings.CompressTokenMinimumSize.Value));
}
} else if (this.ShouldBackendRequestBeAnonymous()) {
headers["X-CommonAccessToken"] = new CommonAccessToken(9).Serialize();
}
Прикрепить заголовок авторизации
Код:
if (this.ProxyKerberosAuthentication) {
// use origin Kerberos Authentication
} else if (this.AuthBehavior.AuthState == AuthState.BackEndFullAuth || this.
ShouldBackendRequestBeAnonymous() || (HttpProxySettings.TestBackEndSupportEnabled.Value
&& !string.IsNullOrEmpty(this.ClientRequest.Headers["TestBackEndUrl"]))) {
// unauthenticated
} else {
serverRequest.Headers["Authorization"] = KerberosUtilities.GenerateKerberosAuthHeader(
serverRequest.Address.Host, this.TraceContext,
ref this.authenticationContext, ref this.kerberosChallenge);
Генерирование Kerberos тикета
Код:
internal static string GenerateKerberosAuthHeader(string host, int traceContext, ref
AuthenticationContext authenticationContext, ref string kerberosChallenge) {
// ...
authenticationContext = new AuthenticationContext();
authenticationContext.InitializeForOutboundNegotiate(AuthenticationMechanism.Kerberos,
"HTTP/" + host, null, null);
SecurityStatus securityStatus = authenticationContext.NegotiateSecurityContext(inputBuffer,
out bytes);
return "Negotiate " + Encoding.ASCII.GetString(bytes);
}
Модуль регидратации.
Код:
private void OnAuthenticateRequest(object source,
EventArgs args) {
if (httpContext.Request.IsAuthenticated) {
this.ProcessRequest(httpContext);
}
}
private void ProcessRequest(HttpContext httpContext) {
CommonAccessToken token;
if (this.TryGetCommonAccessToken(httpContext, out token))
// ...
}
Восстановление идентификационных данных пользователя
Код:
private bool TryGetCommonAccessToken(HttpContext httpContext, out
CommonAccessToken token) {
string text = httpContext.Request.Headers["X-CommonAccessToken"];
flag = this.IsTokenSerializationAllowed(httpContext.User.Identity
as WindowsIdentity);
if (!flag)
throw new BackendRehydrationException(...)
token = CommonAccessToken.Deserialize(text);
httpContext.Items["Item-CommonAccessToken"] = token;
Токен сериализации
Код:
private bool TryGetCommonAccessToken(HttpContext httpContext, out
CommonAccessToken token) {
string text = httpContext.Request.Headers["X-CommonAccessToken"];
flag = this.IsTokenSerializationAllowed(httpContext.User.Identity
as WindowsIdentity);
if (!flag)
throw new BackendRehydrationException(...)
token = CommonAccessToken.Deserialize(text);
httpContext.Items["Item-CommonAccessToken"] = token;
Проверка расширенных прав AD
Код:
private bool IsTokenSerializationAllowed(WindowsIdentity windowsIdentity) {
flag2 = LocalServer.AllowsTokenSerializationBy(clientSecurityContext);
return flag2;
}
private static bool AllowsTokenSerializationBy(ClientSecurityContext clientContext) {
return LocalServer.HasExtendedRightOnServer(clientContext,
WellKnownGuid.TokenSerializationRightGuid); // ms-Exch-EPI-Token-Serialization
}
Auth-Flow в кратком изложении
1. Frontend IIS проверяет подлинность запроса (Windows или базовая аутентификация) и сериализует текущую идентификацию в HTTP-заголовок X-CommonAccessToken
3. Frontend проксирует HTTP-запрос на Backend
4. Внутренний IIS аутентифицирует запрос и проверяет, что аутентифицированный пользователь имеет право на TokenSerialization
5. Бэкэнд восстанавливает пользователя на основе HTTP-заголовка X-CommonAccessToken
Да будет взлом.!.
ProxyLogon
• Самая известная уязвимость Exchange Server в мире
.• Неавторизованный злоумышленник может выполнить произвольный код на Microsoft Exchange Сервер через единственный открытый 443 порт!
• ProxyLogon связан цепью с 2 ошибками:
• CVE-2021-26855 - Предварительный аутентификационный SSRF приводит к обходу аутентификации
• CVE-2021-27065 - произвольная запись файлов после авторизации приводит к RCE
С чего начинается ProxyLogon?
Код:
protected override AnchorMailbox ResolveAnchorMailbox() {
HttpCookie httpCookie = base.ClientRequest.Cookies["X-AnonResource-Backend"];
if (httpCookie != null) {
this.savedBackendServer = httpCookie.Value;
}
return new ServerInfoAnchorMailbox(
BackEndServer.FromString(this.savedBackendServer), this);
Супер SSRF
• В чем причина такого произвольного назначения бэкенда?
• должна адаптировать совместимость между новыми и старыми архитектурами, следовательно, вводит cookie
• Супер SSRF
• Управление почти всеми HTTP-запросами и получение всех ответов
• Прикрепление с помощью билета Kerberos с привилегией учетной записи Exchange$ автоматически
• Использование внутреннего API бэкенда /ecp/proxylogon.ecp для получения действительной сессии панели управления и ошибки записи файлов для
ProxyOracle
• Интересный эксплойт для Exchange Server с другим подходом
• Неаутентифицированный злоумышленник может восстановить имя пользователя и
пароль жертвы
в формате открытого текста, просто подтолкнув пользователя открыть вредоносную
ссылку
• ProxyOracle связан цепью с 2 ошибками:
• CVE-2021-31195 - Отраженный межсайтовый скриптинг
• CVE-2021-31196 - Атака Padding Oracle при разборе cookie-файлов Exchange
Как пользователи входят в OWA/ECP?
COOKIE
Логика шифрования FbaModule
Код:
Псевдо код
@key = GetServerSSLCert().GetPrivateKey()
cadataSig = RSA(@key).Encrypt("Fba Rocks!")
cadataIV = RSA(@key).Encrypt(GetRandomBytes(16))
cadataKey = RSA(@key).Encrypt(GetRandomBytes(16))
@timestamp = GetCurrentTimestamp()
cadataTTL = AES_CBC(cadataKey, cadataIV).Encrypt(@timestamp)
@blob = "Basic " + ToBase64String(UserName + ":" + Password)
cadata = AES_CBC(cadataKey, cadataIV).Encrypt(@blob)
Код:
private void ParseCadataCookies(HttpApplication httpApplication) {
using (ICryptoTransform transform = aesCryptoServiceProvider.CreateDecryptor()) {
try {
byte[] array5 = Convert.FromBase64String(request.Cookies["cadata"].Value);
bytes2 = transform.TransformFinalBlock(array5, 0, array5.Length);
} catch (CryptographicException arg8) {
return;
}
}
}
Теперь мы можем расшифровать файлы cookie Но... Как получить куки клиента?
Мы обнаружили новый XSS для создания цепочки
Однако все конфиденциальные файлы cookie защищены HttpOnly

ProxyShell
• ProxyShell запускается с ошибкой Path Confusion на Exchange Server Explicit Logon feature
• Эта функция предназначена для того, чтобы пользователи могли открыть другой почтовый ящик/календарь и отобразить его в новом окне браузера
• Exchange разобрал адрес почтового ящика и нормализовал URL-адрес на внутреннем уровне
Извлечение адреса почтового ящика из URL
Код:
protected override AnchorMailbox ResolveAnchorMailbox() {
if (RequestPathParser.IsAutodiscoverV2PreviewRequest(base.ClientRequest.Url.AbsolutePath))
text = base.ClientRequest.Params["Email"];
// ...
this.isExplicitLogonRequest = true;
this.explicitLogonAddress = text;
}
public static bool IsAutodiscoverV2PreviewRequest(string path) {
return path.EndsWith("/autodiscover.json", StringComparison.OrdinalIgnoreCase);
}
The Fatal Erase
Код:
protected override UriBuilder GetClientUrlForProxy() {
string absoluteUri = base.ClientRequest.Url.AbsoluteUri;
1 uri = UrlHelper. RemoveExplicitLogonFromUrlAbsoluteUri(absoluteUri,
this.explicitLogonAddress);
return new UriBuilder(uri);
}
public static string RemoveExplicitLogonFromUrlAbsoluteUri(string absoluteUri, string
explicitLogonAddress) {
string text = "/" + explicitLogonAddress;
if (absoluteUri. IndexOf(text) != -1)
2 return absoluteUri. Substring(0, num) + absoluteUri. Substring(num + text.Length);
}
Снова произвольный доступ к бэкэнду!
Exchange PowerShell Remoting
• Exchange PowerShell Remoting - это интерфейс командной строки, который позволяет автоматизировать задачи Exchange.
• Exchange PowerShell Remoting построен на базе PowerShell API и использует Runspace для изоляции. Все операции основаны на протоколе WinRM
• Взаимодействие с бэкендом PowerShell не удается, потому что нет почтового ящика для
Пользователь: SYSTEM
• Мы нашли часть кода, извлекающего Access-Token из URL
Извлечение маркера доступа из URL
Код:
private void OnAuthenticateRequest(object source, EventArgs args) {
HttpContext httpContext = HttpContext.Current;
if (httpContext.Request.IsAuthenticated) {
if (string.IsNullOrEmpty(httpContext.Request.Headers["X-CommonAccessToken"])) {
Uri url = httpContext.Request.Url;
Exception ex = null;
CommonAccessToken commonAccessToken = CommonAccessTokenFromUrl(httpContext.
User.Identity.ToString(), url, out ex);
}
}
Повышение привилегий (Elevation of Privilege, EOP), поскольку мы можем получить прямой доступ к Exchange
PowerShell Backend
• Цель этой операции - быть быстрым прокси для внутреннего обмена. Коммуникации PowerShell
• Укажите Access-Token в X-Rps-CAT для имперсонализации любому пользователю
• Мы используем эту эскалацию привилегий, чтобы "понизить" себя с SYSTEM до администратора Exchange.
Выполнение произвольного Exchange PowerShell от имени администратора
А потом?
Атака Exchange PowerShell
• Последний кусочек головоломки - найти RCE после авторизации, чтобы соединить их в цепочку
• Поскольку теперь мы являемся администраторами Exchange, легко злоупотребить Exchange PowerShell команда New-MailboxExportRequest для экспорта почтового ящика пользователя в UNC
Доставка полезной нагрузки
• Как внедрить вредоносную полезную нагрузку в экспортируемый файл?
• Мы доставляем вредоносную полезную нагрузку по электронной почте (SMTP), но файл зашифрован
.• Экспортированный файл имеет формат личных папок Outlook (PST), прочитав документацию MS- PST, мы узнали, что это простая перестановочная кодировка
Код:
mpbbCrypt = [65, 54, 19, 98, 168, 33, 110, 187, 244, 22, 204, 4, 127, 100, 232, ...]
encode_table = bytes.maketrans((bytearray(mpbbCrypt), bytearray(range(256)))
'<%@ Page Language="Jscript"%>...'.translate(encode_table)
Соберите все вместе
1. Доставить нашу закодированную полезную нагрузку WebShell по SMTP
2. Запустите собственный PowerShell и перехватите протокол WinRM
• Перепишите /PowerShell/ в /Autodiscover/, чтобы вызвать ошибку Path Confusion
• Добавьте строку запроса X-Rps-CAT с соответствующим маркером доступа администратора Exchange
3. Выполнение команд внутри установленного сеанса PowerShell
• New-ManagementRoleAssignment для предоставления нам роли импорта-экспорта почтовых ящиков
• New-MailboxExportRequest для записи ASPX файла в локальный UNC путь
4. Наслаждайтесь оболочкой
Атаки, связанные с прокси
ProxyNotFound
• Уязвимости Exchange, о которых сообщило АНБ (дублируется NCSC Вьетнам)
• CVE-2021-28480 - Обход SSRF/ACL с предварительной авторизацией
• CVE-2021-28481 - Обход SSRF/ACL перед авторизацией
• Также под нашей новой поверхностью атаки
• Ошибка, расположенная на переднем конце CAS
• Основная причина схожа с предварительной частью ProxyLogon, но в качестве цели BackEnd извлекается другой cookie, предоставленный пользователем.
ProxyNotFound: Произвольное назначение бэкэнд
Код:
protected override AnchoredRoutingTarget TryFastTargetCalculationByAnchorMailbox(
AnchorMailbox anchorMailbox) {
if (this.backEndCookie == null || !base.IsRetryOnErrorEnabled) {
this.FetchBackEndServerCookie();
}
BackEndServer backEndServer = anchorMailbox.AcceptBackEndCookie(this.backEndCookie)
}
private void FetchBackEndServerCookie() {
foreach (string text in new string[] {"X-BackEndCookie", "X-BackEndCookie2"}) {
// ...
httpCookie.Values[text] = backEndCookieEntryBase.ToObscureString();
}
}
ProxyToken
• Об уязвимости в бирже сообщил Ле Суан Туен из VNPT ISC (работает с Zero Day Initiative)
• CVE-2021-33766 - Обход аутентификации
• Используйте функцию "делегированной аутентификации" в CAS FrontEnd для выполнения действий по настройке пользователей
• Также под нашей новой поверхностью атаки
• Установите правило переадресации почты для чтения входящей почты любого пользователя
Апрельское обновление убивает (почти) все.
ProxyRelay
• ProxyRelay - это набор атак обхода аутентификации, позволяющий
злоумышленнику выдавать себя за любого пользователя (отлично
работает после апрельского патча!).
• CVE-2021-33768
• CVE-2021-TBA
• ...
• Эксплуатация NTLM-Relay для обхода аутентификации CAS Proxy
• ProxyRelay = CAS Proxy + NTLM-Relay
Проблемы, которые
необходимо решить
• NTLM-Relay требует от пользователя пассивного запуска реле
• Обходной путь: Принтер-жучок на помощь!
• Защита NTLM-Relay: Отражение одного и того же хоста
• Обход: Кластер Exchange часто встречается в корпорациях. Мы передаем NTLM
сервера-A на сервер-B, чтобы не спровоцировать обнаружение Same-Host!
• Защита NTLM-Relay: Подписание SMB / привязка к каналу
• Обход: Мы используем SMB-to-HTTPS. Exchange по умолчанию
отключает привязку канала TLS!
CVE-2021-33768
• MSRC признает 2 исследователей в качестве консультантов
• Тяньцзе Дин (@D1iv3) из лаборатории Tencent Security Xuanwu Lab
• Оранж Цай (@orange_8361) из исследовательской группы DEVCORE
• Концепция эксплуатации:
1. Запуск ошибки принтера на Exchange-A
2. Передача NTLM на FrontEnd сервера Exchange-B для обхода аутентификации CAS
3. Подделать часть авторизации в EWS, чтобы выдать себя за любого пользователя
• Обновление от июня 2021 года устранило CVE, запретив Exchange Machine Account аутентифицироваться во FrontEnd.
CVE-2021-TBA
• CVE-2021-TBA - еще одна похожая, но более фундаментальная уязвимость.
• Отчет для MSRC на 02 июня 2021 года с политикой раскрытия информации за 90(+30) дней
• После обсуждения с MSRC, похоже, что здесь нет быстрых и простых исправлений, а есть изменения на уровне дизайна. Патч должен потребовать определенного времени на подготовку и тестирование
• Как ответственный исследователь, извините, что мы решили НЕ раскрывать информацию сегодня
Поддерживайте сервер Exchange в актуальном состоянии
Не выходите с сервера Exchange Server в Интернет (особенно в веб).
Что касается ProxyRelay, что касается проблемы на уровне дизайна, ваши руки связаны
• Остановите службу Printer Spooler Service
• Отключите NTLM-аутентификацию (пока не проверял, есть ли какие-либо побочные эффекты)
Переход на Office 365 Exchange Online
(шутка)