📄 ntlm挑战模式散列认证加密协议过程,算法实现与一些想法.txt
字号:
NTLM挑战模式散列认证加密协议过程,算法实现与一些想法
创建时间:2003-05-28
文章属性:原创
文章提交:flashsky (flashsky1_at_sina.com)
NTLM挑战模式散列认证加密协议过程,算法实现与一些想法
转摘请注明作者和安全焦点
作者:FLASHSKY
SITE:WWW.XFOCUS.NET,WWW.SHOPSKY.COM
邮件:flashsky@xfocus.org
作者单位:启明星辰积极防御实验室
说明:
本来先从看见小四的《LM/NTLM验证机制》就没准备发的,但是从如下几点考虑还是发:
1。给出完全的算法实现代码
2。小四的关于NTLM,LM的过程针对返回会话KEY的有效,针对完全挑战模式下说明并非完全
3。一些新的思路和思考
4。安全BLOB的结构说明
一:NTLM认证算法过程
1。客户端申请挑战
安全BLOB的结构如下:
{
CHAR DESC[8]=“NTLMSSP”;
DWORD NO=1; //顺序号
DWORD FLAG;
WORD DOMAINNAMELEN; //域名长度
WORD DOMAINNAMEMAXLEN;//域名最大长度
DWORD DOMAINNAMEOFFSET;//域名便移
WORD HOSTNAMELEN; //主机名长度
WORD HOSTNAMEMAXLEN; //主机名最大长度
DWORD HOSTNAMEOFFSET; //主机名便移
CHAR HOSTNAME[HOSTNAMELEN];//主机名
CHAR DOMAINNAME[DOMAINNAMELEN];//域名
}
2。服务器返回挑战,这个挑战是一个8字节的随机数
安全BLOB的结构如下:
{
CHAR DESC[8]=“NTLMSSP”;
DWORD NO=2; //顺序号,必须为2
WORD DOMAINNAMELEN; //帐户域名长度
WORD DOMAINNAMEMAXLEN; //帐户域名最大长度
DWORD DOMAINNAMEOFFSET;////帐户域名偏移
DWORD FLAG;
BYTE CHALLAGE[8]; //8字节挑战
BYTE RE[8]; //保留8字节,必须为0
WORD HOSTNAMELISTLEN; //主机名列表长度
WORD HOSTNAMELISTMAXLEN;//主机名列表最大长度
DWORD HOSTNAMELISTOFFSET;//主机名列表偏移
CHAR DOMAINNAME[DOMAINNAMELEN];
CHAR HOSTLISTNAME[HOSTNAMELISTLEN];
//这里是多个结构组成
//每个结构如下:
//WORD ITEMNO;索引
//WORD HOSTNAMELEN;主机名长度
//BYTE HOSTNAME[HOSTNAMELEN]:主机名
}
3。客户端加密密码散列
其实这个过程还是很复杂的
如果是明文口令,先将此口令转化成散列,如果是系统用户则直接根据令牌从SESSION表中取出散列。
这下面就是小四的文章中没提到的过程
客户端先生成一个随机KEY,然后和服务器端的挑战使用MD5算法生成16字节的散列,但MS只取前8字节做为加密KEY,其实后面的散列加密并非是使用挑战直接加密的,而是
使用此加密KEY进行加密的。
这个算法的代码如下:romkey就是客户端生成的一个随机KEY,challage就是服务器挑战
void challagetorkey(unsigned char * romkey,unsigned char * challage,unsigned char * enkey)
{
unsigned char LM[0x58];
md5init(LM);
*(DWORD *)LM=0x200;
memcpy(LM+0X18,challage,8);
memcpy(LM+0X20,romkey,8);
*(DWORD *)(LM+0x28)=0x80;
*(DWORD *)(LM+0x50)=0x80;
md5final(LM);
memcpy(enkey,LM+8,8);
}
然后再使用生成的加密KEY加密MD4生成的NTLM散列,具体的过程在下四的文章中有详细介绍,这里就不再讲了,后面给出了完全实现的代码。
同时还会先选择一个RC4的KEY,对MD4生成的NTLM散列用MD4再生成一个散列,再用HMACMD5算法,加密KEY,这个散列计算出一个0X10字节的散
列,用此散列和rc4_key函数生成rc4的key表,最后用这个KEY表和先选择的RC4的KEY生成一个0X10字节的RC的结果出来。
但要注意的是:通常情况下,这个RC4的结果并没用于认证。
安全BLOB的结构如下:
{
CHAR DESC[8]=“NTLMSSP”;
DWORD NO=3; //顺序号,必须为3
WORD RANDOMKEYLEN; //客户端随机KEY长度,固定为0X18
WORD RANDOMKEYMAXLEN; //客户端随机KEY最大长度,固定为0X18
//其实客户端随机KEY只有8字节,但是后面补0添满0X18字节
DWORD RANDOMKEYOFFSET;////客户端随机KEY偏移
WORD ENNTLMLEN; //加密的NTLM散列长度,固定为0X18
WORD ENNTLMMAXLEN; //加密的NTLM散列最大长度,固定为0X18
DWORD ENNTLMOFFSET; //加密的NTLM散列偏移
WORD HOSTNAMELEN; //主机名长度
WORD HOSTNAMEMAXLEN;//主机名最大长度
DWORD HOSTNAMEOFFSET;//主机名偏移
WORD USERNAMELEN; //用户名长度
WORD USERNAMEMAXLEN;//用户名最大长度
DWORD USERNAMEOFFSET;//用户名偏移
WORD USERDOMAINLEN; //帐户域名长度
WORD USERDOMAINMAXLEN;//帐户域名最大长度
DWORD USERDOMAINOFFSET;//帐户域名偏移
WORD ENNTLMRC4LEN; //加密的RC4 NTLM散列长度,固定为0X10
WORD ENNTLMRC4MAXLEN; //加密的RC4 NTLM散列最大长度,固定为0X10
DWORD ENNTLMRC4OFFSET; //加密的RC4 NTLM散列偏移
DWORD FLAG;
CHAR HOSTNAME[HOSTNAMELEN];//主机名
CHAR USERNAME[HOSTNAMELEN];//用户名
CHAR USERDOMAINNAME[HOSTNAMELEN];//帐户域名
RANDOMKEY[0X18];//其实客户端随机KEY只有8字节,但是后面补0添满0X18字节
ENNTLM[0X18];
ENRC4NTLM[0X10];
}
4。服务器认证
服务器获得客户端随即KEY以后,也和挑战运算获得此加密KEY,然后计算加密的NTLM散列做对比进行判断。
5。一些思考
主要是关于重放攻击的,其实真正的加密KEY是挑战和客户端随机KEY做MD5运算的结果,那么如果我们嗅探出了一个挑战,随机KEY就
能计算出加密KEY,如果我们发起一个请求获得一个挑战2,如果能计算一个随即KEY2(这个可由我们自己控制),满足MD5(挑战2,随即KEY2)
=加密KEY,那么我们就能发送我们修探获得的安全BLOB中的加密散列的内容来获得认证。
当然MD5的算法本事就是要防碰撞的,但是在MS的SMB实现中,由于MD5生成的是16字节的散列,但是用来做加密KEY的只是前8个字节,那
么对于寻找只满足部分(前8字节)碰撞的算法复杂度就会大大的降低,当然我们在MD5中唯一能控制的东西就是随即KEY,但是挑战KEY已知,其实
针对加密算发是常量(因为加密内容固定为0X1O字节,其他的内容可以常量),应该来说还是很有希望的。不过就我的数学水平而已,尝试了
一下就被难到了,希望对MD5碰撞有研究的人给予指教。下面就是给出的一个针对其挑战,随即KEY到加密KEY的替代函数:
void challagetorkey(unsigned long c1,unsigned long c2,unsigned long r1,unsigned long r2)
{
//我们把挑战看做2个unsigned long的数C1,C2,客户端随即KEY也看做2个unsigned long的数R1,R2
//最后生成的是B1,D1,S1,D2,但其实用做加密KEY的只是B1,D1而已
//如果我们知道B1,D1,C1,C2,是否能找过一个R1,R2,使得满足这个函数呢?
//按照概率是针对每个B1,D1都有可能找到对应的R1,R2,因为MD5的算发要求是每个字节的变化都应该平均分布的
//我们可控的是256的8次方,结果也是256的8次方
//即使不是完全平均分布的,在测试100个挑战中总会有满足的吧?
//而算法复杂度对于16字节的256的16次方应该降低了不少
//那么再退一步说,如果我们知道B1,D1,C1,C2,R1,如果能解出R2也行,我们对R1实行穷举
//R1是256的4次方,4亿左右的穷举对现在的机器不应该是负担吧?当然真正实现工具由于认证要求,可能要求在10分钟以内
//但是如果我们在2天内能找到结果,也能说明很多问题
DWORD a1,d1,d2,s1;
DWORD b1;
b1=(((0x10325476^0x98badcfe)&0xefcdab89)^0x10325476)+c1;
b1=b1+0x67452301+0xd76aa478;
b1=((b1<<0x7)|(b1>>0x19))+0xefcdab89;
//第一轮
a1=(((0x98badcfe^0xefcdab89)&b1)^0x98badcfe)+0xe8c7b756;
d2=0x10325476+c2+a1;
d2=((d2<<0xc)|(d2>>0x14))+b1;
a1=(((0xefcdab89^b1)&d2)^0xefcdab89)+0x242070db;
s1=0x98badcfe+r1+a1;
s1=((s1<<0x11)|(s1>>0xf))+d2;
a1=(((d2^b1)&s1)^b1)+0xc1bdceee;
d1=0xefcdab89+r2+a1;
d1=((d1<<0x16)|(d1>>0xa))+s1;
a1=(((d2^s1)&d1)^d2)+0xf57c0faf;
b1=b1+0x80+a1;
b1=((b1<<0x7)|(b1>>0x19))+d1;
a1=(((s1^d1)&b1)^s1)+0x4787C62A;
d2=d2+a1;
d2=((d2<<0xc)|(d2>>0x14))+b1;
a1=(((d1^b1)&d2)^d1)+0xA8304613;
s1=s1+a1;
s1=((s1<<0x11)|(s1>>0xf))+d2;
a1=(((d2^b1)&s1)^b1)+0xFD469501;
d1=d1+a1;
d1=((d1<<0x16)|(d1>>0xa))+s1;
a1=(((d2^s1)&d1)^d2)+0x698098D8;
b1=b1+a1;
b1=((b1<<0x7)|(b1>>0x19))+d1;
a1=(((s1^d1)&b1)^s1)+0x8B44F7AF;
d2=d2+a1;
d2=((d2<<0xc)|(d2>>0x14))+b1;
a1=(((d1^b1)&d2)^d1)+0xFFFF5BB1;
s1=s1+a1;
s1=((s1<<0x11)|(s1>>0xf))+d2;
a1=(((d2^b1)&s1)^b1)+0x895CD7BE;
d1=d1+a1;
d1=((d1<<0x16)|(d1>>0xa))+s1;
a1=(((d2^s1)&d1)^d2)+0x6B901122;
b1=b1+a1;
b1=((b1<<0x7)|(b1>>0x19))+d1;
a1=(((s1^d1)&b1)^s1)+0xFD987193;
d2=d2+a1;
d2=((d2<<0xc)|(d2>>0x14))+b1;
a1=(((d1^b1)&d2)^d1)+0xA679438E;
s1=s1+0x80+a1;
s1=((s1<<0x11)|(s1>>0xf))+d2;
a1=(((d2^b1)&s1)^b1)+0x49B40821;
d1=d1+a1;
d1=((d1<<0x16)|(d1>>0xa))+s1;
//第二轮
a1=(((s1^d1)&d2)^s1)+0xF61E2562;
b1=b1+c2+a1;
b1=((b1<<0x5)|(b1>>0x1b))+d1;
a1=(((d1^b1)&s1)^d1)+0xC040B340;
d2=d2+a1;
d2=((d2<<0x9)|(d2>>0x17))+b1;
a1=(((d2^b1)&d1)^b1)+0x265E5A51;
s1=s1+a1;
s1=((s1<<0xe)|(s1>>0x12))+d2;
a1=(((d2^s1)&b1)^d2)+0xE9B6C7AA;
d1=d1+c1+a1;
d1=((d1<<0x14)|(d1>>0xc))+s1;
a1=(((s1^d1)&d2)^s1)+0xD62F105D;
b1=b1+a1;
b1=((b1<<0x5)|(b1>>0x1b))+d1;
a1=(((d1^b1)&s1)^d1)+0x2441453;
d2=d2+a1;
d2=((d2<<0x9)|(d2>>0x17))+b1;
a1=(((d2^b1)&d1)^b1)+0xD8A1E681;
s1=s1+a1;
s1=((s1<<0xe)|(s1>>0x12))+d2;
a1=(((d2^s1)&b1)^d2)+0xE7D3FBC8;
d1=d1+0x80+a1;
d1=((d1<<0x14)|(d1>>0xc))+s1;
a1=(((s1^d1)&d2)^s1)+0x21E1CDE6;
b1=b1+a1;
b1=((b1<<0x5)|(b1>>0x1b))+d1;
a1=(((d1^b1)&s1)^d1)+0xC33707D6;
d2=d2+0x80+a1;
d2=((d2<<0x9)|(d2>>0x17))+b1;
a1=(((d2^b1)&d1)^b1)+0xF4D50D87;
s1=s1+r2+a1;
s1=((s1<<0xe)|(s1>>0x12))+d2;
a1=(((d2^s1)&b1)^d2)+0x455A14ED;
d1=d1+a1;
d1=((d1<<0x14)|(d1>>0xc))+s1;
a1=(((s1^d1)&d2)^s1)+0xA9E3E905;
b1=b1+a1;
b1=((b1<<0x5)|(b1>>0x1b))+d1;
a1=(((d1^b1)&s1)^d1)+0xFCEFA3F8;
d2=d2+r1+a1;
d2=((d2<<0x9)|(d2>>0x17))+b1;
a1=(((d2^b1)&d1)^b1)+0x676F02D9;
s1=s1+a1;
s1=((s1<<0xe)|(s1>>0x12))+d2;
a1=(((d2^s1)&b1)^d2)+0x8D2A4C8A;
d1=d1+a1;
d1=((d1<<0x14)|(d1>>0xc))+s1;
//第三轮
a1=((d2^s1)^d1)+0xFFFA3942;
b1=b1+a1;
b1=((b1<<0x4)|(b1>>0x1c))+d1;
a1=((s1^d1)^b1)+0x8771F681;
d2=d2+a1;
d2=((d2<<0xb)|(d2>>0x15))+b1;
a1=(d2^d1)^b1;
s1=s1+0x6D9D6122+a1;
s1=((s1<<0x10)|(s1>>0x10))+d2;
a1=d2^s1;
d1=d1+0x80+0xFDE5380C+(b1^a1);
d1=((d1<<0x17)|(d1>>0x9))+s1;
b1=b1+c2+0xA4BEEA44+(d1^a1);
b1=((b1<<0x4)|(b1>>0x1c))+d1;
a1=((s1^d1)^b1)+0x4BDECFA9;
d2=d2+0x80+a1;
d2=((d2<<0xb)|(d2>>0x15))+b1;
a1=(d2^d1)^b1;
s1=s1+0xF6BB4B60+a1;
s1=((s1<<0x10)|(s1>>0x10))+d2;
a1=(d2^s1);
d1=d1+0xBEBFBC70+(b1^a1);
d1=((d1<<0x17)|(d1>>0x9))+s1;
b1=b1+0x289B7EC6+(d1^a1);
b1=((b1<<0x4)|(b1>>0x1c))+d1;
a1=((s1^d1)^b1)+0xEAA127FA;
d2=d2+c1+a1;
d2=((d2<<0xb)|(d2>>0x15))+b1;
a1=(d2^d1)^b1;
s1=s1+r2+0xD4EF3085+a1;
s1=((s1<<0x10)|(s1>>0x10))+d2;
a1=d2^s1;
d1=d1+0x4881D05+(b1^a1);
d1=((d1<<0x17)|(d1>>0x9))+s1;
a1=d1^a1;
b1=b1+a1+0xD9D4D039;
b1=((b1<<0x4)|(b1>>0x1c))+d1;
a1=((s1^d1)^b1)+0xE6DB99E5;
d2=d2+a1;
d2=((d2<<0xb)|(d2>>0x15))+b1;
a1=((d2^d1)^b1);
s1=s1+0x1FA27CF8+a1;
s1=((s1<<0x10)|(s1>>0x10))+d2;
a1=((d2^s1)^b1);
d1=d1+r1+0xC4AC5665+a1;
d1=((d1<<0x17)|(d1>>0x9))+s1;
//第4轮
a1=(((d2^0xFFFFFFFF)|d1)^s1)+0xF4292244;
b1=b1+c1+a1;
b1=((b1<<0x6)|(b1>>0x1a))+d1;
a1=(((s1^0xFFFFFFFF)|b1)^d1)+0x432AFF97;
d2=d2+a1;
d2=((d2<<0xa)|(d2>>0x16))+b1;
a1=(((d1^0xFFFFFFFF)|d2)^b1)+0xAB9423A7;
s1=s1+0x80+a1;
s1=((s1<<0xf)|(s1>>0x11))+d2;
a1=(((b1^0xFFFFFFFF)|s1)^d2);
d1=d1+0xFC93A039+a1;
d1=((d1<<0x15)|(d1>>0xb))+s1;
a1=(((d2^0xFFFFFFFF)|d1)^s1)+0x655B59C3;
b1=b1+a1;
b1=((b1<<0x6)|(b1>>0x1a))+d1;
a1=(((s1^0xFFFFFFFF)|b1)^d1)+0x8F0CCC92;
d2=d2+r2+a1;
d2=((d2<<0xa)|(d2>>0x16))+b1;
a1=(((d1^0xFFFFFFFF)|d2)^b1)+0xFFEFF47D;
s1=s1+a1;
s1=((s1<<0xf)|(s1>>0x11))+d2;
a1=(((b1^0xFFFFFFFF)|s1)^d2)+0x85845DD1;
d1=d1+c2+a1;
d1=((d1<<0x15)|(d1>>0xb))+s1;
a1=(((d2^0xFFFFFFFF)|d1)^s1)+0x6FA87E4F;
b1=b1+a1;
b1=((b1<<0x6)|(b1>>0x1a))+d1;
a1=(((s1^0xFFFFFFFF)|b1)^d1)+0xFE2CE6E0;
d2=d2+a1;
d2=((d2<<0xa)|(d2>>0x16))+b1;
a1=(((d1^0xFFFFFFFF)|d2)^b1)+0xA3014314;
s1=s1+a1;
s1=((s1<<0xf)|(s1>>0x11))+d2;
a1=(((b1^0xFFFFFFFF)|s1)^d2)+0x4E0811A1;
d1=d1+a1;
d1=((d1<<0x15)|(d1>>0xb))+s1;
a1=(((d2^0xFFFFFFFF)|d1)^s1)+0xF7537E82;
b1=b1+0x80+a1;
b1=((b1<<0x6)|(b1>>0x1a))+d1;
a1=(((s1^0xFFFFFFFF)|b1)^d1)+0xBD3AF235;
d2=d2+a1;
d2=((d2<<0xa)|(d2>>0x16))+b1;
a1=(((d1^0xFFFFFFFF)|d2)^b1)+0x2AD7D2BB;
s1=s1+r1+a1;
s1=((s1<<0xf)|(s1>>0x11))+d2;
a1=(((b1^0xFFFFFFFF)|s1)^d2)+0xEB86D391;
d1=d1+a1;
d1=((d1<<0x15)|(d1>>0xb))+s1;
b1=b1+0x67452301;
d1=d1+0xefcdab89;
s1=s1+0x98badcfe;
d2=d2+0x10325476;
}
6。实现代码的说明
这个代码是完全在TCP只上实现NTLM挑战认证的,密码的计算不调用任何加密的API。
如果知道散列也可以去掉口令到散列的部分直接使用散列进行处理。
#include <stdio.h>
#include <winsock2.h>
#include <windows.h>
#include <wincrypt.h>
#include <process.h>
#include <string.h>
#include <winbase.h>
# include <wincrypt.h>
#define SECURITY_WIN32
# include <Security.h>
# include <Ntsecapi.h>
# include "smb.h"
void SmbNegotiate(SMBP * psmbp);
void SmbSessionSetupAndX1(SMBP * psmbp);
void SmbSessionSetupAndX2(SMBP * psmbp,wchar_t * username,wchar_t * domainname,wchar_t * password);
void deskey(char * LmPass,unsigned char * desecb);
void passtoowf(wchar_t * password,unsigned char * paswdowf);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -