pwn 0.5.258 → 0.5.260

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 443e67e982f8e4a3dcc8545434d8a26d25e2bbf09077d55c5a34c43a452f8046
4
- data.tar.gz: 9a7c64e5d7d5b8147d7c6035d9420d9e23c1942956e12c10d157fa7f011f881d
3
+ metadata.gz: 86b3d84a00228717b3006d33ebad22d955b97fae6550df11ddd3ec6efe00e21c
4
+ data.tar.gz: 382592644674ac400a9eb7f341d33ff5136a321fae2277d78fc0f467bab4f58e
5
5
  SHA512:
6
- metadata.gz: e690ce4edcfad20e36cdc78969fdf02763f0158d6396a2fb768168afa36b0463de714fbc47d1a9f401f6ad51e067fb6f7d1c41ec54a55cb277a0ac32c173ede0
7
- data.tar.gz: 25ce8c4a5668197d47a6a943e9a2db6f79b55ff69be35d8c9f588d9b7e87d7eb6055653997df5baf2aace84ff9f5f42f6671b7316f001f1ccc9d1603a5c220ca
6
+ metadata.gz: 3c4c8b8b013f272e97a50f600751eaf861605bd331cc12f94945ce60e584824fcc253239792ea6242e82afdd91aea5429e9a8d925e97d78d5bdae278fcfa6aef
7
+ data.tar.gz: 8335d9dfcdd0fce241db4ca601b54137a32dcf773f615f2ca5bb04d8917452b4ee73857af68fd2d76ea4b455fdb776b827269742357d896b0115968805c49e87
data/.rubocop_todo.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2025-04-21 18:26:41 UTC using RuboCop version 1.75.2.
3
+ # on 2025-04-22 21:19:03 UTC using RuboCop version 1.75.3.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
@@ -41,7 +41,7 @@ Lint/RedundantTypeConversion:
41
41
  - 'lib/pwn/plugins/jenkins.rb'
42
42
  - 'lib/pwn/plugins/repl.rb'
43
43
 
44
- # Offense count: 319
44
+ # Offense count: 303
45
45
  # This cop supports safe autocorrection (--autocorrect).
46
46
  # Configuration parameters: AutoCorrect.
47
47
  Lint/UselessAssignment:
@@ -89,7 +89,7 @@ Metrics/MethodLength:
89
89
  Exclude:
90
90
  - 'lib/pwn/banner/code_cave.rb'
91
91
 
92
- # Offense count: 11
92
+ # Offense count: 12
93
93
  # Configuration parameters: CountComments, Max, CountAsOne.
94
94
  Metrics/ModuleLength:
95
95
  Exclude:
@@ -103,6 +103,7 @@ Metrics/ModuleLength:
103
103
  - 'lib/pwn/plugins/open_ai.rb'
104
104
  - 'lib/pwn/plugins/packet.rb'
105
105
  - 'lib/pwn/plugins/repl.rb'
106
+ - 'lib/pwn/plugins/son_micro_rfid.rb'
106
107
  - 'lib/pwn/plugins/transparent_browser.rb'
107
108
 
108
109
  # Offense count: 1
data/README.md CHANGED
@@ -37,7 +37,7 @@ $ cd /opt/pwn
37
37
  $ ./install.sh
38
38
  $ ./install.sh ruby-gem
39
39
  $ pwn
40
- pwn[v0.5.258]:001 >>> PWN.help
40
+ pwn[v0.5.260]:001 >>> PWN.help
41
41
  ```
42
42
 
43
43
  [![Installing the pwn Security Automation Framework](https://raw.githubusercontent.com/0dayInc/pwn/master/documentation/pwn_install.png)](https://youtu.be/G7iLUY4FzsI)
@@ -52,7 +52,7 @@ $ rvm use ruby-3.4.1@pwn
52
52
  $ gem uninstall --all --executables pwn
53
53
  $ gem install --verbose pwn
54
54
  $ pwn
55
- pwn[v0.5.258]:001 >>> PWN.help
55
+ pwn[v0.5.260]:001 >>> PWN.help
56
56
  ```
57
57
 
58
58
  If you're using a multi-user install of RVM do:
@@ -62,7 +62,7 @@ $ rvm use ruby-3.4.1@pwn
62
62
  $ rvmsudo gem uninstall --all --executables pwn
63
63
  $ rvmsudo gem install --verbose pwn
64
64
  $ pwn
65
- pwn[v0.5.258]:001 >>> PWN.help
65
+ pwn[v0.5.260]:001 >>> PWN.help
66
66
  ```
67
67
 
68
68
  PWN periodically upgrades to the latest version of Ruby which is reflected in `/opt/pwn/.ruby-version`. The easiest way to upgrade to the latest version of Ruby from a previous PWN installation is to run the following script:
@@ -36,11 +36,6 @@ OptionParser.new do |options|
36
36
  end
37
37
  end.parse!
38
38
 
39
- # if opts.empty?
40
- # puts `#{File.basename($PROGRAM_NAME)} --help`
41
- # exit 1
42
- # end
43
-
44
39
  begin
45
40
  block_dev = opts[:block_dev]
46
41
  baud = opts[:baud]
@@ -31,18 +31,17 @@ OptionParser.new do |options|
31
31
  opts[:parity] = p
32
32
  end
33
33
 
34
- options.on('-fFLOWCTRL', '--flow-control=FLOWCTRL', '<Optional - none||hard||soft (defaults to none)>') do |f|
34
+ options.on('-fFLOWCTRL', '--flow-control=FLOWCTRL', '<Optional - none|hard|soft (defaults to none)>') do |f|
35
35
  opts[:flow_control] = f
36
36
  end
37
- end.parse!
38
37
 
39
- if opts.empty?
40
- puts `#{File.basename($PROGRAM_NAME)} --help`
41
- exit 1
42
- end
38
+ options.on('-v', '--verbose', '<Optional - Enable verbose output>') do
39
+ opts[:verbose] = true
40
+ end
41
+ end.parse!
43
42
 
44
43
  begin
45
- block_dev = opts[:block_dev] if File.exist?(opts[:block_dev])
44
+ block_dev = opts[:block_dev] if opts[:block_dev] && File.exist?(opts[:block_dev])
46
45
  baud = opts[:baud]
47
46
  data_bits = opts[:data_bits]
48
47
  stop_bits = opts[:stop_bits]
@@ -59,9 +58,11 @@ begin
59
58
  )
60
59
 
61
60
  puts "- Welcome to #{File.basename($PROGRAM_NAME)} -"
62
- puts "Connected via: #{block_dev} @ #{son_micro_rfid_obj[:serial_conn].modem_params}"
63
- puts "Flow Control: #{son_micro_rfid_obj[:serial_conn].flow_control}"
64
- puts "Signals: #{son_micro_rfid_obj[:serial_conn].signals}"
61
+ if opts[:verbose]
62
+ puts "Connected via: #{block_dev} @ #{son_micro_rfid_obj[:serial_conn].modem_params}"
63
+ puts "Flow Control: #{son_micro_rfid_obj[:serial_conn].flow_control}"
64
+ puts "Signals: #{son_micro_rfid_obj[:serial_conn].signals}"
65
+ end
65
66
 
66
67
  exec_resp = PWN::Plugins::SonMicroRFID.exec(
67
68
  son_micro_rfid_obj: son_micro_rfid_obj,
@@ -78,11 +79,20 @@ begin
78
79
  # Main Menu
79
80
  menu_msg = ''
80
81
  loop do
82
+ unless menu_msg.include?('ERROR')
83
+ exec_resp = PWN::Plugins::SonMicroRFID.exec(
84
+ son_micro_rfid_obj: son_micro_rfid_obj,
85
+ cmd: :antenna_power,
86
+ params: :on
87
+ )
88
+ end
89
+
81
90
  puts "\n>> MAIN MENU OPTIONS:"
82
- puts '[(R)ead Card]'
83
- puts '[(B)ackup Card]'
84
- puts '[(C)opy Card]'
85
- puts '[(L)oad Card from File]'
91
+ puts '[(R)ead Tag]'
92
+ puts '[(B)ackup Tag]'
93
+ puts '[(C)lone Tag]'
94
+ puts '[(L)oad Data from File and Write to Tag]'
95
+ puts '[(U)pdate Tag]'
86
96
  puts '[(W)arm Reset]'
87
97
  puts '[(Q)uit]'
88
98
  puts menu_msg
@@ -92,41 +102,91 @@ begin
92
102
 
93
103
  case option
94
104
  when :R
95
- menu_msg = 'READ CARD'
96
- rfid_data = PWN::Plugins::SonMicroRFID.read_card(
97
- son_micro_rfid_obj: son_micro_rfid_obj
98
- )
105
+ menu_msg = 'READ TAG'
106
+ begin
107
+ rfid_data = PWN::Plugins::SonMicroRFID.read_tag(son_micro_rfid_obj: son_micro_rfid_obj)
108
+ puts "\nRFID Data:"
109
+ puts "Tag ID: #{rfid_data[:tag_id]}"
110
+ puts "Block Data: #{rfid_data[:block_data]}" if rfid_data[:block_data]
111
+ rescue StandardError => e
112
+ menu_msg = "ERROR: Failed to read tag - #{e.message}"
113
+ end
99
114
  when :B
100
- menu_msg = 'BACKUP CARD TO FILE'
101
- rfid_data = PWN::Plugins::SonMicroRFID.backup_card(
102
- son_micro_rfid_obj: son_micro_rfid_obj
103
- )
115
+ menu_msg = 'BACKUP TAG TO FILE'
116
+ begin
117
+ rfid_data = PWN::Plugins::SonMicroRFID.backup_tag(son_micro_rfid_obj: son_micro_rfid_obj)
118
+ puts "\nBackup successful. RFID Data:"
119
+ puts "Tag ID: #{rfid_data[:tag_id]}"
120
+ puts "Block Data: #{rfid_data[:block_data]}" if rfid_data[:block_data]
121
+ rescue StandardError => e
122
+ menu_msg = "ERROR: Failed to backup tag - #{e.message}"
123
+ end
104
124
  when :C
105
- menu_msg = 'COPY CARD'
106
- rfid_data = PWN::Plugins::SonMicroRFID.copy_card(
107
- son_micro_rfid_obj: son_micro_rfid_obj
108
- )
125
+ menu_msg = 'CLONE TAG'
126
+ print 'This will overwrite the target tag. Continue? [y/N]: '
127
+ next unless gets.chomp.strip.upcase == 'Y'
128
+
129
+ begin
130
+ rfid_data = PWN::Plugins::SonMicroRFID.clone_tag(son_micro_rfid_obj: son_micro_rfid_obj)
131
+ puts "\nClone successful. RFID Data:"
132
+ puts "Tag ID: #{rfid_data[:tag_id]}"
133
+ puts "Block Data: #{rfid_data[:block_data]}" if rfid_data[:block_data]
134
+ rescue StandardError => e
135
+ menu_msg = "ERROR: Failed to clone tag - #{e.message}"
136
+ end
109
137
  when :L
110
138
  menu_msg = 'LOAD FROM FILE'
111
- rfid_data = PWN::Plugins::SonMicroRFID.load_card_from_file(
112
- son_micro_rfid_obj: son_micro_rfid_obj
113
- )
139
+ print 'This will overwrite the target tag. Continue? [y/N]: '
140
+ next unless gets.chomp.strip.upcase == 'Y'
141
+
142
+ begin
143
+ rfid_data = PWN::Plugins::SonMicroRFID.load_tag_from_file(son_micro_rfid_obj: son_micro_rfid_obj)
144
+ puts "\nLoad successful. RFID Data:"
145
+ puts "Tag ID: #{rfid_data[:tag_id]}"
146
+ puts "Block Data: #{rfid_data[:block_data]}" if rfid_data[:block_data]
147
+ rescue StandardError => e
148
+ menu_msg = "ERROR: Failed to load tag - #{e.message}"
149
+ end
150
+ when :U
151
+ menu_msg = 'UPDATE TAG'
152
+ print 'This will modify the tag\'s data. Continue? [y/N]: '
153
+ next unless gets.chomp.strip.upcase == 'Y'
154
+
155
+ begin
156
+ rfid_data = PWN::Plugins::SonMicroRFID.update_tag(son_micro_rfid_obj: son_micro_rfid_obj)
157
+ puts "\nUpdate successful. RFID Data:"
158
+ puts "Tag ID: #{rfid_data[:tag_id]}"
159
+ puts "Block Data: #{rfid_data[:block_data]}" if rfid_data[:block_data]
160
+ rescue StandardError => e
161
+ menu_msg = "ERROR: Failed to update tag - #{e.message}"
162
+ end
114
163
  when :W
115
164
  menu_msg = 'WARM RESET'
116
- exec_resp = PWN::Plugins::SonMicroRFID.exec(
117
- son_micro_rfid_obj: son_micro_rfid_obj,
118
- cmd: :reset
119
- )
165
+ begin
166
+ exec_resp = PWN::Plugins::SonMicroRFID.exec(
167
+ son_micro_rfid_obj: son_micro_rfid_obj,
168
+ cmd: :reset
169
+ )
170
+ puts 'Warm reset complete.'
171
+ rescue StandardError => e
172
+ menu_msg = "ERROR: Failed to reset - #{e.message}"
173
+ end
120
174
  when :Q
121
175
  exit
122
176
  else
123
177
  menu_msg = '****** ERROR: Invalid Menu Option Selected ******'
178
+ exec_resp = PWN::Plugins::SonMicroRFID.exec(
179
+ son_micro_rfid_obj: son_micro_rfid_obj,
180
+ cmd: :antenna_power,
181
+ params: :off
182
+ )
124
183
  end
125
184
  end
126
185
  rescue StandardError => e
186
+ puts "ERROR: #{e.message}"
127
187
  raise e
128
188
  rescue SystemExit, Interrupt
129
189
  puts "\nGoodbye."
130
190
  ensure
131
- son_micro_rfid_obj = PWN::Plugins::SonMicroRFID.disconnect(son_micro_rfid_obj: son_micro_rfid_obj) if son_micro_rfid_obj
191
+ PWN::Plugins::SonMicroRFID.disconnect(son_micro_rfid_obj: son_micro_rfid_obj) if son_micro_rfid_obj
132
192
  end
@@ -10,7 +10,7 @@ module PWN
10
10
  module MSR206
11
11
  # Logger instance for auditing and debugging
12
12
  private_class_method def self.logger
13
- @logger ||= Logger.new('msr206.log')
13
+ @logger ||= Logger.new('/tmp/pwn-msr206.log')
14
14
  end
15
15
 
16
16
  # Supported Method Parameters::
@@ -1,11 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'logger'
4
+ require 'timeout'
5
+
3
6
  module PWN
4
7
  module Plugins
5
8
  # This plugin is used for interacting with a SonMicro
6
- # SM132 USB RFID Reader / Writer (PCB V3) &&
7
- # SM2330-USB Rev.0
9
+ # SM132 USB RFID Reader / Writer (PCB V3) && SM2330-USB Rev.0
8
10
  module SonMicroRFID
11
+ # Logger instance for auditing and debugging
12
+ private_class_method def self.logger
13
+ @logger ||= Logger.new('/tmp/pwn-sonmicro_rfid.log')
14
+ end
15
+
9
16
  # Supported Method Parameters::
10
17
  # son_micro_rfid_obj = PWN::Plugins::SonMicroRFID.connect(
11
18
  # block_dev: 'optional - serial block device path (defaults to /dev/ttyUSB0)',
@@ -17,24 +24,25 @@ module PWN
17
24
  # )
18
25
 
19
26
  public_class_method def self.connect(opts = {})
20
- # Default Baud Rate for this Device is 19200
21
- opts[:block_dev] = '/dev/ttyUSB0' unless opts[:block_dev]
22
- opts[:baud] = 19_200 unless opts[:baud]
23
- opts[:data_bits] = 8 unless opts[:data_bits]
24
- opts[:stop_bits] = 1 unless opts[:stop_bits]
25
- opts[:parity] = :none unless opts[:parity]
26
- opts[:flow_control] = :none unless opts[:flow_control]
27
- son_micro_rfid_obj = PWN::Plugins::Serial.connect(opts)
27
+ opts[:block_dev] ||= '/dev/ttyUSB0'
28
+ opts[:baud] ||= 19_200
29
+ opts[:data_bits] ||= 8
30
+ opts[:stop_bits] ||= 1
31
+ opts[:parity] ||= :none
32
+ opts[:flow_control] ||= :none
33
+
34
+ logger.info("Connecting to #{opts[:block_dev]} at baud #{opts[:baud]}")
35
+ PWN::Plugins::Serial.connect(opts)
28
36
  rescue StandardError => e
37
+ logger.error("Connection failed: #{e.message}")
29
38
  disconnect(son_micro_rfid_obj: son_micro_rfid_obj) unless son_micro_rfid_obj.nil?
30
39
  raise e
31
40
  end
32
41
 
33
42
  # Supported Method Parameters::
34
- # cmds = PWN::Plugins::SonMicroRFID.list_cmds
43
+ # cmds = PWN::Plugins::SonMicroRFID.list_cmds
35
44
  public_class_method def self.list_cmds
36
- # Returns an Array of Symbols
37
- cmds = %i[
45
+ %i[
38
46
  reset
39
47
  firmware
40
48
  seek_for_tag
@@ -56,13 +64,15 @@ module PWN
56
64
  poll_buffer
57
65
  ]
58
66
  rescue StandardError => e
67
+ logger.error("Error listing commands: #{e.message}")
59
68
  raise e
60
69
  end
61
70
 
62
71
  # Supported Method Parameters::
63
- # params = PWN::Plugins::SonMicroRFID.list_params(
64
- # cmd: 'required - cmd returned from #list_cmds method',
72
+ # params = PWN::Plugins::SonMicroRFID.list_params(
73
+ # cmd: 'required - cmd returned from #list_cmds method'
65
74
  # )
75
+
66
76
  public_class_method def self.list_params(opts = {})
67
77
  cmd = opts[:cmd].to_s.scrub.strip.chomp
68
78
 
@@ -70,17 +80,17 @@ module PWN
70
80
  when :reset
71
81
  params = %i[reset_not_implemented]
72
82
  when :firmware
73
- params = %i[firmware_not_implemented]
83
+ params = %i[firmware_no_params_required]
74
84
  when :seek_for_tag
75
85
  params = %i[seek_for_tag_no_params_required]
76
86
  when :select_tag
77
87
  params = %i[select_tag_no_params_required]
78
88
  when :authenticate
79
- params = %i[authenticate_not_implemented]
89
+ params = %i[block key_type key]
80
90
  when :read_block
81
- params = %i[read_block_not_implemented]
91
+ params = %i[block]
82
92
  when :write_block
83
- params = %i[write_block_not_implemented]
93
+ params = %i[block data]
84
94
  when :write_value
85
95
  params = %i[write_value_not_implemented]
86
96
  when :write_four_byte_block
@@ -94,11 +104,11 @@ module PWN
94
104
  when :antenna_power
95
105
  params = %i[off on reset]
96
106
  when :read_port
97
- params = %i[antenna_power_not_implemented]
107
+ params = %i[read_port_not_implemented]
98
108
  when :write_port
99
109
  params = %i[write_port_not_implemented]
100
110
  when :halt
101
- params = %i[halt_not_implemented]
111
+ params = %i[halt_no_params_required]
102
112
  when :set_baud_rate
103
113
  params = %i[set_baud_rate_not_implemented]
104
114
  when :sleep
@@ -106,148 +116,134 @@ module PWN
106
116
  when :poll_buffer
107
117
  params = %i[poll_buffer_not_implemented]
108
118
  else
109
- raise "Unsupported Command: #{cmd}. Supported commands are:\n#{list_cmds}\n\n\n"
119
+ logger.error("Unsupported command: #{cmd}")
120
+ raise "Unsupported Command: #{cmd}. Supported commands are:\n#{list_cmds.join("\n")}\n"
110
121
  end
111
122
 
112
123
  params
113
124
  rescue StandardError => e
125
+ logger.error("Error listing parameters for #{cmd}: #{e.message}")
114
126
  raise e
115
127
  end
116
128
 
117
129
  # Supported Method Parameters::
118
130
  # parsed_cmd_resp_arr = parse_responses(
119
- # son_micro_rfid_obj: 'required - son_micro_rfid_obj returned from #connect method'
120
- # cmd_resp: 'required - command response string'
131
+ # son_micro_rfid_obj: 'required - son_micro_rfid_obj returned from #connect method',
132
+ # cmd: 'required - command symbol'
121
133
  # )
122
134
 
123
135
  private_class_method def self.parse_responses(opts = {})
124
136
  son_micro_rfid_obj = opts[:son_micro_rfid_obj]
125
137
  cmd = opts[:cmd].to_s.scrub.strip.chomp
126
138
 
127
- keep_parsing_responses = true
128
- next_response_detected = false
129
- all_cmd_responses = []
130
- a_cmd_r_len = 0
131
- last_a_cmd_r_len = 0
132
-
133
- parsed_cmd_resp_arr = []
134
- bytes_in_cmd_resp = 0
135
- cmd_resp = ''
136
-
137
- # Parse All Responses and add them to parsed_cmd_resp_arr
138
- while keep_parsing_responses
139
- until next_response_detected
140
- all_cmd_responses = PWN::Plugins::Serial.response(
141
- serial_obj: son_micro_rfid_obj
142
- )
143
- cmd_resp = all_cmd_responses.last
144
- bytes_in_cmd_resp = cmd_resp.split.length if cmd_resp
145
- a_cmd_r_len = all_cmd_responses.length
146
-
147
- # Dont proceed until the expected_cmd_resp_byte_len byte appears
148
- next_response_detected = true if bytes_in_cmd_resp > 3 &&
149
- a_cmd_r_len > last_a_cmd_r_len
150
- end
139
+ Timeout.timeout(5) do
140
+ keep_parsing_responses = true
151
141
  next_response_detected = false
152
- last_a_cmd_r_len = a_cmd_r_len
153
-
154
- # Third byte
155
- expected_cmd_resp_byte_len = cmd_resp.split[2].to_i(16) + 4
156
-
157
- # Fourth byte
158
- cmd_hex = cmd_resp.split[3]
142
+ all_cmd_responses = []
143
+ a_cmd_r_len = 0
144
+ last_a_cmd_r_len = 0
145
+
146
+ parsed_cmd_resp_arr = []
147
+ bytes_in_cmd_resp = 0
148
+ cmd_resp = ''
149
+
150
+ while keep_parsing_responses
151
+ until next_response_detected
152
+ all_cmd_responses = PWN::Plugins::Serial.response(serial_obj: son_micro_rfid_obj)
153
+ cmd_resp = all_cmd_responses.last
154
+ bytes_in_cmd_resp = cmd_resp.split.length if cmd_resp
155
+ a_cmd_r_len = all_cmd_responses.length
156
+ next_response_detected = true if bytes_in_cmd_resp > 3 && a_cmd_r_len > last_a_cmd_r_len
157
+ end
158
+ next_response_detected = false
159
+ last_a_cmd_r_len = a_cmd_r_len
159
160
 
160
- while bytes_in_cmd_resp < expected_cmd_resp_byte_len
161
- all_cmd_responses = PWN::Plugins::Serial.response(
162
- serial_obj: son_micro_rfid_obj
163
- )
161
+ expected_cmd_resp_byte_len = cmd_resp.split[2].to_i(16) + 4
162
+ cmd_hex = cmd_resp.split[3]
164
163
 
165
- cmd_resp = all_cmd_responses.last
166
- bytes_in_cmd_resp = cmd_resp.split.length
167
- # puts "EXPECTED CMD BYTE LEN: #{expected_cmd_resp_byte_len}"
168
- # puts "LAST CMD BYTE LEN: #{bytes_in_cmd_resp} >>>"
169
- # puts all_cmd_responses
170
- # puts "COMMAND HEX: #{cmd_hex}\n\n\n"
171
- end
164
+ while bytes_in_cmd_resp < expected_cmd_resp_byte_len
165
+ all_cmd_responses = PWN::Plugins::Serial.response(serial_obj: son_micro_rfid_obj)
166
+ cmd_resp = all_cmd_responses.last
167
+ bytes_in_cmd_resp = cmd_resp.split.length
168
+ end
172
169
 
173
- # puts "\nALL CMD RESPS >>>"
174
- # puts "#{all_cmd_responses}\n\n\n"
175
- parsed_cmd_resp_hash = {}
176
- parsed_cmd_resp_hash[:cmd_hex] = cmd_hex
177
- parsed_cmd_resp_hash[:cmd_desc] = cmd.to_sym
178
- parsed_cmd_resp_hash[:hex_resp] = cmd_resp
179
- resp_code = '?'
180
-
181
- # TODO: Detect EMV
182
- case cmd_hex
183
- when '82', '83'
184
- resp_code = cmd_resp.split[4]
170
+ parsed_cmd_resp_hash = {}
171
+ parsed_cmd_resp_hash[:cmd_hex] = cmd_hex
172
+ parsed_cmd_resp_hash[:cmd_desc] = cmd.to_sym
173
+ parsed_cmd_resp_hash[:hex_resp] = cmd_resp
174
+ resp_code = cmd_resp.split[4] || '?'
185
175
  parsed_cmd_resp_hash[:resp_code_hex] = resp_code
186
- case resp_code
187
- when '01'
188
- parsed_cmd_resp_hash[:resp_code_desc] = :mifare_ultralight
189
- parsed_cmd_resp_hash[:tag_id] = cmd_resp.split[5..-2].join(
190
- ' '
191
- )
192
- when '02'
193
- parsed_cmd_resp_hash[:resp_code_desc] = :mifare_classic_1k
194
- parsed_cmd_resp_hash[:tag_id] = cmd_resp.split[5..-2].join(
195
- ' '
196
- )
197
- when '03'
198
- parsed_cmd_resp_hash[:resp_code_desc] = :mifare_classic_4k
199
- parsed_cmd_resp_hash[:tag_id] = cmd_resp.split[5..-2].join(
200
- ' '
201
- )
202
- when '4C'
203
- parsed_cmd_resp_hash[:resp_code_desc] = :seeking_tag
204
- parsed_cmd_resp_hash[:tag_id] = :seeking_tag
205
- when '4E'
206
- parsed_cmd_resp_hash[:resp_code_desc] = :no_tag_present
207
- parsed_cmd_resp_hash[:tag_id] = :not_available
208
- when '55'
209
- parsed_cmd_resp_hash[:resp_code_desc] = :antenna_off
210
- parsed_cmd_resp_hash[:tag_id] = :not_available
211
- when 'FF'
212
- parsed_cmd_resp_hash[:resp_code_desc] = :unknown_tag_type
213
- parsed_cmd_resp_hash[:tag_id] = cmd_resp.split[5..-2].join(
214
- ' '
215
- )
176
+
177
+ case cmd_hex
178
+ when '82', '83'
179
+ case resp_code
180
+ when '01'
181
+ parsed_cmd_resp_hash[:resp_code_desc] = :mifare_ultralight
182
+ parsed_cmd_resp_hash[:tag_id] = cmd_resp.split[5..-2].join(' ')
183
+ when '02'
184
+ parsed_cmd_resp_hash[:resp_code_desc] = :mifare_classic_1k
185
+ parsed_cmd_resp_hash[:tag_id] = cmd_resp.split[5..-2].join(' ')
186
+ when '03'
187
+ parsed_cmd_resp_hash[:resp_code_desc] = :mifare_classic_4k
188
+ parsed_cmd_resp_hash[:tag_id] = cmd_resp.split[5..-2].join(' ')
189
+ when '4C'
190
+ parsed_cmd_resp_hash[:resp_code_desc] = :seeking_tag
191
+ parsed_cmd_resp_hash[:tag_id] = :seeking_tag
192
+ when '4E'
193
+ parsed_cmd_resp_hash[:resp_code_desc] = :no_tag_present
194
+ parsed_cmd_resp_hash[:tag_id] = :not_available
195
+ when '55'
196
+ parsed_cmd_resp_hash[:resp_code_desc] = :antenna_off
197
+ parsed_cmd_resp_hash[:tag_id] = :not_available
198
+ when 'FF'
199
+ parsed_cmd_resp_hash[:resp_code_desc] = :unknown_tag_type
200
+ parsed_cmd_resp_hash[:tag_id] = cmd_resp.split[5..-2].join(' ')
201
+ else
202
+ parsed_cmd_resp_hash[:resp_code_desc] = :unknown_resp_code
203
+ parsed_cmd_resp_hash[:tag_id] = :not_available
204
+ end
205
+ when '85' # Assumed for authenticate
206
+ parsed_cmd_resp_hash[:resp_code_desc] = resp_code == '00' ? :auth_success : :auth_failed
207
+ when '86' # Assumed for read_block
208
+ parsed_cmd_resp_hash[:resp_code_desc] = resp_code == '00' ? :read_success : :read_failed
209
+ parsed_cmd_resp_hash[:block_data] = cmd_resp.split[5..-2].join(' ')
210
+ when '87' # Assumed for write_block
211
+ parsed_cmd_resp_hash[:resp_code_desc] = resp_code == '00' ? :write_success : :write_failed
216
212
  else
217
- parsed_cmd_resp_hash[:resp_code_desc] = :unknown_resp_code
218
- parsed_cmd_resp_hash[:tag_id] = :not_available
213
+ parsed_cmd_resp_hash[:resp_code_desc] = :unknown_response
219
214
  end
220
- else
221
- parsed_cmd_resp_hash[:cmd_desc] = :not_available
222
- end
223
215
 
224
- keep_parsing_responses = false unless resp_code == '4C'
216
+ keep_parsing_responses = false unless resp_code == '4C'
217
+ parsed_cmd_resp_arr.push(parsed_cmd_resp_hash)
218
+ end
225
219
 
226
- parsed_cmd_resp_arr.push(parsed_cmd_resp_hash)
220
+ parsed_cmd_resp_arr
227
221
  end
228
-
229
- parsed_cmd_resp_arr
222
+ rescue Timeout::Error
223
+ logger.error("Device response timed out for command: #{cmd}")
224
+ raise 'ERROR: Device response timed out'
230
225
  rescue StandardError => e
226
+ logger.error("Error parsing response for command #{cmd}: #{e.message}")
231
227
  raise e
232
228
  ensure
233
- # Flush Responses for Next Request
234
229
  PWN::Plugins::Serial.flush_session_data
235
230
  end
236
231
 
237
232
  # Supported Method Parameters::
238
- # PWN::Plugins::SonMicroRFID.exec(
239
- # son_micro_rfid_obj: 'required - son_micro_rfid_obj returned from #connect method'
233
+ # PWN::Plugins::SonMicroRFID.exec(
234
+ # son_micro_rfid_obj: 'required - son_micro_rfid_obj returned from #connect method',
240
235
  # cmd: 'required - cmd returned from #list_cmds method',
241
236
  # params: 'optional - parameters for specific command returned from #list_params method'
242
237
  # )
238
+
243
239
  public_class_method def self.exec(opts = {})
244
240
  son_micro_rfid_obj = opts[:son_micro_rfid_obj]
245
241
  cmd = opts[:cmd].to_s.scrub.strip.chomp
246
- params = opts[:params].to_s.scrub.strip.chomp
242
+ params = opts[:params]
247
243
 
248
- params_bytes = []
244
+ logger.info("Executing command: #{cmd} with params: #{params.inspect}")
249
245
 
250
- # Use the Mifare Panel Application to reverse enginner commands.
246
+ params_bytes = []
251
247
  case cmd.to_sym
252
248
  when :reset
253
249
  cmd_bytes = [0xFF, 0x00, 0x01, 0x80, 0x81]
@@ -258,32 +254,40 @@ module PWN
258
254
  when :select_tag
259
255
  cmd_bytes = [0xFF, 0x00, 0x01, 0x83, 0x84]
260
256
  when :authenticate
261
- # Last two bytes not correct
262
- cmd_bytes = [0xFF, 0x00, 0x01, 0x00, 0x00]
257
+ raise "Parameters must be a hash with :block, :key_type, :key for #{cmd}" unless params.is_a?(Hash)
258
+
259
+ # Placeholder: [block, key_type, key]
260
+ cmd_bytes = [0xFF, 0x00, 0x07, 0x85]
261
+ block = params[:block].to_i
262
+ key_type = params[:key_type] == :key_a ? 0x60 : 0x61
263
+ key = params[:key] || [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]
264
+ params_bytes = [block, key_type] + key
263
265
  when :read_block
264
- # Last two bytes not correct
265
- cmd_bytes = [0xFF, 0x00, 0x01, 0x00, 0x01]
266
+ # Placeholder: [block]
267
+ cmd_bytes = [0xFF, 0x00, 0x02, 0x86]
268
+ params_bytes = [params[:block].to_i] if params.is_a?(Hash) && params[:block]
266
269
  when :write_block
267
- # Last two bytes not correct
268
- cmd_bytes = [0xFF, 0x00, 0x01, 0x00, 0x02]
270
+ raise "Parameters must be a hash with :block, :data for #{cmd}" unless params.is_a?(Hash) && params[:block] && params[:data]
271
+
272
+ # Placeholder: [block, data]
273
+ cmd_bytes = [0xFF, 0x00, 0x06, 0x87] # Adjust length based on data
274
+ block = params[:block].to_i
275
+ data = params[:data].is_a?(Array) ? params[:data] : params[:data].split.map { |b| b.to_i(16) }
276
+ params_bytes = [block] + data
277
+ cmd_bytes[2] = params_bytes.length + 2 # Update length
269
278
  when :write_value
270
- # Last two bytes not correct
271
279
  cmd_bytes = [0xFF, 0x00, 0x01, 0x00, 0x03]
272
280
  when :write_four_byte_block
273
- # Last two bytes not correct
274
281
  cmd_bytes = [0xFF, 0x00, 0x01, 0x00, 0x04]
275
282
  when :write_key
276
- # Last two bytes not correct
277
283
  cmd_bytes = [0xFF, 0x00, 0x01, 0x00, 0x05]
278
284
  when :increment
279
- # Last two bytes not correct
280
285
  cmd_bytes = [0xFF, 0x00, 0x01, 0x00, 0x06]
281
286
  when :decrement
282
- # Last two bytes not correct
283
287
  cmd_bytes = [0xFF, 0x00, 0x01, 0x00, 0x07]
284
288
  when :antenna_power
285
289
  cmd_bytes = [0xFF, 0x00, 0x02, 0x90]
286
- case params.to_sym
290
+ case params.to_s.to_sym
287
291
  when :off
288
292
  params_bytes = [0x00, 0x92]
289
293
  when :on
@@ -291,94 +295,225 @@ module PWN
291
295
  when :reset
292
296
  params_bytes = [0x02, 0x94]
293
297
  else
294
- raise "Unsupported Parameters: #{params} for #{cmd}. Supported parameters for #{cmd} are:\n#{list_params(cmd: cmd)}\n\n\n"
298
+ raise "Unsupported Parameters: #{params} for #{cmd}. Supported parameters are:\n#{list_params(cmd: cmd).join("\n")}\n"
295
299
  end
296
300
  when :read_port
297
- # Last two bytes not correct
298
- cmd_bytes = [0xFF, 0x00, 0x01]
301
+ cmd_bytes = [0xFF, 0x00, 0x01, 0x00, 0x08]
299
302
  when :write_port
300
- # Last two bytes not correct
301
- cmd_bytes = [0xFF, 0x00, 0x01, 0x08]
303
+ cmd_bytes = [0xFF, 0x00, 0x01, 0x08, 0x09]
302
304
  when :halt
303
305
  cmd_bytes = [0xFF, 0x00, 0x01, 0x93, 0x94]
304
306
  when :set_baud_rate
305
- # Last two bytes not correct
306
- cmd_bytes = [0xFF, 0x00, 0x01, 0x09]
307
+ cmd_bytes = [0xFF, 0x00, 0x01, 0x09, 0x0A]
307
308
  when :sleep
308
- # Last two bytes not correct
309
- cmd_bytes = [0xFF, 0x00, 0x01, 0x0a]
309
+ cmd_bytes = [0xFF, 0x00, 0x01, 0x0A, 0x0B]
310
310
  when :poll_buffer
311
311
  cmd_bytes = [0xFF, 0x00, 0x01, 0xB0, 0xB1]
312
312
  else
313
- raise "Unsupported Command: #{cmd}. Supported commands are:\n#{list_cmds}\n\n\n"
313
+ logger.error("Unsupported command: #{cmd}")
314
+ raise "Unsupported Command: #{cmd}. Supported commands are:\n#{list_cmds.join("\n")}\n"
314
315
  end
315
316
 
316
- # If parameters to a command are set, append them.
317
317
  cmd_bytes += params_bytes unless params_bytes.empty?
318
- # Execute the command.
319
318
  PWN::Plugins::Serial.request(
320
319
  serial_obj: son_micro_rfid_obj,
321
320
  payload: cmd_bytes
322
321
  )
323
322
 
324
- # Parse commands response(s).
325
- # Return an array of hashes.
326
- parse_responses(
323
+ response = parse_responses(
327
324
  son_micro_rfid_obj: son_micro_rfid_obj,
328
325
  cmd: cmd.to_sym
329
326
  )
327
+ logger.info("Response for #{cmd}: #{response.inspect}")
328
+ response
330
329
  rescue StandardError => e
330
+ logger.error("Error executing command #{cmd}: #{e.message}")
331
331
  raise e
332
332
  ensure
333
- # Flush Responses for Next Request
334
333
  PWN::Plugins::Serial.flush_session_data
335
334
  end
336
335
 
337
336
  # Supported Method Parameters::
338
- # PWN::Plugins::SonMicroRFID.read_card(
339
- # son_micro_rfid_obj: 'required - son_micro_rfid_obj returned from #connect method'
337
+ # PWN::Plugins::SonMicroRFID.read_tag(
338
+ # son_micro_rfid_obj: 'required - son_micro_rfid_obj returned from #connect method',
339
+ # authn: 'optional - authentication flag (default: false)',
340
+ # key: 'optional - key for authentication (default: [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF])'
340
341
  # )
341
342
 
342
- public_class_method def self.read_card(opts = {})
343
+ public_class_method def self.read_tag(opts = {})
343
344
  son_micro_rfid_obj = opts[:son_micro_rfid_obj]
344
- print 'Reader Activated. Please Scan Card...'
345
+ authn = opts[:authn] ||= false
346
+ key = opts[:key] ||= [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]
347
+
348
+ logger.info('Starting read_tag')
349
+
350
+ print 'Reader Activated. Please Scan Tag...'
345
351
  exec_resp = exec(
346
352
  son_micro_rfid_obj: son_micro_rfid_obj,
347
353
  cmd: :seek_for_tag
348
354
  )
349
355
 
350
356
  rfid_data = exec_resp.last
351
- puts "#{rfid_data[:resp_code_desc]} >>> #{rfid_data[:tag_id]}"
357
+ logger.info(rfid_data)
352
358
 
359
+ if rfid_data[:resp_code_desc] == :no_tag_present
360
+ logger.error('No RFID tag detected')
361
+ raise 'No RFID tag detected'
362
+ end
363
+
364
+ # Read block data (e.g., block 4 for Ultralight, sector 0 block 0 for Classic)
365
+ case rfid_data[:resp_code_desc]
366
+ when :mifare_ultralight
367
+ exec_resp = exec(
368
+ son_micro_rfid_obj: son_micro_rfid_obj,
369
+ cmd: :read_block,
370
+ params: { block: 4 } # Example block
371
+ )
372
+ rfid_data[:block_data] = exec_resp.last[:block_data] if exec_resp.last[:resp_code_desc] == :read_success
373
+ when :mifare_classic_1k, :mifare_classic_4k
374
+ if authn
375
+ exec_resp = exec(
376
+ son_micro_rfid_obj: son_micro_rfid_obj,
377
+ cmd: :authenticate,
378
+ params: { block: 0, key_type: :key_a, key: key }
379
+ )
380
+ if exec_resp.last[:resp_code_desc] == :auth_success
381
+ exec_resp = exec(
382
+ son_micro_rfid_obj: son_micro_rfid_obj,
383
+ cmd: :read_block,
384
+ params: { block: 0 }
385
+ )
386
+ rfid_data[:block_data] = exec_resp.last[:block_data] if exec_resp.last[:resp_code_desc] == :read_success
387
+ else
388
+ logger.error("Authentication failed for #{rfid_data[:resp_code_desc]}")
389
+ raise 'Authentication failed'
390
+ end
391
+ end
392
+ end
393
+
394
+ puts "\n#{rfid_data[:resp_code_desc]} >>> Tag ID: #{rfid_data[:tag_id]}"
395
+ puts "Block Data: #{rfid_data[:block_data]}" if rfid_data[:block_data]
396
+ logger.info("Read tag successful: #{rfid_data.inspect}")
353
397
  rfid_data
354
398
  rescue StandardError => e
399
+ logger.error("Error reading tag: #{e.message}")
400
+ puts "ERROR: Failed to read tag - #{e.message}"
355
401
  raise e
356
402
  end
357
403
 
358
404
  # Supported Method Parameters::
359
- # PWN::Plugins::SonMicroRFID.write_card(
360
- # son_micro_rfid_obj: 'required - son_micro_rfid_obj returned from #connect method'
405
+ # PWN::Plugins::SonMicroRFID.write_tag(
406
+ # son_micro_rfid_obj: 'required - son_micro_rfid_obj returned from #connect method',
407
+ # rfid_data: 'required - RFID data to write (see #read_tag for structure)',
408
+ # authn: 'optional - authentication flag (default: false)',
409
+ # key: 'optional - key for authentication (default: [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF])'
361
410
  # )
362
411
 
363
- public_class_method def self.write_card(opts = {})
412
+ public_class_method def self.write_tag(opts = {})
364
413
  son_micro_rfid_obj = opts[:son_micro_rfid_obj]
365
414
  rfid_data = opts[:rfid_data]
366
- # TODO: write card
367
- puts rfid_data.inspect
415
+ authn = opts[:authn] ||= false
416
+ key = opts[:key] ||= [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]
417
+ logger.info('Starting write_tag')
418
+
419
+ unless rfid_data.is_a?(Hash) && rfid_data[:resp_code_desc] && rfid_data[:tag_id]
420
+ logger.error('Invalid rfid_data: must be a hash with :resp_code_desc and :tag_id')
421
+ raise 'Invalid rfid_data: must be a hash with :resp_code_desc and :tag_id'
422
+ end
423
+
424
+ puts "\nWriting to tag with Tag ID: #{rfid_data[:tag_id]}"
425
+ print 'This will overwrite the tag. Continue? [y/N]: '
426
+ unless gets.chomp.strip.upcase == 'Y'
427
+ logger.info('Write cancelled by user')
428
+ puts 'Write cancelled.'
429
+ return rfid_data
430
+ end
431
+
432
+ # Select tag
433
+ exec_resp = exec(
434
+ son_micro_rfid_obj: son_micro_rfid_obj,
435
+ cmd: :select_tag
436
+ )
437
+ if exec_resp.last[:resp_code_desc] == :no_tag_present
438
+ logger.error('No RFID tag detected for writing')
439
+ raise 'No RFID tag detected for writing'
440
+ end
441
+
442
+ # Write block data
443
+ case rfid_data[:resp_code_desc]
444
+ when :mifare_ultralight
445
+ if rfid_data[:block_data]
446
+ data = rfid_data[:block_data].split.map { |b| b.to_i(16) }
447
+ exec_resp = exec(
448
+ son_micro_rfid_obj: son_micro_rfid_obj,
449
+ cmd: :write_block,
450
+ params: { block: 4, data: data }
451
+ )
452
+ unless exec_resp.last[:resp_code_desc] == :write_success
453
+ logger.error('Failed to write block for Ultralight')
454
+ raise 'Failed to write block'
455
+ end
456
+ end
457
+ when :mifare_classic_1k, :mifare_classic_4k
458
+ if authn
459
+ exec_resp = exec(
460
+ son_micro_rfid_obj: son_micro_rfid_obj,
461
+ cmd: :authenticate,
462
+ params: { block: 0, key_type: :key_a, key: key }
463
+ )
464
+ if exec_resp.last[:resp_code_desc] == :auth_success
465
+ if rfid_data[:block_data]
466
+ data = rfid_data[:block_data].split.map { |b| b.to_i(16) }
467
+ exec_resp = exec(
468
+ son_micro_rfid_obj: son_micro_rfid_obj,
469
+ cmd: :write_block,
470
+ params: { block: 0, data: data }
471
+ )
472
+ unless exec_resp.last[:resp_code_desc] == :write_success
473
+ logger.error('Failed to write block for Classic')
474
+ raise 'Failed to write block'
475
+ end
476
+ end
477
+ else
478
+ logger.error("Authentication failed for #{rfid_data[:resp_code_desc]}")
479
+ raise 'Authentication failed'
480
+ end
481
+ end
482
+ else
483
+ logger.error("Unsupported tag type: #{rfid_data[:resp_code_desc]}")
484
+ raise "Unsupported tag type: #{rfid_data[:resp_code_desc]}"
485
+ end
486
+
487
+ # Verify write by re-reading
488
+ read_data = read_tag(son_micro_rfid_obj: son_micro_rfid_obj)
489
+ if read_data[:block_data] == rfid_data[:block_data]
490
+ puts 'Write verification successful.'
491
+ logger.info('Write verification successful')
492
+ else
493
+ puts 'ERROR: Written data does not match read data.'
494
+ logger.error('Written data does not match read data')
495
+ end
368
496
 
497
+ logger.info("Write tag successful: #{rfid_data.inspect}")
498
+ puts 'Tag written successfully.'
369
499
  rfid_data
370
500
  rescue StandardError => e
501
+ logger.error("Error writing tag: #{e.message}")
502
+ puts "ERROR: Failed to write tag - #{e.message}"
371
503
  raise e
372
504
  end
373
505
 
374
506
  # Supported Method Parameters::
375
- # PWN::Plugins::SonMicroRFID.backup_card(
507
+ # PWN::Plugins::SonMicroRFID.backup_tag(
376
508
  # son_micro_rfid_obj: 'required - son_micro_rfid_obj returned from #connect method'
377
509
  # )
378
510
 
379
- public_class_method def self.backup_card(opts = {})
511
+ public_class_method def self.backup_tag(opts = {})
380
512
  son_micro_rfid_obj = opts[:son_micro_rfid_obj]
381
- rfid_data = read_card(son_micro_rfid_obj: son_micro_rfid_obj)
513
+ logger.info('Starting backup_tag')
514
+
515
+ rfid_data = read_tag(son_micro_rfid_obj: son_micro_rfid_obj)
516
+
382
517
  file = ''
383
518
  backup_msg = ''
384
519
  loop do
@@ -390,41 +525,64 @@ module PWN
390
525
  backup_msg = "\n****** ERROR: Directory #{file_dir} for #{file} does not exist ******"
391
526
  puts backup_msg
392
527
  end
393
- File.write(file, "#{JSON.pretty_generate(rfid_data)}\n")
394
528
 
395
- puts 'complete.'
529
+ File.write(file, "#{JSON.pretty_generate(rfid_data)}\n")
530
+ logger.info("Backup saved to #{file}")
531
+ puts 'Backup complete.'
396
532
  rfid_data
397
533
  rescue StandardError => e
534
+ logger.error("Error backing up tag: #{e.message}")
535
+ puts "ERROR: Failed to backup tag - #{e.message}"
398
536
  raise e
399
537
  end
400
538
 
401
539
  # Supported Method Parameters::
402
- # PWN::Plugins::SonMicroRFID.copy_card(
540
+ # PWN::Plugins::SonMicroRFID.clone_tag(
403
541
  # son_micro_rfid_obj: 'required - son_micro_rfid_obj returned from #connect method'
404
542
  # )
405
543
 
406
- public_class_method def self.copy_card(opts = {})
544
+ public_class_method def self.clone_tag(opts = {})
407
545
  son_micro_rfid_obj = opts[:son_micro_rfid_obj]
408
- rfid_data = read_card(son_micro_rfid_obj: son_micro_rfid_obj)
409
- write_card(
546
+ logger.info('Starting clone_tag')
547
+
548
+ print 'This will overwrite the target tag. Continue? [y/N]: '
549
+ unless gets.chomp.strip.upcase == 'Y'
550
+ logger.info('Copy cancelled by user')
551
+ puts 'Copy cancelled.'
552
+ return nil
553
+ end
554
+
555
+ rfid_data = read_tag(son_micro_rfid_obj: son_micro_rfid_obj)
556
+ write_tag(
410
557
  son_micro_rfid_obj: son_micro_rfid_obj,
411
558
  rfid_data: rfid_data
412
559
  )
413
560
  rescue StandardError => e
561
+ logger.error("Error copying tag: #{e.message}")
562
+ puts "ERROR: Failed to copy tag - #{e.message}"
414
563
  raise e
415
564
  end
416
565
 
417
566
  # Supported Method Parameters::
418
- # PWN::Plugins::SonMicroRFID.load_card_from_file(
567
+ # PWN::Plugins::SonMicroRFID.load_tag_from_file(
419
568
  # son_micro_rfid_obj: 'required - son_micro_rfid_obj returned from #connect method'
420
569
  # )
421
570
 
422
- public_class_method def self.load_card_from_file(opts = {})
571
+ public_class_method def self.load_tag_from_file(opts = {})
423
572
  son_micro_rfid_obj = opts[:son_micro_rfid_obj]
573
+ logger.info('Starting load_tag_from_file')
574
+
575
+ print 'This will overwrite the target tag. Continue? [y/N]: '
576
+ unless gets.chomp.strip.upcase == 'Y'
577
+ logger.info('Load cancelled by user')
578
+ puts 'Load cancelled.'
579
+ return nil
580
+ end
581
+
424
582
  file = ''
425
583
  restore_msg = ''
426
584
  loop do
427
- print 'Enter File Name to Restore to Card: '
585
+ print 'Enter File Name to Restore to Tag: '
428
586
  file = gets.scrub.chomp.strip
429
587
  break if File.exist?(file)
430
588
 
@@ -432,17 +590,89 @@ module PWN
432
590
  puts restore_msg
433
591
  end
434
592
 
435
- rfid_data = JSON.parse(
436
- File.read(file),
437
- symbolize_names: true
593
+ rfid_data = JSON.parse(File.read(file), symbolize_names: true)
594
+ write_tag(
595
+ son_micro_rfid_obj: son_micro_rfid_obj,
596
+ rfid_data: rfid_data
438
597
  )
598
+ rescue StandardError => e
599
+ logger.error("Error loading tag from file: #{e.message}")
600
+ puts "ERROR: Failed to load tag from file - #{e.message}"
601
+ raise e
602
+ end
603
+
604
+ # Supported Method Parameters::
605
+ # PWN::Plugins::SonMicroRFID.update_tag(
606
+ # son_micro_rfid_obj: 'required - son_micro_rfid_obj returned from #connect method'
607
+ # )
608
+
609
+ public_class_method def self.update_tag(opts = {})
610
+ son_micro_rfid_obj = opts[:son_micro_rfid_obj]
611
+ logger.info('Starting update_tag')
612
+
613
+ print 'This will modify the tag\'s data. Continue? [y/N]: '
614
+ unless gets.chomp.strip.upcase == 'Y'
615
+ logger.info('Update cancelled by user')
616
+ puts 'Update cancelled.'
617
+ return nil
618
+ end
619
+
620
+ rfid_data = read_tag(son_micro_rfid_obj: son_micro_rfid_obj)
621
+ unless rfid_data.is_a?(Hash) && rfid_data[:resp_code_desc] && rfid_data[:tag_id]
622
+ logger.error('Invalid rfid_data structure')
623
+ raise 'Invalid rfid_data structure'
624
+ end
625
+
626
+ # Update block data
627
+ block_data = rfid_data[:block_data] || ''
628
+ puts "\nCurrent Block Data: #{block_data}"
629
+ print 'Enter Updated Block Data (hex bytes, space-separated, press Enter to keep original): '
630
+ updated_value = gets.scrub.chomp.strip
631
+
632
+ if updated_value.empty?
633
+ puts "Keeping original value: #{block_data}"
634
+ logger.info("Keeping original block data: #{block_data}")
635
+ updated_value = block_data
636
+ else
637
+ # Validate hex bytes
638
+ unless updated_value.match?(/\A([\da-fA-F]{2}\s)*[\da-fA-F]{2}\z/)
639
+ logger.error('Invalid block data: must be hex bytes (e.g., FF 00)')
640
+ raise 'Invalid block data: must be hex bytes (e.g., FF 00)'
641
+ end
642
+ # Validate length based on tag type
643
+ data_bytes = updated_value.split.map { |b| b.to_i(16) }
644
+ max_bytes = rfid_data[:resp_code_desc] == :mifare_ultralight ? 4 : 16
645
+ unless data_bytes.length <= max_bytes
646
+ logger.error("Block data too long: max #{max_bytes} bytes")
647
+ raise "Block data too long: max #{max_bytes} bytes"
648
+ end
649
+ logger.info("Updated block data: #{updated_value}")
650
+ end
651
+
652
+ rfid_data[:block_data] = updated_value
653
+
654
+ # Confirm changes
655
+ puts "\nUpdated RFID Data:"
656
+ puts "Tag ID: #{rfid_data[:tag_id]}"
657
+ puts "Block Data: #{rfid_data[:block_data]}"
658
+ print 'Confirm writing these changes to the tag? [y/N]: '
659
+ unless gets.chomp.strip.upcase == 'Y'
660
+ logger.info('Update cancelled by user')
661
+ puts 'Update cancelled.'
662
+ return rfid_data
663
+ end
439
664
 
440
- # TODO: Save Original Card Contents
441
- write_card(
665
+ rfid_data = write_tag(
442
666
  son_micro_rfid_obj: son_micro_rfid_obj,
443
667
  rfid_data: rfid_data
444
668
  )
669
+
670
+ logger.info("Update tag successful: #{rfid_data.inspect}")
671
+ puts 'tag updated successfully.'
672
+ rfid_data
445
673
  rescue StandardError => e
674
+ logger.error("Error updating tag: #{e.message}")
675
+ puts "ERROR: Failed to update tag - #{e.message}"
446
676
  raise e
447
677
  end
448
678
 
@@ -452,19 +682,29 @@ module PWN
452
682
  # )
453
683
 
454
684
  public_class_method def self.disconnect(opts = {})
455
- PWN::Plugins::Serial.disconnect(
456
- serial_obj: opts[:son_micro_rfid_obj]
685
+ son_micro_rfid_obj = opts[:son_micro_rfid_obj]
686
+
687
+ logger.info('Disabling antenna power')
688
+ exec(
689
+ son_micro_rfid_obj: son_micro_rfid_obj,
690
+ cmd: :antenna_power,
691
+ params: :off
457
692
  )
693
+
694
+ logger.info('Disconnecting from device')
695
+
696
+ PWN::Plugins::Serial.disconnect(serial_obj: son_micro_rfid_obj)
458
697
  rescue StandardError => e
698
+ logger.error("Error disconnecting: #{e.message}")
459
699
  raise e
460
700
  end
461
701
 
462
702
  # Author(s):: 0day Inc. <support@0dayinc.com>
463
703
 
464
704
  public_class_method def self.authors
465
- "AUTHOR(S):
705
+ 'AUTHOR(S):
466
706
  0day Inc. <support@0dayinc.com>
467
- "
707
+ '
468
708
  end
469
709
 
470
710
  # Display Usage for this Module
@@ -476,13 +716,14 @@ module PWN
476
716
  baud: 'optional (defaults to 19_200)',
477
717
  data_bits: 'optional (defaults to 8)',
478
718
  stop_bits: 'optional (defaults to 1)',
479
- parity: 'optional - :even|:mark|:odd|:space|:none (defaults to :odd)'
719
+ parity: 'optional - :even|:mark|:odd|:space|:none (defaults to :none)',
720
+ flow_control: 'optional - :none|:hard|:soft (defaults to :none)'
480
721
  )
481
722
 
482
723
  cmds = #{self}.list_cmds
483
724
 
484
725
  params = #{self}.list_params(
485
- cmd: 'required - cmd returned from #list_cmds method',
726
+ cmd: 'required - cmd returned from #list_cmds method'
486
727
  )
487
728
 
488
729
  parsed_cmd_resp_arr = #{self}.exec(
@@ -491,6 +732,35 @@ module PWN
491
732
  params: 'optional - parameters for specific command returned from #list_params method'
492
733
  )
493
734
 
735
+ rfid_data = #{self}.read_tag(
736
+ son_micro_rfid_obj: 'required son_micro_rfid_obj returned from #connect method',
737
+ authn: 'optional - authentication flag (default: false)',
738
+ key: 'optional - key for authentication (default: [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF])'
739
+ )
740
+
741
+ rfid_data = #{self}.write_tag(
742
+ son_micro_rfid_obj: 'required son_micro_rfid_obj returned from #connect method',
743
+ rfid_data: 'required - RFID data to write',
744
+ authn: 'optional - authentication flag (default: false)',
745
+ key: 'optional - key for authentication (default: [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF])'
746
+ )
747
+
748
+ rfid_data = #{self}.backup_tag(
749
+ son_micro_rfid_obj: 'required son_micro_rfid_obj returned from #connect method'
750
+ )
751
+
752
+ rfid_data = #{self}.clone_tag(
753
+ son_micro_rfid_obj: 'required son_micro_rfid_obj returned from #connect method'
754
+ )
755
+
756
+ rfid_data = #{self}.load_tag_from_file(
757
+ son_micro_rfid_obj: 'required son_micro_rfid_obj returned from #connect method'
758
+ )
759
+
760
+ rfid_data = #{self}.update_tag(
761
+ son_micro_rfid_obj: 'required son_micro_rfid_obj returned from #connect method'
762
+ )
763
+
494
764
  #{self}.disconnect(
