//注:本文不涉及到NTLM具体的认证算法,但是给出通过SSPI的API实现的过程。
SMB协议可以说是WIN系统的核心协议,这里大致给大家讲解一下SMB认证登陆的过程:
1。协议解析栈
CLIENT:APP----->NET/MRP/RPC API------>MRXSMB.SYS------>NETBT.SYS---->TCP/IP
SERVER: SRV.SYS<-----------MRXSMB.SYS<----------NETBT.SYS<--------TCP/IP
应用程序调用NET/MPR/RPC等API将信息写入WKSSVC管道,这个管道是一个特殊的管道,对应的其实是一个LPC端口。
MRXSMB.SYS是一个SMB的小端口驱动,负责完成客户端的SMB的封装和实现,如发起协商,认证的加密/解密过程等。对于服务器端则主要
完成会话状态的一些管理,然后将SMB包发送给下级驱动处理。
NETBT.SYS是NETBIOS OVER TCP/IP的驱动,一般都是作为MRXSMB的下级驱动,不过MRXSMB也可以有其他的下级驱动,最后通过一个网络驱
动实现通讯,这里假设是使用TCP/IP。
SRV.SYS是服务器端的SMB协议驱动程序,实现完成真正的SMB的协议功能,然后将结果按原路返回给客户端。
2。SMB的认证协议。
通过SMB进行认证客户端一般都要先使用SMB的协商命令(0X72)发送客户端支持的认证协议给服务器,服务器则选择其中的一个,然后返回
给客户端,客户端然后再使用SMB的会话设置和X命令(0X73)进行认证和登陆。
MS的SMB已经使用的SMB认证协议有:
协议 备注
PC NETWORK PROGRAM 1.0 W2K支持
PCLAN1.0 W2K支持
MICROSOFT NETWORKS 1.03 W2K支持
MICROSOFT NETWORKS 3.0 W2K支持
LANMAN1.0 W2K支持
DOS LM1.2X002 W2K支持
Windows for Workgroups 3.1a W2K不支持,老的Windows for Workgroups 3.1使用此协议方式。
LM1.2X002 W2K支持
DOS LANMAN2.1 W2K支持
LANMAN2.1 W2K支持
NT LM 0.12 W2K支持
Cairo 0.xa W2K支持,这是NT后MS自己开发的一个认证协议
这里我们首先讲讲大家用的最多和最熟悉的NTLM认证方式:
3。NTLM认证过程
NTLM在发展的过程中也存在这兼容的一些问题,现在的NTLM是支持挑战方式的,但原始的W9X的NTLM则不支持挑战会话方式的,所以
从协议过程来看存在2种方式,一种是支持挑战加密的方式(NT,2K等),一种是不支持加密挑战的方式(W9X)。
i. 认证过程1
? (客户端发起:会话协商协议)包含支持NTLM认证的选项且支持挑战选项。
? (服务器:会话协商协议)选择了NTLM认证协议,且包含服务器的GUID
? (客户端发起:会话设置和X协议)申请挑战会话的安全BLOB
? (服务器:会话设置和X协议)返回带有挑战KEY的安全BLOB
? (客户端发起:会话设置和X协议)带会话KEY加密的口令散列的安全BLOB
? 认证成功:会话设置和X协议返回成功信息
认证失败:会话设置和X协议返回失败信息,发起注销协议包
ii. 认证过程2
? (客户端发起:会话协商协议)包含支持NTLM认证的选项且不支持挑战选项。
? (服务器:会话协商协议)选择了NTLM认证协议,且包含会话KEY
? (客户端发起:会话设置和X协议)使用会话KEY加密的密码散列
//注意可以指定是非加密方式的密码明文来进行登陆
? 认证成功:会话设置和X协议返回成功信息
认证失败:会话设置和X协议返回失败信息,发起注销协议包
我们这里主要讲认证过程1,其实过程2一般为很多工具使用,因为可以通过特定的FLAG字段指定不使用挑战和加密方式进行登陆,
那么就可以方便的使用明文口令,如SMBCRACK主要使用这种方法进行口令暴力破解,但是需要注意的一点是,这种属于老的方式,是可以在
服务器端禁止的。
客户端APP通过写入WKSSVC管道(LPC端口发起)相关命令和数据
客户端MRXSMB.SYS的SmbCeNegotiate函数负责进行协商包的封装并交下级驱动封装
服务器SRV.SYS的SrvSmbNegotiate函数负责选取一个认证协议并返回给客户端,并返回服务器的GUID
客户端的MRXSMB.SYS的SmbCeReferenceSession负责进行认证:
模拟过程函数如下:
BYTE buf1[0x404c];
TOKEN_STATISTICS ts;
CredHandle phs;
CredHandle phc;
CtxtHandle cthc;
CtxtHandle cths;
SECURITY_STATUS ss;
wchar_t TargetName[]=L"HOST/192.168.0.34";
int len;
typedef struct _Credentials
{
char * pusername;
DWORD usernamelen;
char * pdomainname;
DWORD domainnamelen;
char * ppassword;
DWORD passwordlen;
DWORD credtype;
BYTE info[0x200];
}Credentials,* PCredentials;
void client1()
{
Credentials crt;
LUID LogonID;
HANDLE tk;
TimeStamp Lifetime;
DWORD ContextAttributes;
DWORD rlen;
char crtinfo[]={'a',0,'d',0,'m',0,'i',0,'n',0,'i',0,'s',0,'t',0,'r',0,'a',0,'t',0,'o',0,'r',0,0,0,'p',0,'s',0,'w',0,'d',0,0,0};
SecBufferDesc OutBuffDesc;
SecBufferDesc InBuffDesc;
SecBuffer InSecBuff;
SecBuffer OutSecBuff;
LogonID.HighPart =0;
LogonID.LowPart = 0x7d80;
//输入的口令在此需要CRT
//自动的则不需要
OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &tk);
GetTokenInformation(tk,TokenStatistics,&ts,sizeof(ts),&rlen);
crt.pusername = crt.info;
crt.usernamelen = 0xd;
crt.pdomainname =0;
crt.domainnamelen = 0;
crt.ppassword = crt.info+0x1c;
crt.passwordlen =4;
crt.credtype = 6;
memcpy(crt.info,crtinfo,0x22);
ss=AcquireCredentialsHandleW
(NULL,L"Negotiate",SECPKG_CRED_OUTBOUND,&ts.AuthenticationId,&crt,NULL,NULL,&phc,&Lifetime);
InBuffDesc.ulVersion = 0;
InBuffDesc.cBuffers = 1;
InBuffDesc.pBuffers = &InSecBuff;
InSecBuff.BufferType =SECBUFFER_TOKEN;
InSecBuff.cbBuffer =0;
InSecBuff.pvBuffer = buf1;
OutBuffDesc.ulVersion = 0;
OutBuffDesc.cBuffers = 1;
SMB协议可以说是WIN系统的核心协议,这里大致给大家讲解一下SMB认证登陆的过程:
1。协议解析栈
CLIENT:APP----->NET/MRP/RPC API------>MRXSMB.SYS------>NETBT.SYS---->TCP/IP
SERVER: SRV.SYS<-----------MRXSMB.SYS<----------NETBT.SYS<--------TCP/IP
应用程序调用NET/MPR/RPC等API将信息写入WKSSVC管道,这个管道是一个特殊的管道,对应的其实是一个LPC端口。
MRXSMB.SYS是一个SMB的小端口驱动,负责完成客户端的SMB的封装和实现,如发起协商,认证的加密/解密过程等。对于服务器端则主要
完成会话状态的一些管理,然后将SMB包发送给下级驱动处理。
NETBT.SYS是NETBIOS OVER TCP/IP的驱动,一般都是作为MRXSMB的下级驱动,不过MRXSMB也可以有其他的下级驱动,最后通过一个网络驱
动实现通讯,这里假设是使用TCP/IP。
SRV.SYS是服务器端的SMB协议驱动程序,实现完成真正的SMB的协议功能,然后将结果按原路返回给客户端。
2。SMB的认证协议。
通过SMB进行认证客户端一般都要先使用SMB的协商命令(0X72)发送客户端支持的认证协议给服务器,服务器则选择其中的一个,然后返回
给客户端,客户端然后再使用SMB的会话设置和X命令(0X73)进行认证和登陆。
MS的SMB已经使用的SMB认证协议有:
协议 备注
PC NETWORK PROGRAM 1.0 W2K支持
PCLAN1.0 W2K支持
MICROSOFT NETWORKS 1.03 W2K支持
MICROSOFT NETWORKS 3.0 W2K支持
LANMAN1.0 W2K支持
DOS LM1.2X002 W2K支持
Windows for Workgroups 3.1a W2K不支持,老的Windows for Workgroups 3.1使用此协议方式。
LM1.2X002 W2K支持
DOS LANMAN2.1 W2K支持
LANMAN2.1 W2K支持
NT LM 0.12 W2K支持
Cairo 0.xa W2K支持,这是NT后MS自己开发的一个认证协议
这里我们首先讲讲大家用的最多和最熟悉的NTLM认证方式:
3。NTLM认证过程
NTLM在发展的过程中也存在这兼容的一些问题,现在的NTLM是支持挑战方式的,但原始的W9X的NTLM则不支持挑战会话方式的,所以
从协议过程来看存在2种方式,一种是支持挑战加密的方式(NT,2K等),一种是不支持加密挑战的方式(W9X)。
i. 认证过程1
? (客户端发起:会话协商协议)包含支持NTLM认证的选项且支持挑战选项。
? (服务器:会话协商协议)选择了NTLM认证协议,且包含服务器的GUID
? (客户端发起:会话设置和X协议)申请挑战会话的安全BLOB
? (服务器:会话设置和X协议)返回带有挑战KEY的安全BLOB
? (客户端发起:会话设置和X协议)带会话KEY加密的口令散列的安全BLOB
? 认证成功:会话设置和X协议返回成功信息
认证失败:会话设置和X协议返回失败信息,发起注销协议包
ii. 认证过程2
? (客户端发起:会话协商协议)包含支持NTLM认证的选项且不支持挑战选项。
? (服务器:会话协商协议)选择了NTLM认证协议,且包含会话KEY
? (客户端发起:会话设置和X协议)使用会话KEY加密的密码散列
//注意可以指定是非加密方式的密码明文来进行登陆
? 认证成功:会话设置和X协议返回成功信息
认证失败:会话设置和X协议返回失败信息,发起注销协议包
我们这里主要讲认证过程1,其实过程2一般为很多工具使用,因为可以通过特定的FLAG字段指定不使用挑战和加密方式进行登陆,
那么就可以方便的使用明文口令,如SMBCRACK主要使用这种方法进行口令暴力破解,但是需要注意的一点是,这种属于老的方式,是可以在
服务器端禁止的。
客户端APP通过写入WKSSVC管道(LPC端口发起)相关命令和数据
客户端MRXSMB.SYS的SmbCeNegotiate函数负责进行协商包的封装并交下级驱动封装
服务器SRV.SYS的SrvSmbNegotiate函数负责选取一个认证协议并返回给客户端,并返回服务器的GUID
客户端的MRXSMB.SYS的SmbCeReferenceSession负责进行认证:
模拟过程函数如下:
BYTE buf1[0x404c];
TOKEN_STATISTICS ts;
CredHandle phs;
CredHandle phc;
CtxtHandle cthc;
CtxtHandle cths;
SECURITY_STATUS ss;
wchar_t TargetName[]=L"HOST/192.168.0.34";
int len;
typedef struct _Credentials
{
char * pusername;
DWORD usernamelen;
char * pdomainname;
DWORD domainnamelen;
char * ppassword;
DWORD passwordlen;
DWORD credtype;
BYTE info[0x200];
}Credentials,* PCredentials;
void client1()
{
Credentials crt;
LUID LogonID;
HANDLE tk;
TimeStamp Lifetime;
DWORD ContextAttributes;
DWORD rlen;
char crtinfo[]={'a',0,'d',0,'m',0,'i',0,'n',0,'i',0,'s',0,'t',0,'r',0,'a',0,'t',0,'o',0,'r',0,0,0,'p',0,'s',0,'w',0,'d',0,0,0};
SecBufferDesc OutBuffDesc;
SecBufferDesc InBuffDesc;
SecBuffer InSecBuff;
SecBuffer OutSecBuff;
LogonID.HighPart =0;
LogonID.LowPart = 0x7d80;
//输入的口令在此需要CRT
//自动的则不需要
OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &tk);
GetTokenInformation(tk,TokenStatistics,&ts,sizeof(ts),&rlen);
crt.pusername = crt.info;
crt.usernamelen = 0xd;
crt.pdomainname =0;
crt.domainnamelen = 0;
crt.ppassword = crt.info+0x1c;
crt.passwordlen =4;
crt.credtype = 6;
memcpy(crt.info,crtinfo,0x22);
ss=AcquireCredentialsHandleW
(NULL,L"Negotiate",SECPKG_CRED_OUTBOUND,&ts.AuthenticationId,&crt,NULL,NULL,&phc,&Lifetime);
InBuffDesc.ulVersion = 0;
InBuffDesc.cBuffers = 1;
InBuffDesc.pBuffers = &InSecBuff;
InSecBuff.BufferType =SECBUFFER_TOKEN;
InSecBuff.cbBuffer =0;
InSecBuff.pvBuffer = buf1;
OutBuffDesc.ulVersion = 0;
OutBuffDesc.cBuffers = 1;