更确切的来说此章应为介绍如何使用 MD4 编码, 而此章所表达的重点更应该为 NTLM 中 DES 与 MD4 所扮演的角色. 使用 MD4 编码来加密资料, 在 Perl 的世界中, 有许多开发者创造了许多模组供我们使用, 而 Digest::MD4 就是其中一个, 它是由Mike McCauley所开发, 使用它可以将 MD4 加密变的很简单, 只要使用它所提供的方法, 以下就是一个例子:
上例实现了 $a 字串用 MD4 加密所得出的 16 进位字串.
有了 DES 及 MD4 的基础后, 可以回到 《NTLM Protocol - 1.NTLM 的通讯过程》中所提到的 LMRESP 及 NTRESP 这两个长度为 24 Bytes 加密后的资料, 首先先介绍 LMRESP 是如何根据 PASSWORD 与 NONCE 计算出来的.
举例来说, 假设 PASSWORD = '1qaz2wsx3WSX', 将其全部转成大写可得 '1QAZ2WSX3WSX', 再转成 16 进位字串可得 UK, 以 Byte 来表示如下:
UK[0] = 31, UK[1] = 51, UK[2] = 41, UK[3] = 5a, UK[4] = 32, UK[5] = 57
UK[6] = 53, UK[7] = 58, UK[8] = 33, UK[9] = 57, UK[10] = 53, UK[11] = 58
以 8 Byte 为一组当作一个 Base Key, 第二个 Base Key 会重复第一个 Base Key 的最后一个值, 再依序排出, 后面不够的补 \x00, 所以可以得到 BK1 与 BK2 如下:
BK1[0] = 31 BK2[0] = 58
BK1[1] = 51 BK2[1] = 33
BK1[2] = 41 BK2[2] = 57
BK1[3] = 5a BK2[3] = 53
BK1[4] = 32 BK2[4] = 58
BK1[5] = 57 BK2[5] = 00
BK1[6] = 53 BK2[6] = 00
BK1[7] = 58 BK2[7] = 00
将两组基钥经过如下转换公式, 我们称为 Z 函数, 可得出 CK1 与 CK2, 转换公式为:
key[0] = key_56[0];
key[1] = ((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1);
key[2] = ((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2);
key[3] = ((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3);
key[4] = ((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4);
key[5] = ((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5);
key[6] = ((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6);
key[7] = (key_56[6] << 1) & 0xFF;
key_56 表示所传入的 BK1 或 BK2 的前 7 位元素, 于是由上式可得转变后的 CK1 与 CK2 分别为:
CK1[0] = 31 CK2[0] = 58
CK1[1] = a8 CK2[1] = 19
CK1[2] = 50 CK2[2] = d5
CK1[3] = 2b CK2[3] = ea
CK1[4] = a3 CK2[4] = 35
CK1[5] = 92 CK2[5] = c0
CK1[6] = 5d CK2[6] = 00
CK1[7] = a6 CK2[7] = 00
将 CK1 与 CK2 中的每一个元素, 都经过 set_odd_parity 的运算后, 可得到 DK1 与 DK2, 而 set_odd_parity 则可以如下算法表达:
#!/usr/bin/perl @odd_parity=( 1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14, 16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31, 32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47, 49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62, 64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79, 81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94, 97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110, 112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127, 128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143, 145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158, 161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174, 176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191, 193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206, 208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223, 224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239, 241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254); for $i (@ARGV) { if($i =~ /^0x/) { # convert hex to binary $i = pack("H*", substr($i,2)); } @bytes=unpack("C*", $i); print join(',', @bytes)."\n"; print "0x"; foreach $byte (@bytes) { $byte = $odd_parity[$byte]; print sprintf("%02x", $byte); } print "\n"; #print "\""; foreach $byte (@bytes) { $byte = $odd_parity[$byte]; #print pack("c", $byte); } #print "\"\n"; }
CK1[0] => 31, CK1[1] => a8, CK1[2] => 51, CK1[3] => 2a, 所以由此可得 DK1 与 DK2 为:
DK1[0] = 31 DK2[0] = 58
DK1[1] = a8 DK2[1] = 19
DK1[2] = 51 DK2[2] = d5
DK1[3] = 2a DK2[3] = ea
Dk1[4] = a2 DK2[4] = 34
DK1[5] = 92 DK2[5] = c1
DK1[6] = 5d DK2[6] = 01
DK1[7] = a7 DK2[7] = 01
分别以 DK1 与 DK2 为 KEY, 对 MAGIC = '4b47532140232425' 进行 DES 编码则分别可得 EK1 与 EK2, EK1 = '77110412e3544d2f', EK2 = '083b7034aafcdbff', 将 EK1 与 EK2 组合起来后, 后 5 位以 \x00 递补, 则成 21 Bytes 的 FK, FK = '\x77110412e3544d2f083b7034aafcdbff0000000000', 将 FK 分成三段, 每段 8 个数, 下一段重复上一段的最后一个再往后算上 7 个, 于是可以得到 GK1, GK2, GK3:
GK1[0] = 77 GK2[0] = 2f GK3[0] = db
GK1[1] = 11 GK2[1] = 08 GK3[1] = ff
GK1[2] = 04 GK2[2] = 3b GK3[2] = 00
GK1[3] = 12 GK2[3] = 70 GK3[3] = 00
GK1[4] = e3 GK2[4] = 34 GK3[4] = 00
GK1[5] = 54 GK2[5] = aa GK3[5] = 00
GK1[6] = 4d GK2[6] = fc GK3[6] = 00
GK1[7] = 2f GK2[7] = db GK3[7] = 00
GK1, GK2, GK3 经过前面所提到的 Z 函数转换后可得到 HK1, HK2, HK3:
HK1[0] = 77 HK2[0] = 2f HK3[0] = db
HK1[1] = 88 HK2[1] = 84 HK3[1] = ff
HK1[2] = 41 HK2[2] = 0e HK3[2] = c0
HK1[3] = 82 HK2[3] = 6e HK3[3] = 00
HK1[4] = 2e HK2[4] = 03 HK3[4] = 00
HK1[5] = 1a HK2[5] = a5 HK3[5] = 00
HK1[6] = 51 HK2[6] = ab HK3[6] = 00
HK1[7] = 9a HK2[7] = f8 HK3[7] = 00
HK1, HK2, HK3 再经过 set_odd_parity 运算即可得到 IK1, IK2, IK3
IK1[0] = 76 IK2[0] = 2f IK3[0] = da
IK1[1] = 89 IK2[1] = 85 IK3[1] = fe
IK1[2] = 40 IK2[2] = 0e IK3[2] = c1
IK1[3] = 83 IK2[3] = 6e IK3[3] = 01
IK1[4] = 2f IK2[4] = 02 IK3[4] = 01
IK1[5] = 1a IK2[5] = a4 IK3[5] = 01
IK1[6] = 51 IK2[6] = ab IK3[6] = 01
IK1[7] = 9b IK2[7] = f8 IK3[7] = 01
分别以 IK1, IK2, IK3 为 KEY 对 NONCE = '6b4ba459beedb768' 进行 DES 加密则可得到 EN1, EN2, EN3:
EN1 = '6aeb443da7d6243c'
EN2 = '781b33ca6efaff4a'
EN3 = '208d2e7dea6f481a'
EN1 就是 LMRESP[0..7], EN2 就是 LMRESP[8..15], EN3 就是 LMRESP[16..23], 这样就可以得到 LMRESP[24]:
计算出 LMRESP 后再来看看 NTRESP 是怎么计算出来的?
将 PASSWORD = '1qaz2wsx3WSX' 展成 16 进位字串 '3171617a3277737833575358', 在每一位后递补一个 \x00, 于是新形成的 PW = '3100710061007a0032007700730078003300570053005800', PW 经过 MD4 加密后可得 PWMD4 = '3e1d8884fa3bffb0f8cadd475428e47f', 这在介绍本章稍早前算过, 不再赘述, 与计算 LMRESP 类似, 将 PWMD4 后面补上 5 个 \x00, 成为长度 21 的一个基钥, 将其分为三段, 每段 8 Bytes, 则可得 MK1, MK2, Mk3:
MK1[0] = 3e MK2[0] = b0 MK3[0] = e4
MK1[1] = 1d MK2[1] = f8 MK3[1] = 7f
MK1[2] = 88 MK2[2] = ca MK3[2] = 00
MK1[3] = 84 MK2[3] = dd MK3[3] = 00
MK1[4] = fa MK2[4] = 47 MK3[4] = 00
MK1[5] = 3b MK2[5] = 54 MK3[5] = 00
MK1[6] = ff MK2[6] = 28 MK3[6] = 00
MK1[7] = b0 MK2[7] = e4 MK3[7] = 00
再经过 Z 函数以及 set_odd_parity 之后, 可得 NK1, NK2, NK3, 分别以这三组 KEY 对 NONCE 'ad77b52ebce8f59b' 进行 DES 加密后, 组合起来即可得到 NTRESP[24]:
至此 DES 编码及 MD4 编码已介绍完毕, 下一章节将再回顾 NTLM 的完整的通讯流程.