วันนี้เราจะมาทำความเข้าใจเกี่ยวกับรูปแบบการเก็บ password ของ Windows โดยแต่ก่อนจนถึงปัจจุบันก็มีพัฒนาการมาเรื่อยๆครับ ซึ่งจะเริ่มจาก
LM (Lan Manager) hash
โดย LM นั้นเป็นรูปแบบดั้งเดิมในการเก็บ password ของ Windows ตั้งแต่ยุค 1980 ซึ่งในช่วงนั้นยังมีจำนวน charset ที่ยังจำกัดอยู่(16-bits characters) ซึ่งทำให้การ crack password นั้นทำได้ง่ายมากโดยดึงจาก SAM database บน Windows หรือว่า NTDS บน Domain Controller (Active Directory) ได้เลย
โดยขั้นตอนการเปลี่ยน password อยู่ในรูปแบบ LM hash คือ
- เปลี่ยนอักษรทั้งหมดเป็นตัวใหญ่
- หากตัวอักษรไม่ครบ 14 ตัวอักษรก็จะเติมตัวอักษรทั้งหมดให้เต็มด้วย NULL characters
- แบ่งเป็น 2 กลุ่ม กลุ่มละ 7 ตัวอักษร
- สร้าง DES key จาก character 7 ตัวทั้ง 2 กลุ่ม ก็จะได้ DES key 2 ชุด (ชุดละ 64 bit)
- นำ DES key ไปเข้ารหัส static string “KGS!@#$%” ด้วย DES (ECB)
- นำ encrypted strings ทั้ง 2 อันมาต่อกัน ก็จะได้เป็น LM Hash
เช่น สมมติ password เป็น password
- password => password000000
- PASSWORD000000
- PASSWOR, D000000
- นำ PASSWOR ซึ่งเป็น 56bit ไปแปลงเป็น 64bit โดยการใส่ parity bit เข้าไปต่อท้ายทุกๆ byte
- DES Key ที่ได้จะเป็น
- 0101000000100000010101000110101000110101101110100011110110100100111000001000001101110000101000011111010101010101
- 0100010000011000000011000000011000000011100000011100000001100000000100000000010011010000101111100001001101010110
- DES Key ที่ได้จะเป็น
- นำ DES Key ไปเข้ารหัส string “KGS!@#$%” โดยใช้ Algorithm ECB จะได้เป็น
- e52cac67419a9a22
- 4a3b108f3fa6cb6d
- ผลลัพธ์จะได้เป็น e52cac67419a9a224a3b108f3fa6cb6d

ตัวอย่าง source code สำหรับการเข้ารหัส LM Hash
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
#include <stdint.h> #include <inttypes.h> #include <openssl/des.h> #include <string.h> #include <ctype.h> static void password_to_key(const uint8_t password[7], uint8_t key[8]) { /* make room for parity bits */ key[0] = (password[0] >> 0); key[1] = ((password[0]) << 7) | (password[1] >> 1); key[2] = ((password[1]) << 6) | (password[2] >> 2); key[3] = ((password[2]) << 5) | (password[3] >> 3); key[4] = ((password[3]) << 4) | (password[4] >> 4); key[5] = ((password[4]) << 3) | (password[5] >> 5); key[6] = ((password[5]) << 2) | (password[6] >> 6); key[7] = ((password[6]) << 1); } char* stringToBinary(char* s) { if(s == NULL) return 0; /* no input string */ size_t len = strlen(s); char *binary = malloc(len*8 + 1); // each char is one byte (8 bits) and + 1 at the end for null terminator binary[0] = '\0'; for(size_t i = 0; i < len; ++i) { char ch = s[i]; for(int j = 7; j >= 0; --j){ if(ch & (1 << j)) { strcat(binary,"1"); } else { strcat(binary,"0"); } } } return binary; } static void des(const uint8_t password[7], const uint8_t data[8], uint8_t result[]) { DES_cblock key; DES_key_schedule schedule; password_to_key(password, key); DES_set_odd_parity(&key); DES_set_key_unchecked(&key, &schedule); DES_ecb_encrypt((DES_cblock*)data, (DES_cblock*)result, &schedule, DES_ENCRYPT); } int main() { char result[16]; size_t i; uint8_t password1[7]; uint8_t password2[7]; uint8_t kgs[] = "KGS!@#$%"; uint8_t hash1[8]; uint8_t hash2[8]; char password[]="password"; /* Initialize passwords to NULLs. */ memset(password1, 0, 7); memset(password2, 0, 7); /* Copy passwords over, convert to uppercase, they're automatically padded with NULLs. */ for(i = 0; i < 7; i++) { if(i < strlen(password)) password1[i] = toupper(password[i]); if(i + 7 < strlen(password)) password2[i] = toupper(password[i + 7]); } /* Do the encryption. */ des(password1, kgs, hash1); des(password2, kgs, hash2); /* Copy the result to the return parameter. */ memcpy(result + 0, hash1, 8); memcpy(result + 8, hash2, 8); //printf("Hash is %s\n",result); printf("Hash is "); for (size_t i = 0; i < sizeof(result); ++i) printf("%02x", result[i]); return 0; } |
1 2 3 4 5 |
gcc -o test password_key.c -lssl -lcrypto ./test Hash1 is e52cac67419a9a22 Hash2 is 4a3b108f3fa6cb6d Hash is ffffffe52cffffffac6741ffffff9affffff9a224a3b10ffffff8f3fffffffa6ffffffcb6d |
วันนี้เอาเริ่มต้นแค่นี้ก่อน เดี๋ยวมาต่อกันเรื่อง NTLM และ NTLMv2 ต่ออีกทีครับ
ส่วนวิธีการ crack นั้นในที่นี้จะใช้ John-The-Ripper, Hashcat ครับ โดยใช้คำสั่งเป็น
1 2 |
john --format=lm hash.txt hashcat -m 3000 -a 3 hash.txt |
Source::
- https://blog.skullsecurity.org/2008/lanman-and-ntlm-not-as-complex-as-you-think
- https://www.slideshare.net/AnkitMehta24/kerberos-ntlm-and-lmhash
- https://www.hacking-lab.com/misc/downloads/event_2010/daniel_stirnimann_pass_the_hash_attack.pdf