返回
顶部

前言

在这篇文章中我们探讨一下windows空会话

参考链接:

抓包分析

使用rpcclient尝试建立空连接并调用querydispinfo方法

rpcclient -U'%' 192.168.60.160 -c 'querydispinfo'

返回NT_STATUS_ACCESS_DENIED

从抓包结果上分析,认证和授权这两个部分是分开的

1603184650425

可以看到在执行Connect5方法时已经开始报出STATUS_ACCES_DEBIED错误,根据微软官方文档SAMR协议的描述并对比连接成功时的数据包,可以看出来客户端会逐一尝试Connect方法,直到Connect2失败,结束请求,返回错误

你可以认证到ipc$共享上,也可以打开ipc$共享,但是你没办法在其所暴露出来的samr这个命名管道上调用QueryDisplayInfo方法,也就是说你可以建立空连接,但是你无法执行特定的RPC方法

这里描述了各命名管道上能够调用的方法:

使用nmap的 smb-enum-users脚本可以枚举出远程系统的所有用户,有时间我要研究一下这个脚本是怎么回事

image1

但是我本地复现时无法枚举远程系统的用户,网上搜了一下,说是需要使用比较老的nmap版本

我尝试找了一下哪些管道可以进行匿名连接,还有就是在这些管道上可以调用哪些方法

最后我锁定了netlogon管道上的GetDcName, DsrGetDcName, DsrGetDcNameExDsrGetDcNameEx2这些方法

这些方法可以被用来检测在DC中是否存在特定的用户

直接调用DsrGetDcNameEx2方法

先用rpcclient工具执行如下命令调用DsrGetDcNameEx2方法

rpcclient 192.168.60.160 -U'%' -c 'dsr_getdcnameex2 Administrator 512 domain2.com'

可以根据响应结果来进行判断:

  • 存在则返回一个结构体
  • 不存在则返回WERR_NO_SUCH_USER

下面看抓包情况

下面是DsrGetDcNameEx2请求的参数,16进制的200就是512,代表域用户

img

用户存在会返回success

asdasd

用户不存在则返回unknown

asdasd

在抓取到的数据包中,可以看到如下数据包:

asdasd

可以看到空连接建立之后我们可以通过调用lsarpc命名管道上的LsarQueryInformationPolicy2方法来获取域的SID

root@ubuntu:/# rpcclient 192.168.60.160 -U'%' -c 'lsaquery'

Domain Name: DOMAIN2

Domain Sid: S-1-5-21-1986246999-2617435358-1981060215

根据上面的分析编写一个脚本,功能如下:

使用impacket框架调用DsrGetDcNameEx2方法,接收一个用户名列表,然后依次使用该列表中的用户针对远程主机(DC)调用DsrGetDcNameEx2方法,根据返回结果来进行枚举

使用上面这种直接调用RPC方法来枚举用户是有一定的局限性的的,因为如果目标DC使用Network security: Restrict NTLM: Incoming NTLM traffic组策略禁用账户使用NTLM认证方式,那么直接调用RPC也就会失败

该策略是在Default Domain Controllers Policy中设置的

1603173464262

1603173729418

构造数据包进行枚举

为了加速枚举,我调查了一下DsrGetDcNameEx2是如何工作的,该方法和GetDcName, DsrGetDcName, DsrGetDcNameEx都可以根据提供的域名来定位到该域的DC

这些方法会使用DNS、LDAP或者NetBIOS等方法获取域控制器在域内的位置

他们的工作方式大致如下所述:

  • 1、通过DNS查询或者NetBIOS广播提供的域名获取到域控制器的IP
  • 2、如果通过DNS获取到了域控制器的IP,则请求端会向DC发送一个LDAP ping报文,如果通过NetBIOS获取到了DC的IP,会向DC发送一个mailslot ping报文,这两个数据包都是通过UDP协议进行发送的,这两个报文中都有一个Filter,其实就是请求的一些参数
  • 3、DC检查自己是否符合这些要求并发送响应报文
  • 4、最后调用方法的系统会处理发送回来的响应报文

LDAP ping

发起调用的主机会向远程主机发送一个CLDAP报文(基于UDP,无连接),对于用户存在的情况,DC会向源主机返回操作码为23的报文,如果用户不存在则返回操作码25

因此我需要自己构造出CLDAP报文,Samba源代码中有一个示例脚本,该脚本可以构造出这样的CLDAP报文

根据那个示例脚本我写出了下面这个脚本:

https://github.com/sensepost/UserEnum/blob/master/UserEnum_LDAP.py

