$ sudo cat /etc/shadow | grep test test:$6$sT8RMKkg$QLhvrR/iSwurIM0Z0.ZVkxPKRvKXueuAx7fZzq5Umffm3ydNbeGujV7mUKnTNYtbpLIbIdJ2.Q4Spw9OUjhl91:16502:0:99999:7:::
其格式爲:linux
{用戶名}:{加密後的口令密碼}:{口令最後修改時間距原點(1970-1-1)的天數}:{口令最小修改間隔(防止修改口令,若是時限未到,將恢復至舊口令):{口令最大修改間隔}:{口令失效前的警告天數}:{帳戶不活動天數}:{帳號失效天數}:{保留}算法
【注】:shadow文件爲可讀文件,普通用戶沒有讀寫權限,超級用戶擁有讀寫權限。若是密碼字符串爲*,則表示系統用戶不能被登入;若是字符串爲!,則表示用戶名被禁用;若是字符串爲空,則表示沒有密碼。咱們能夠使用passwd –d 用戶名 清空一個用戶的口令密碼。函數
對於示例的密碼域$6$sT8RMKkg$QLhvrR/iSwurIM0Z0.ZVkxPKRvKXueuAx7fZzq5Umffm3ydNbeGujV7mUKnTNYtbpLIbIdJ2.Q4Spw9OUjhl91,咱們參考了linux標準源文件passwd.c,在其中的pw_encrypt函數中找到了加密方法。測試
咱們發現所謂的加密算法,其實就是用明文密碼和一個叫salt的東西經過函數crypt()完成加密。編碼
而所謂的密碼域密文也是由三部分組成的,即:$id$salt$encrypted。加密
【注】: id爲1時,採用md5進行加密;id爲5時,採用SHA256進行加密;id爲6時,採用SHA512進行加密。spa
1). 頭文件:指針
#define _XOPEN_SOURCEcode
#include <unistd.h>md5
2). 函數原型:
char *crypt(const char *key, const char *salt);
3). 函數說明:
crypt()將使用DES演算法將參數key所指的字符串加以 編碼,key字符串長度僅取前8個字符,超過此長度的字符沒有意義。參數salt爲兩個字符組成的字符串,由a-z、A-Z、0-9,’.’和’/’所組 成,用來決定使用4096種不一樣內建表格的哪種。函數執行成功後會返回指向編碼過的字符串指針,參數key所指向的字符串不會有所改動。編碼過的字符串 長度爲13個字符,前兩個字符爲參數salt表明的字符串。
4). 返回值:返回一個指向以'\0'結尾的密碼字符串
5). 附加說明:使用GCC編譯時須要加上 –lcrypt
在咱們的示例密碼域中salt爲sT8RMKkg,那麼它又是如何來的?
咱們仍是從標準源文件passwd.c中查找答案。在passwd.c中,咱們找到了與salt相關的函數crypt_make_salt。
在函數crypt_make_salt中出現了不少的判斷條件來選擇以何種方式加密(經過id值來判斷),但其中對咱們最重要的一條語句是gensalt(salt_len)。
咱們繼續查看了函數static char *gensalt (unsigned int salt_size),才發現原來神祕無比的salt參數只是某個固定長度的隨機字符串而已。
在咱們每次改寫密碼時,都會隨機生成一個這樣的salt。咱們登陸時輸入的明文密碼通過上述的演化後與shadow裏的密碼域進行字符串比較,以此來判斷是否容許用戶登陸。
【注】:通過上述的分析,咱們發現破解linux下的口令也不是什麼難事,但前提是你有機會拿到對方的shadow文件。
#include <stdio.h> #include <string.h> #include <unistd.h> #include <shadow.h> #include <crypt.h> #include <errno.h> int main(int argc, char *argv[]) { int i, j; char salt[16]; char crypt_char[128]; struct spwd *shd; /* 參數檢查 */ if (argc != 3) { fprintf(stderr, "./passwd USERNAME PASSWORD\n"); return -1; } /* 訪問shadow文件,參數爲所指定的用戶名 */ shd = getspnam(argv[1]); if (shd == NULL) { printf("Error: %s\n", strerror(errno)); return 0; } strcpy(crypt_char, shd->sp_pwdp); i = 0, j = 0; /* 讀到第三個'$'符號*/ while ((crypt_char[i] != '\0') && (j != 3)) { salt[i] = crypt_char[i]; if (crypt_char[i] == '$') { j++; } i++; } salt[i] = '\0'; printf("salt:\n%s\n", salt); printf("crypt_pw:\n%s\n", shd->sp_pwdp); printf("------------------count ----------------\n"); printf("salt:\n%s\n", salt); printf("crypt_pw:\n%s\n", crypt(argv[2], salt)); return 0; }
編譯並運行
$ gcc passwd.c -o passwd -lcrypt $ sudo ./passwd test shadow1234 salt: $6$sT8RMKkg$ crypt_pw: $6$sT8RMKkg$QLhvrR/iSwurIM0Z0.ZVkxPKRvKXueuAx7fZzq5Umffm3ydNbeGujV7mUKnTNYtbpLIbIdJ2.Q4Spw9OUjhl91 ------------------count ---------------- salt: $6$sT8RMKkg$ crypt_pw: $6$sT8RMKkg$QLhvrR/iSwurIM0Z0.ZVkxPKRvKXueuAx7fZzq5Umffm3ydNbeGujV7mUKnTNYtbpLIbIdJ2.Q4Spw9OUjhl91