25, 1/1 ȸ¿ø°¡ÀÔ  ·Î±×ÀΠ 
   ¸Û¸Û
   http://hackerschool.org
   do_mremap() Ãë¾àÁ¡ : Ãë¾àÁ¡ ºÐ¼®

http://www.hackerschool.org/HS_Boards/zboard.php?id=advisory&no=12 [º¹»ç]


ISEC ±×·ì¿¡¼­ ¹ßÇ¥ÇÑ º¸¾È ±Ç°í¹®À» ÷ºÎÇϵµ·Ï ÇÏ°Ú½À´Ï´Ù.


Synopsis:  Linux kernel do_mremap() local privilege escalation
           vulnerability
Product:   Linux kernel
Version:   2.4 up to 2.4.23, 2.6.0
Vendor:    http://www.kernel.org/
Author:    Paul Starzetz ,
           Wojciech Purczynski

Date:      January 5, 2004
Update:    January 15, 2004
Update:    January 16, 2004 (reformatted)


Issue:
======

A critical security vulnerability has been found in the Linux kernel
memory management code in mremap(2) system call due to incorrect bound
checks.


Details:
========

The mremap system call provides functionality of resizing (shrinking or
growing) as well as moving across process's addressable space of
existing virtual memory areas (VMAs) or any of its parts.

A typical VMA covers at least one memory page (which is exactly 4kB on
the i386  architecture). An incorrect bound check discovered inside the
do_mremap() kernel code performing remapping of a virtual memory area
may lead to creation of a virtual memory area of 0 bytes in length.

The problem bases on the general mremap flaw that remapping of 2 pages
from inside a VMA creates a memory hole of only one page in length but
also an additional VMA of two pages. In the case of a zero sized
remapping request no VMA hole is created but an additional VMA
descriptor of 0 bytes in length is created.

Such a malicious virtual memory area may disrupt the operation of the
other parts of the kernel memory management subroutines finally leading
to unexpected behavior.

A typical process's memory layout showing invalid VMA created with
mremap system call:

    08048000-0804c000 r-xp 00000000 03:05 959142     /tmp/test
    0804c000-0804d000 rw-p 00003000 03:05 959142     /tmp/test
    0804d000-0804e000 rwxp 00000000 00:00 0
    40000000-40014000 r-xp 00000000 03:05 1544523    /lib/ld-2.3.2.so
    40014000-40015000 rw-p 00013000 03:05 1544523    /lib/ld-2.3.2.so
    40015000-40016000 rw-p 00000000 00:00 0
    4002c000-40158000 r-xp 00000000 03:05 1544529    /lib/libc.so.6
    40158000-4015d000 rw-p 0012b000 03:05 1544529    /lib/libc.so.6
    4015d000-4015f000 rw-p 00000000 00:00 0
[*] 60000000-60000000 rwxp 00000000 00:00 0
    bfffe000-c0000000 rwxp fffff000 00:00 0

The broken VMA in the above example has been marked with a [*].


Exploitation:
=============

The iSEC team has identified multiple attack vectors for the bug
discovered.  In this section we want to describe the page counter method
however we strongly believe that a much faster and more convenient
method exists.

As mentioned above a VMA of 0 bytes in size can be introduced into the
process's virtual memory list. Its unusual size renders such a VMA
partially invisible to the kernel main VM helper routine called
find_vma(). The find_vma(ADDR) function returns the first VMA descriptor
(START, END) from the current process's list satysfying ADDR < END or
NULL if none. Obviously given a VMA starting and ending at the same
address ADDR the condition is violated if one searches for ADDR's VMA
thus the next VMA in the list will be returned.

The mremap() code calls the insert_vm_struct() helper function after
creating the bogus VMA descriptor in kernel memory which in turn checks
the new location calling the find_vma() helper which returns the wrong
result if a zero sized VMA is already present in the new location.
Therefore it is possible to introduce multiple bogus VMA descriptors for
the same virtual memory address. This happens only if the adjacent zero
sized VMAs differ in their descriptor flags because otherwise they will
be linked together in insert_vm_struct().

Later the process virtual memory list could look like:

    08048000-080a2000 r-xp 00000000 03:02 53159      /tmp/test
    080a2000-080a5000 rw-p 00059000 03:02 53159      /tmp/test
    080a5000-080a6000 rwxp 00000000 00:00 0
    40000000-40001000 r--p 00000000 00:00 0
    60000000-60000000 r--p 00000000 00:00 0
    60000000-60000000 rw-p 00000000 00:00 0
    60000000-60000000 r--p 00000000 00:00 0
    60000000-60001000 rwxp 00000000 00:00 0
    bffff000-c0000000 rwxp 00000000 00:00 0


Further we have found that there is an off-by-one increment inside the
copy_page_range() function for the page counter of the first VMA page
directly following a zero sized VMA area. This is not a bug in the
copy_page_range code(), it is just a feature for a combined zero and
non-zero VMA. The copy_page_range function is called on fork() to copy
parent's page tables into the child process.

Moreover we must note that it is possible to remove a zero-sized VMA
from the virtual memory list if another suitable VMA is mapped directly
below the starting address of the 0-VMA. Suitable means that the new VMA
must have exactly the same attributes (read, exec, etc) as the following
zero-sized VMA and do not map a file. This again is a feature of the
mmap() system call which will try to minimize the number of used VMA
descriptors merging them if possible. Note that merging the VMAs doesn't
influence any page counters in following VMAs.

Combining the findings above we conclude that it is possible to
arbitrarily increment the page counter of the first VMA page by forking
more and more a process with a zero-sized VMA 'sandwich'. Cleanup must
be done in the child before it can exit() otherwise the kernel would
print a nasty error message while trying to remove the bogus VMA
mappings.

The goal is to overflow the page counter to become 1 again in the child
process.  If the corresponding VMA is unmapped now, the page counter
will become 0 and the page returned to the kernel memory management.
Note that the parent will still hold a reference to the freed page in
its page table thus making a manipulation of kernel memory possible.

Let's take a closer look at the incrementing of the page counter.  We
can introduce M (marked with A's and B's) 0-sized VMAs directly before
the victim VMA hosting the page we want the counter to overflow. If the
victim maps anonymous memory, the first write access to the victim VMA
page (marked with P) will allocate and insert a fresh page frame into
the process's page table and the page counter will be set to 1:

[A][B][A][B] ... [A][P  VICTIM  ]

After the first fork() P's page counter will become 1 + M + 1 where the
first one is for the original copy in the parent, M for the bogus VMAs
and one for the copy in the child. Cleaning up the 0-VMAs in the child
will not change the page counter however it will be decremented by one
on child's exit. Thus after the first fork()-exit() pair it will become
1 + M. We can conclude for N forks taking integer overflows into account
that without the final exit() call in the child following equation
holds:

1 + M*N + 1 = 1

or that

M*N = 2^32-1 = 3 * 5 * 17 * 257 * 65537

Thus we can for example choose to create (3*5*257) 0-sized VMAs and fork
the parent (17*65537) times to overflow P's page counter. This may be a
quite longish task. Times ranging from about one hour on a fast machine
to more than 10 hours have been observed.

Further exploitation proves to be easy because the kernel page
management has the nice property to use a kind of reversed LRU policy
for page allocation.  That means that if a page has been released to the
kernel MM subsystem it will be returned on a subsequent allocation
request. The released page could be for example allocated to a file
mapping we can normally only read from or to kernel structures, etc.

It is worth noting that the parent's page reference (PTE) must be
unprotected before we can use it to modify page contents because fork()
will mark it as read only (for copy-on-write reasons).


Impact:
=======

No special privileges are required to use the mremap(2) system call thus
any process may misuse its unexpected behavior to disrupt the kernel
memory management subsystem. Proper exploitation of this vulnerability
may lead to local privilege escalation including execution of  arbitrary
code with kernel level access. Proof-of-concept exploit code has been
created and successfully tested giving UID 0 shell on vulnerable
systems.

All users are encouraged to patch all vulnerable systems as soon as
appropriate vendor patches are released.


Credits:
========

Paul Starzetz has identified the vulnerability and
performed further research.


Disclaimer:
===========

This  document and all the information it contains are provided "as is",
for educational purposes only, without warranty of any kind, whether
express or implied.

The  authors reserve the right not to be responsible for the topicality,
correctness, completeness or quality of the information provided in
this document. Liability claims regarding damage caused by the use of
any information provided, including any kind of information which is
incomplete or incorrect, will therefore be rejected.

[Ãë¾àÁ¡ ÆÐÄ¡]

  Hit : 4073     Date : 2004/01/27 02:58



    
