Modbus Client Utility - Metasploit
This page contains detailed information about how to use the auxiliary/scanner/scada/modbusclient metasploit module. For list of all metasploit modules, visit the Metasploit Module Library.
Module Overview
Name: Modbus Client Utility
Module: auxiliary/scanner/scada/modbusclient
Source code: modules/auxiliary/scanner/scada/modbusclient.rb
Disclosure date: -
Last modification time: 2022-01-20 12:23:01 +0000
Supported architecture(s): -
Supported platform(s): -
Target service / protocol: -
Target network port(s): 502
List of CVEs: -
This module allows reading and writing data to a PLC using the Modbus protocol. This module is based on the 'modiconstop.rb' Basecamp module from DigitalBond, as well as the mbtget perl script.
Module Ranking and Traits
Module Ranking:
- normal: The exploit is otherwise reliable, but depends on a specific version and can't (or doesn't) reliably autodetect. More information about ranking can be found here.
Basic Usage
msf > use auxiliary/scanner/scada/modbusclient
msf auxiliary(modbusclient) > show targets
... a list of targets ...
msf auxiliary(modbusclient) > set TARGET target-id
msf auxiliary(modbusclient) > show options
... show and set options ...
msf auxiliary(modbusclient) > exploit
Required Options
RHOSTS: The target host(s), range CIDR identifier, or hosts file with syntax 'file:<path>'
DATA_ADDRESS: Modbus data address
Go back to menu.
Msfconsole Usage
Here is how the scanner/scada/modbusclient auxiliary module looks in the msfconsole:
msf6 > use auxiliary/scanner/scada/modbusclient
msf6 auxiliary(scanner/scada/modbusclient) > show info
Name: Modbus Client Utility
Module: auxiliary/scanner/scada/modbusclient
License: Metasploit Framework License (BSD)
Rank: Normal
Provided by:
EsMnemon <[email protected]>
Arnaud SOULLIE <[email protected]>
Alexandrine TORRENTS <[email protected]>
Mathieu CHEVALIER <[email protected]>
AZSG <[email protected]>
Available actions:
Name Description
---- -----------
READ_COILS Read bits from several coils
READ_DISCRETE_INPUTS Read bits from several DISCRETE INPUTS
READ_HOLDING_REGISTERS Read words from several HOLDING registers
READ_INPUT_REGISTERS Read words from several INPUT registers
WRITE_COIL Write one bit to a coil
WRITE_COILS Write bits to several coils
WRITE_REGISTER Write one word to a register
WRITE_REGISTERS Write words to several registers
Check supported:
No
Basic options:
Name Current Setting Required Description
---- --------------- -------- -----------
DATA no Data to write (WRITE_COIL and WRITE_REGISTER modes only)
DATA_ADDRESS yes Modbus data address
DATA_COILS no Data in binary to write (WRITE_COILS mode only) e.g. 0110
DATA_REGISTERS no Words to write to each register separated with a comma (WRITE_REGISTERS mode only) e.g. 1,2,3,4
NUMBER 1 no Number of coils/registers to read (READ_COILS, READ_DISCRETE_INPUTS, READ_HOLDING_REGISTERS, READ_INPUT_REGISTERS modes only)
RHOSTS yes The target host(s), range CIDR identifier, or hosts file with syntax 'file:<path>'
RPORT 502 yes The target port (TCP)
UNIT_NUMBER 1 no Modbus unit number
Description:
This module allows reading and writing data to a PLC using the
Modbus protocol. This module is based on the 'modiconstop.rb'
Basecamp module from DigitalBond, as well as the mbtget perl script.
Module Options
This is a complete list of options available in the scanner/scada/modbusclient auxiliary module:
msf6 auxiliary(scanner/scada/modbusclient) > show options
Module options (auxiliary/scanner/scada/modbusclient):
Name Current Setting Required Description
---- --------------- -------- -----------
DATA no Data to write (WRITE_COIL and WRITE_REGISTER modes only)
DATA_ADDRESS yes Modbus data address
DATA_COILS no Data in binary to write (WRITE_COILS mode only) e.g. 0110
DATA_REGISTERS no Words to write to each register separated with a comma (WRITE_REGISTERS mode only) e.g. 1,2,3,4
NUMBER 1 no Number of coils/registers to read (READ_COILS, READ_DISCRETE_INPUTS, READ_HOLDING_REGISTERS, READ_INPUT_REGISTERS modes only)
RHOSTS yes The target host(s), range CIDR identifier, or hosts file with syntax 'file:<path>'
RPORT 502 yes The target port (TCP)
UNIT_NUMBER 1 no Modbus unit number
Auxiliary action:
Name Description
---- -----------
READ_HOLDING_REGISTERS Read words from several HOLDING registers
Advanced Options
Here is a complete list of advanced options supported by the scanner/scada/modbusclient auxiliary module:
msf6 auxiliary(scanner/scada/modbusclient) > show advanced
Module advanced options (auxiliary/scanner/scada/modbusclient):
Name Current Setting Required Description
---- --------------- -------- -----------
CHOST no The local client address
CPORT no The local client port
ConnectTimeout 10 yes Maximum number of seconds to establish a TCP connection
Proxies no A proxy chain of format type:host:port[,type:host:port][...]
SSL false no Negotiate SSL/TLS for outgoing connections
SSLCipher no String for SSL cipher - "DHE-RSA-AES256-SHA" or "ADH"
SSLVerifyMode PEER no SSL verification method (Accepted: CLIENT_ONCE, FAIL_IF_NO_PEER_CERT, NONE, PEER)
SSLVersion Auto yes Specify the version of SSL/TLS to be used (Auto, TLS and SSL23 are auto-negotiate) (Accepted: Auto, TLS, SSL23, SSL3, TLS1, TLS1.1, TLS1.2)
VERBOSE false no Enable detailed status messages
WORKSPACE no Specify the workspace for this module
Auxiliary Actions
This is a list of all auxiliary actions that the scanner/scada/modbusclient module can do:
msf6 auxiliary(scanner/scada/modbusclient) > show actions
Auxiliary actions:
Name Description
---- -----------
READ_COILS Read bits from several coils
READ_DISCRETE_INPUTS Read bits from several DISCRETE INPUTS
READ_HOLDING_REGISTERS Read words from several HOLDING registers
READ_INPUT_REGISTERS Read words from several INPUT registers
WRITE_COIL Write one bit to a coil
WRITE_COILS Write bits to several coils
WRITE_REGISTER Write one word to a register
WRITE_REGISTERS Write words to several registers
Evasion Options
Here is the full list of possible evasion options supported by the scanner/scada/modbusclient auxiliary module in order to evade defenses (e.g. Antivirus, EDR, Firewall, NIDS etc.):
msf6 auxiliary(scanner/scada/modbusclient) > show evasion
Module evasion options:
Name Current Setting Required Description
---- --------------- -------- -----------
TCP::max_send_size 0 no Maxiumum tcp segment size. (0 = disable)
TCP::send_delay 0 no Delays inserted before every send. (0 = disable)
Go back to menu.
Error Messages
This module may fail with the following error messages:
- Error : ILLEGAL FUNCTION
- Error : ILLEGAL DATA ADDRESS
- Error : ILLEGAL DATA VALUE
- Error : SLAVE DEVICE FAILURE
- Error : SLAVE DEVICE BUSY
- Unknown error
- Coils addresses go from 0 to 65535. You cannot go beyond.
- No answer for the READ COILS
- Unknown answer
- DISCRETE INPUT addresses go from 0 to 65535. You cannot go beyond.
- No answer for the READ DISCRETE INPUTS
- Unknown answer
- Holding Registers addresses go from 0 to 65535. You cannot go beyond.
- No answer for the READ HOLDING REGISTERS
- Unknown answer
- Input Registers addresses go from 0 to 65535. You cannot go beyond.
- No answer for the READ INPUT REGISTERS
- Unknown answer
- Data value must be 0 or 1 in WRITE_COIL mode
- No answer for the WRITE COIL
- Unknown answer
- DATA_COILS size must be between 0 and 65535
- DATA_COILS value must only contain 0s and 1s without space
- No answer for the WRITE COILS
- Unknown answer
- Data to write must be an integer between 0 and 65535 in WRITE_REGISTER mode
- No answer for the WRITE REGISTER
- Unknown answer
- DATA_REGISTERS cannot contain two consecutive commas
- DATA_REGISTERS value must only contain numbers and commas without space
- Registers addresses go from 0 to 65535. You cannot go beyond.
- Each word to write must be an integer between 0 and 65535 in WRITE_REGISTERS mode
- No answer for the WRITE REGISTERS
- Unknown answer
- The following option is needed in WRITE_COILS mode: DATA_COILS.
- The following option is needed in WRITE_REGISTERS mode: DATA_REGISTERS.
- Invalid ACTION
Check for the possible causes from the code snippets below found in the module source code. This can often times help in identifying the root cause of the problem.
Error : ILLEGAL FUNCTION
Here is a relevant code snippet related to the "Error : ILLEGAL FUNCTION" error message:
124: end
125:
126: def handle_error(response)
127: case response.reverse.unpack("c")[0].to_i
128: when 1
129: print_error("Error : ILLEGAL FUNCTION")
130: when 2
131: print_error("Error : ILLEGAL DATA ADDRESS")
132: when 3
133: print_error("Error : ILLEGAL DATA VALUE")
134: when 4
Error : ILLEGAL DATA ADDRESS
Here is a relevant code snippet related to the "Error : ILLEGAL DATA ADDRESS" error message:
126: def handle_error(response)
127: case response.reverse.unpack("c")[0].to_i
128: when 1
129: print_error("Error : ILLEGAL FUNCTION")
130: when 2
131: print_error("Error : ILLEGAL DATA ADDRESS")
132: when 3
133: print_error("Error : ILLEGAL DATA VALUE")
134: when 4
135: print_error("Error : SLAVE DEVICE FAILURE")
136: when 6
Error : ILLEGAL DATA VALUE
Here is a relevant code snippet related to the "Error : ILLEGAL DATA VALUE" error message:
128: when 1
129: print_error("Error : ILLEGAL FUNCTION")
130: when 2
131: print_error("Error : ILLEGAL DATA ADDRESS")
132: when 3
133: print_error("Error : ILLEGAL DATA VALUE")
134: when 4
135: print_error("Error : SLAVE DEVICE FAILURE")
136: when 6
137: print_error("Error : SLAVE DEVICE BUSY")
138: else
Error : SLAVE DEVICE FAILURE
Here is a relevant code snippet related to the "Error : SLAVE DEVICE FAILURE" error message:
130: when 2
131: print_error("Error : ILLEGAL DATA ADDRESS")
132: when 3
133: print_error("Error : ILLEGAL DATA VALUE")
134: when 4
135: print_error("Error : SLAVE DEVICE FAILURE")
136: when 6
137: print_error("Error : SLAVE DEVICE BUSY")
138: else
139: print_error("Unknown error")
140: end
Error : SLAVE DEVICE BUSY
Here is a relevant code snippet related to the "Error : SLAVE DEVICE BUSY" error message:
132: when 3
133: print_error("Error : ILLEGAL DATA VALUE")
134: when 4
135: print_error("Error : SLAVE DEVICE FAILURE")
136: when 6
137: print_error("Error : SLAVE DEVICE BUSY")
138: else
139: print_error("Unknown error")
140: end
141: return
142: end
Unknown error
Here is a relevant code snippet related to the "Unknown error" error message:
134: when 4
135: print_error("Error : SLAVE DEVICE FAILURE")
136: when 6
137: print_error("Error : SLAVE DEVICE BUSY")
138: else
139: print_error("Unknown error")
140: end
141: return
142: end
143:
144: def read_coils
Coils addresses go from 0 to 65535. You cannot go beyond.
Here is a relevant code snippet related to the "Coils addresses go from 0 to 65535. You cannot go beyond." error message:
141: return
142: end
143:
144: def read_coils
145: if datastore['NUMBER']+datastore['DATA_ADDRESS'] > 65535
146: print_error("Coils addresses go from 0 to 65535. You cannot go beyond.")
147: return
148: end
149: @function_code = 0x1
150: print_status("Sending READ COILS...")
151: response = send_frame(make_read_payload)
No answer for the READ COILS
Here is a relevant code snippet related to the "No answer for the READ COILS" error message:
149: @function_code = 0x1
150: print_status("Sending READ COILS...")
151: response = send_frame(make_read_payload)
152: values = []
153: if response.nil?
154: print_error("No answer for the READ COILS")
155: return
156: elsif response.unpack("C*")[7] == (0x80 | @function_code)
157: handle_error(response)
158: elsif response.unpack("C*")[7] == @function_code
159: loop = (datastore['NUMBER']-1)/8
Unknown answer
Here is a relevant code snippet related to the "Unknown answer" error message:
167: end
168: values = values[0..(datastore['NUMBER']-1)]
169: print_good("#{datastore['NUMBER']} coil values from address #{datastore['DATA_ADDRESS']} : ")
170: print_good("#{values}")
171: else
172: print_error("Unknown answer")
173: end
174: end
175:
176: def read_discrete_inputs
177: if datastore['NUMBER']+datastore['DATA_ADDRESS'] > 65535
DISCRETE INPUT addresses go from 0 to 65535. You cannot go beyond.
Here is a relevant code snippet related to the "DISCRETE INPUT addresses go from 0 to 65535. You cannot go beyond." error message:
173: end
174: end
175:
176: def read_discrete_inputs
177: if datastore['NUMBER']+datastore['DATA_ADDRESS'] > 65535
178: print_error("DISCRETE INPUT addresses go from 0 to 65535. You cannot go beyond.")
179: return
180: end
181: @function_code = 0x2
182: print_status("Sending READ DISCRETE INPUTS...")
183: response = send_frame(make_read_payload)
No answer for the READ DISCRETE INPUTS
Here is a relevant code snippet related to the "No answer for the READ DISCRETE INPUTS" error message:
181: @function_code = 0x2
182: print_status("Sending READ DISCRETE INPUTS...")
183: response = send_frame(make_read_payload)
184: values = []
185: if response.nil?
186: print_error("No answer for the READ DISCRETE INPUTS")
187: return
188: elsif response.unpack("C*")[7] == (0x80 | @function_code)
189: handle_error(response)
190: elsif response.unpack("C*")[7] == @function_code
191: loop = (datastore['NUMBER']-1)/8
Unknown answer
Here is a relevant code snippet related to the "Unknown answer" error message:
199: end
200: values = values[0..(datastore['NUMBER']-1)]
201: print_good("#{datastore['NUMBER']} DISCRETE INPUT values from address #{datastore['DATA_ADDRESS']} : ")
202: print_good("#{values}")
203: else
204: print_error("Unknown answer")
205: end
206: end
207:
208: def read_holding_registers
209: if datastore['NUMBER']+datastore['DATA_ADDRESS'] > 65535
Holding Registers addresses go from 0 to 65535. You cannot go beyond.
Here is a relevant code snippet related to the "Holding Registers addresses go from 0 to 65535. You cannot go beyond." error message:
205: end
206: end
207:
208: def read_holding_registers
209: if datastore['NUMBER']+datastore['DATA_ADDRESS'] > 65535
210: print_error("Holding Registers addresses go from 0 to 65535. You cannot go beyond.")
211: return
212: end
213: @function_code = 3
214: print_status("Sending READ HOLDING REGISTERS...")
215: response = send_frame(make_read_payload)
No answer for the READ HOLDING REGISTERS
Here is a relevant code snippet related to the "No answer for the READ HOLDING REGISTERS" error message:
213: @function_code = 3
214: print_status("Sending READ HOLDING REGISTERS...")
215: response = send_frame(make_read_payload)
216: values = []
217: if response.nil?
218: print_error("No answer for the READ HOLDING REGISTERS")
219: elsif response.unpack("C*")[7] == (0x80 | @function_code)
220: handle_error(response)
221: elsif response.unpack("C*")[7] == @function_code
222: for i in 0..(datastore['NUMBER']-1)
223: values.push(response[9+2*i..10+2*i].unpack("n")[0])
Unknown answer
Here is a relevant code snippet related to the "Unknown answer" error message:
223: values.push(response[9+2*i..10+2*i].unpack("n")[0])
224: end
225: print_good("#{datastore['NUMBER']} register values from address #{datastore['DATA_ADDRESS']} : ")
226: print_good("#{values}")
227: else
228: print_error("Unknown answer")
229: end
230: end
231:
232: def read_input_registers
233: if datastore['NUMBER']+datastore['DATA_ADDRESS'] > 65535
Input Registers addresses go from 0 to 65535. You cannot go beyond.
Here is a relevant code snippet related to the "Input Registers addresses go from 0 to 65535. You cannot go beyond." error message:
229: end
230: end
231:
232: def read_input_registers
233: if datastore['NUMBER']+datastore['DATA_ADDRESS'] > 65535
234: print_error("Input Registers addresses go from 0 to 65535. You cannot go beyond.")
235: return
236: end
237: @function_code = 4
238: print_status("Sending READ INPUT REGISTERS...")
239: response = send_frame(make_read_payload)
No answer for the READ INPUT REGISTERS
Here is a relevant code snippet related to the "No answer for the READ INPUT REGISTERS" error message:
237: @function_code = 4
238: print_status("Sending READ INPUT REGISTERS...")
239: response = send_frame(make_read_payload)
240: values = []
241: if response.nil?
242: print_error("No answer for the READ INPUT REGISTERS")
243: elsif response.unpack("C*")[7] == (0x80 | @function_code)
244: handle_error(response)
245: elsif response.unpack("C*")[7] == @function_code
246: for i in 0..(datastore['NUMBER']-1)
247: values.push(response[9+2*i..10+2*i].unpack("n")[0])
Unknown answer
Here is a relevant code snippet related to the "Unknown answer" error message:
247: values.push(response[9+2*i..10+2*i].unpack("n")[0])
248: end
249: print_good("#{datastore['NUMBER']} register values from address #{datastore['DATA_ADDRESS']} : ")
250: print_good("#{values}")
251: else
252: print_error("Unknown answer")
253: end
254: end
255:
256: def write_coil
257: @function_code = 5
Data value must be 0 or 1 in WRITE_COIL mode
Here is a relevant code snippet related to the "Data value must be 0 or 1 in WRITE_COIL mode" error message:
258: if datastore['DATA'] == 0
259: data = 0
260: elsif datastore['DATA'] == 1
261: data = 255
262: else
263: print_error("Data value must be 0 or 1 in WRITE_COIL mode")
264: return
265: end
266: print_status("Sending WRITE COIL...")
267: response = send_frame(make_write_coil_payload(data))
268: if response.nil?
No answer for the WRITE COIL
Here is a relevant code snippet related to the "No answer for the WRITE COIL" error message:
264: return
265: end
266: print_status("Sending WRITE COIL...")
267: response = send_frame(make_write_coil_payload(data))
268: if response.nil?
269: print_error("No answer for the WRITE COIL")
270: elsif response.unpack("C*")[7] == (0x80 | @function_code)
271: handle_error(response)
272: elsif response.unpack("C*")[7] == @function_code
273: print_good("Value #{datastore['DATA']} successfully written at coil address #{datastore['DATA_ADDRESS']}")
274: else
Unknown answer
Here is a relevant code snippet related to the "Unknown answer" error message:
270: elsif response.unpack("C*")[7] == (0x80 | @function_code)
271: handle_error(response)
272: elsif response.unpack("C*")[7] == @function_code
273: print_good("Value #{datastore['DATA']} successfully written at coil address #{datastore['DATA_ADDRESS']}")
274: else
275: print_error("Unknown answer")
276: end
277: end
278:
279: def write_coils
280: @function_code = 15
DATA_COILS size must be between 0 and 65535
Here is a relevant code snippet related to the "DATA_COILS size must be between 0 and 65535" error message:
279: def write_coils
280: @function_code = 15
281: temp = datastore['DATA_COILS']
282: check = temp.split("")
283: if temp.size > 65535
284: print_error("DATA_COILS size must be between 0 and 65535")
285: return
286: end
287: for j in check
288: if j=="0" or j=="1"
289: else
DATA_COILS value must only contain 0s and 1s without space
Here is a relevant code snippet related to the "DATA_COILS value must only contain 0s and 1s without space" error message:
285: return
286: end
287: for j in check
288: if j=="0" or j=="1"
289: else
290: print_error("DATA_COILS value must only contain 0s and 1s without space")
291: return
292: end
293: end
294: byte_number = (temp.size-1)/8 + 1
295: data = []
No answer for the WRITE COILS
Here is a relevant code snippet related to the "No answer for the WRITE COILS" error message:
297: data.push(temp[(i*8+0)..(i*8+7)])
298: end
299: print_status("Sending WRITE COILS...")
300: response = send_frame(make_write_coils_payload(data, byte_number))
301: if response.nil?
302: print_error("No answer for the WRITE COILS")
303: elsif response.unpack("C*")[7] == (0x80 | @function_code)
304: handle_error(response)
305: elsif response.unpack("C*")[7] == @function_code
306: print_good("Values #{datastore['DATA_COILS']} successfully written from coil address #{datastore['DATA_ADDRESS']}")
307: else
Unknown answer
Here is a relevant code snippet related to the "Unknown answer" error message:
303: elsif response.unpack("C*")[7] == (0x80 | @function_code)
304: handle_error(response)
305: elsif response.unpack("C*")[7] == @function_code
306: print_good("Values #{datastore['DATA_COILS']} successfully written from coil address #{datastore['DATA_ADDRESS']}")
307: else
308: print_error("Unknown answer")
309: end
310: end
311:
312: def write_register
313: @function_code = 6
Data to write must be an integer between 0 and 65535 in WRITE_REGISTER mode
Here is a relevant code snippet related to the "Data to write must be an integer between 0 and 65535 in WRITE_REGISTER mode" error message:
310: end
311:
312: def write_register
313: @function_code = 6
314: if datastore['DATA'] < 0 || datastore['DATA'] > 65535
315: print_error("Data to write must be an integer between 0 and 65535 in WRITE_REGISTER mode")
316: return
317: end
318: print_status("Sending WRITE REGISTER...")
319: response = send_frame(make_write_register_payload(datastore['DATA']))
320: if response.nil?
No answer for the WRITE REGISTER
Here is a relevant code snippet related to the "No answer for the WRITE REGISTER" error message:
316: return
317: end
318: print_status("Sending WRITE REGISTER...")
319: response = send_frame(make_write_register_payload(datastore['DATA']))
320: if response.nil?
321: print_error("No answer for the WRITE REGISTER")
322: elsif response.unpack("C*")[7] == (0x80 | @function_code)
323: handle_error(response)
324: elsif response.unpack("C*")[7] == @function_code
325: print_good("Value #{datastore['DATA']} successfully written at registry address #{datastore['DATA_ADDRESS']}")
326: else
Unknown answer
Here is a relevant code snippet related to the "Unknown answer" error message:
322: elsif response.unpack("C*")[7] == (0x80 | @function_code)
323: handle_error(response)
324: elsif response.unpack("C*")[7] == @function_code
325: print_good("Value #{datastore['DATA']} successfully written at registry address #{datastore['DATA_ADDRESS']}")
326: else
327: print_error("Unknown answer")
328: end
329: end
330:
331: def write_registers
332: @function_code = 16
DATA_REGISTERS cannot contain two consecutive commas
Here is a relevant code snippet related to the "DATA_REGISTERS cannot contain two consecutive commas" error message:
332: @function_code = 16
333: check = datastore['DATA_REGISTERS'].split("")
334: for j in 0..(check.size-1)
335: if check[j] == "0" or check[j]== "1" or check[j]== "2" or check[j]== "3" or check[j]== "4" or check[j]== "5" or check[j]== "6" or check[j]== "7" or check[j]== "8" or check[j]== "9" or check[j]== ","
336: if check[j] == "," and check[j+1] == ","
337: print_error("DATA_REGISTERS cannot contain two consecutive commas")
338: return
339: end
340: else
341: print_error("DATA_REGISTERS value must only contain numbers and commas without space")
342: return
DATA_REGISTERS value must only contain numbers and commas without space
Here is a relevant code snippet related to the "DATA_REGISTERS value must only contain numbers and commas without space" error message:
336: if check[j] == "," and check[j+1] == ","
337: print_error("DATA_REGISTERS cannot contain two consecutive commas")
338: return
339: end
340: else
341: print_error("DATA_REGISTERS value must only contain numbers and commas without space")
342: return
343: end
344: end
345: list = datastore['DATA_REGISTERS'].split(",")
346: if list.size+datastore['DATA_ADDRESS'] > 65535
Registers addresses go from 0 to 65535. You cannot go beyond.
Here is a relevant code snippet related to the "Registers addresses go from 0 to 65535. You cannot go beyond." error message:
342: return
343: end
344: end
345: list = datastore['DATA_REGISTERS'].split(",")
346: if list.size+datastore['DATA_ADDRESS'] > 65535
347: print_error("Registers addresses go from 0 to 65535. You cannot go beyond.")
348: return
349: end
350: data = []
351: for i in 0..(list.size-1)
352: data[i] = list[i].to_i
Each word to write must be an integer between 0 and 65535 in WRITE_REGISTERS mode
Here is a relevant code snippet related to the "Each word to write must be an integer between 0 and 65535 in WRITE_REGISTERS mode" error message:
351: for i in 0..(list.size-1)
352: data[i] = list[i].to_i
353: end
354: for j in 0..(data.size-1)
355: if data[j] < 0 || data[j] > 65535
356: print_error("Each word to write must be an integer between 0 and 65535 in WRITE_REGISTERS mode")
357: return
358: end
359: end
360: print_status("Sending WRITE REGISTERS...")
361: response = send_frame(make_write_registers_payload(data, data.size))
No answer for the WRITE REGISTERS
Here is a relevant code snippet related to the "No answer for the WRITE REGISTERS" error message:
358: end
359: end
360: print_status("Sending WRITE REGISTERS...")
361: response = send_frame(make_write_registers_payload(data, data.size))
362: if response.nil?
363: print_error("No answer for the WRITE REGISTERS")
364: elsif response.unpack("C*")[7] == (0x80 | @function_code)
365: handle_error(response)
366: elsif response.unpack("C*")[7] == @function_code
367: print_good("Values #{datastore['DATA_REGISTERS']} successfully written from registry address #{datastore['DATA_ADDRESS']}")
368: else
Unknown answer
Here is a relevant code snippet related to the "Unknown answer" error message:
364: elsif response.unpack("C*")[7] == (0x80 | @function_code)
365: handle_error(response)
366: elsif response.unpack("C*")[7] == @function_code
367: print_good("Values #{datastore['DATA_REGISTERS']} successfully written from registry address #{datastore['DATA_ADDRESS']}")
368: else
369: print_error("Unknown answer")
370: end
371: end
372:
373: def run
374: @modbus_counter = 0x0000 # used for modbus frames
The following option is needed in WRITE_COILS mode: DATA_COILS.
Here is a relevant code snippet related to the "The following option is needed in WRITE_COILS mode: DATA_COILS." error message:
386: write_coil
387: when "WRITE_REGISTER"
388: write_register
389: when "WRITE_COILS"
390: if datastore['DATA_COILS'] == nil
391: print_error("The following option is needed in WRITE_COILS mode: DATA_COILS.")
392: return
393: else
394: write_coils
395: end
396: when "WRITE_REGISTERS"
The following option is needed in WRITE_REGISTERS mode: DATA_REGISTERS.
Here is a relevant code snippet related to the "The following option is needed in WRITE_REGISTERS mode: DATA_REGISTERS." error message:
393: else
394: write_coils
395: end
396: when "WRITE_REGISTERS"
397: if datastore['DATA_REGISTERS'] == nil
398: print_error("The following option is needed in WRITE_REGISTERS mode: DATA_REGISTERS.")
399: return
400: else
401: write_registers
402: end
403: else
Invalid ACTION
Here is a relevant code snippet related to the "Invalid ACTION" error message:
398: print_error("The following option is needed in WRITE_REGISTERS mode: DATA_REGISTERS.")
399: return
400: else
401: write_registers
402: end
403: else
404: print_error("Invalid ACTION")
405: end
406: disconnect
407: end
408: end
Go back to menu.
Related Pull Requests
- #12295 Merged Pull Request: Update to modbusclient.rb to use modbus read functions 2 and 4
- #8716 Merged Pull Request: Print_Status -> Print_Good (And OCD bits 'n bobs)
- #8338 Merged Pull Request: Fix msf/core and self.class msftidy warnings
- #6655 Merged Pull Request: use MetasploitModule as a class name
- #6648 Merged Pull Request: Change metasploit class names
- #6545 Merged Pull Request: Addition of multiple read/write to auxiliary/scanner/scada/modbusclie…
- #3459 Merged Pull Request: Add newline at EOF
- #3309 Merged Pull Request: Add read/write capabilities to modbusclient
- #2525 Merged Pull Request: Change module boilerplate
Go back to menu.
See Also
Check also the following modules related to this module:
- auxiliary/scanner/scada/bacnet_l3
- auxiliary/scanner/scada/digi_addp_reboot
- auxiliary/scanner/scada/digi_addp_version
- auxiliary/scanner/scada/digi_realport_serialport_scan
- auxiliary/scanner/scada/digi_realport_version
- auxiliary/scanner/scada/indusoft_ntwebserver_fileaccess
- auxiliary/scanner/scada/koyo_login
- auxiliary/scanner/scada/modbus_banner_grabbing
- auxiliary/scanner/scada/modbusdetect
- auxiliary/scanner/scada/modbus_findunitid
- auxiliary/scanner/scada/moxa_discover
- auxiliary/scanner/scada/pcomclient
- auxiliary/scanner/scada/profinet_siemens
- auxiliary/scanner/scada/sielco_winlog_fileaccess
- auxiliary/admin/scada/advantech_webaccess_dbvisitor_sqli
- auxiliary/admin/scada/ge_proficy_substitute_traversal
- auxiliary/admin/scada/modicon_command
- auxiliary/admin/scada/modicon_password_recovery
- auxiliary/admin/scada/modicon_stux_transfer
- auxiliary/admin/scada/moxa_credentials_recovery
- auxiliary/admin/scada/multi_cip_command
- auxiliary/admin/scada/pcom_command
- auxiliary/admin/scada/phoenix_command
- auxiliary/admin/scada/yokogawa_bkbcopyd_client
- auxiliary/dos/scada/allen_bradley_pccc
- auxiliary/dos/scada/beckhoff_twincat
- auxiliary/dos/scada/d20_tftp_overflow
- auxiliary/dos/scada/igss9_dataserver
- auxiliary/dos/scada/siemens_siprotec4
- auxiliary/dos/scada/yokogawa_logsvr
Authors
- EsMnemon <esm[at]mnemonic.no>
- Arnaud SOULLIE <arnaud.soullie[at]solucom.fr>
- Alexandrine TORRENTS <alexandrine.torrents[at]eurecom.fr>
- Mathieu CHEVALIER <mathieu.chevalier[at]eurecom.fr>
- AZSG <[email protected]>
Version
This page has been produced using Metasploit Framework version 6.2.23-dev. For more modules, visit the Metasploit Module Library.
Go back to menu.