⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ntlm挑战模式散列认证加密协议过程,算法实现与一些想法.txt

📁 NTLM挑战模式散列认证加密协议过程
💻 TXT
📖 第 1 页 / 共 5 页
字号:
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 + -