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

linux的PAM认证和shadow文件中密码的加密方式

2012-09-28 12:43 399 查看
PAM全称是:Pluggable Authentication Modules,中文叫“可插入认证模块”。

它是一种统一的认证方案。PAM 让您能随时改变您的认证方法以及需求,并且不需要重新编译任何代码就封装了所有本地认证方法。具体见
PAM 网站。
对于 PAM 您只需要做:

对您的密码采用不同于 DES 的加密方式(让它们面对暴力解码(brute-force decode)时更为坚固)。

对您所有用户使用资源限额,以防止他们进行拒绝服务(denial-of-service)攻击(进程数、占用内存量等等)。

随时启用 shadow password (见下文)

只在特定的时间允许特定的用户从特定的地方登录

上述介绍来源于http://man.ddvip.com/linux/Mandrakelinux10server/password-security.html

========================================

我们看看shadow文件的格式:

root@localhost:~# cat /etc/shadow

root:$1$Bg1H/4mz$X89TqH7tpi9dX1B9j5YsF.:14838:0:99999:7:::

daemon:*:14838:0:99999:7:::

bin:*:14838:0:99999:7:::

sys:*:14838:0:99999:7:::

sync:*:14838:0:99999:7:::

games:*:14838:0:99999:7:::

man:*:14838:0:99999:7:::

lp:*:14838:0:99999:7:::

mail:*:14838:0:99999:7:::

news:*:14838:0:99999:7:::

uucp:*:14838:0:99999:7:::

proxy:*:14838:0:99999:7:::

www-data:*:14838:0:99999:7:::

backup:*:14838:0:99999:7:::

list:*:14838:0:99999:7:::

irc:*:14838:0:99999:7:::

gnats:*:14838:0:99999:7:::

nobody:*:14838:0:99999:7:::

libuuid:!:14838:0:99999:7:::

Debian-exim:!:14838:0:99999:7:::

statd:*:14838:0:99999:7:::

sshd:*:14838:0:99999:7:::

test::14879:0:99999:7:::

格式是:

{用户名}:{加密后的密码字符串}:{口令最后修改时间距1970年1月1日的天数}:{ 口令能被修改之前的天数(防止修改口令,然后立刻将它改回到老口令)}:{口令必须被修改之后的天数}:{口令期满前的天数}:{口令期满后的天数}:{保留}

shadow是个可读文件,普通用户没有读写权限,超级用户权限为可读写。

如果密码字符串为*,表示系统用户不能被登入,为!表示用户名被禁用,如果密码字符串为空,表示没有密码,通过passwd -d 用户名 可以清空一个用户的口令。

具体shadow可参考shadow how-to,尽管这是一份已经过时了的文档。

下面探讨下shadow中的密码加密问题:

还是以上面的root用户为例:

root:$1$Bg1H/4mz$X89TqH7tpi9dX1B9j5YsF.:14838:0:99999:7:::

其中的密码域为$1$Bg1H/4mz$X89TqH7tpi9dX1B9j5YsF. ,参考linux标准的passwd.c源文件,在pw_encrypt函数中可以找到加密方法:

40 char *pw_encrypt (const char *clear, const char *salt)

41 {

42 static char cipher[128];

43 char *cp;

44

45 cp = crypt (clear, salt);

46 if (!cp) {

47 /*

48 * Single Unix Spec: crypt() may return a null pointer,

49 * and set errno to indicate an error. The caller doesn't

50 * expect us to return NULL, so...

51 */

52 perror ("crypt");

53 exit (1);

54 }

55

56 /* The GNU crypt does not return NULL if the algorithm is not

57 * supported, and return a DES encrypted password. */

58 if (salt && salt[0] == '$' && strlen (cp) <= 13)

59 {

..........

79 fprintf (stderr,

80 _("crypt method not supported by libcrypt? (%s)\n"),

81 method);

82 exit (1);

83 }

84

85 if (strlen (cp) != 13)

86 return cp; /* nonstandard crypt() in libc, better bail out */

87 strcpy (cipher, cp);

88

89 return cipher;

90 }

也就是说加密用明文密码和一个叫salt的东西用crypt()加密生成密文。

再来看看crypt的帮助:

http://www.kernel.org/doc/man-pages/online/pages/man3/crypt.3.html

可发现原来crypt密文里是由三部分组成的,即:$id$salt$encrypted

目前常用的是当id为1时,使用md5加密,id为5,采用SHA256进行加密,id为6采用SHA512进行加密。

分析上面的函数,可看出我们的shadow密码中,直接把$id$salt$encrypted 当做salt参数带入进行crypt加密。

那好,我们可以写个简短的代码进行试验:

#include <pwd.h>

#include <stddef.h>

#include <string.h>

#include <shadow.h>

#include <stdio.h>

#include <unistd.h>

int main(int argc, char *argv[])

