这题我拿到手的是一个压缩文件

首先,解压这个文件我们得到一个文本文档,由于太长,此处就不贴出来了。

看到这么长的一个文件,最后还有一个=号,让我们不禁就会往base64上想,那么我们首先就用base64对其进行解码看看:

base64 -D 2.1xcode.txt > tmp

这个时候,如果电脑智能的话,可以自己识别出这个文件的类型的,如果电脑没有识别出来,我们可以直接用编辑器查看这个文件,我们会发现文件的开头有PNG字样,那么这应该就是个png图像文件了,改下后缀就好了。

得到图像如下:

如此看下来的话,这题其实也不难,也是属于可以秒破的级别。


昨天刚听学长说,这题在扫描这张二维码之后好像给的是个很简单的密码还是什么的来着的,反正也很简单就是了,不过据说题目坑了,一直拿到了答案却不知道是答案,知道最后主办方给提示才搞定。

2.1xcode.zip

bashbug
  

很多人或许对上半年发生的安全问题“心脏流血”(Heartbleed Bug)事件记忆颇深,这两天,又出现了另外一个“毁灭级”的漏洞——Bash软件安全漏洞。这个漏洞由法国GNU/Linux爱好者Stéphane Chazelas所发现。随后,美国电脑紧急应变中心(US-CERT)、红帽以及多家从事安全的公司于周三(北京时间9月24日)发出警告。 关于这个安全漏洞的细节可参看美国政府计算安全的这两个漏洞披露:CVE-2014-6271CVE-2014-7169

这个漏洞其实是非常经典的“注入式攻击”,也就是可以向 bash注入一段命令,从bash1.14 到4.3都存在这样的漏洞。我们先来看一下这个安全问题的症状。

Shellshock (CVE-2014-6271)

下面是一个简单的测试:

$ env VAR='() { :;}; echo Bash is vulnerable!' bash -c "echo Bash Test"

如果你发现上面这个命令在你的bash下有这样的输出,那你就说明你的bash是有漏洞的:

Bash is vulnerable!
Bash Test

简单地看一下,其实就是向环境变量中注入了一段代码 echo Bash is vulnerable。关于其中的原理我会在后面给出。

很快,CVE-2014-6271的官方补丁出来的了——Bash-4.3 Official Patch 25

AfterShock – CVE-2014-7169 (又叫Incomplete fix to Shellshock)

但随后,马上有人在Twitter上发贴——说这是一个不完整的fix,并给出了相关的攻击方法。

也就是下面这段测试代码(注意,其中的sh在linux下等价于bash):

env X='() { (a)=>' sh -c "echo date"; cat echo

上面这段代码运行起来会报错,但是它要的就是报错,报错后会在你在当前目录下生成一个echo的文件,这个文件的内容是一个时间文本。下面是上面 这段命令执行出来的样子。

