从代码层面分析域内LM | NTLM Hash的生成过程

admin 2022年7月6日00:41:15评论8 views字数 7034阅读23分26秒阅读模式

"只有继续从容向前,才能不辜负那些在漫漫长夜中依旧褶褶生辉的旧时光"

从代码层面分析域内LM | NTLM Hash的生成过程

本文作者:蛇皮怪怪


概述

本文章主要是从代码实现的角度,来对lm | Ntlm Hash的生成过程进行理解

关于lm hash

LM Hash的全名为"LAN Manager Hash" 是微软为了提高windows操作系统的安全性而采用的散列加密算法,其本质是DES加密,尽管LM Hash比较容易被破解,但是为了保证系统的兼容性,Windows只是将LM Hash禁用了(从windows vista和windows server 2008版本开始,windows默认禁用LM Hash)LM hash明文密码被限定在14位以内,也就是说,如果要停止使用LM Hash,将用户的密码设置至14位以上即可,如果LM Hash被禁用了,攻击者通过工具抓取的LM Hash通常为"ad3435b51404eead3b435b51404ee"(表示LM Hash为空值或被禁用)

生成步骤如下

- 用户的密码被限制为最多14个字符。- 用户的密码转换为大写。- 密码转换为16进制字符串,不足14字节将会用0来再后面补全。- 密码的16进制字符串被分成两个7byte部分。每部分转换成比特流,并且长度位56bit,长度不足使用0在左边补齐长度,再分7bit为一组末尾加0,组成新的编码(str_to_key()函数处理)- 上步骤得到的8byte二组,分别作为DES key为"KGS!@#$%"进行加密。- 将二组DES加密后的编码拼接,得到最终LM HASH值。

实现代码如下(C++)

#include <iostream>#include <string>#include <openssl/des.h>
using namespace std;
#pragma comment(lib,"libssl.lib")#pragma comment(lib,"libcrypto.lib")
class SecretToLMHash {  string operationString;//待操作字符串  string operationStringHex;//待操作字符串十六进制格式  string leftoperationString;//补零操作时,用于记录拆分开的字符串  string rightoperationString;
  string cLeftoperationString;//密文形式  string cRightoperationString;

  string binLeftoperationString;//补零操作时,用于记录拆分开的字符串的二进制形式  string binRightoperationString;
  string hexChar[16] = { "0","1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F" };

public:  //没有功能的构造函数,可直接无视  SecretToLMHash();
  //设置明文字符  bool setSecret(string secret);
  //检查密码长度  bool checkLength(string secret);    //将小写转换为大写  void lowercaseToUppercase();
  //将密码转化为十六进制字符串  string toHexString(unsigned char x);
  //补零操作  void zeroPadding();
  //操作字符串拆分  void operationStringBreak();
  //将十六进制字符串转换为二进制bit流,并为二进制字符串补零  string binZeroPadding(string hex);
  //将二进制转换为十六进制字符串  string binToHex(string bin);
  //进行des加密 key为“KGS!@#$%”  string desEncryption(string s);
  //获取LMHash结果  string getLMHash();
};
SecretToLMHash::SecretToLMHash() {
}
//设置明文字符bool SecretToLMHash::setSecret(string secret) {  if (!checkLength(secret)) {//长度超过了14字符    cout << "明文长度超过14字符,请重新输入" << endl;    return false;  }  this->operationString = secret;//设置明文信息  return true;}
//检查密码长度bool SecretToLMHash::checkLength(string secret) {  if (secret.length() > 14) {    return false;  }  else {    return true;  }}
//将小写转换为大写void SecretToLMHash::lowercaseToUppercase() {  for (unsigned int i = 0; i < operationString.length(); i++) {    operationString[i] = toupper(operationString[i]);  }}
//将密码转化为十六进制字符串string SecretToLMHash::toHexString(unsigned char x) {  return hexChar[x/ 16] + hexChar[x % 16];}
//补零操作void SecretToLMHash::zeroPadding() {  int i = 28 - operationStringHex.length();
  while (i) {    operationStringHex = operationStringHex + "0";    i--;  }}