25   setsockopt() Ãë¾àÁ¡ : ¹æ¾î ¸ðµâÀ» ÀÌ¿ëÇÑ ÆÐÄ¡[1]     ¸Û¸Û
05/20 3438
24   setsockopt() Ãë¾àÁ¡ : Ä¿³Î ¾÷±×·¹À̵带 ÅëÇÑ ÆÐÄ¡[5]     ¸Û¸Û
05/20 3186
23   setsockopt() Ãë¾àÁ¡ : Ãë¾àÁ¡ ÆÐÄ¡     ¸Û¸Û
05/20 3556
22   setsockopt() Ãë¾àÁ¡ : Ãë¾àÁ¡ ºÐ¼®     ¸Û¸Û
05/20 5123
21   setsockopt() Ãë¾àÁ¡ : °ø°ÝÀÇ ÇÇÇØ     ¸Û¸Û
05/20 3765
20   setsockopt() Ãë¾àÁ¡ : °ø°ÝÀÇ ´ë»ó[4]     ¸Û¸Û
05/20 4453
19   [5¿ù 20ÀÏ] 2.6.3°ú 2.4.25 ÀÌÇÏ ¸®´ª½º Ä¿³ÎÀÇ setsockopt ½Ã½ºÅÛ ÄÝ Ãë¾àÁ¡[5]     ¸Û¸Û
05/20 9255
18   do_mremap() Ãë¾àÁ¡ 2 : Ãë¾àÁ¡ ÆÐÄ¡[1]     ¸Û¸Û
03/16 3356
17   do_mremap() Ãë¾àÁ¡ 2 : Ãë¾àÁ¡ ºÐ¼®[4]     ¸Û¸Û
03/16 3580
16   do_mremap() Ãë¾àÁ¡ 2 : Ãë¾àÁ¡ ÇÇÇØ     ¸Û¸Û
03/16 3388
15   do_mremap() Ãë¾àÁ¡ 2 : °ø°ÝÀÇ ´ë»ó[2]     ¸Û¸Û
03/16 3365
14   [3¿ù 1ÀÏ] ¸®´ª½º Ä¿³Î do_mremap ³»ºÎ ÇÔ¼öÀÇ ¶Ç ´Ù¸¥ Ãë¾àÁ¡.     ¸Û¸Û
03/15 5057
13   do_mremap() Ãë¾àÁ¡ : Ãë¾àÁ¡ ÆÐÄ¡[8]     ¸Û¸Û
01/27 3646
  do_mremap() Ãë¾àÁ¡ : Ãë¾àÁ¡ ºÐ¼®     ¸Û¸Û
01/27 4072
11   do_mremap() Ãë¾àÁ¡ : °ø°ÝÀÇ ÇÇÇØ     ¸Û¸Û
01/27 3434
10   do_mremap() Ãë¾àÁ¡ : Ãë¾àÁ¡ È®ÀÎ[1]     ¸Û¸Û
01/27 3474
9   do_mremap() Ãë¾àÁ¡ : °ø°ÝÀÇ ´ë»ó     ¸Û¸Û
01/27 4018
8   [1¿ù 15ÀÏ] ¹öÀü 2.4.23 & 2.6.0 ÀÌÇÏ ¸®´ª½º Ä¿³ÎÀÇ do_mremap() Ãë¾àÁ¡[1]     ¸Û¸Û
01/27 7395
7   do_brk() Ãë¾àÁ¡ : ¹æ¾î ¸ðµâÀ» ÀÌ¿ëÇÑ ÆÐÄ¡[3]     ¸Û¸Û
12/17 4019
6   do_brk() Ãë¾àÁ¡ : Ä¿³Î ¾÷±×·¹À̵带 ÅëÇÑ ÆÐÄ¡     ¸Û¸Û
12/17 3810
5   do_brk() Ãë¾àÁ¡ : Ãë¾àÁ¡ ÆÐÄ¡[3]     ¸Û¸Û
12/17 4411
4   do_brk() Ãë¾àÁ¡ : Ãë¾àÁ¡ ºÐ¼®[1]     ¸Û¸Û
12/17 6332
3   do_brk() Ãë¾àÁ¡ : °ø°ÝÀÇ ÇÇÇØ     ¸Û¸Û
12/17 4602
2   do_brk() Ãë¾àÁ¡ : °ø°ÝÀÇ ´ë»ó     ¸Û¸Û
12/17 4993
1   [12¿ù 17ÀÏ] ¹öÀü 2.4.22 ÀÌÇÏ ¸®´ª½º Ä¿³ÎÀÇ do_brk() Ãë¾àÁ¡[2]     ¸Û¸Û
12/17 7537
1

Copyright 1999-2024 Zeroboard / skin by Hackerschool.org / Secure Patch by Hackerschool.org