$ env X='() { (a)=>' sh -c "echo date"; cat echo
 sh: X: line 1: syntax error near unexpected token `='
 sh: X: line 1: `'
 sh: error importing function definition for `X'
 Sat Sep 27 22:06:29 CST 2014

这段测试脚本代码相当的诡异,就像“天书”一样,我会在后面详细说明这段代码的原理。

原理和技术细节

要说清楚这个原理和细节,我们需要从 bash的环境变量开始说起。

bash的环境变量

环境变量大家知道吧,这个不用我普及了吧。环境变量是操作系统运行shell中的变量,很多程序会通过环境变量改变自己的执行行为。在bash中要定义一个环境变量的语法很简单(注:=号的前后不能有空格):

$ var="hello world"

然后你就可以使用这个变量了,比如:echo $var什么的。但是,我们要知道,这个变量只是一个当前shell的“局部变量”,只在当前的shell进程中可以访问,这个shell进程fork出来的进程是访问不到的。

你可以做这样的测试:

 $ var="hello coolshell"
 $ echo $var
 hello coolshell
 $ bash
 $ echo $var

上面的测试中,第三个命令执行了一个bash,也就是开了一个bash的子进程,你就会发现var不能访问了。

为了要让shell的子进程可以访问,我们需要export一下:

$ export var="hello coolshell"

这样,这个环境变量就会在其子进程中可见了。

如果你要查看一下有哪些环境变量可以在子进程中可见(也就是是否被export了),你可使用env命令。不过,env命令也可以用来定义export的环境变量。如下所示:

$ env $var="hello haoel"

有了这些基础知识还不够,我们还要知道一个基础知识——shell的函数。

bash的函数

在bash下定义一个函数很简单,如下所示:

$ foo(){ echo "hello coolshell"; }
 $ foo
 hello coolshell

有了上面的环境变量的基础知识后,你一定会想试试这个函数是否可以在子进程中调用,答案当然是不行的。

$ foo(){ echo "hello coolshell"; }
 $ foo
 hello coolshell
 $ bash
 $ foo
 bash: foo: command not found

你看,和环境变量是一样的,如果要在子进程中可以访问的话,那么,还是一样的,需要export,export有个参数 -f,意思是export一个函数。如:

$ foo(){ echo "hello coolshell"; }
 $ foo
 hello coolshell
 $ export -f foo
 $ bash
 $ foo
 hello coolshell

好了,我讲了这么半天的基础知识,别烦,懂了这些,你才会很容易地理解这两个漏洞是怎么回事。

好,现在要进入正题。

bash的bug

从上面我们可以看到,bash的变量和函数用了一模一样的机制,如果你用env命令看一下export出来的东西,你会看到上面我们定义的变量和函数都在,如下所示(我省略了其它的环境变量):

$ env
 var=hello coolshell
 foo=() { echo "hello coolshell"
 }

原来,都用同样的方式啊——无论是函数还是变量都是变量啊。于是,看都不用看bash的源代码,聪明的黑客就能猜得到——bash判断一个环境变量是不是一个函数,就看它的值是否以”()”开始。于是,一股邪念涌上心头。

黑客定义了这样的环境变量(注:() 和 { 间的空格不能少):

$ export X='() { echo "inside X"; }; echo "outside X";'

env一下,你会看到X已经在了:

$ env
 X=(){ echo "inside X"; }; echo "outside X";

然后,当我们在当前的bash shell进程下产生一个bash的子进程时,新的子进程会读取父进程的所有export的环境变量,并复制到自己的进程空间中,很明显,上面的X变量的函数的后面还注入了一条命令:echo “outside X”,这会在父进程向子进程复制的过程中被执行吗?(关于fork相关的东西你可以看一下我以前写的《fork的一个面试题》)

答案是肯定的。

$ export X='() { echo "inside X"; }; echo "outside X";'
 $ bash
 outside X

你看,一个代码注入就这样完成了。这就是bash的bug—— 函数体外面的代码被默认地执行了

我们并不一定非要像上面那样创建另一个bash的子进程,我们可以使用bash -c的参数来执行一个bash子进程命令。就像这个安全漏洞的测试脚本一样:

env VAR='() { :;}; echo Bash is vulnerable!' bash -c "echo Bash Test"

其中,() { :;} 中的冒号就相当于/bin/true,返回true并退出。而bash -c其实就是在spawn一个bash的echo的子进程,用于触发函数体外的echo命令。所以,更为友好一点的测试脚本应该是:

env VAR='() { :;}; echo Bash is vulnerable!' bash -c "echo 如果你看到了vulnerable字样说明你的bash有安全问题"

OK,你应该明白这个漏洞是怎么一回事了吧。

bash漏洞的影响有多大

在网上看到好多人说这个漏洞不大,还说这个事只有那些陈旧的执行CGI脚本的网站才会有,现在已经没有网站用CGI了。我靠,这真是无知者无畏啊。

我举个例子,如果你的网站中有调用操作系统的shell命令,比如你用PHP执行个exec之类的东西。这样的需求是有的,特别是对于一些需要和操作系统交互的重要的后台用于系统管理的程序。于是就会开一个bash的进程来执行。

我们还知道,现在的HTTP服务器基本上都是以子进程式的,所以,其中必然会存在export 一些环境变量的事,而有的环境变量的值是从用户端来的,比如:HTTP_USER_AGENT这样的环境变量,只由浏览器发出的。其实这个变量你想写成什么就写成什么。

于是,我可以把这个HTTP_USER_AGENT的环境变量设置成上述的测试脚本,只不过,我会把echo Bash is vulnerable!这个东西换成别的更为凶残的命令。呵呵。

关于这个漏洞会影响哪些已有的系统,你可以自己Google,几乎所有的报告这个漏洞的文章都说了(比如:这篇这篇),我这里就不复述了。

注:如果你要看看你的网站有没有这样的问题,你可以用这个在线工具测试一下:‘ShellShock’ Bash Vulnerability CVE-2014-6271 Test Tool

现在,你知道这事可能会很大了吧。还不赶快去打补丁。(注,yum update bash 把bash版本升级到 4.1.2-15.el6_5.2 , 但是这个版本还没有fix CVE-2014-7169,载止本文发布之时,目前还没有正式的CVE-2014-7169的补丁,你可以关注Redhat的官方关于CVE-2014-7169 的 ticket

关于 AfterShock – CVE-2014-7169 测试脚本的解释

很多同学没有看懂下面这个测试脚本是什么意思,我这里解释一下。

env X='() { (a)=>' sh -c "echo date"; cat echo
  • X='() { (a)=>’ 这个不用说了,定义一个X的环境变量。但是,这个函数不完整啊,是的,这是故意的。另外你一定要注意,’不是为了单引号的转义,X这个变量的值就是 () { (a)=>

  • 其中的 (a)=这个东西目的就是为了让bash的解释器出错(语法错误)。

  • 语法出错后,在缓冲区中就会只剩下了 “>”这两个字符。

  • 于是,这个神奇的bash会把后面的命令echo date换个行放到这个缓冲区中,然后执行。

相当于在shell 下执行了下面这个命令:

$ >
 echo date

如果你了解bash,你会知道 是用于命令行上换行的,于是相当于执行了:

 $ >echo date

这不就是一个重定向么?上述的命令相当于:

$ date > echo

于是,你的当前目录下会出现一个echo的文件,这个文件的内容就是date命令的输出。

能发现这个种玩法的人真是个变态,完全是为bash的源代码量身定制的一个攻击

(全文完)

原文链接:http://coolshell.cn/articles/11973.htm

在CoolShell上看到一道关于fork的题的讲解,但自己实验却发现事情并不是像他讲的那么简单,程序如下:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
 
int main(void)
{
   int i;
   for(i=0; i<2; i++){
      fork();
      printf("-");
   }
 
   return 0;
}

题目就是求解此程序会输出几个"-"。

要做这道题,首先要了解fork,fork会产生一个子进程,子进程和父进程都运行到fork返回处,只是子进程返回值为0,父进程的返回值则是新进程(子进程)的进程id,于是,据此可以算出这题应该是会输出6个"-"。

然而这样是不对的,本题的另一个关键点在于printf,printf输出到stdout,而stdout是有输出缓冲的,故此处i=1的时候做fork,子进程会将父进程的输出缓冲区也继承下来,这样事实上,每一个子进程都输出了2个"-",一共就是8个了。

但是,当我们将这个程序编译运行多次之后,会不幸的发现,结果并非和我们预料的一样,输出十分不稳定,2、4、6、8个的情况都有出现,难道是我们上面的分析错了?

我们先将程序修改如下:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main(void)
{
    int i;
    int pid;
    pid = fork();
    printf("%d-", pid);
    pid = fork();
    printf("%d*", pid);
    return 0;
}

这样我们就可以看清楚每一个输出都是由哪个进程哪一次输出的了,以下是四次运行的结果:

➜  Sites  ./a
4687-4688*4687-0*0-4689*%
➜  Sites  ./a
4695-4696*4695-0*%
➜  Sites  ./a
4706-4707*%
➜  Sites  ./a
4716-4717*4716-0*0-4718*0-0*%

这四次结果正好涵盖了2、4、6、8的情况(%是zsh下没换行的情况下显示的标志)。

我们首先对4个进程编号:最初进程为1,1第一次fork出的子进程为2,1第二次fork出的子进程为3,2 fork出的子进程为4。

  • 查看8的情况:4716-4717*为1的输出,4716-0*为3的输出,0-4718*为2的输出,0-0*为4的输出;

  • 查看6的情况:4687-4688*为1的输出,4687-0*为3的输出,0-4689*为2的输出

  • 查看4的情况:4695-4696*为1的输出,4695-0*为3的输出

  • 查看2的情况:4706-4707*为1的输出

我们其实可以看出,任何一个进程如果输出了,那么他的父进程一定也输出了,而反之则不然。

排查下来看的话,这里应该就是由于父进程较早的退出,导致子进程的行为没有按照我们的预料,没能输出,至于为什么这里父进程退出后,子进程就没能输出,暂时我还没有研究清楚。

我们可以修改程序如下:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
 
int main(void)
{
   int i;
   for(i=0; i<2; i++){
      fork();
      printf("-");
   }
   wait(NULL);
   wait(NULL);
 
   return 0;
}

这样保证了父进程会等到子进程结束之后才会退出,于是乎再运行的时候,结果会稳定输出8个"-"。

注意这里一定要加2个wait,只加一个wait会出现如下情况:

➜  Sites  ./a
8390-0*8390-8391*0-0*%

1等待2结束,2输出了8390-0*,1输出了8390-8391*,3等待4结束,4输出了0-0*,但是1没有等待3结束,在3输出之前1就结束了,导致3没能输出。

今天终于到了决赛时间了,虽然没能去比赛,但是比赛结束后还是第一时间就收到了前方同学的消息。

本来之前还担心把他们送进决赛后,他们会不会拿0分的,结果竟然直接被告知密码题是样题,顿时就无语了,最最神奇的是,第一名竟然连这道题都没能做出来,心中顿时千万只草泥马奔腾而过,这要是去了的话,岂不是很有希望拿1W了,和酱油一分也是5000啊,都要赶上奖学金了,看着满满的大洋就这么从眼前飘过,真是欲哭无泪啊!!!

最近真的是太背了,做啥啥不顺,丢东西倒是丢的挺顺的,真不知道这样的日子还得持续多久,都说物极必反,也不知道我能不能等到反的那一天了。

这学期上操作系统,不得不说,这个实验绝对是上大学以来做过的最难的实验了。

MIT的Frans Kaashoek等在2006年参考PDP-11上的UNIX Version 6写了一个可在X86上跑的操作系统xv6(基于MIT License),用于学生学习操作系统。我们可以站在他们的肩膀上,基于xv6的设计,尝试着一步一步完成一个从“空空 如也”到“五脏俱全”的“麻雀”操作系统—ucore,此“麻雀”包含虚存管理、进程管理、处理器调度、同步互斥、进程间通信、文件系统等主要内核功能,总的内核代码量(C+asm)不会超过5K行。充分体现了“小而全”的指导思想。

说句实在话,本来没做之前想的是,一个课程实验能有多难,毕竟总得照顾到所有的学生吧,结果一开始做,就彻底无话可说了。

诺大一个程序,加之本来对于很多底层的编程就不熟悉,还有那以前向来不看的makefile,看起来真的是非一般的累,虽说实验配了大量的资料,但资料茫茫多,又还有点乱,真是不忍直视。

花了好长的时间,才算勉强开好了头,把实验1的前几问琢磨清楚了(找BIOS还真是废了好大劲),然后心血来潮,决定做点奇奇怪怪的事。

首先写了个hello.c:

#include <stdio.h>

int main(void) {
    printf("hello world!n");
    return 0;
}

然后静态编译:

gcc -o hello hello.c -static -m32 -g

接着,用hello替换掉lab1中编译生成的bin/kernel,最后重新make debug,本来指望看能不能成功输出hello world的,结果谁知道,一运行,竟然直接qemu直接花屏了,好不震惊。

最后,终于确定,是在bootmain中将hello程序的各个段载入内存的时候导致的花屏,然后突然想到原来看汇编的,有一块内存区域对应于console的频幕输出的,大概就只能是这块区域被覆盖了。

在使用VGA方式时,其文本方式和低分辨率图形方式占用了0xB8000-0xBFFFF的空间,然后程序的地址是0x8048000之后的空间,只是,由于bootmain中,对所有载入地址,做了个&0xffffff的操作,故变成了从0x48000开始。

然后查看程序段头:

  [ 6] .text             PROGBITS        08048300 000300 07f513 00  AX  0   0 16

发现text段是从0x08048300开始的0x07f513个字节,这样,计算后会发现,0xB8000正好对应于文件的0x70000的位置,还在.text段中,同时,通过对qemu进行debug,我们也会发现花屏后0xB8000出的数据值正好与文件0x70000处的数据完全相同,验证了我们的想法。

取证类样题一共两道题,题目如下:

1、从一个磁盘镜像(DD格式)中恢复一个有关火星登陆探索车的JPEG文件,并递交其MD5值。文件下载(200分)

2、图片里的奥秘。文件下载(200分)

本来对于取证是完全不会的,不过不得不说这两道题确实是太简单。

第一题是要从中找出JPEG,个人不知道怎么直接还原,但是观察会发现,里面的内容是没有压缩的,故可以直接从文件中找出JPEG的部分。

首先,找到JPEG的格式,可参见这里。前四个字节是0xffd8ffe0,第7-11个字节是0x4A46494600(JFIF),结束是0xffd9。

如果我们直接找到这个开头,然后找其后第一个结尾,部分图片是对的,但有部分确实错误的,因为在APPn段中,也可以存储一份微缩图像,微缩图像也是同样的开头和结尾。特殊判断处理一下即可:

import re

fp = open('Image.raw', 'rb')
content = fp.read()
fp.close()

cnt = 0
start = 0
while True:
    pic_start = content.find('xffxd8xffxe0', start)
    if pic_start == -1:
        break
    start = pic_start + 1
    while True:
        first_start = content.find('xffxd8xffxe0', start)
        first_end = content.find('xffxd9', start)
        if first_start != -1 and first_start < first_end:
            start = first_end + 1
        else:
            break
    pic_end = first_end
    
    cnt += 1
    fp = open('%d.jpg' % cnt, 'wb')
    fp.write(content[pic_start:pic_end + 2])
    fp.close()
    start = pic_end + 1

于是得到此图应该为题目中所需图片,计算MD5即可。

第二题就更水了,看起来似乎挺高端的样子,但其实,只要直接把文件当文本文件查看,便会发现其中直接写着flag,这种信息隐藏程度真是让人无话可说。

以下是xxd的部分结果:

0000140: 0000 0001 0000 0048 0000 0001 ffd8 ffe0  .......H........
0000150: 0010 4a46 4946 0001 0200 0048 0048 0000  ..JFIF.....H.H..
0000160: ffed 000c 4164 6f62 655f 434d 0001 ffee  ....Adobe_CM....
0000170: 000e 4164 6f62 6500 6480 0000 0001 ffdb  ..Adobe.d.......
0000180: 0084 000c 0808 0809 080c 0909 0c11 0b0a  ................
0000190: 666c 6167 3d73 6563 2e68 6475 2e65 6475  flag=sec.hdu.edu
00001a0: 2e63 6e0c 0c0c 110c 0c0c 0c0c 0c0c 0c0c  .cn.............

最后,今天把初赛做了,已经彻底和决赛说再见了,之前规则明明写着有25道题,结果突然变成了100道题,然后毫不知情的情况下就慢悠悠的做,到最后十来分钟,发现怎么还有第26题,还当系统出了问题,最后却发现外面规则写着有100道题,已经无话可说了,到最后也就做了30题左右的,这就是直接不看题蒙期望也是25分,这还怎么玩。太伤心了,本来还说决赛能好好玩玩的,没想到竟是这样收场,最近真是诸事不顺……

一个人跑去帮同学做了下初赛,竟然都把他们送进了决赛,对这比赛无语了……

5.1.Image.rar

5.2.key.jpg

题目如下:

下载test.exe文件,请想方法找到key。文件下载(500分)

首先是逆向,仿写程序如下:

const char password[] = "$1$k3Eadsf$blee.9JxQ75A/dSQSxW3v/";

void authorized()
{
    printf("V0RGbGFnPQ==");
    printf("{");
    printf("%sn", md5("kernel32.GetVesion"));
    printf("}");
}

void doit()
{
    char s[8];
    printf("please input the password:");
    gets(s);
    if (strcmp(md5(s), password) == 0) {
        authorized();
    }
}

int main()
{
    doit();
    return 0;
}

其中V0RGbGFnPQ==为WDFlag=的base64加密后的值。

程序是逆向清楚了,但是完全不明白这个题目是想让我们干嘛了,难道说key就是kernel32.GetVesion,那直接逆向就得到了,还要溢出干嘛,并且溢出得到的是md5之后的值,要由md5得到kernel32.GetVesion也不容易吧……

不管咋样,还是溢出一个吧,s和doit()的返回地址差了8 bytes,authorized的地址0x00401C40,所以溢出如下:

python -c "print 'a' * 8 + 'x40x1Cx40x00'" > payload
6.test.exe < payload

对于这题,只能说完全不知道想干嘛了,反正是样题,就这样吧~~~

6.test.exe.zip

密码类样题一共两道题,这是第一题,题目如下:

1、有一天,小明攻入一个服务器中发现一个可疑的字符串, aHR0cDovLyU3MyU2NSU2MyUyRSU2OCU2NCU3NSUyRSU2NSU2NCU3NSUyRSU2MyU2RS9TZWNIb21lLw,你能帮小明解密吗?(200分)

刚看到题的时候有点晕乎,这神马玩意,不过一试便会发现其实很简单,只是自己这方面的意识还是太弱。

对这个字符串做一下base64解码,得到:

http://%73%65%63%2E%68%64%75%2E%65%64%75%2E%63%6E/SecHome

很明显应该方向对了,但是网址还是不对,于是再做一下urldecode:

>>> urllib.unquote('http://%73%65%63%2E%68%64%75%2E%65%64%75%2E%63%6E/SecHome')
'http://sec.hdu.edu.cn/SecHome'

这题便得以解决~~~

密码类样题一共两道题,这是第二题,题目如下:

2、截获到一段加密信息:
PCQ VMJYPD LBYK LYSO KBXBJXWXV BXV ZCJPO EYPD KBXBJYUXJ LBJOO KCPK. CP LBO LBCMKXPV XPV IYJKL PYDBL,QBOP KBO BXV OPVOV LBO LXRO, KBO JCKO XPV EYKKOV LBO DJCMPV ZOICJO BYS, KXUYPD:“DJOXL EYPD, ICJ XLBCMKXPV XPV CPO PYDBLK Y BXNO ZOOP JOACMPLYPD LC UCM LBO IXZROK CI FXKL XDOK XPV LBO RODOPVK CI XPAYOPL EYPDK. SXU Y SXEO KC ZCRV XK LC AJXNO X IXNCMJ CI UCMJ SXGOKLU?” 请帮助解密。(400分)

这题一看连空格都没有去掉,就觉得应该不难了。

首先还是老规矩,频率统计:

o : 33
x : 29
p : 27
k : 23
c : 22
b : 22
l : 21
y : 17
j : 16
v : 16
d : 12

pv => 9
lb => 9
bo => 8
cm => 7
xp => 7

ypd => 6
xpv => 6
lbo => 5

首先猜测lbo为the,然后替换后发现有thJee,于是猜测J对应r。

紧接着,由于ypd和xpv第二个字母相同,于是两者应该是ing和and,首先尝试xpv为and、ypd为ing。

替换后发现有thiK、nightK,猜测K对应s;

发现有Iirst,猜测I对应f;

发现有anAient,猜测A对应c;

发现有dMring,猜测M对于u;

发现有thCusand,猜测C对应o;

发现有haNe Zeen,猜测N对应v,Z对应b;

发现由noQ during this tiSe开头,猜测Q对应w,S对应m;

发现有saUing:,猜测U对于y;

然后就只剩jklpqxz了。

发现有Regend,猜测R对应l;

发现有maEe,猜测E对应k;

发现有maGesty,猜测G对应j;

发现有Fast,猜测F对应p;

然后只剩shahraWad中W未知了,应该为z。

其实在中途就可以通过google搜到原文了:

now during this time shahrazad had borne king shahriyar three sons. on the thousand and first night, when she had ended the tale of ma’aruf, she rose and kissed the ground before him, saying: “great king, for a thousand and one nights i have been recounting to you the fables of past ages and the legends of ancient kings. may i make so bold as to crave a favour of your majesty?”

搜索也发现在这里的例子中就有我们这道题。

样题果然很水,也不知道比赛的时候题目难度能不能也这么水~~~

1) 查看shadow文件的内容

cat /etc/shadow

可以得到shadow文件的内容,限于篇幅,我们举例说明:

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

其格式为:

{用户名}:{加密后的口令密码}:{口令最后修改时间距原点(1970-1-1)的天数}:{口令最小修改间隔(防止修改口令,如果时限未到,将恢复至旧口令):{口令最大修改间隔}:{口令失效前的警告天数}:{账户不活动天数}:{账号失效天数}:{保留}

【注】:shadow文件为可读文件,普通用户没有读写权限,超级用户拥有读写权限。如果密码字符串为*,则表示系统用户不能被登入;如果字符串为!,则表示用户名被禁用;如果字符串为空,则表示没有密码。

我们可以使用passwd –d 用户名 清空一个用户的口令密码。

2) 解析shadow文件中密码字符串的内容

对于示例的密码域$1$Bg1H/4mz$X89TqH7tpi9dX1B9j5YsF.,我们参考了linux标准源文件passwd.c,在其中的pw_encrypt函数中找到了加密方法。

我们发现所谓的加密算法,其实就是用明文密码和一个叫salt的东西通过函数crypt()完成加密。

而所谓的密码域密文也是由三部分组成的,即:$id$salt$encrypted。

【注】: id为1时,采用md5进行加密;

id为5时,采用SHA256进行加密;

id为6时,采用SHA512进行加密。

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

i. 头文件:#define _XOPEN_SOURCE

#include </span> </p>

ii. 函数原型:char *crypt(const char *key, const char *salt);

iii. 函数说明:crypt()将使用DES演算法将参数key所指的字符串加以编码,key字符串长度仅取前8个字符,超过此长度的字符没有意义。参数salt为两个字符组成的字符串,由a-z、A-Z、0-9,’.’和’/’所组成,用来决定使用4096种不同内建表格的哪一种。函数执行成功后会返回指向编码过的字符串指针,参数key所指向的字符串不会有所改动。编码过的字符串长度为13个字符,前两个字符为参数salt代表的字符串。

iv. 返回值:返回一个指向以NULL结尾的密码字符串

v. 附加说明:使用GCC编译时需要加上 –lcrypt

</blockquote> 4) 加密参数salt的由来

在我们的示例密码域中salt为Bg1H/4mz,那么它又是如何来的?

我们还是从标准源文件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 
#include 
#include 
#include 
#include 
#include 
int main(int argc, char *argv[])</span> </p>

{

if(argc < 2)

{

printf("no usrname input");

return 1;

}

if (geteuid() != 0)

{

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

return -1;

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]!='')

{

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

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

{

j++;

if(j==3)

{

salt[i+1]='';

break;

}

}

i++;

}

if(j<3)

perror("file error or user cannot use.");

if(argc==3)

{

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

printf("shadowd passwd: %sn", shd->sp_pwdp);

}

}

return 0;

}

编译: gcc passwd.c -lcrypt -o passwd

运行: ./passwd root 123

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

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

</blockquote>
原文链接:http://blog.csdn.net/jinyuhongye/article/details/7950961