pwn 0.5.258 → 0.5.259

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: 5c06971d7679da9c7582786d7fdf1058eb60fffa8e7e3be9a0381e8c64c27669
4
+ data.tar.gz: 18a09f571db9d4e5f7eccf39a1377afbaacc2b6ac7cedbf065d7efb7dc7c35f5
5
5
  SHA512:
6
- metadata.gz: e690ce4edcfad20e36cdc78969fdf02763f0158d6396a2fb768168afa36b0463de714fbc47d1a9f401f6ad51e067fb6f7d1c41ec54a55cb277a0ac32c173ede0
7
- data.tar.gz: 25ce8c4a5668197d47a6a943e9a2db6f79b55ff69be35d8c9f588d9b7e87d7eb6055653997df5baf2aace84ff9f5f42f6671b7316f001f1ccc9d1603a5c220ca
6
+ metadata.gz: 0a5cb4e5cba87d9924fd7d8154993fc21c7b2846e219ec7b98437d275264d63e7b67dd811c618964afed1c50f43e3e47a2d497702eaa4ac932357d6c3865e0cb
7
+ data.tar.gz: cff2183bfc7eaaba2f5069ecaab994c25c3eb672555f885fade51b77b8c46e9ed81ca51c2f350bf390cb691b9edfc60a9c626bf999694abe9e2d487236e68ab7
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.259]: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.259]: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.259]: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
@@ -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('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,212 @@ 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(
337
+ # PWN::Plugins::SonMicroRFID.read_tag(
339
338
  # son_micro_rfid_obj: 'required - son_micro_rfid_obj returned from #connect method'
340
339
  # )
341
340
 
342
- public_class_method def self.read_card(opts = {})
341
+ public_class_method def self.read_tag(opts = {})
343
342
  son_micro_rfid_obj = opts[:son_micro_rfid_obj]
344
- print 'Reader Activated. Please Scan Card...'
343
+ logger.info('Starting read_tag')
344
+
345
+ print 'Reader Activated. Please Scan Tag...'
345
346
  exec_resp = exec(
346
347
  son_micro_rfid_obj: son_micro_rfid_obj,
347
348
  cmd: :seek_for_tag
348
349
  )
349
350
 
350
351
  rfid_data = exec_resp.last
351
- puts "#{rfid_data[:resp_code_desc]} >>> #{rfid_data[:tag_id]}"
352
+ logger.info("\n#{rfid_data.inspect}")
353
+
354
+ if rfid_data[:resp_code_desc] == :no_tag_present
355
+ logger.error('No RFID tag detected')
356
+ raise 'No RFID tag detected'
357
+ end
352
358
 
359
+ # Read block data (e.g., block 4 for Ultralight, sector 0 block 0 for Classic)
360
+ case rfid_data[:resp_code_desc]
361
+ when :mifare_ultralight
362
+ exec_resp = exec(
363
+ son_micro_rfid_obj: son_micro_rfid_obj,
364
+ cmd: :read_block,
365
+ params: { block: 4 } # Example block
366
+ )
367
+ rfid_data[:block_data] = exec_resp.last[:block_data] if exec_resp.last[:resp_code_desc] == :read_success
368
+ when :mifare_classic_1k, :mifare_classic_4k
369
+ exec_resp = exec(
370
+ son_micro_rfid_obj: son_micro_rfid_obj,
371
+ cmd: :authenticate,
372
+ params: { block: 0, key_type: :key_a, key: [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF] }
373
+ )
374
+ if exec_resp.last[:resp_code_desc] == :auth_success
375
+ exec_resp = exec(
376
+ son_micro_rfid_obj: son_micro_rfid_obj,
377
+ cmd: :read_block,
378
+ params: { block: 0 }
379
+ )
380
+ rfid_data[:block_data] = exec_resp.last[:block_data] if exec_resp.last[:resp_code_desc] == :read_success
381
+ else
382
+ logger.error("Authentication failed for #{rfid_data[:resp_code_desc]}")
383
+ raise 'Authentication failed'
384
+ end
385
+ end
386
+
387
+ puts "\n#{rfid_data[:resp_code_desc]} >>> Tag ID: #{rfid_data[:tag_id]}"
388
+ puts "Block Data: #{rfid_data[:block_data]}" if rfid_data[:block_data]
389
+ logger.info("Read tag successful: #{rfid_data.inspect}")
353
390
  rfid_data
354
391
  rescue StandardError => e
392
+ logger.error("Error reading tag: #{e.message}")
393
+ puts "ERROR: Failed to read tag - #{e.message}"
355
394
  raise e
356
395
  end
357
396
 
358
397
  # Supported Method Parameters::
359
- # PWN::Plugins::SonMicroRFID.write_card(
360
- # son_micro_rfid_obj: 'required - son_micro_rfid_obj returned from #connect method'
398
+ # PWN::Plugins::SonMicroRFID.write_tag(
399
+ # son_micro_rfid_obj: 'required - son_micro_rfid_obj returned from #connect method',
400
+ # rfid_data: 'required - RFID data to write (see #read_tag for structure)'
361
401
  # )
362
402
 
363
- public_class_method def self.write_card(opts = {})
403
+ public_class_method def self.write_tag(opts = {})
364
404
  son_micro_rfid_obj = opts[:son_micro_rfid_obj]
365
405
  rfid_data = opts[:rfid_data]
366
- # TODO: write card
367
- puts rfid_data.inspect
406
+ logger.info('Starting write_tag')
407
+
408
+ unless rfid_data.is_a?(Hash) && rfid_data[:resp_code_desc] && rfid_data[:tag_id]
409
+ logger.error('Invalid rfid_data: must be a hash with :resp_code_desc and :tag_id')
410
+ raise 'Invalid rfid_data: must be a hash with :resp_code_desc and :tag_id'
411
+ end
412
+
413
+ puts "\nWriting to tag with Tag ID: #{rfid_data[:tag_id]}"
414
+ print 'This will overwrite the tag. Continue? [y/N]: '
415
+ unless gets.chomp.strip.upcase == 'Y'
416
+ logger.info('Write cancelled by user')
417
+ puts 'Write cancelled.'
418
+ return rfid_data
419
+ end
420
+
421
+ # Select tag
422
+ exec_resp = exec(
423
+ son_micro_rfid_obj: son_micro_rfid_obj,
424
+ cmd: :select_tag
425
+ )
426
+ if exec_resp.last[:resp_code_desc] == :no_tag_present
427
+ logger.error('No RFID tag detected for writing')
428
+ raise 'No RFID tag detected for writing'
429
+ end
430
+
431
+ # Write block data
432
+ case rfid_data[:resp_code_desc]
433
+ when :mifare_ultralight
434
+ if rfid_data[:block_data]
435
+ data = rfid_data[:block_data].split.map { |b| b.to_i(16) }
436
+ exec_resp = exec(
437
+ son_micro_rfid_obj: son_micro_rfid_obj,
438
+ cmd: :write_block,
439
+ params: { block: 4, data: data }
440
+ )
441
+ unless exec_resp.last[:resp_code_desc] == :write_success
442
+ logger.error('Failed to write block for Ultralight')
443
+ raise 'Failed to write block'
444
+ end
445
+ end
446
+ when :mifare_classic_1k, :mifare_classic_4k
447
+ exec_resp = exec(
448
+ son_micro_rfid_obj: son_micro_rfid_obj,
449
+ cmd: :authenticate,
450
+ params: { block: 0, key_type: :key_a, key: [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF] }
451
+ )
452
+ if exec_resp.last[:resp_code_desc] == :auth_success
453
+ if rfid_data[:block_data]
454
+ data = rfid_data[:block_data].split.map { |b| b.to_i(16) }
455
+ exec_resp = exec(
456
+ son_micro_rfid_obj: son_micro_rfid_obj,
457
+ cmd: :write_block,
458
+ params: { block: 0, data: data }
459
+ )
460
+ unless exec_resp.last[:resp_code_desc] == :write_success
461
+ logger.error('Failed to write block for Classic')
462
+ raise 'Failed to write block'
463
+ end
464
+ end
465
+ else
466
+ logger.error("Authentication failed for #{rfid_data[:resp_code_desc]}")
467
+ raise 'Authentication failed'
468
+ end
469
+ else
470
+ logger.error("Unsupported tag type: #{rfid_data[:resp_code_desc]}")
471
+ raise "Unsupported tag type: #{rfid_data[:resp_code_desc]}"
472
+ end
473
+
474
+ # Verify write by re-reading
475
+ read_data = read_tag(son_micro_rfid_obj: son_micro_rfid_obj)
476
+ if read_data[:block_data] == rfid_data[:block_data]
477
+ puts 'Write verification successful.'
478
+ logger.info('Write verification successful')
479
+ else
480
+ puts 'ERROR: Written data does not match read data.'
481
+ logger.error('Written data does not match read data')
482
+ end
368
483
 
484
+ logger.info("Write tag successful: #{rfid_data.inspect}")
485
+ puts 'Tag written successfully.'
369
486
  rfid_data
370
487
  rescue StandardError => e
488
+ logger.error("Error writing tag: #{e.message}")
489
+ puts "ERROR: Failed to write tag - #{e.message}"
371
490
  raise e
372
491
  end
373
492
 
374
493
  # Supported Method Parameters::
375
- # PWN::Plugins::SonMicroRFID.backup_card(
494
+ # PWN::Plugins::SonMicroRFID.backup_tag(
376
495
  # son_micro_rfid_obj: 'required - son_micro_rfid_obj returned from #connect method'
377
496
  # )
378
497
 
379
- public_class_method def self.backup_card(opts = {})
498
+ public_class_method def self.backup_tag(opts = {})
380
499
  son_micro_rfid_obj = opts[:son_micro_rfid_obj]
381
- rfid_data = read_card(son_micro_rfid_obj: son_micro_rfid_obj)
500
+ logger.info('Starting backup_tag')
501
+
502
+ rfid_data = read_tag(son_micro_rfid_obj: son_micro_rfid_obj)
503
+
382
504
  file = ''
383
505
  backup_msg = ''
384
506
  loop do
@@ -390,41 +512,64 @@ module PWN
390
512
  backup_msg = "\n****** ERROR: Directory #{file_dir} for #{file} does not exist ******"
391
513
  puts backup_msg
392
514
  end
393
- File.write(file, "#{JSON.pretty_generate(rfid_data)}\n")
394
515
 
395
- puts 'complete.'
516
+ File.write(file, "#{JSON.pretty_generate(rfid_data)}\n")
517
+ logger.info("Backup saved to #{file}")
518
+ puts 'Backup complete.'
396
519
  rfid_data
397
520
  rescue StandardError => e
521
+ logger.error("Error backing up tag: #{e.message}")
522
+ puts "ERROR: Failed to backup tag - #{e.message}"
398
523
  raise e
399
524
  end
400
525
 
401
526
  # Supported Method Parameters::
402
- # PWN::Plugins::SonMicroRFID.copy_card(
527
+ # PWN::Plugins::SonMicroRFID.clone_tag(
403
528
  # son_micro_rfid_obj: 'required - son_micro_rfid_obj returned from #connect method'
404
529
  # )
405
530
 
406
- public_class_method def self.copy_card(opts = {})
531
+ public_class_method def self.clone_tag(opts = {})
407
532
  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(
533
+ logger.info('Starting clone_tag')
534
+
535
+ print 'This will overwrite the target tag. Continue? [y/N]: '
536
+ unless gets.chomp.strip.upcase == 'Y'
537
+ logger.info('Copy cancelled by user')
538
+ puts 'Copy cancelled.'
539
+ return nil
540
+ end
541
+
542
+ rfid_data = read_tag(son_micro_rfid_obj: son_micro_rfid_obj)
543
+ write_tag(
410
544
  son_micro_rfid_obj: son_micro_rfid_obj,
411
545
  rfid_data: rfid_data
412
546
  )
413
547
  rescue StandardError => e
548
+ logger.error("Error copying tag: #{e.message}")
549
+ puts "ERROR: Failed to copy tag - #{e.message}"
414
550
  raise e
415
551
  end
416
552
 
417
553
  # Supported Method Parameters::
418
- # PWN::Plugins::SonMicroRFID.load_card_from_file(
554
+ # PWN::Plugins::SonMicroRFID.load_tag_from_file(
419
555
  # son_micro_rfid_obj: 'required - son_micro_rfid_obj returned from #connect method'
420
556
  # )
421
557
 
422
- public_class_method def self.load_card_from_file(opts = {})
558
+ public_class_method def self.load_tag_from_file(opts = {})
423
559
  son_micro_rfid_obj = opts[:son_micro_rfid_obj]
560
+ logger.info('Starting load_tag_from_file')
561
+
562
+ print 'This will overwrite the target tag. Continue? [y/N]: '
563
+ unless gets.chomp.strip.upcase == 'Y'
564
+ logger.info('Load cancelled by user')
565
+ puts 'Load cancelled.'
566
+ return nil
567
+ end
568
+
424
569
  file = ''
425
570
  restore_msg = ''
426
571
  loop do
427
- print 'Enter File Name to Restore to Card: '
572
+ print 'Enter File Name to Restore to Tag: '
428
573
  file = gets.scrub.chomp.strip
429
574
  break if File.exist?(file)
430
575
 
@@ -432,17 +577,89 @@ module PWN
432
577
  puts restore_msg
433
578
  end
434
579
 
435
- rfid_data = JSON.parse(
436
- File.read(file),
437
- symbolize_names: true
580
+ rfid_data = JSON.parse(File.read(file), symbolize_names: true)
581
+ write_tag(
582
+ son_micro_rfid_obj: son_micro_rfid_obj,
583
+ rfid_data: rfid_data
438
584
  )
585
+ rescue StandardError => e
586
+ logger.error("Error loading tag from file: #{e.message}")
587
+ puts "ERROR: Failed to load tag from file - #{e.message}"
588
+ raise e
589
+ end
439
590
 
440
- # TODO: Save Original Card Contents
441
- write_card(
591
+ # Supported Method Parameters::
592
+ # PWN::Plugins::SonMicroRFID.update_tag(
593
+ # son_micro_rfid_obj: 'required - son_micro_rfid_obj returned from #connect method'
594
+ # )
595
+
596
+ public_class_method def self.update_tag(opts = {})
597
+ son_micro_rfid_obj = opts[:son_micro_rfid_obj]
598
+ logger.info('Starting update_tag')
599
+
600
+ print 'This will modify the tag\'s data. Continue? [y/N]: '
601
+ unless gets.chomp.strip.upcase == 'Y'
602
+ logger.info('Update cancelled by user')
603
+ puts 'Update cancelled.'
604
+ return nil
605
+ end
606
+
607
+ rfid_data = read_tag(son_micro_rfid_obj: son_micro_rfid_obj)
608
+ unless rfid_data.is_a?(Hash) && rfid_data[:resp_code_desc] && rfid_data[:tag_id]
609
+ logger.error('Invalid rfid_data structure')
610
+ raise 'Invalid rfid_data structure'
611
+ end
612
+
613
+ # Update block data
614
+ block_data = rfid_data[:block_data] || ''
615
+ puts "\nCurrent Block Data: #{block_data}"
616
+ print 'Enter Updated Block Data (hex bytes, space-separated, press Enter to keep original): '
617
+ updated_value = gets.scrub.chomp.strip
618
+
619
+ if updated_value.empty?
620
+ puts "Keeping original value: #{block_data}"
621
+ logger.info("Keeping original block data: #{block_data}")
622
+ updated_value = block_data
623
+ else
624
+ # Validate hex bytes
625
+ unless updated_value.match?(/\A([\da-fA-F]{2}\s)*[\da-fA-F]{2}\z/)
626
+ logger.error('Invalid block data: must be hex bytes (e.g., FF 00)')
627
+ raise 'Invalid block data: must be hex bytes (e.g., FF 00)'
628
+ end
629
+ # Validate length based on tag type
630
+ data_bytes = updated_value.split.map { |b| b.to_i(16) }
631
+ max_bytes = rfid_data[:resp_code_desc] == :mifare_ultralight ? 4 : 16
632
+ unless data_bytes.length <= max_bytes
633
+ logger.error("Block data too long: max #{max_bytes} bytes")
634
+ raise "Block data too long: max #{max_bytes} bytes"
635
+ end
636
+ logger.info("Updated block data: #{updated_value}")
637
+ end
638
+
639
+ rfid_data[:block_data] = updated_value
640
+
641
+ # Confirm changes
642
+ puts "\nUpdated RFID Data:"
643
+ puts "Tag ID: #{rfid_data[:tag_id]}"
644
+ puts "Block Data: #{rfid_data[:block_data]}"
645
+ print 'Confirm writing these changes to the tag? [y/N]: '
646
+ unless gets.chomp.strip.upcase == 'Y'
647
+ logger.info('Update cancelled by user')
648
+ puts 'Update cancelled.'
649
+ return rfid_data
650
+ end
651
+
652
+ rfid_data = write_tag(
442
653
  son_micro_rfid_obj: son_micro_rfid_obj,
443
654
  rfid_data: rfid_data
444
655
  )
656
+
657
+ logger.info("Update tag successful: #{rfid_data.inspect}")
658
+ puts 'tag updated successfully.'
659
+ rfid_data
445
660
  rescue StandardError => e
661
+ logger.error("Error updating tag: #{e.message}")
662
+ puts "ERROR: Failed to update tag - #{e.message}"
446
663
  raise e
447
664
  end
448
665
 
@@ -452,19 +669,29 @@ module PWN
452
669
  # )
453
670
 
454
671
  public_class_method def self.disconnect(opts = {})
455
- PWN::Plugins::Serial.disconnect(
456
- serial_obj: opts[:son_micro_rfid_obj]
672
+ son_micro_rfid_obj = opts[:son_micro_rfid_obj]
673
+
674
+ logger.info('Disabling antenna power')
675
+ exec(
676
+ son_micro_rfid_obj: son_micro_rfid_obj,
677
+ cmd: :antenna_power,
678
+ params: :off
457
679
  )
680
+
681
+ logger.info('Disconnecting from device')
682
+
683
+ PWN::Plugins::Serial.disconnect(serial_obj: son_micro_rfid_obj)
458
684
  rescue StandardError => e
685
+ logger.error("Error disconnecting: #{e.message}")
459
686
  raise e
460
687
  end
461
688
 
462
689
  # Author(s):: 0day Inc. <support@0dayinc.com>
463
690
 
464
691
  public_class_method def self.authors
465
- "AUTHOR(S):
692
+ 'AUTHOR(S):
466
693
  0day Inc. <support@0dayinc.com>
467
- "
694
+ '
468
695
  end
469
696
 
470
697
  # Display Usage for this Module
@@ -476,13 +703,14 @@ module PWN
476
703
  baud: 'optional (defaults to 19_200)',
477
704
  data_bits: 'optional (defaults to 8)',
478
705
  stop_bits: 'optional (defaults to 1)',
479
- parity: 'optional - :even|:mark|:odd|:space|:none (defaults to :odd)'
706
+ parity: 'optional - :even|:mark|:odd|:space|:none (defaults to :none)',
707
+ flow_control: 'optional - :none|:hard|:soft (defaults to :none)'
480
708
  )
481
709
 
482
710
  cmds = #{self}.list_cmds
483
711
 
484
712
  params = #{self}.list_params(
485
- cmd: 'required - cmd returned from #list_cmds method',
713
+ cmd: 'required - cmd returned from #list_cmds method'
486
714
  )
487
715
 
488
716
  parsed_cmd_resp_arr = #{self}.exec(
@@ -491,6 +719,31 @@ module PWN
491
719
  params: 'optional - parameters for specific command returned from #list_params method'
492
720
  )
493
721
 
722
+ rfid_data = #{self}.read_tag(
723
+ son_micro_rfid_obj: 'required son_micro_rfid_obj returned from #connect method'
724
+ )
725
+
726
+ rfid_data = #{self}.write_tag(
727
+ son_micro_rfid_obj: 'required son_micro_rfid_obj returned from #connect method',
728
+ rfid_data: 'required - RFID data to write'
729
+ )
730
+
731
+ rfid_data = #{self}.backup_tag(
732
+ son_micro_rfid_obj: 'required son_micro_rfid_obj returned from #connect method'
733
+ )
734
+
735
+ rfid_data = #{self}.clone_tag(
736
+ son_micro_rfid_obj: 'required son_micro_rfid_obj returned from #connect method'
737
+ )
738
+
739
+ rfid_data = #{self}.load_tag_from_file(
740
+ son_micro_rfid_obj: 'required son_micro_rfid_obj returned from #connect method'
741
+ )
742
+
743
+ rfid_data = #{self}.update_tag(
744
+ son_micro_rfid_obj: 'required son_micro_rfid_obj returned from #connect method'
745
+ )
746
+
494
747
  #{self}.disconnect(
495
748
  son_micro_rfid_obj: 'required son_micro_rfid_obj returned from #connect method'
496
749
  )
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.259'
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.259
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