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:

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.


Go back to menu.

See Also


Check also the following modules related to this module:

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.