实现PROXY穿越(10):NTLMv2 response

  最近忙,事比较多,活多了,还要降薪,唉。没什么时间看书,将以前的一些技术blog也移到这里。NTLM在去年年底和今年年初研究过一阵子,写了总结和例子程序。里面涉及很多算法,在网上查了很久。(下面是以前的博客)最近想实现一个通过PROXY穿越的网络编程,将相关的内容进行一下汇总。很多东西来自网络共产主义,也应该为共产主义有所回馈。介绍NTLMv2 response的实现。

  NTLMv1由两部分组成,LM response和NTLM response。NTLMv2也一样,分为LMv2 response和NTLMv2 response。这里使用了一个HMAC-MD5的计算,需要从RFC2104的附录中获得算法的C代码。需要注意两点:

  • 函数参数类型caddr_t实际是unsigned char *
  • bzero和bcopy在VC中需要使用memset和memcpy来代替,其中memcpy和bcopy的参数src和dst的位置是颠倒的,需要特别注意。我就将顺序搞反了,导致down掉。

  对于NTLM的详细说明可以参考 http://davenport.sourceforge.net/ntlm.html ,虽然是英文、洋文、鸡肠文,还是很值得读一读。NTLMv2的输入参数比较多:

  1. 密码: passwd,假设密码为:SecREt01
  2. 用户名:user_name,假设为user
  3. 域名:domain,假设为DOMAIN
  4. 从server中获得的challenge,固定长度为8,假设为0x0123456789abcdef
  5. 从server中获得的target_info,长度不固定,因此需要一个表示长度的target_info_len,假设为:0x02000c0044004f00 4d00410049004e00 01000c0053004500 5200560045005200 0400140064006f00 6d00610069006e00 2e0063006f006d00 0300220073006500 7200760065007200 2e0064006f006d00 610069006e002e00 63006f006d000000 0000
  6. 客户端自己生成的8位client_nonce,假设为0xff ff ff 00 11 22 33 44

  输出 如下:

  1. NTLMv2 response,长度不固定,需要给出len
  2. LMv2 response,长度为24

void ntlmv2_response(IN char * passwd, IN char * user_name,IN char * domain,
                     IN unsigned char * chanllenge, IN unsigned char * target_info,
                     IN int target_info_len,IN unsigned char * client_nonce,
                     OUT unsigned char * ntlm_response, OUT int * ntlm_response_len,
                     OUT unsigned char * lm_response,   OUT int * lm_response_len){
    unsigned char hash[16],digest[16];
    unsigned char * name;
    int len ;
    long cur_time = 0;
    __int64 t;
}

步骤一:v2-Hash = HMAC-MD5(NT-Hash, user name, domain name)
step1:获取NT-HASH,存放在digest中

nt_hash(passwd, 0,digest,NULL);
digest = 0xcd 06 ca 7c 7e 10 c9 9b 1d 33 b7 48 5a 2e d8 08

step2:获取v2-Hash

将user和domain合并然后全部改为大写换为unicode的测试,例如user=“user”,domain=“domain”,合并为USERDOMAIN为此我们提供了一个函数:
static void ntlmv2_unicode(IN char * user_name, IN char * domain,
                                       OUT char * user){
    char * temp;
    int len1 = strlen(user_name);
    int len2 = strlen(domain);

    temp = ( char *) malloc(len1 + len2 + 1);
    strcpy(temp,user_name);
    strcat(temp,domain);
    strtoupper(temp);
    unicode(temp,len1 + len2,user,NULL);
    free(temp);
}
得到的值使用step1计算出来的digest作为key进行HMAC_MD5计算,得到的值放置在hash中。程序如下:
    len = (strlen(user_name) + strlen(domain)) * 2;
    name = (unsigned char *) malloc(len);
    ntlmv2_unicode(user_name,domain,(char *)name);
    HMAC_MD5(name, len, digest,16,hash);
    free(name);
得到hash=0x04 b8 e0 ba 74 28 9c c5 40 82 6b ab 1d ee 63 ae


步骤二:计算NTLMv2 response:NTv2 = HMAC-MD5(v2-Hash, CS, CC*)
step1:我们需要根据系统时间、challenge、client_nonce、target_info获得一个blob的数据,用于后面的HMAC_MD5计算,及CC* = (X, time, CC, domain name)

