Chinaunix首页 | 论坛 | 博客
  • 博客访问: 327020
  • 博文数量: 81
  • 博客积分: 1810
  • 博客等级: 上尉
  • 技术积分: 725
  • 用 户 组: 普通用户
  • 注册时间: 2008-05-25 17:38
文章分类

全部博文(81)

文章存档

2016年(4)

2015年(11)

2014年(16)

2013年(37)

2012年(11)

2011年(2)

我的朋友

分类: LINUX

2014-07-30 15:00:29

http://linfengdu.blog.163.com/blog/static/1177107320119209614584/
linux的PAM认证和shadow文件中密码的加密方式
2010年09月27日 14:21
PAM是个什么东西,词典上没有。
放狗查了下,这是个简写,全称是:
Pluggable Authentication Modules,中文叫“可插入认证模块”。
它是一种统一的认证方案。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中的密码加密问题:

还是以上面的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的帮助:

可发现原来crypt密文里是由三部分组成的,即:$id$salt$encrypted
目前常用的是当id为1时,使用md5加密,id为5,采用SHA256进行加密,id为6采用SHA512进行加密。
分析上面的函数,可看出我们的shadow密码中,直接把$id$salt$encrypted 当做salt参数带入进行crypt加密。
那好,我们可以写个简短的代码进行试验:
#include
#include
#include
#include
#include
#include
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文件,这个前提条件貌似很难。
本文只是篇技术分析文章,没有其它任何初衷。


http://colinzhouyj.blog.51cto.com/2265679/790981
/etc/shadow中的root信息:
root:$1$nnXLnQUR$eqQTkzEx/xP2yvVDyb6jO.:15225:0:99999:7:::
 
其中,”$1$nnXLnQUR$eqQTkzEx/xP2yvVDyb6jO.” 就是加密后的root密码,我们在修改用户密码的时候都使用passwd命令,其实也可以直接修改/etc/shadow文件。那要面临的问题就是如何生成这个加密过的shadow密码($1$ 表示加密算法是基于MD5的,不同发行版本默认的加密算法可能不同)。
方法一(交互式)
[root@centos-vb2 ~]# openssl passwd -1 -salt $(< /dev/urandom tr -dc '[:alnum:]' | head -c 32)
Password:    # input your password here
$1$MZrDxgEw$i2XBgmDMo0Wf1.OVvOGGQ1
 
方法二(适用于脚本)
[root@centos-vb2 ~]# echo "xansun" | openssl passwd -1 -salt $(< /dev/urandom tr -dc '[:alnum:]' | head -c 32) -stdin
$1$KsRJO8kG$M9co4G7T6.5KcITsSCRNS/
阅读(4673) | 评论(0) | 转发(0) |
0

上一篇:X-macro

下一篇:MII接口全家福

给主人留下些什么吧!~~