shadow文件中密碼的加密方式

1. 查看shadow文件中關於用戶test的內容

$ sudo cat /etc/shadow | grep test
test:$6$sT8RMKkg$QLhvrR/iSwurIM0Z0.ZVkxPKRvKXueuAx7fZzq5Umffm3ydNbeGujV7mUKnTNYtbpLIbIdJ2.Q4Spw9OUjhl91:16502:0:99999:7:::

其格式爲:linux

        {用戶名}:{加密後的口令密碼}:{口令最後修改時間距原點(1970-1-1)的天數}:{口令最小修改間隔(防止修改口令,若是時限未到,將恢復至舊口令):{口令最大修改間隔}:{口令失效前的警告天數}:{帳戶不活動天數}:{帳號失效天數}:{保留}算法

        【注】:shadow文件爲可讀文件,普通用戶沒有讀寫權限,超級用戶擁有讀寫權限。若是密碼字符串爲*,則表示系統用戶不能被登入;若是字符串爲!,則表示用戶名被禁用;若是字符串爲空,則表示沒有密碼。咱們能夠使用passwd –d 用戶名 清空一個用戶的口令密碼。函數

2. 解析shadow文件中密碼字符串的內容

        對於示例的密碼域$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

3. 數據加密函數crypt()講解

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

4. 加密參數salt的由來

        在咱們的示例密碼域中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參數只是某個固定長度的隨機字符串而已。

5. 最終結論

        在咱們每次改寫密碼時,都會隨機生成一個這樣的salt。咱們登陸時輸入的明文密碼通過上述的演化後與shadow裏的密碼域進行字符串比較,以此來判斷是否容許用戶登陸。

        【注】:通過上述的分析,咱們發現破解linux下的口令也不是什麼難事,但前提是你有機會拿到對方的shadow文件。

6. 示例代碼(測試代碼):

#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
相關文章
相關標籤/搜索