blob的格式为:1-8字节为chanllenge;9-12字节为0x010100,17-24字节为根据当前时间获得一个数值;25-32字节为client_nonce,第37字节开始为target_info,在target_info后补充4个空字节。没有提及的字节填写0。
时间值的计算如下:当前时间(1970年1月1日开始计算的秒)+11644473600,成为1601年1月1日开始计算的秒,然后乘以10000000(1E+7)。获得的64比特的整数。要注意long的长度为32比特,不足以存放,我们使用__int64来解决这个问题。
cur_time = time(NULL);
t = (cur_time + 11644473600) * 10000000;
blob的获取如下:
len = 40 + target_info_len ;
name = (unsigned char *)malloc(len);
memset(name,0,len);
memcpy(name,chanllenge,8);
name[8] = 0x01;
name[9] = 0x01;
memcpy(name + 16, &t , 8);
memcpy(name + 24,client_nonce,8);
memcpy(name + 36,target_info,target_info_len);
以我们提供的例子,得到的blob长度为138,cur_time=1229419943,为:
 01 23 45 67 89 ab cd ef 01 01 00 00 00 00 00 00
 80 2d 11 33 61 5f c9 01 ff ff ff 00 11 22 33 44
 00 00 00 00 02 00 0c 00 44 00 4f 00 4d 00 41 00
 49 00 4e 00 01 00 0c 00 53 00 45 00 52 00 56 00
 45 00 52 00 04 00 14 00 64 00 6f 00 6d 00 61 00
 69 00 6e 00 2e 00 63 00 6f 00 6d 00 03 00 22 00
 73 00 65 00 72 00 76 00 65 00 72 00 2e 00 64 00
 6f 00 6d 00 61 00 69 00 6e 00 2e 00 63 00 6f 00
 6d 00 00 00 00 00 00 00 00 00

step2:计算ntlmv2 response

HMAC_MD5(name, len, hash,16,ntlm_response);
获得16字节的数据:0x3f 7e 49 5e 5c 39 c1 46 34 28 54 00 4f a4 5a 26,用其代替blob原来0-8字节的数据数据,就是我们最后得到的ntlmv2_response;
    memcpy(ntlm_response + 16,name + 8,len - 8);
    if(ntlm_response_len != NULL)
        * ntlm_response_len = len + 8;
    free(name);


步骤三:计算LMv2的response,LMv2 = HMAC-MD5(v2-Hash, CS, CC)
  计算LMv2 response比较简单,固定长度为24字节。
step1:将挑战值和client_nonce合并,使用步骤一的hash作为key进行HMAC_MD5计算
step2:将步骤一得到的16字节长度加上8字节的client_nonce就是LMv2的response。

    name = (unsigned char *) malloc(16);
    memcpy(name,chanllenge,8);
    memcpy(name + 8,client_nonce,8);
    HMAC_MD5(name, 16, hash,16,lm_response);
    memcpy(lm_response + 16,client_nonce,8);
    if(lm_response_len != NULL)
        * lm_response_len = 24;
    free(name);
根据我们的输入值,得到24字节的结果:0xd6 e6 15 2e a2 5d 03 b7 c6 ba 66 29 c2 d6 aa f0 ff ff ff 00 11 22 33 44

  ^_^,终于完成了NTLMv2应答值的计算,擦把汗,发现还有NTLM v2 session的方式,这个世界就是这样,有人在不断地加密,有人在不断地解密,将事情搞得越来越复杂。因为大家都很无聊,就搞出更多无聊的事情,也就有更多的人可以混饭吃。

 

相关链接:我的网络通信相关文章

NTLM的实现:

  • 实现PROXY穿越(16):NTLM的PROXY穿越
  • 实现PROXY穿越(15):NTLM Session Security
  • 实现PROXY穿越(14):NTLM type3 Message
  • 实现PROXY穿越(13):NTLM type2 Message
  • 实现PROXY穿越(12):NTLM type1 Message
  • 实现PROXY穿越(11):NTLMv2 session response
  • 实现PROXY穿越(10):NTLMv2 response
  • 实现PROXY穿越(9):NTLMv1 response
  • 实现PROXY穿越(8):NT-Hash的实现
  • 实现PROXY穿越(7):MD4和MD5
  • 实现PROXY穿越(6):LM-Hash的实现
  • 实现PROXY穿越(5):DES算法之三
  • 实现PROXY穿越(4):DES算法之二
  • 实现PROXY穿越(3):DES算法之一
  • 实现PROXY穿越(2):Base64算法
  • 实现PROXY穿越(1):流程和NTLM算法

你可能感兴趣的:(算法,session,server,user,null,domain)