Brk漏洞分析和修补
brk漏洞分析^-iv P8o0{7S^h有点out of date乐:(E)`9eT+t,Q;N
希望对大家还能有点用。。。
Brk漏洞分析1pFLj})b/{x
by icbm@0x557
u8yHAX
1.brk漏洞背景
2.漏洞原理分析
3.漏洞利用分析
4.该漏洞引发的思考
o$t'tz {/{p k4J
+|5_qs9wyG:y |a
Brk漏洞分析,C^m.[&gBg~s"p
1.brk漏洞背景
2.漏洞原理分析
3.漏洞利用分析$V6}2?;`B]}e6S
4.该漏洞引发的思考
]6@9pkf`c[
前言: p/i6Lc!Hg*]mX
11月21日在Full-Disclosure邮件列表里收到一封Debian Linux安全小组发来的题目为"Some Debian Project machines have been compromised"的安全公告。该安全公告中提到Debian多台服务器被入侵,令人奇怪的是其中并没有提到任何被入侵的细节只是说过几天后会公布对被入侵主机的取证结果。这个公告引起了我们极大兴趣,因为Debian Linux的安全性是非常好的,攻击者肯定使用了一些未知漏洞获取了管理员权限,几天后终于获悉攻击者使用的就是本文将要分析的漏洞do_brk()边界检查不充分导致本地权限提升漏洞。因为这种漏洞对系统产生的影响非常罕见,所以我们甚至可以定义此漏洞为一种新的漏洞类型。在下面的篇幅中我们就此漏洞的背景,成因,利用方法展开讨论。3@,W\%{5t?6m
:z/a*KZt
1.brk漏洞背景 e/J7iH"]9tP5DG4V
在分析该漏洞前先来看看它的一些背景资料,说不定我们可以从这些背景资料中能发现一些有用东西:)
其实这个brk漏洞在今年9月份就被Linux内核开发人员发现,并在9月底发布的Linux kernel 2.6.0-test6中就对该漏洞作了修补。奇怪的是Linux Kernel开发人员可能认为这个漏洞并不是什么严重问题,所以对该漏洞的发现修补未作任何安全公告,如此轻率的处理安全漏洞的举动使Linux系统管理员完全没有注意到此漏洞的存在更不要说对系统修补此漏洞了。同时在2003年9月底的时候,isec的Paul (IhaQueR) Starzetz 也发现了此漏洞,isec对此漏洞进行了详细地分析并且对此漏洞的攻击方法作了深入的研究,写出了brk漏洞非常稳定且有效的攻击程序。因为攻击程序的泄漏,所以使攻击者利用此攻击程序获取了Debian的多台服务器管理员权限。11月28日Linux内核推出了2.4.23版本中才修补了此漏洞,至此经过了两个多月此漏洞才被在Linux稳定版本内核中修补。'Em WL!E%Tw!z0y.t1u
6EmlF{)GU2p
2.漏洞原理分析7@kh4x`,P'R f Q
在分析此漏洞之前我们先来简单介绍一下Linux内存管理方面的知识。
在Linux中每一个进程都可以访问0-4G的虚拟线性内存地址,而其中0-3G(0xc0000000)为用户空间,用户进程可以访问其中任何一个地址。这个最大值(0xc0000000)在Linux中通常被定义为TASK_SIZE,这个值也就是用户空间所能访问的极限。从3G-4G的虚拟内存地址空间为内核态地址空间,其中存放的数据由所有进程共享但只能由内核访问,用户进程不能访问。用户进程可以通过中断或者系统调用使操作系统的用户态切换到内核态来访问内核态数据。如果读者想进一步了解Linux内存管理方面的知识可以阅读参考1。
Ok,现在我们有了Linux内存管理的一些概念后,大家可以大胆的设想一下:如果我们可以通过某些操作(或者某些Linux操作系统中的漏洞)来突破这个TASK_SIZE的限制将会出现什么情况呢?嗯,对了,这时候我们就可以访问到内核中敏感的数据,通过对这些敏感数据的操作就可以获得系统的最高权限。.B+q(] mbI
然后让我们回到这次讨论的brk漏洞中,该漏洞被发现于brk系统调用中。brk系统调用可以对用户进程的堆的大小进行操作,使堆扩展或者缩小。我们先来看一下从Linux内核源代码中抽取的系统调用sys_brk的部分源代码。5o!s3Q8rh]6{XN
--------------------[linux/mm/mmap.c line:147]----------------------------------------------E7C-EG _9sz\0m
p#nMy4Q c? CDW
asmlinkage unsigned long sys_brk(unsigned long brk)
{
unsigned long rlim, retval;
unsigned long newbrk, oldbrk;
struct mm_struct *mm = current->mm;
down_write(&mm->mmap_sem);
if (brk < mm->end_code)
goto out;d Yb${-\ t~3z.^
newbrk = PAGE_ALIGN(brk);wN5z5ynDE^Eb
oldbrk = PAGE_ALIGN(mm->brk);
if (oldbrk == newbrk)+]Eu} mh#h
goto set_brk;1v+f*U~P"{
7h;S1`/u+|#` ?Nq
/* Always allow shrinking brk. */
if (brk <= mm->brk) {
if (!do_munmap(mm, newbrk, oldbrk-newbrk))E2[ ?cQ
goto set_brk;
goto out;