From 2ff35859a687c16a5545283104dee3e628e3da8c Mon Sep 17 00:00:00 2001 From: adfoster-r7 Date: Sun, 23 Jul 2023 01:59:12 +0100 Subject: [PATCH] wip --- foo.rb | 75 +++++++++++++++--------- modules/payloads/singles/osx/x64/exec.rb | 2 +- 2 files changed, 47 insertions(+), 30 deletions(-) diff --git a/foo.rb b/foo.rb index f0469d6c09c9..ebf86ac33f98 100644 --- a/foo.rb +++ b/foo.rb @@ -1,3 +1,5 @@ +require 'shellwords' + class Payload def align_to_16_bytes(value, required_byte_alignment: 16) return value if value % required_byte_alignment == 0 @@ -14,70 +16,85 @@ def register_byte_size end def required_bytes_for_argv_pointers - register_byte_size * 2 + null_pointer_count = 1 + register_byte_size * (argc + null_pointer_count) + end + + def argc + # program name + 1 end - def write_program_name_and_argv_pointers(bytes) + # @param [Array] cmd_parts Such as ['/bin/bash', '-c' 'whoami'] + def write_program_name_and_argv_pointers(cmd_parts) asm = [] - required_stack_size = align_to_16_bytes(bytes.length + required_bytes_for_argv_pointers) + # 16 byte align each cmd_part value + argv_byte_size = cmd_parts.sum { |cmd_part| align_to_16_bytes("#{cmd_part}\x00".length) } + required_stack_size = argv_byte_size + 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" + raise NotImplementedError, "Byte length #{required_stack_size} too large and cannot be encoded" end stack_pointer_offset = required_stack_size - - # Program name, argv - bytes.each_slice(register_byte_size) do |slice| + argv_stack_offsets = [] + cmd_parts.each do |cmd_part| + argv_stack_offsets << stack_pointer_offset + cmd_part_bytes = cmd_part.bytes # 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 + cmd_part_bytes += [0] * (align_to_16_bytes(cmd_part_bytes.length) - cmd_part_bytes.length) + cmd_part_bytes.each_slice(register_byte_size) do |slice| + asm << "# Starting bytes: #{slice.inspect} (#{slice.map { |value| value >= 32 && value <= 126 ? value.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 end + argv_pointer_offset = stack_pointer_offset # 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 << "sub x5, x5, ##{argv_stack_offsets[0]} // 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[] = { #{cmd_parts.map(&:inspect).join(", ")}, NULL };" - asm + require 'pry-byebug'; binding.pry + { asm: asm, argv_byte_size: argv_byte_size, argc_offset: argv_pointer_offset } end def datastore - datastore = { 'CMD' => '/bin/bash' } - # datastore = { 'CMD' => '/Users/jenkins/testing/test' } + # datastore = { 'CMD' => '/bin/bash' } + datastore = { 'CMD' => '/Users/jenkins/testing/test a b c d' } datastore end def generate asm = [] - # enc = ''.b - # enc << "".b - - puts asm.join("\n") + cmd_str = datastore['CMD'] || '' + cmd_parts = Shellwords.shellsplit(cmd_str) # Uses program = datastore['CMD'] bytes = program.bytes # Write the bytes to the stack below the current sp location - bytes = "#{program.b}\x00".bytes + # 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) + data_result = write_program_name_and_argv_pointers(cmd_parts) + data_asm = data_result[:asm] + data_argv_byte_size = data_result[:argv_byte_size] + asm += data_asm 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 << "sub x0, x0, ##{data_argv_byte_size} // 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" diff --git a/modules/payloads/singles/osx/x64/exec.rb b/modules/payloads/singles/osx/x64/exec.rb index 425e91b1e307..6bbaec29f3ed 100644 --- a/modules/payloads/singles/osx/x64/exec.rb +++ b/modules/payloads/singles/osx/x64/exec.rb @@ -55,6 +55,6 @@ def generate(_opts = {}) # "\x48\xc7\xc0\x3b\x00\x00\x02" + # mov rax, 0x200003b (execve) # "\x0f\x05" # syscall - "\x40\x00\x20\xD4".force_encoding("ASCII") + " end end