Module: PWN::Plugins::Serial

Defined in:
lib/pwn/plugins/serial.rb

Overview

This plugin is used for interacting with serial devices including, but not limited to, modems (including cellphone radios), legacy equipment, arduinos, & other misc ftdi devices

Class Method Summary collapse

Class Method Details

.authorsObject

Author(s)

0day Inc. <[email protected]>



266
267
268
269
270
# File 'lib/pwn/plugins/serial.rb', line 266

public_class_method def self.authors
  "AUTHOR(S):
    0day Inc. <[email protected]>
  "
end

.connect(opts = {}) ⇒ Object

Supported Method Parameters

serial_obj = PWN::Plugins::Serial.connect(

block_dev: 'optional - serial block device path (defaults to /dev/ttyUSB0)',
baud: 'optional - (defaults to 9600)',
data_bits: 'optional - (defaults to 8)',
stop_bits: 'optional - (defaults to 1)',
parity: 'optional - :even|:mark|:odd|:space|:none (defaults to :none)',
flow_control: 'optional - :none||:hard||:soft (defaults to :none)'

)



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/pwn/plugins/serial.rb', line 23

public_class_method def self.connect(opts = {})
  block_dev = opts[:block_dev].to_s if File.exist?(
    opts[:block_dev].to_s
  )
  block_dev = '/dev/ttyUSB0' if opts[:block_dev].nil?

  baud = if opts[:baud].nil?
           9_600
         else
           opts[:baud].to_i
         end

  data_bits = if opts[:data_bits].nil?
                8
              else
                opts[:data_bits].to_i
              end

  stop_bits = if opts[:stop_bits].nil?
                1
              else
                opts[:stop_bits].to_i
              end

  case opts[:parity].to_s.to_sym
  when :even
    parity = SerialPort::EVEN
  when :mark
    parity = SerialPort::MARK
  when :odd
    parity = SerialPort::ODD
  when :space
    parity = SerialPort::SPACE
  else
    parity = SerialPort::NONE
  end

  case opts[:flow_control].to_s.to_sym
  when :hard
    flow_control = SerialPort::HARD
  when :soft
    flow_control = SerialPort::SOFT
  else
    flow_control = SerialPort::NONE
  end

  serial_conn = SerialPort.new(
    block_dev,
    baud,
    data_bits,
    stop_bits,
    parity,
    flow_control
  )

  serial_obj = {}
  serial_obj[:serial_conn] = serial_conn
  serial_obj[:session_thread] = init_session_thread(
    serial_conn: serial_conn
  )

  serial_obj
rescue StandardError => e
  disconnect(serial_obj: serial_obj) unless serial_obj.nil?
  raise e
end

.disconnect(opts = {}) ⇒ Object

Supported Method Parameters

PWN::Plugins::Serial.disconnect(

serial_obj: 'required - serial_obj returned from #connect method'

)



252
253
254
255
256
257
258
259
260
261
262
# File 'lib/pwn/plugins/serial.rb', line 252

public_class_method def self.disconnect(opts = {})
  serial_obj = opts[:serial_obj]
  serial_conn = serial_obj[:serial_conn]
  session_thread = serial_obj[:session_thread]
  flush_session_data
  session_thread.terminate
  serial_conn.close
  serial_conn = nil
rescue StandardError => e
  raise e
end

.dump_session_dataObject

Supported Method Parameters

session_data = PWN::Plugins::Serial.dump_session_data



232
233
234
235
236
# File 'lib/pwn/plugins/serial.rb', line 232

public_class_method def self.dump_session_data
  @session_data
rescue StandardError => e
  raise e
end

.flush_session_dataObject

Supported Method Parameters

session_data = PWN::Plugins::Serial.flush_session_data



241
242
243
244
245
# File 'lib/pwn/plugins/serial.rb', line 241

public_class_method def self.flush_session_data
  @session_data.clear
rescue StandardError => e
  raise e
end

.get_line_state(opts = {}) ⇒ Object

Supported Method Parameters

line_state = PWN::Plugins::Serial.get_line_state(

serial_obj: 'required serial_obj returned from #connect method'

)



124
125
126
127
128
129
130
131
# File 'lib/pwn/plugins/serial.rb', line 124

public_class_method def self.get_line_state(opts = {})
  serial_obj = opts[:serial_obj]
  serial_conn = serial_obj[:serial_conn]
  serial_conn.get_signals