{

if(argc < 2)

{

printf("no usrname input");

return 1;

}

if (geteuid() != 0)

fprintf(stderr, "must be setuid root");

struct passwd *pwd;

pwd = getpwnam(argv[1]);

if(pwd ==NULL)

printf("no username found.\n");

else

{

printf("passwd: %s\n", pwd->pw_passwd);

if(strcmp(pwd->pw_passwd, "x") == 0)

{

printf("shadow used.\n");

struct spwd *shd= getspnam(argv[1]);

if(shd != NULL)

{

static char crypt_char[80];

strcpy(crypt_char, shd->sp_pwdp);

char salt[13];

int i=0,j=0;

while(shd->sp_pwdp[i]!='\0'){

salt[i]=shd->sp_pwdp[i];

if(salt[i]=='$'){

j++;

if(j==3){

salt[i+1]='\0';

break;

}

}

i++;

}

if(j<3)perror("file error or user cannot use.");

if(argc==3)

printf("salt: %s, crypt: %s\n", salt, crypt(argv[2], salt));

printf("shadowd passwd: %s\n", shd->sp_pwdp);

}

}

}

return 0;

}

保存后执行gcc passwd.c -lcrypt -o passwd

编译成功后运行./passwd root 123

其中./passwd是生成的命令,root是我们用来测试的账户,123是测试系统中root用户的密码,执行的结果是:

passwd: x

shadow used.

salt: $1$Bg1H/4mz$, crypt: $1$Bg1H/4mz$X89TqH7tpi9dX1B9j5YsF.

shadowd passwd: $1$Bg1H/4mz$X89TqH7tpi9dX1B9j5YsF.

可见,我们得到了和系统shadow文件下相同结果的密文。

根据我们的运行结果我们可以看到,在root用户的shadow中,他的salt是$1$Bg1H/4mz$

我们之前也是用这个salt来进行加密匹配的。但是,问题是:这个salt到底是怎么来的??

还是分析标准的passwd.c,

在passwd.c中,找到了生成salt的函数:crypt_make_salt

201 char *crypt_make_salt (const char *meth, void *arg)

202 {

203 /* Max result size for the SHA methods:

204 * +3 $5$

205 * +17 rounds=999999999$

206 * +16 salt

207 * +1 \0

208 */

209 static char result[40];

210 size_t salt_len = 8;

211 const char *method;

212

213 result[0] = '\0';

214

215 if (NULL != meth)

216 method = meth;

217 else {

218 if ((method = getdef_str ("ENCRYPT_METHOD")) == NULL)

219 method = getdef_bool ("MD5_CRYPT_ENAB") ? "MD5" : "DES";

220 }

221

222 if (!strcmp (method, "MD5")) {

223 MAGNUM(result, '1');

224 #ifdef USE_SHA_CRYPT

225 } else if (!strcmp (method, "SHA256")) {

226 MAGNUM(result, '5');

227 strcat(result, SHA_salt_rounds((int *)arg));

228 salt_len = SHA_salt_size();

229 } else if (!strcmp (method, "SHA512")) {

230 MAGNUM(result, '6');

231 strcat(result, SHA_salt_rounds((int *)arg));

232 salt_len = SHA_salt_size();

233 #endif

234 } else if (0 != strcmp (method, "DES")) {

235 fprintf (stderr,

236 _("Invalid ENCRYPT_METHOD value: '%s'.\n"

237 "Defaulting to DES.\n"),

238 method);

239 result[0] = '\0';

240 }

241

242 /*

243 * Concatenate a pseudo random salt.

244 */

245 assert (sizeof (result) > strlen (result) + salt_len);

246 strncat (result, gensalt (salt_len),

247 sizeof (result) - strlen (result) - 1);

248

249 return result;

250 }

除了一大段条件判断语句,其实最重要的只有一句gensalt(salt_len)。

看看gensalt的定义:

167

168 static char *gensalt (unsigned int salt_size)

169 {

170 static char salt[32];

171

172 salt[0] = '\0';

173

174 assert (salt_size >= MIN_SALT_SIZE &&

175 salt_size <= MAX_SALT_SIZE);

176 seedRNG ();

177 strcat (salt, l64a (random()));

178 do {

179 strcat (salt, l64a (random()));

180 } while (strlen (salt) < salt_size);

181 salt[salt_size] = '\0';

182

183 return salt;

184 }

哦,原来神秘的salt只是某个的固定长度的随机的可见的字符串而已。

每次改写密码时,都会再随机生成一个这样的salt。而用户登入时,会拿用户登入的明文密码经过上述演示的步骤生成密文后和shadow里的密码域进行比较。

有了上述的分析,要暴利破解linux下的口令也不是什么问题,但前提是你有机会拿到shadow文件,这个前提条件貌似很难。

本文只是篇技术分析文章,没有其它任何初衷。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: