您的位置:首页 > 运维架构 > Linux

shadow文件中密码的加密方式

2015-03-11 15:56 417 查看
摘要: shadow文件中密码的加密方式(测试的操作系统为Ubuntu 14.04 64位)

1. 查看shadow文件中关于用户test的内容

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

其格式为:

{用户名}:{加密后的口令密码}:{口令最后修改时间距原点(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进行加密。

3. 数据加密函数crypt()讲解

1). 头文件:

#define _XOPEN_SOURCE

#include <unistd.h>

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. 示例代码(测试代码):

[code=plain]#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;
}

编译并运行

[code=plain]$ 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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Linux shadow