rescue StandardError => e
  disconnect(serial_obj: serial_obj) unless serial_obj.nil?
  raise e
end

.get_modem_params(opts = {}) ⇒ Object

Supported Method Parameters

modem_params = PWN::Plugins::Serial.get_modem_params(

serial_obj: 'required - serial_obj returned from #connect method'

)



138
139
140
141
142
143
144
145
# File 'lib/pwn/plugins/serial.rb', line 138

public_class_method def self.get_modem_params(opts = {})
  serial_obj = opts[:serial_obj]
  serial_conn = serial_obj[:serial_conn]
  serial_conn.get_modem_params
rescue StandardError => e
  disconnect(serial_obj: serial_obj) unless serial_obj.nil?
  raise e
end

.helpObject

Display Usage for this Module



274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
# File 'lib/pwn/plugins/serial.rb', line 274

public_class_method def self.help
  puts "USAGE:
    serial_obj = #{self}.connect(
      block_dev: 'optional serial block device path (defaults to /dev/ttyUSB0)',
      baud: 'optional (defaults to 9600)',
      data_bits: 'optional (defaults to 8)',
      stop_bits: 'optional (defaults to 1)',
      parity: 'optional - :even|:mark|:odd|:space|:none (defaults to :none)',
      flow_control: 'optional - :none||:hard||:soft (defaults to :none)'
    )

    line_state = #{self}.get_line_state(
      serial_obj: 'required serial_obj returned from #connect method'
    )

    modem_params = #{self}.get_modem_params(
      serial_obj: 'required serial_obj returned from #connect method'
    )

    #{self}.request(
      serial_obj: 'required serial_obj returned from #connect method',
      payload: 'required - array of bytes OR string to write to serial device (e.g. [0x00, 0x41, 0x90, 0x00] OR \"ATDT+15555555\r\n\"'
    )

    #{self}.response(
      serial_obj: 'required serial_obj returned from #connect method'
    )

    session_data_arr = #{self}.dump_session_data

    #{self}.flush_session_data

    #{self}.disconnect(
      serial_obj: 'required serial_obj returned from #connect method'
    )

    #{self}.authors
  "
end

.request(opts = {}) ⇒ Object

Supported Method Parameters

PWN::Plugins::Serial.request(

serial_obj: 'required serial_obj returned from #connect method',
payload: 'required - array of bytes OR string to write to serial device (e.g. [0x00, 0x41, 0x90, 0x00] OR "ATDT+15555555\r\n"'

)



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/pwn/plugins/serial.rb', line 153

public_class_method def self.request(opts = {})
  serial_obj = opts[:serial_obj]
  payload = opts[:payload]
  serial_conn = serial_obj[:serial_conn]

  byte_arr = payload
  byte_arr = payload.chars if payload.instance_of?(String)

  byte_arr.each do |byte|
    serial_conn.putc(byte)
  end

  serial_conn.flush
rescue StandardError => e
  disconnect(serial_obj: serial_obj) unless serial_obj.nil?
  raise e
end

.response(opts = {}) ⇒ Object

Supported Method Parameters

PWN::Plugins::Serial.response(

serial_obj: 'required - serial_obj returned from #connect method'

)



188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'lib/pwn/plugins/serial.rb', line 188

public_class_method def self.response(opts = {})
  serial_obj = opts[:serial_obj]

  raw_byte_arr = dump_session_data

  hex_esc_raw_resp = ''
  raw_byte_arr.each do |byte|
    # this_byte = "\s#{byte.unpack1('H*')}"
    this_byte = byte.unpack1('H*')
    # Needed when #unpack1 returns 2 bytes instead of one
    # e.g."ް" translates to deb0 (that's not a double quote ")
    # instead of de b0
    # this condition is ghetto-hacker-ish.
    if this_byte.length == 4
      byte_one = this_byte[1..2]
      byte_two = this_byte[-2..-1]
      hex_esc_raw_resp = "#{hex_esc_raw_resp}\s#{byte_one}"
      hex_esc_raw_resp = "#{hex_esc_raw_resp}\s#{byte_two}"
    else
      hex_esc_raw_resp = "#{hex_esc_raw_resp}\s#{this_byte}"
    end
  end

  # Return command response array in space-delimited hex
  cmd_response_arr = hex_esc_raw_resp.upcase.strip.split(/(?=FF)/)
  cmd_response_arr.map(&:strip)
rescue StandardError => e
  # Flush Responses for Next Request
  flush_session_data

  raise e
end