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 ca3d77d commit 2ff3585
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 30 deletions.
75 changes: 46 additions & 29 deletions foo.rb
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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<String>] 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"
Expand Down
2 changes: 1 addition & 1 deletion modules/payloads/singles/osx/x64/exec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit 2ff3585

Please sign in to comment.