安装这个工具需要asn1tools模块,安装的时候有需要安装diskcache模块,但是使用python2安装的时候他会出现错误,因为默认安装的是针对python3的模块,因此需要指定diskcache模块的版本

pip install diskcache==4.1.0

还有就是上面安装好之后还是会报错ImportError: cannot import name WordCompleter

然后我们使用如下方式解决

python2 -m pip install scapy==2.4.0

python2 -m pip install asn1tools==0.55.0

pip install prompt_toolkit==1 .0.15

如果运行的时候报下面的错误

asn1tools.codecs.EncodeError: protocolOp: typesOnly: Expected data of type bool, but got 0.

则使用如下方法解决

pip install asn1tools==0.100.0

之前写的脚本是直接调用的rpc方法,这种调用方法的方式相比较直接构造数据包发送请求的方式要慢很多

我测试了一下,1万多个用户名,只用了10秒左右

下面是抓的请求包

asdasd

这里acc进行了映射,映射表是下面这俩:

https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/10bf6c8e-34af-4cf9-8dff-6b6330922863?redirectedfrom=MSDN

https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/8a193181-a7a2-49df-a8b1-f689aaa6987c?redirectedfrom=MSDN

从第一个表中我们可以找到普通域用户也就是16进制的200,对应的是UF_NORMAL_ACCOUNT

从第二个表中我们可以看到UF_NORMAL_ACCOUNT对应的是USER_NORMAL_ACCOUNT

USER_ACCOUNT这里可以找到, 可以看到USER_NORMAL_ACCOUNT的值是0x00000010

从上面wireshark抓的包我们可以看到显示的是10:00:00:00

这个不能直接读,细心的话你可以发现这里没有加0x前缀,我们就必须将网络序(大端)转换成小端来读,转换之后就是00:00:00:10,正好符合上面的映射

后面我又在想我可以使用通配符来猜解用户名,比如B*Bo*等,但是当我这样写的时候,构造出来的CLDAP包就无法正常响应

我又捣鼓了一会儿,发现确实没办法实现这种功能,不过既然CLDAP可以达到这种爆破用户名的效果,那么NetBIOS呢?

首先我使用rpcclient发起了一个DsrGetDcNameEx2方法的调用,然后在wireshark中,我捕捉到了如下数据包:

这里发起请求的机器为DC01(192.168.57.2),目标机器为black(192.168.57.120)

  • 1、57.2发送NBNS广播报文,查询black这个名称
  • 2、12057.2返回NBNS响应报文,其中包含自己的IP(192.168.57.120
  • 3、57.2发送SMB_NETLOGON广播报文(UDP),这里明明已经知道了black域的DC的IP却还要发送广播,我也不知道为啥
  • 4、120发送NBNS广播报文,查询DC01这个名称
  • 5、57.2向120发送SMB_NETLOGON报文
  • 6、57.2向120返回NBNS报文,报告自己的IP
  • 7、12057.2返回SMB_NETLOGON响应报文

从以上的报文我们可以看出来,通信双方都发起了NBNS查询,而且查询的名称我们是可以控制的,响应的IP地址我们也可以控制(这个我们只能控制DC01),但是通信过程一直都是UDP,没有SMB TCP报文,我们无法使用Responder来利用,但是我发现至少可以利用这个来方法来中毒目标系统的NETBIOS名称-ip对应表缓存

mailslot ping

看完了ldap之后我们再来看一下NetBIOS

可以使用这个脚本枚举

在运行该脚本的时候可能会遇到如下错误:

from scapy.all import *

ImportError: No module named scapy.all

解决方法

pip install scapy==2.4.3

使用方法:

Python2 UserEnum_NBS.py 192.168.60.148 192.168.60.160 DOMAIN2 userlist.txt

注意这里域名是NetBIOS名称,也就是大写字母,比如domain.local在这里应该写成DOMAIN,注意这里写的并不是FQDN

可以看到netlogon的响应包

asdasd

参考对照表0x17 (十进制的23)表示LOGON_SAM_LOGON_RESPONSE_EX而并不是user unknown,这里应该是wireshark的问题,LOGON_SAM_USER_UNKNOWN_EX应该是0x19

因此操作码为0x17说明用户存在

后记

以上提到的三种方式中,CLDAP是最快的,其实次NetBIOS,最慢的是直接调用DsrGetDcNameEx2 方法,且前两者不会产生日志,最后一种方法会产生匿名登录日志

这个是直接调用rpc方法产生的日志

img