//操作字符串拆分void SecretToLMHash::operationStringBreak() {  for (int i = 0; i < 14; i++) {    leftoperationString = leftoperationString + operationStringHex[i];  }
  for (int i = 14; i < 28; i++) {    rightoperationString = rightoperationString + operationStringHex[i];  }}
//将十六进制字符串转换为二进制bit流,并为二进制字符串补零string SecretToLMHash::binZeroPadding(string hex) {
  string tempString;
  string hexToBin[16] = {    "0000","0001","0010","0011","0100","0101","0110","0111","1000","1001","1010","1011","1100","1101","1110","1111"  };
  for (unsigned int i = 0; i < hex.length(); i++) {
    if (hex[i] >= '0' && hex[i] <= '9') {      tempString = tempString + hexToBin[(char)hex[i] - '0'];    }    else {      tempString = tempString + hexToBin[(char)hex[i] - 'A' + 10];    }  }
    hex = "";
  for (unsigned int i = 0; i < tempString.length(); i++) {    if (i % 7 == 0 && i) {      hex = hex + "0";    }    hex = hex + tempString[i];  }  hex = hex + "0";  //cout << hex << endl;  return hex;}
//将二进制转换为十六进制字符串string SecretToLMHash::binToHex(string bin) {  int index = 0;//记录在四位二进制的第几个位置  int tempHex = 0;//记录四位二进制的十进制值  bool flag = false;//第一次进入循环时,不需要进行转换的拼接
  string result;//记录转换的结果    for (unsigned int i = 0; i < bin.length(); i++) {
    if (index == 0) {      if (flag) {        result = result + hexChar[tempHex];        tempHex = 0;      }else {        flag = true;      }    }
    switch (index) {    case 0: {      tempHex = tempHex + (bin[i] - '0') * 8;      break;    }    case 1: {      tempHex = tempHex + (bin[i] - '0') * 4;      break;    }    case 2: {      tempHex = tempHex + (int)(bin[i] - '0') * 2;      break;    }    case 3: {      tempHex = tempHex + (int)(bin[i] - '0') * 1;      break;    }    }
    index++;    index = index % 4;  }    result = result + hexChar[tempHex];//将最后四位的十六进制加上  return result;}
//进行des加密 key为“KGS!@#$%”string SecretToLMHash::desEncryption(string s) {  return s;}
//获取LMHash结果string SecretToLMHash::getLMHash() {  //明文全部大写  lowercaseToUppercase();
  //cout << operationString << endl;
  //将明文转换为十六进制  for (unsigned int i = 0; i < operationString.length(); i++) {    operationStringHex = operationStringHex + toHexString((char)operationString[i]);  }
  //为十六进制字符串补零  zeroPadding();
  //cout << operationStringHex << endl;
  //将十六进制字符串拆分  operationStringBreak();
  //cout << leftoperationString << endl;  //cout << rightoperationString << endl;
  //将十六进制字符串转换为二进制bit流,并为二进制字符串补零  binLeftoperationString = binZeroPadding(leftoperationString);  binRightoperationString = binZeroPadding(rightoperationString);    //cout << leftoperationString << endl;  //cout << rightoperationString << endl;
  leftoperationString = binToHex(binLeftoperationString);  rightoperationString = binToHex(binRightoperationString);
  //cout << leftoperationString << endl;  //cout << rightoperationString << endl;
  cLeftoperationString = desEncryption(leftoperationString);  cRightoperationString = desEncryption(rightoperationString);

  return cLeftoperationString + "   " + cRightoperationString;}

int main() {  SecretToLMHash x;
  x.setSecret("WELCOME");
  cout << x.getLMHash() << endl;    return 0;}

