Scannerl ICS modules open-source

Scannerl is our de facto tool to perform large-scale fingerprinting campaigns. It was recently open-sourced on github and is freely available here.

We are releasing all the modules we used in order to fingerprint available ICS services on the entire IPv4 address space. See our previous post 6 months of ICS scanning.

We are releasing the following fingerprinting modules:

  • bacnet (UDP)
  • chargen (UDP)
  • fox (TCP)
  • mqtt (TCP)
  • mqtts (SSL)
  • modbus (TCP)

The bacnet module () can be used to identify if the remote host is running the bacnet service. It does so by sending a readPropertyMultiple query that requests several information on bacnet objects. The first byte of the response is checked against bacnet’s magic byte 0x81. The result returned is true in case the service was positively identified.

The chargen module (fp_chargen.erl) is used to identify hosts running the chargen service (UDP/19). It does so by sending data to the chargen port and monitoring the size of the response. Since chargen is a character generation service (will respond with a stream of random characters each time it is queried), it can be quite interesting for bad minded people and a potential risk for other Internet actors. The typical output in case data are returned is the amplification factor; that is the result of the division data-received/data-sent (headers excluded). During our fingerprinting campaigns, we have seen amplifications of up to 455 (thus 8192 bytes in the response). This is especially interesting since the protocol specifies that any UDP connection must be answer with up to 512 characters:

When a datagram is received, an answering datagram is sent containing
a random number (between 0 and 512) of characters

It seems some funky implementations of this protocol are used out there.

The fox module (fp_fox.erl) is used to identify fox services. Fox is a text based protocol used in ICS. A query for information on the service is sent to the remote host and the matching is done on the fox keyword. The information returned are not directly parsed by the module but such an improvements could be easily done. Information like the hostname, the IP and the version are typically returned by a valid fox service. Here is an anonymized example of the returned data:

fox a 0 -1 fox hello
app.version=s: HotSpot(TM) Client VM
sysInfo=o:bog 80[80[<bog version="1.0">
<p m="b=baja" t="b:Facets" v="realms=s:airmasters"/>

fox a 1 -1 fox challenge

fox a 2 -1 fox rejected
fatal=s:EOFException: EOF

The mqtt and mqtts modules (fp_mqtt.erl and fp_mqtts.erl) are modules that can be used to identify mqtt services (either directly on TCP or over SSL). Mqtt is a publish-subscribe messaging system which uses a message broker to distribute messages to the right subscriber. Our module sends a connect action to the remote host and looks for a connack response. In case the response matches the expected data, mqtt is returned to show that the service has been identified as mqtt.

The modbus module (fp_modbus.erl) identifies modbus services. It does so by sending a Read device identification query that should return information on the remote service. Interesting information are usually returned as part of the response, like for example the vendor, product and version:

.....2.+........Schneider Electric ..BMX P34 2020..v2.9

Our module however doesn’t parse these results but will output true in case the response is positively parsed as a modbus packet.

Beside the above modules we are also open-sourcing output modules that could be useful for parsing the data. This is mostly modules that re-format the output to be used for post-processing. The csv output is probably the most useful for those wishing to play with the data in a more comfortable format.

Happy ICS fingerprinting !

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s