495
765
  son_micro_rfid_obj: 'required son_micro_rfid_obj returned from #connect method'
496
766
  )
data/lib/pwn/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PWN
4
- VERSION = '0.5.258'
4
+ VERSION = '0.5.260'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pwn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.258
4
+ version: 0.5.260
5
5
  platform: ruby
6
6
  authors:
7
7
  - 0day Inc.
@@ -1952,7 +1952,6 @@ files:
1952
1952
  - lib/pwn/www/uber.rb
1953
1953
  - lib/pwn/www/upwork.rb
1954
1954
  - lib/pwn/www/youtube.rb
1955
- - msr206.log
1956
1955
  - packer/daemons/msfrpcd.rb
1957
1956
  - packer/daemons/openvas.rb
1958
1957
  - packer/deploy_docker_containers.sh
data/msr206.log DELETED
@@ -1,68 +0,0 @@
1
- # Logfile created on 2025-04-22 11:39:09 -0600 by logger.rb/v1.7.0
2
- I, [2025-04-22T11:39:09.967768 #2867164] INFO -- : Connecting to /dev/ttyUSB0 at baud 9600
3
- I, [2025-04-22T11:39:09.991620 #2867164] INFO -- : Setting protocol to usi0
4
- I, [2025-04-22T11:39:09.991792 #2867164] INFO -- : Executing command: proto_usi0 with params: nil
5
- I, [2025-04-22T11:39:10.092182 #2867164] INFO -- : Response for proto_usi0: {cmd: :proto_usi0, cmd_bytes: ["0x55", "0x53", "0x49", "0x30"], msg: :response, hex: ["A1 AB A1"], binary: ["10100001", "10101011", "10100001"], decoded: "¿+¿"}
6
- I, [2025-04-22T11:39:10.092353 #2867164] INFO -- : Executing command: simulate_power_cycle_warm_reset with params: nil
7
- I, [2025-04-22T11:39:10.996840 #2867164] INFO -- : Response for simulate_power_cycle_warm_reset: {cmd: :simulate_power_cycle_warm_reset, cmd_bytes: ["0x7f"], msg: :power_on_report, hex: ["BA"], binary: ["10111010"], decoded: ":"}
8
- I, [2025-04-22T11:39:10.996886 #2867164] INFO -- : Retrieving configuration
9
- I, [2025-04-22T11:39:10.996905 #2867164] INFO -- : Executing command: configuration_request with params: nil
10
- I, [2025-04-22T11:39:11.097145 #2867164] INFO -- : Response for configuration_request: {cmd: :configuration_request, cmd_bytes: ["0x23"], msg: :response, hex: ["F7"], binary: ["11110111"], decoded: "w"}
11
- I, [2025-04-22T11:39:11.097302 #2867164] INFO -- : Configuration: {track1_read: true, track2_read: true, track3_read: true, track1_write: true, track2_write: true, track3_write: true, not_used: false, parity: true}
12
- I, [2025-04-22T11:39:11.097349 #2867164] INFO -- : Executing command: version_report with params: nil
13
- I, [2025-04-22T11:39:11.198353 #2867164] INFO -- : Response for version_report: {cmd: :version_report, cmd_bytes: ["0x39"], msg: :response, hex: ["32 B0 B6 C1 CD 31 37 31 20 31 32 AD 46 E5 62 AD 32 B0 B0 B9"], binary: ["00110010", "10110000", "10110110", "11000001", "11001101", "00110001", "00110111", "00110001", "00100000", "00110001", "00110010", "10101101", "01000110", "11100101", "01100010", "10101101", "00110010", "10110000", "10110000", "10111001"], decoded: "206AM171 12-Feb-2009"}
14
- I, [2025-04-22T11:39:11.198500 #2867164] INFO -- : Executing command: green_flash with params: nil
15
- I, [2025-04-22T11:39:11.298726 #2867164] INFO -- : Response for green_flash: {cmd: :green_flash, cmd_bytes: ["0x28"], msg: :ack_command_completed, hex: ["5E"], binary: ["01011110"], decoded: "^"}
16
- I, [2025-04-22T11:39:19.913780 #2867164] INFO -- : Executing command: green_off with params: nil
17
- I, [2025-04-22T11:39:20.014016 #2867164] INFO -- : Response for green_off: {cmd: :green_off, cmd_bytes: ["0x6c"], msg: :ack_command_completed, hex: ["5E"], binary: ["01011110"], decoded: "^"}
18
- I, [2025-04-22T11:39:20.014120 #2867164] INFO -- : Executing command: yellow_off with params: nil
19
- I, [2025-04-22T11:39:20.114315 #2867164] INFO -- : Response for yellow_off: {cmd: :yellow_off, cmd_bytes: ["0x6b"], msg: :ack_command_completed, hex: ["5E"], binary: ["01011110"], decoded: "^"}
20
- I, [2025-04-22T11:39:20.114443 #2867164] INFO -- : Executing command: red_off with params: nil
21
- I, [2025-04-22T11:39:20.214668 #2867164] INFO -- : Response for red_off: {cmd: :red_off, cmd_bytes: ["0x6d"], msg: :ack_command_completed, hex: ["5E"], binary: ["01011110"], decoded: "^"}
22
- I, [2025-04-22T11:39:20.214799 #2867164] INFO -- : Disconnecting from device
23
- I, [2025-04-22T11:41:46.363878 #2872588] INFO -- : Connecting to /dev/ttyUSB0 at baud 19200
24
- I, [2025-04-22T11:41:46.379386 #2872588] INFO -- : Setting protocol to usi0
25
- I, [2025-04-22T11:41:46.379521 #2872588] INFO -- : Executing command: proto_usi0 with params: nil
26
- I, [2025-04-22T11:41:46.479918 #2872588] INFO -- : Response for proto_usi0: {cmd: :proto_usi0, cmd_bytes: ["0x55", "0x53", "0x49", "0x30"], msg: :response, hex: ["06 FE 06 FE"], binary: ["00000110", "11111110", "00000110", "11111110"], decoded: "¿~¿~"}
27
- I, [2025-04-22T11:41:46.480081 #2872588] INFO -- : Executing command: simulate_power_cycle_warm_reset with params: nil
28
- I, [2025-04-22T11:41:46.580309 #2872588] INFO -- : Response for simulate_power_cycle_warm_reset: {cmd: :simulate_power_cycle_warm_reset, cmd_bytes: ["0x7f"], msg: :response, hex: ["06 FE"], binary: ["00000110", "11111110"], decoded: "¿~"}
29
- I, [2025-04-22T11:41:46.580431 #2872588] INFO -- : Retrieving configuration
30
- I, [2025-04-22T11:41:46.580459 #2872588] INFO -- : Executing command: configuration_request with params: nil
31
- I, [2025-04-22T11:41:46.680666 #2872588] INFO -- : Response for configuration_request: {cmd: :configuration_request, cmd_bytes: ["0x23"], msg: :response, hex: ["06 FE"], binary: ["00000110", "11111110"], decoded: "¿~"}
32
- I, [2025-04-22T11:41:46.680809 #2872588] INFO -- : Configuration: {track1_read: false, track2_read: true, track3_read: true, track1_write: false, track2_write: false, track3_write: false, not_used: false, parity: false}
33
- I, [2025-04-22T11:41:46.680848 #2872588] INFO -- : Executing command: version_report with params: nil
34
- I, [2025-04-22T11:41:46.781057 #2872588] INFO -- : Response for version_report: {cmd: :version_report, cmd_bytes: ["0x39"], msg: :response, hex: ["06 FE"], binary: ["00000110", "11111110"], decoded: "¿~"}
35
- I, [2025-04-22T11:41:46.781196 #2872588] INFO -- : Executing command: green_flash with params: nil
36
- I, [2025-04-22T11:41:46.881415 #2872588] INFO -- : Response for green_flash: {cmd: :green_flash, cmd_bytes: ["0x28"], msg: :response, hex: ["06 FE"], binary: ["00000110", "11111110"], decoded: "¿~"}
37
- I, [2025-04-22T11:41:50.780641 #2872588] INFO -- : Executing command: green_off with params: nil
38
- I, [2025-04-22T11:41:50.880870 #2872588] INFO -- : Response for green_off: {cmd: :green_off, cmd_bytes: ["0x6c"], msg: :response, hex: ["06 FE"], binary: ["00000110", "11111110"], decoded: "¿~"}
39
- I, [2025-04-22T11:41:50.880965 #2872588] INFO -- : Executing command: yellow_flash with params: nil
40
- I, [2025-04-22T11:41:50.981179 #2872588] INFO -- : Response for yellow_flash: {cmd: :yellow_flash, cmd_bytes: ["0x7c"], msg: :response, hex: ["06 FE"], binary: ["00000110", "11111110"], decoded: "¿~"}
41
- I, [2025-04-22T11:41:52.023768 #2872588] INFO -- : Executing command: green_off with params: nil
42
- I, [2025-04-22T11:41:52.124015 #2872588] INFO -- : Response for green_off: {cmd: :green_off, cmd_bytes: ["0x6c"], msg: :response, hex: ["06 FE"], binary: ["00000110", "11111110"], decoded: "¿~"}
43
- I, [2025-04-22T11:41:52.124151 #2872588] INFO -- : Executing command: yellow_off with params: nil
44
- I, [2025-04-22T11:41:52.224358 #2872588] INFO -- : Response for yellow_off: {cmd: :yellow_off, cmd_bytes: ["0x6b"], msg: :response, hex: ["06 FE"], binary: ["00000110", "11111110"], decoded: "¿~"}
45
- I, [2025-04-22T11:41:52.224491 #2872588] INFO -- : Executing command: red_off with params: nil
46
- I, [2025-04-22T11:41:52.324786 #2872588] INFO -- : Response for red_off: {cmd: :red_off, cmd_bytes: ["0x6d"], msg: :response, hex: ["06 FE"], binary: ["00000110", "11111110"], decoded: "¿~"}
47
- I, [2025-04-22T11:41:52.324969 #2872588] INFO -- : Disconnecting from device
48
- I, [2025-04-22T11:41:56.503719 #2872594] INFO -- : Connecting to /dev/ttyUSB0 at baud 19200
49
- I, [2025-04-22T11:41:56.518859 #2872594] INFO -- : Setting protocol to usi0
50
- I, [2025-04-22T11:41:56.519014 #2872594] INFO -- : Executing command: proto_usi0 with params: nil
51
- I, [2025-04-22T11:41:56.619439 #2872594] INFO -- : Response for proto_usi0: {cmd: :proto_usi0, cmd_bytes: ["0x55", "0x53", "0x49", "0x30"], msg: :response, hex: ["A1 AB A1"], binary: ["10100001", "10101011", "10100001"], decoded: "¿+¿"}
52
- I, [2025-04-22T11:41:56.619599 #2872594] INFO -- : Executing command: simulate_power_cycle_warm_reset with params: nil
53
- I, [2025-04-22T11:41:57.524457 #2872594] INFO -- : Response for simulate_power_cycle_warm_reset: {cmd: :simulate_power_cycle_warm_reset, cmd_bytes: ["0x7f"], msg: :power_on_report, hex: ["BA"], binary: ["10111010"], decoded: ":"}
54
- I, [2025-04-22T11:41:57.524512 #2872594] INFO -- : Retrieving configuration
55
- I, [2025-04-22T11:41:57.524530 #2872594] INFO -- : Executing command: configuration_request with params: nil
56
- I, [2025-04-22T11:41:57.624761 #2872594] INFO -- : Response for configuration_request: {cmd: :configuration_request, cmd_bytes: ["0x23"], msg: :response, hex: ["F7"], binary: ["11110111"], decoded: "w"}
57
- I, [2025-04-22T11:41:57.624863 #2872594] INFO -- : Configuration: {track1_read: true, track2_read: true, track3_read: true, track1_write: true, track2_write: true, track3_write: true, not_used: false, parity: true}
58
- I, [2025-04-22T11:41:57.624906 #2872594] INFO -- : Executing command: version_report with params: nil
59
- I, [2025-04-22T11:41:57.725212 #2872594] INFO -- : Response for version_report: {cmd: :version_report, cmd_bytes: ["0x39"], msg: :response, hex: ["32 B0 B6 C1 CD 31 37 31 20 31 32 AD 46 E5 62 AD 32 B0 B0 B9"], binary: ["00110010", "10110000", "10110110", "11000001", "11001101", "00110001", "00110111", "00110001", "00100000", "00110001", "00110010", "10101101", "01000110", "11100101", "01100010", "10101101", "00110010", "10110000", "10110000", "10111001"], decoded: "206AM171 12-Feb-2009"}
60
- I, [2025-04-22T11:41:57.725298 #2872594] INFO -- : Executing command: green_flash with params: nil
61
- I, [2025-04-22T11:41:57.825519 #2872594] INFO -- : Response for green_flash: {cmd: :green_flash, cmd_bytes: ["0x28"], msg: :ack_command_completed, hex: ["5E"], binary: ["01011110"], decoded: "^"}
62
- I, [2025-04-22T11:42:03.647677 #2872594] INFO -- : Executing command: green_off with params: nil
63
- I, [2025-04-22T11:42:03.747938 #2872594] INFO -- : Response for green_off: {cmd: :green_off, cmd_bytes: ["0x6c"], msg: :ack_command_completed, hex: ["5E"], binary: ["01011110"], decoded: "^"}
64
- I, [2025-04-22T11:42:03.748010 #2872594] INFO -- : Executing command: yellow_off with params: nil
65
- I, [2025-04-22T11:42:03.848227 #2872594] INFO -- : Response for yellow_off: {cmd: :yellow_off, cmd_bytes: ["0x6b"], msg: :ack_command_completed, hex: ["5E"], binary: ["01011110"], decoded: "^"}
66
- I, [2025-04-22T11:42:03.848295 #2872594] INFO -- : Executing command: red_off with params: nil
67
- I, [2025-04-22T11:42:03.948506 #2872594] INFO -- : Response for red_off: {cmd: :red_off, cmd_bytes: ["0x6d"], msg: :ack_command_completed, hex: ["5E"], binary: ["01011110"], decoded: "^"}
68
- I, [2025-04-22T11:42:03.948574 #2872594] INFO -- : Disconnecting from device