Module: Msf::Exploit::Remote::Gdb

Includes:
Tcp
Defined in:
lib/msf/core/exploit/gdb.rb

Overview

Implement some helpers for communicating with a remote gdb instance.

More info on the gdb protocol can be found here: sourceware.org/gdb/current/onlinedocs/gdb/Overview.html#Overview

Defined Under Namespace

Classes: BadAckError, BadChecksumError, BadResponseError

Constant Summary collapse

GDB_FEATURES =

Default list of supported GDB features to send them to the target

'qSupported:multiprocess+;qRelocInsn+;qvCont+;'
PC_REGISTERS =

Maps index of register in GDB that holds $PC to architecture

{
  '08' => ARCH_X86,
  '10' => ARCH_X64
}

Instance Attribute Summary

Attributes included from Tcp

#sock

Instance Method Summary collapse

Methods included from Tcp

#chost, #cleanup, #connect, #connect_timeout, #cport, #deregister_tcp_options, #disconnect, #handler, #initialize, #lhost, #lport, #peer, #print_prefix, #proxies, #rhost, #rport, #set_tcp_evasions, #shutdown, #ssl, #ssl_cipher, #ssl_verify_mode, #ssl_version

Instance Method Details

#checksum(str) ⇒ String

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).

Parameters:

  • str (String)

    the string to calculate the checksum of

Returns:

  • (String)

    hex string containing checksum


97
98
99
# File 'lib/msf/core/exploit/gdb.rb', line 97

def checksum(str)
  "%02x" % str.bytes.inject(0) { |b, sum| (sum+b)%256 }
end

#continue(opts = {}) ⇒ Object

Continues execution of the remote process

Parameters:

  • opts (Hash) (defaults to: {})

    the options hash

Options Hash (opts):

  • :read (Boolean)

    read the response


143
144
145
146
# File 'lib/msf/core/exploit/gdb.rb', line 143

def continue(opts={})
  send_cmd 'vCont;c'
  read_response if opts.fetch(:read, true)
end

#decode_rle(msg) ⇒ String

Implements decoding of gdbserver's Run-Length-Encoding that is applied on some hex values to collapse repeated characters.

sourceware.org/gdb/current/onlinedocs/gdb/Overview.html#Binary-Data

Parameters:

  • msg (String)

    the message to decode

Returns:

  • (String)

    the decoded result


86
87
88
89
90
91
# File 'lib/msf/core/exploit/gdb.rb', line 86

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

#detach(opts = {}) ⇒ Object

Detaches from the remote process

Parameters:

  • opts (Hash) (defaults to: {})

    the options hash

Options Hash (opts):

  • :read (Boolean)

    read the response


151
152
153
154
# File 'lib/msf/core/exploit/gdb.rb', line 151

def detach(opts={})
  send_cmd 'D'
  read_response if opts.fetch(:read, true)
end

#enable_extended_modeObject


175
176
177
178
# File 'lib/msf/core/exploit/gdb.rb', line 175

def enable_extended_mode
  send_cmd("!")
  read_response
end

#handshake(features = GDB_FEATURES) ⇒ Object

Performs a handshake packet exchange

Parameters:

  • features (String) (defaults to: GDB_FEATURES)

    the list of supported features to tell the remote host that the client supports (defaults to DEFAULT_GDB_FEATURES)


183
184
185
186
# File 'lib/msf/core/exploit/gdb.rb', line 183

def handshake(features=GDB_FEATURES)
  send_cmd features
  read_response # lots of flags, nothing interesting
end

#process_infoHash

Steps execution and finds $PC pointer and architecture

Returns:

  • (Hash)

    with :arch and :pc keys containing architecture and PC pointer

Raises:


121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/msf/core/exploit/gdb.rb', line 121

def process_info
  data = step
  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]

  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)), ''),
    pc_raw: Integer(pc, 16)
  }
end

#read_ackObject

Reads an ACK packet from the wire

Raises:


44
45
46
47
48
49
# File 'lib/msf/core/exploit/gdb.rb', line 44

def read_ack
  unless sock.get_once(1) == '+'
    raise BadAckError
  end
  vprint_status('Received ack...')
end

#read_response(opts = {}) ⇒ String

Reads (and possibly decodes) from the socket and sends an ACK to verify receipt

Parameters:

  • opts (Hash) (defaults to: {})

    the options hash

Options Hash (opts):

  • :decode (Boolean)

    rle decoding should be applied to the response

  • :verify (Boolean)

    verify the response's checksum

Returns:

  • (String)

    the response

Raises:


68
69
70
71
72
73
74
75
76
77
# File 'lib/msf/core/exploit/gdb.rb', line 68

def read_response(opts={})
  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
  res
end

#run(filename) ⇒ Object


170
171
172
173
# File 'lib/msf/core/exploit/gdb.rb', line 170

def run(filename)
  send_cmd "vRun;#{Rex::Text.to_hex(filename, '')}"
  read_response
end

#send_ackObject

Send an ACK packet


37
38
39
40
# File 'lib/msf/core/exploit/gdb.rb', line 37

def send_ack
  sock.put('+')
  vprint_status('Sending ack...')
end

#send_cmd(cmd) ⇒ Object

Sends a command and receives an ACK from the remote.

Parameters:

  • cmd (String)

    the gdb command to run. The command is will be wrapped '$' prefix and checksum.


54
55
56
57
58
59
# File 'lib/msf/core/exploit/gdb.rb', line 54

def send_cmd(cmd)
  full_cmd = '$' + cmd + '#' + checksum(cmd)
  vprint_status('Sending cmd: '+full_cmd)
  sock.put(full_cmd)
  read_ack
end

#stepString

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)

Returns:

  • (String)

    a list of key/value pairs, including current PC


165
166
167
168
# File 'lib/msf/core/exploit/gdb.rb', line 165

def step
  send_cmd 'vCont;s'
  read_response(decode: true)
end

#verify_checksum(res) ⇒ Boolean

Verifies a response's checksum

Parameters:

  • res (String)

    the response to check

Returns:

  • (Boolean)

    whether the checksum is valid


104
105
106
107
# File 'lib/msf/core/exploit/gdb.rb', line 104

def verify_checksum(res)
  msg, chksum = res.match(/^\$(.*)#(\h{2})$/)[1..2]
  checksum(msg) == chksum
end

#write(buf, addr) ⇒ Object

Writes the buffer buf to the address addr in the remote process's memory

Parameters:

  • buf (String)

    the buffer to write

  • addr (String)

    the hex-encoded address to write to


112
113
114
115
116
# File 'lib/msf/core/exploit/gdb.rb', line 112

def write(buf, addr)
  hex = Rex::Text.to_hex(buf, '')
  send_cmd "M#{addr},#{buf.length.to_s(16)}:#{hex}"
  read_response
end