软件安全实验——pre2
Posted 大灬白
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了软件安全实验——pre2相关的知识,希望对你有一定的参考价值。
第1题
1、linux下用open函数打开文件时,要是采用O_WRONLY模式为何容易产生竞争条件漏洞?换成O_WRONLY | O_CREAT | O_EXCL 模式后情况会如何?
使用open函数用来打开一个设备int open(const char pathname, int oflag, …/, mode_t mode * / ) ;
它返回的是一个整型变量,如果这个值等于-1,说明打开文件出现错误,如果为大于0的值,说明打开文件正常。
打开的操作类型有如下几种:
- O_RDONLY 只读打开
- O_WRONLY 只写打开
- O_RDWR 读、写打开
采用O_WRONLY模式用open函数打开文件时,root总是可以创建文件,即便锁文件已经存在,这意味着该锁不能为root正常工作。
换成O_WRONLY | O_CREAT | O_EXCL 模式后,就将权限设置为0,使同一用户的其他进程无法获得锁。
第2题
2、(不需要完全搞懂全部流程)阅读一篇文章“从一个漏洞谈到ptrace的漏洞发现及利用方法”,地址为http://www.nsfocus.net/index.php?act=magazine&do=view&mid=1795。描述其中的竞争条件漏洞出现的原因。
首先,漏洞出现的原因如原文分析:代码是在内核线程exec_modprobe()的上下文运行的,程序的current指向这个内核线程的task_struct结构,而与创建这个线程时的current不同,那时候的current指向当时的当前进程,即exec_modprobe()的父进程。内核线程exec_modprobe()从其父进程继承了绝大部分资源和特性,包括它的fs_struct的内容和打开的所有文件,以及它的进程号、组号,还有所有的特权。但是这些特性在这个函数里大多被拚弃了(见源码的19行到42行,这里设置了该内核线程的信号euid、egid等,使之变成超级用户),不过在摒弃这些特性之前之前,我们的父进程,或同组进程是应该可以调试该内核线程的。漏洞也就在这里。
之后,我认为:Linux kernel/kmod.c程序以不安全的方式建立内核线程,本地攻击者利用这个漏洞通过竞争条件攻击方法获得root用户权限。
当进程请求的功能在模块中的情况下,内核就会派生一子进程,并把子进程的euid和egid设置为0并调用execve("/sbin/modprobe")。问题是在euid更改到子进程前可以被ptrace()挂接调试,因此攻击者可以插入任意代码到进程中并以root用户的权限运行。
要利用此漏洞,必须需要如下条件:
1、Linux内核以模块方式构建,内核模块可装载。
2、/proc/sys/kernel/modprobe包含一些合法可执行的路径。
3、ptrace()没有被限制。
这段代码是在内核线程exec_modprobe()的上下文运行的,所以这里的current指向这个内核线程的task_struct结构,而与创建这个线程时的current不同,那时候的current指向当时的当前进程,即exec_modprobe()的父进程。内核线程exec_modprobe()从其父进程继承了绝大部分资源和特性,包括它的fs_struct的内容和打开的所有文件,以及它的进程号、组号,还有所有的特权。
但是这些特性在这个函数里大多被拚弃了(见源码的19行到42行,这里设置了该内核线程的信号、euid 、egid等,使之变成超级用户),不过在拚弃这些特性之前之前,我们的父进程,或同组进程是应该可以调试该内核线程的。漏洞也就在这里。
我们再回到myptrace.c中,来看看myptrace.c中的do_child()函数。
该函数在103行-105行调用do-while()语句一直循环等待,直到期望的进程号(也就是父进程派生的那个内核线程)出现,在该线程刚刚出现还没有拚弃父进程的特性(主要是uid, gid, euid, egid等)之前,子进程可以调试该内核线程,所以子进程就可以调用ptrace()系统调用ATTACH该内核线程。然后,如上所述,该内核线程就会拚其父进程的特性,变成超级用户。
接下来,子进程利用信号处理程序,在第78行调用函数putcode() 向该内核线程的空间写入一些代码,当内核线程再次运行时,就会执行这段代码,使我们的程序变成所有者为root,并设置了“set_uid”标示位。
再看我们的父进程在do_parent()中一直监视自身文件(125行-127行),一旦状态改变,就调用函数system()重新运行自身文件,此时在main()函数中会运行parent()函数,该函数即运行了一个超级用户的shell。
第3题
3、(选做)上网搜索CVE-2016-5195漏洞的相关资料。描述其中的整数溢出和竞争条件漏洞出现的原因。
概述一下这个漏洞的触发原理:
当调用write系统调用向/proc/self/mem文件中写入数据时,进入内核态后内核会调用get_user_pages函数获取要写入内存地址。get_user_pages会调用follow_page_mask来获取这块内存的页表项,并同时要求页表项所指向的内存映射具有可写的权限。
第一次获取内存的页表项会因为缺页而失败。get_user_page调用faultin_page进行缺页处理后第二次调用follow_page_mask获取这块内存的页表项,如果需要获取的页表项指向的是一个只读的映射,那第二次获取也会失败。这时候get_user_pages函数会第三次调用follow_page_mask来获取该内存的页表项,并且不再要求页表项所指向的内存映射具有可写的权限,这时是可以成功获取的,获取成功后内核会对这个只读的内存进行强制的写入操作。
这个实现是没有问题的,因为本来写入/proc/self/mem就是一个无视映射权限的强行写入,就算是文件映射到虚拟内存中,也不会出现越权写:
如果写入的虚拟内存是一个VM_PRIVATE的映射,那在缺页的时候内核就会执行COW操作产生一个副本来进行写入,写入的内容是不会同步到文件中的
如果写入的虚拟内存是一个VM_SHARE的映射,那mmap能够映射成功的充要条件就是进程拥有对该文件的写权限,这样写入的内容同步到文件中也不算越权了。
但是,在上述流程中,如果第二次获取页表项失败之后,另一个线程调用madvice(addr,addrlen, MADV_DONTNEED),其中addr~addr+addrlen是一个只读文件的VM_PRIVATE的只读内存映射,那该映射的页表项会被置空。这时如果get_user_pages函数第三次调用follow_page_mask来获取该内存的页表项。由于这次调用不再要求该内存映射具有写权限,所以在缺页处理的时候内核也不再会执行COW操作产生一个副本以供写入。所以缺页处理完成后后第四次调用follow_page_mask获取这块内存的页表项的时候,不仅可以成功获取,而且获取之后强制的写入的内容也会同步到映射的只读文件中。从而导致了只读文件的越权写。
第4题
4、echo “crack:”$(openssl passwd -1 -salt a3g1 123456)":0:0:,:/root:/bin/bash"的输出结果是什么?root用户把输出的这一行加入/etc/passwd末尾会产生什么效果?(提前备份该文件,之后恢复)
答:(1)openssl passwd -1 命令可以输出shadow里面的密码,把这个命令生成的秘串更改为你shadow里的密码,那么下次你登录系统就可以用你的生成密码的口令来登录了,使用这个命令,即使口令一样,多次执行生成的密码串也不一样。那个hash值对应的密码是完全随机的基于64位字符编码的28位长,因此要破解它是非常困难的,只要不用那些密码已经公布出来的hash值创建账号,即使这些密码文件被公布也还是比较安全的。
[root@WEB01 ~]# openssl passwd -1 -salt a3g1 123456
上面命令中的 salt 自己随便输入些东西,因为设置密码的时候密码密文是MD5加密的,在产生哈希值的时候系统回在密文中加如盐从而使密文无法反向破译。passwd加密的时候系统加的salt是时间。
第一次的输出结果是:
第二次root用户把输出的这一行加入/etc/passwd末尾的输出结果是:没有这个文件
密码文件被删除了。
以上是关于软件安全实验——pre2的主要内容,如果未能解决你的问题,请参考以下文章