-
Notifications
You must be signed in to change notification settings - Fork 14.9k
Add Ignition gateway fingerprint scanner (auxiliary/scanner/scada/ignition_statusping) #21273
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| ## Vulnerable Application | ||
|
|
||
| Inductive Automation Ignition is a widely deployed SCADA platform used in critical | ||
| infrastructure worldwide. This module targets an unauthenticated information disclosure | ||
| present across all major Ignition versions — the gateway exposes version, run state, | ||
| OS, Java runtime, and GAN redundancy topology without any credentials. | ||
|
|
||
| Ignition can be downloaded from [inductiveautomation.com](https://inductiveautomation.com/downloads/ignition). | ||
| A free 2-hour trial license is available and resets on restart, which is sufficient | ||
| for testing purposes. | ||
|
|
||
| The endpoint and response format differ by version: | ||
|
|
||
| | Version | Endpoint | Format | | ||
| |---|---|---| | ||
| | 7.9.x | `/main/system/gwinfo` | semicolon-delimited key=value | | ||
| | 8.0.x | `/system/gwinfo` | semicolon-delimited key=value | | ||
| | 8.1.x | `/system/StatusPing` | JSON | | ||
| | 8.3.x | `/system/gwinfo` | semicolon-delimited key=value | | ||
|
|
||
| This module has been tested against the following Ignition versions on Linux: | ||
|
|
||
| * 7.9.21 | ||
| * 8.1.15 | ||
| * 8.1.17 | ||
| * 8.3.4 | ||
|
|
||
| 8.0.x behavior is inferred from the source of the existing | ||
| `exploit/multi/scada/inductive_ignition_rce` module, which uses `/system/gwinfo` | ||
| for version detection prior to exploitation. | ||
|
|
||
| ## Verification Steps | ||
|
|
||
| 1. Install Ignition (any version 7.9+) and complete initial gateway commissioning | ||
| 2. Start msfconsole | ||
| 3. `use auxiliary/scanner/scada/ignition_statusping` | ||
| 4. `set RHOSTS <target IP>` | ||
| 5. `run` | ||
| 6. The module should return gateway version, state, OS, Java runtime, and GAN role | ||
|
|
||
| ## Options | ||
|
|
||
| ### RHOSTS | ||
|
|
||
| The target host(s) or CIDR range to scan. Supports standard MSF RHOSTS syntax | ||
| including comma-separated IPs and CIDR notation (e.g. `10.10.0.0/24`). | ||
|
|
||
| ### RPORT | ||
|
|
||
| The Ignition gateway HTTP port. Default: `8088`. Ignition can be configured to run | ||
| on alternate ports — common alternatives include `80`, `443`, `8043`. | ||
|
|
||
| ## Scenarios | ||
|
|
||
| ### Ignition 7.9.21 — Single host | ||
|
|
||
| ``` | ||
| msf6 > use auxiliary/scanner/scada/ignition_statusping | ||
| msf6 auxiliary(scanner/scada/ignition_statusping) > set RHOSTS 159.203.120.32 | ||
| RHOSTS => 159.203.120.32 | ||
| msf6 auxiliary(scanner/scada/ignition_statusping) > run | ||
|
|
||
| [+] 159.203.120.32:8088 - Ignition 7.9.21 | State: RUNNING | OS: Linux | GAN role: Independent | ||
| [*] Scanned 1 of 1 hosts (100% complete) | ||
| [*] Auxiliary module execution completed | ||
| ``` | ||
|
|
||
| ### Mixed version network scan — CIDR range | ||
|
|
||
| The following shows a scan across a /24 containing gateways at multiple versions, | ||
| including an 8.3.4 GAN redundancy pair (Master + Backup): | ||
|
|
||
| ``` | ||
| msf6 > use auxiliary/scanner/scada/ignition_statusping | ||
| msf6 auxiliary(scanner/scada/ignition_statusping) > set RHOSTS 10.10.0.0/24 | ||
| RHOSTS => 10.10.0.0/24 | ||
| msf6 auxiliary(scanner/scada/ignition_statusping) > run | ||
|
|
||
| [+] 10.10.0.3:8088 - Ignition 8.3.4 | State: RUNNING | OS: Linux | Java: 17.0.17 | GAN role: Master | ||
| [+] 10.10.0.4:8088 - Ignition 8.3.4 | State: RUNNING | OS: Linux | Java: 17.0.17 | GAN role: Backup | ||
| [+] 10.10.0.7:8088 - Ignition 7.9.21 | State: RUNNING | OS: Linux | GAN role: Independent | ||
| [+] 10.10.0.8:8088 - Ignition 8.1.15 | State: RUNNING | OS: Linux | Java: 11.0.14.1 | GAN role: Independent | ||
| [*] Scanned 256 of 256 hosts (100% complete) | ||
| [*] Auxiliary module execution completed | ||
| ``` | ||
|
|
||
| The GAN role output (Master/Backup) identifies redundancy pairs and reveals the network | ||
| topology of the Ignition deployment without authentication. This complements | ||
| `exploit/multi/scada/inductive_ignition_rce` (CVE-2020-10644), which targets 8.0.x only, | ||
| by extending fingerprinting coverage to 7.9.x, 8.1.x, and 8.3.x. | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,151 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ## | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # This module requires Metasploit: https://metasploit.com/download | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Current source: https://github.com/rapid7/metasploit-framework | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ## | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1
to
+4
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class MetasploitModule < Msf::Auxiliary | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| include Msf::Exploit::Remote::HttpClient | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| include Msf::Auxiliary::Scanner | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| include Msf::Auxiliary::Report | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def initialize(info = {}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| super( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| update_info( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| info, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'Name' => 'Inductive Automation Ignition Gateway Fingerprint', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'Description' => %q{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Fingerprints Inductive Automation Ignition gateways across all major versions | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| by probing version-specific info endpoints. Extracts version, run state, OS, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Java runtime, and GAN redundancy role without authentication. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Endpoint and format by version: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 7.9.x — /main/system/gwinfo (key=value) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8.0.x — /system/gwinfo (key=value) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8.1.x — /system/StatusPing (JSON) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8.3.x — /system/gwinfo (key=value, includes RuntimeVersion/RequireSsl) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| For 8.0.x exploitation see exploit/multi/scada/inductive_ignition_rce. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| For 8.1.x CVE modules see auxiliary/scanner/scada/ignition_auth_bypass and | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| auxiliary/scanner/scada/ignition_deser_check. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+28
to
+29
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| For 8.1.x CVE modules see auxiliary/scanner/scada/ignition_auth_bypass and | |
| auxiliary/scanner/scada/ignition_deser_check. | |
| For 8.1.x, consult the relevant Ignition security research or available | |
| Metasploit modules for the target version. |
Copilot
AI
Apr 23, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
parse_statusping is extracting JSON fields via regex and broad rescue StandardError. Per project guidance, prefer parsing HTTP JSON responses via res.get_json_document (and handle parse failure explicitly) rather than regexing JSON text; this is more robust across formatting/ordering changes and avoids silently masking unexpected errors.
| info = {} | |
| info['version'] = begin | |
| body.match(/"version"\s*:\s*"([^"]+)"/)[1] | |
| rescue StandardError | |
| nil | |
| end | |
| info['state'] = begin | |
| body.match(/"state"\s*:\s*"([^"]+)"/)[1] | |
| rescue StandardError | |
| nil | |
| end | |
| info['role'] = begin | |
| body.match(/"role"\s*:\s*"([^"]+)"/)[1] | |
| rescue StandardError | |
| nil | |
| end | |
| info['peerAddress'] = begin | |
| body.match(/"peerAddress"\s*:\s*"([^"]+)"/)[1] | |
| rescue StandardError | |
| nil | |
| end | |
| info['os'] = begin | |
| body.match(/"os"\s*:\s*"([^"]+)"/)[1] | |
| rescue StandardError | |
| nil | |
| end | |
| info.compact | |
| document = if body.respond_to?(:get_json_document) | |
| body.get_json_document | |
| else | |
| JSON.parse(body) | |
| end | |
| info = {} | |
| %w[version state role peerAddress os].each do |key| | |
| info[key] = document[key] if document.is_a?(Hash) && document[key] | |
| end | |
| info | |
| rescue JSON::ParserError | |
| {} |
Copilot
AI
Apr 23, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the StatusPing (JSON) probe path, runtime is never populated, so build_info_string can never include the Java/runtime version for 8.1.x responses. This conflicts with the module description/docs claiming Java runtime is extracted; either parse the runtime field from the JSON response (if present) or adjust the description/docs accordingly.
Copilot
AI
Apr 23, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid printing host info as #{ip}:#{rport} because it doesn't handle IPv6 correctly. Use peer / Rex::Socket.to_authority(ip, rport) consistently for output messages.
Copilot
AI
Apr 23, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
report_host is intended for OS fingerprint attributes (e.g., os_name like "Linux"/"Windows"). Setting os_name to the product name (Ignition Gateway) will pollute OS reporting in the database. Consider either reporting the actual OS (from the response) in os_name/os_flavor, and storing the Ignition product/version in info or a report_note, or omit report_host entirely and rely on report_service/notes.
| report_host(host: ip, os_name: 'Ignition Gateway', os_flavor: version) |
Copilot
AI
Apr 23, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reported service is always named http, but this module can be run with SSL enabled (and common Ignition ports include 443/8043). Use ssl ? 'https' : 'http' (or equivalent) so the database service name matches the actual transport.
| name: 'http', | |
| name: ssl ? 'https' : 'http', |
Copilot
AI
Apr 23, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The module always prints No Ignition endpoint responded after the probe loop, even when a probe succeeds (the break only exits the loop, not run_host). This leads to misleading output for every detected gateway; track whether a match was found and only print this message when none matched, or return immediately after reporting a successful fingerprint.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The documentation examples use a public IP address (159.203.120.32). Project guidance is to use RFC 5737 TEST-NET ranges (e.g., 192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24) for example/non-routable addresses in docs to avoid referencing real hosts.