linux中是不可以依附别的进程的,更不可能创建远程线程,然而一种不太正规的方式却可以做到这一点,这就是ptrace接口,ptrace可以依附任何用户进程,用特殊的参数甚至可以更改任何进程的寄存器和内存映射,这个功力和创建远程线程不相上下,甚至比其更加灵活,如果理解了elf映像在内存的布局便可以通过ptrace修改被调试进程的任意内存。然而有一个限制就是ptrace接口只能调试属于自己用户的进程,也就是说它不能调试别的用户的进程,一种显而易见的限制就是普通用户的ptrace不能调试root进程,但是2.6.29内核有一个明显的漏洞使得普通用户可以提升本地权限。
简单来讲就是在调用exec的时候需要经历一系列的uid,euid的计算,特别是exec具有suid的程序的时候,这种计算将变得更加繁复,在ptrace的时候需要进行一系列uid,euid的判断,那么如果处理不好这二者之间的同步,必然会导致ptrace的误判,就是说ptrace调试了一个根本不属于自己的进程,甚至可能是root进程,如果ptrace的调用进程更改了被调用的具有suid的进程的内存,使之exec了一个shell,那么该shell将会是属于root的,因为exec的调用进程也就是suid进程的euid是0.
早期的内核心漏洞更加明显,看看2.4.9的内核中的ptrace_attach
int ptrace_attach(struct task_struct *task)
{
task_lock(task);
...
if(((current->uid != task->euid) ||
(current->uid != task->suid) ||
(current->uid != task->uid) ||
(current->gid != task->egid) ||
(current->gid != task->sgid) ||
(!cap_issubset(task->cap_permitted, current->cap_permitted)) ||
(current->gid != task->gid)) && !capable(CAP_SYS_PTRACE))
goto bad;
...
task->ptrace |= PT_PTRACED;
task_unlock(task);
write_lock_irq(&tasklist_lock);
if (task->p_pptr != current) {
REMOVE_LINKS(task);
task->p_pptr = current;
SET_LINKS(task);
}
write_unlock_irq(&tasklist_lock);
send_sig(SIGSTOP, task, 1);
return 0;
bad:
task_unlock(task);
return -EPERM;
}
这个早期的内核显得很整洁,因为都到了很少量的锁,锁的粒度自然很粗,不过还好,看看第一行就锁住了这个进程,然后进行一系列的判断,锁住进程的目的就是阻止别的执行绪在判断期间更改进程的属性,这个想法很好,但是再看一个函数:
void compute_creds(struct linux_binprm *bprm)
{
kernel_cap_t new_permitted, working;
int do_unlock = 0;
new_permitted = cap_intersect(bprm->cap_permitted, cap_bset);
working = cap_intersect(bprm->cap_inheritable,
current->cap_inheritable);
new_permitted = cap_combine(new_permitted, working);
if (bprm->e_uid != current->uid || bprm->e_gid != current->gid ||
!cap_issubset(new_permitted, current->cap_permitted)) {
current->mm->dumpable = 0;
lock_kernel(); //仅仅锁住了kernel而没有锁住task,只要没有执行绪和该执行绪竞争kernel锁,那么谁也不会等待
if (must_not_trace_exec(current) //注意这个函数,见下面。该位置设为A
|| atomic_read(¤t->fs->count) > 1
|| atomic_read(¤t->files->count) > 1
|| atomic_read(¤t->sig->count) > 1) {
if(!capable(CAP_SETUID)) { //千万别通过这个if语句,否则弊大于利,攻击成功的可能性就小了
bprm->e_uid = current->uid;
bprm->e_gid = current->gid;
}
...
}
do_unlock = 1;
}
...//注意,以下就应该设置新进程的各种ID了。下面的位置设置为B。
current->suid = current->euid = current->fsuid = bprm->e_uid;
current->sgid = current->egid = current->fsgid = bprm->e_gid;
if(do_unlock)
unlock_kernel();
current->keep_capabilities = 0;
}
static inline int must_not_trace_exec(struct task_struct * p)
{
return (p->ptrace & PT_PTRACED) && !cap_raised(p->p_pptr->cap_effective, CAP_SYS_PTRACE);
}
注意,在A和B之间并没有进行task本身的保护,最起码没有和ptrace互斥,ptrace为了安全起见被设计为不能跟踪自己用户id之外的进程,比如一个普通用户进程不能跟踪suid进程,但是看看2.4.9的代码,如果在A和B之间,ptrace闯了进来要跟踪正在exec的进程,此时被跟踪的正在 exec的进程尚未设置好新的euid和egid之类的id,那么ptrace进程很容易就得逞了,通过了ptrace_attach中的那么长的判断,从而成功实现一个普通用户进程进程跟踪一个suid进程,以后可以通过ptrace接口修改被跟踪的suid进程的内存实现注入,如果注入一个root的 shell,那么一切就成功了!在后来的内核中,在compute_creds也用了task_lock(task)将进程锁住,那么exec和 ptrace就不能同时进入到竞争态了,如果exec先进去,那么ptrace_attach的一系列判断会将有企图的进程赶出去,如果ptrace先进去,那么must_not_trace_exec会将exec新进程的euid设置成current的uid从而降低了它的权限,仔细看 must_not_trace_exec发现它也有漏洞,如果suid程序本身有漏洞,那么!cap_raised(p->p_pptr->cap_effective, CAP_SYS_PTRACE)这个条件就有可能被利用,从而绕过bprm->e_uid = current->uid,因此后来的版本将该函数的后一个条件去掉了,只要一个进程被跟踪了,那么就尽可能不让它获得特权,除非它的调用者本身就是特权程序,皇帝自杀谁也挡不住啊。
虽然后来的版本在两个地方加上了锁,但是一下子就锁一个进程未免粒度过大,于是2.6的后期内核将锁的粒度减小,引入了ptrace和exec进行互斥的专用锁,这样内核的效率会更高,和这二者无关的操作不会因为锁而被阻拦。
分享到:
相关推荐
对Linux下的系统调用Ptrace()所拥有的进程跟踪和控制调试功能进行了...结合内核漏洞的具体实例研究其对系统可能造成的安全威胁;最后就病毒技术中的一项关键技术——隐藏,讨论了Ptrace()在Linux病毒隐藏技术中的应用。
ptracer -- 一个用于Python程序的基于ptrace跟踪的库
一个Linux内核模块,用于设置观察点而无需通过ptrace 该模块旨在成为ptrace模块的轻量级替代品,用于设置和报告值。 用法 汇编 要编译观察点,请在顶层目录中单击make 。 载入中 将监视点作为常规内核模块(如...
ptrace注入实例代码,
2.3 几个重要的数据结构和函数 2.4 越界访问 2.5 用户堆栈的扩展 2.6 物理页面的使用和周转 2.7 物理页面的分配 2.8 页面的定期换出 2.9 页面的换入 2.10 内核缓冲区的管理 2.11 外部设备存储空间的地址映射 2.12 ...
ptrace注入与zygote区别和联系.V2EEptrace注入与zygote区别和联系.V2EE
Ptrace-for-Android 一个用户级调试应用程序,可以跟踪 Android 中正在运行的应用程序。 一、简介: Ptrace 是 linux 发行版中最重要的系统调用之一。大多数调试实用程序(如 gdb 和其他常见调试实践)在很大程度上...
安卓程序ptrace注入例子,送给想研究手游注入的同学 使用JNI注入
GDB & ptrace() System Call
ptrace:一个用于Linux的PoC,可以绕过代理,该代理记录了正在执行的命令,没有root特权。 Linux低权限模糊化执行的程序名和参数,避开基于execve系统调用监控的命令日志
Linux下Ptrace()调用的安全分析.pdf
Linux ‘PTRACE_TRACEME’提权漏洞(CVE-2019-13272)分析 安全开发 安全开发 法律法规 安全管理 信息安全研究
2.3 几个重要的数据结构和函数 2.4 越界访问 2.5 用户堆栈的扩展 2.6 物理页面的使用和周转 2.7 物理页面的分配 2.8 页面的定期换出 2. 9 页面的换入 2.10 内核缓冲区的管理 2.11 外部设备存储空间...
利用Ptrace在Android平台实现应用程序控制
ptrace 越狱调试防护
kernel 5.1.17之前版本中存在安全漏洞,该...研究人员概述的方案涉及一个父进程,该进程创建一个子进程,这个子进程会创建子进程。第一个子进程使用命令pkexec(用于以root身份运行程序),第二个子进程运行PTRACE_T
ptrace是process和trace的简写,直译为进程跟踪。它提供了一种使父进程得以监视和控制其子进程的方式,它还能够改变子进程中的寄存器和内核映像,因而可以实现系统调用的跟踪和断点调试。
一个被跟踪的进程运行中,直到发生一个信号。则进程被中止,并且通知其父进程。在进程中止的状态下,进程的内存空间可以被读写。父进程还可以使子进程继续执行,并选择是否是否忽略引起中止的信号。
2.3 几个重要的数据结构和函数 2.4 越界访问 2.5 用户堆栈的扩展 2.6 物理页面的使用和周转 2.7 物理页面的分配 2.8 页面的定期换出 2.9 页面的换入 2.10 内核缓冲区的管理 2.11 外部设备存储空间的地址映射...