实现缺陷

  • 预计使用openssl库中的des函数,对最后一步进行加密处理

  • 但是经过测试发现,openssl库中的des加密算法与生成mlHash的des加密算法不同,预估是置换表不同

  • 经过大量查找,还是未找到对应的C++ des库与之对应

  • 因此最后一个步骤需要用其他软件来补齐(des计算器)

    • 明文:4B47532140232425(KGS!@#$%)

    • 秘钥:程序计算的结果

从代码层面分析域内LM | NTLM Hash的生成过程

总结

  • lmHash在计算过程中,将小写全部转换为大写,造成了一个lmHash值对应多个明文密码,hello与HELLO所产生的lmHash相同。减少了密码碰撞的强度

  • 计算过程限制了密码的长度,密码只能14个字符,超过14个字符将突破lmHash的计算规则

  • lmHash应用范围

    • 个人版从Windows Vista以前

    • 服务器版从Wndows Sever2003以前

NTLM Hash生成步骤如下

  • 转化为ASCII字符串  

  • 转换为十六进制字符串    

  • 转化为Unicode字符串    

  • 使用MD4消息摘要算法  

实现代码如下

#include <iostream>#include <string>#include <openssl/md4.h>
using namespace std;
#pragma comment(lib,"libssl.lib")#pragma comment(lib,"libcrypto.lib")
#include <iostream>#include <string>#include <openssl/des.h>
using namespace std;
#pragma comment(lib,"libssl.lib")#pragma comment(lib,"libcrypto.lib")
class SecretToNTLMHash {  string operationString;//待操作字符串  string operationStringHex;//待操作字符串十六进制格式  string operationStringUnicode;//待操作字符串Unicode格式  unsigned char result[128] = {"�"};  string hexChar[16] = { "0","1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F" };

public:  //没有功能的构造函数,可直接无视  SecretToNTLMHash();
  //设置明文字符  bool setSecret(string secret);

  //将密码转化为十六进制字符串  string toHexString(unsigned char x);
  //补零操作  void zeroPadding();
  //获取结果  unsigned char * getNTLMHash();
};
SecretToNTLMHash::SecretToNTLMHash() {
}
//设置明文字符bool SecretToNTLMHash::setSecret(string secret) {  this->operationString = secret;//设置明文信息  return true;}
//将密码转化为十六进制字符串string SecretToNTLMHash::toHexString(unsigned char x) {  return hexChar[x / 16] + hexChar[x % 16];}
//补零操作void SecretToNTLMHash::zeroPadding() {
  for (int i = 0; i < operationStringHex.length(); i += 2) {    operationStringUnicode = operationStringUnicode + operationStringHex[i] + operationStringHex[i + 1] + "00";  }}
//获取结果unsigned char * SecretToNTLMHash::getNTLMHash() {
  for (int i = 0; i < operationString.length(); i++) {    operationStringHex = operationStringHex + toHexString(operationString[i]);  }
  zeroPadding();
  //cout << operationStringUnicode << endl;
  MD4((unsigned char *)(operationStringUnicode.c_str()), operationStringUnicode.length(), result);  return result;}
int main() {  SecretToNTLMHash op;  op.setSecret("123456");  cout << op.getNTLMHash() << endl;  return 0;}

实现缺陷

  • 预计使用openssl库中的md5函数,对最后一步进行加密处理

  • 但是经过测试发现,openssl库中的md5加密算法与生成ntlmHash的md5加密算法也不同

  • 因此,上述代码仅作为对ntlmHash生成过程的深刻理解,不可用于ntlmHash的爆破

总结

  • ntlmHash应用范围

    • 个人版从Windows Vista以后

    • 服务器版从Wndows Sever2003以后

原文始发于微信公众号(0x00实验室):从代码层面分析域内LM | NTLM Hash的生成过程

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年7月6日00:41:15
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   从代码层面分析域内LM | NTLM Hash的生成过程https://cn-sec.com/archives/1160643.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息