Exploit Development # 1 : Attacking PLCs with Digital Input Forcing – Vulnerability Analysis @ Hack-the-Box

Exploit development Force PLC

Welcome to the thrilling first chapter of our “Exploit Development” series! Buckle up for “Attacking PLCs with Digital Input Forcing” – a title that’s almost as exciting as the content itself!

Ever wondered how to bend PLCs to your will and force those digital inputs into submission? Well, you’re in the right place! In this guide, we’ll unravel the secrets of exploiting PLC systems with a touch of vulnerability analysis & PoC.

Get ready for a hands-on adventure where we turn theory into practice and make PLC security feel like a high-stakes game of digital dodgeball. Whether you’re a seasoned hacker or just here for the tech thrills, prepare to dive deep into the world of PLC exploits with a few laughs along the way!

Objective

In this blog post, “Attacking PLCs with Digital Input Forcing” we aim to provide a comprehensive guide on developing and implementing a Python exploit to force digital inputs on PLCs. We will cover several key phases:

  1. Vulnerability Analysis: We start with an overview of the PLC systems and the specific vulnerability related to forcing. We analyze the PLC’s security weaknesses exposed by our exploit, highlighting potential risks and impacts.
  2. Exploit Development: We delve into crafting a Python script designed to exploit this vulnerability, including detailed explanations of the code and its functions.
  3. Real-World Application: Using real PLC hardware, we demonstrate the exploit in action and discuss its practical implications.
  4. Hack-the-Box PoC: Finally, we tie it all together with a Hack-the-Box approach, proof of concept (PoC), how this exploit fits into a broader cybersecurity framework.

By the end of this post, you’ll have a solid understanding of how to develop, test, and analyze a PLC exploit, enriched with practical insights and hands-on experience

MicroLogix 1400 PLC: Device under test (DUT)

The MicroLogix 1400 PLC by Rockwell Automation’s Allen-Bradley is vital in many industries. Disruptions to its operation or configuration can lead to severe consequences, including halting critical processes and causing significant equipment damage.

Identified Vulnerability: Remote Forcing PLCs

The PLC’s forcing feature is used to manually enable or disable inputs and outputs, which is valuable for troubleshooting. For example, if a pump isn’t working due to a faulty sensor, forcing the pump to stay on can temporarily resolve the issue.

However, this feature can also be exploited maliciously. Attackers can remotely manipulate these forces to disrupt operations or gain unauthorized control. This vulnerability.

Confused? 🤔 Check Out These Phases for a Step-by-Step Guide!

Feeling a bit lost in the technical jungle? 🏞️ Don’t worry! If you find yourself tangled in the weeds of this phase, just take a detour and revisit our previous adventures:

  • Phase 1: Basic Network Discovery – The quest for finding those elusive IP and MAC addresses. – – – – – – – – – –> [Explore Phase 1 🔍]
  • Phase 2: MITM Attack and Protocol Packet Sneaking – Uncovering the secrets hidden in network traffic. – – – –> [Explore Phase 2 🔍]
  • Phase 3: Packet Communication Protocol Analysis – Getting under the hood of how packets tick. – – – – – – – –> [Explore Phase 3 🔍]
  • Phase 4: Deep Packet Inspection (DPI) – Diving deep into packet content for a closer look. – – – – – – – – – – – –> [Explore Phase 4 🔍]
  • Phase 5: Exploit Development – Crafting the ultimate exploit for PLCs. – – – – – – – – – – – – – – – – – – – – – – –> [Explore Phase 5 🔍]

They’re like your trusty map and compass, guiding you through the dense forest of PLC hacking. Trust me, even Indiana Jones needed a roadmap! So, grab your digital backpack and check out those earlier phases to clear up any confusion and get back on track. Happy exploring! 🚀

Exploit Development: Forcing Digital Inputs of PLC

Here’s a closer look at the Python script that powers our exploit. We’ll walk through its key components, so you know exactly what’s happening under the hood.

1. hexadecimal padding – pad_hex()

Purpose: Ensures that your hexadecimal string is the right size by padding it with leading zeros. Think of it as giving your hexadecimal string a well-fitted jacket!

Details: This function takes a hexadecimal string (hex_str) and ensures it matches the desired size (size) by padding it with zeros.

Reference: This function is fundamental when dealing with any kind of packet construction, so make sure to check out for [Explore Phase 4 🔍] a deep dive into how we build packets.

2. Generation of Transaction Number : get_tns()

Purpose: Generates a unique transaction number for each command. It’s like giving your commands a unique name tag at a party—no confusion, just pure, unadulterated identity!

Details: This function creates a 4-byte hexadecimal string from a random integer, which is crucial for tracking commands.

Reference: Curious about how we track commands throughout the PLC interaction? Take a look at [Explore Phase 4 🔍] for insights into managing transaction numbers.

3. Create a Register session packet : Register_Session()

Purpose: Registers a session with the PLC, much like signing up for a new club. This function sends a registration command and retrieves a session handle.

Details: Constructs and sends a registration request to the PLC, then processes the response to get the session handle. This handle is essential for subsequent communication.

Reference: Dive into [Explore Phase 4 🔍] for a detailed walkthrough of session registration and its importance in setting up your attack vector.

4. Build an ENIP packet : Build_Ethernet_instruction()

Purpose: Crafts the Ethernet instruction payload for the PLC. Think of this as assembling the perfect care package for your PLC—everything it needs in one neat little box.

Details: Builds a complete payload for sending instructions to the PLC, including encapsulation headers and PCCC command data. This function ensures the message is formatted correctly and includes all necessary information.

Reference: For a full breakdown of how to assemble these payloads, check out [Explore Phase 4 🔍]. It’s where we dissect and understand the intricacies of packet construction.

5. Create a Function to send Packets: send_instruction()

Purpose: Sends an instruction to the PLC and waits for a response. This is your direct line to the PLC, like making a call and eagerly waiting for the answer.

Details: Uses the Build_Ethernet_instruction function to construct the payload, sends it to the PLC, and then waits for and processes the response.

Reference: Curious about what happens when you send these instructions? [Explore Phase 4 🔍] covers the nuances of sending and receiving data, ensuring that every command gets the response it deserves.

6. Function to send Program routine packet : program_register()

Purpose: Programs the PLC by sending a series of instructions to change CPU modes, execute commands, and more. It’s like performing a detailed operation—one step at a time—to ensure everything is set up correctly.

Details: Sends a sequence of commands to the PLC to prepare it for accepting forced writes. This includes changing CPU modes, executing commands, and applying port configurations.

After Sending above 7 CIP-PCCC packet to PLC, Our PLC is ready to accept Force Write to ANy register address.

Reference: For a deeper dive into programming the PLC and understanding each step’s significance, refer to [Explore Phase 4 🔍] . It’s the comprehensive guide to getting your PLC ready for action.

7. Setting Up the Connection

  • PLC: This variable holds the IP address of the PLC '192.168.0.102'
  • PORT: Port 44818 is commonly used for EtherNet/IP ML1400 PLC communication, but it might vary based on your PLC configuration.
  • socket.socket: Creates a new socket object using IPv4 addressing (AF_INET) and TCP (SOCK_STREAM) to establish a connection with the PLC.
  • sock.connect: Connects to the PLC using the IP address and port specified. This action establishes a communication channel between your script and the PLC.

8. Crafting a Malicious Payload: Forcing Digital Inputs

Alright, folks, buckle up! We’re about to take a detour from the official playbook.

The standard protocol says, “Stick to the approved function codes and file types, or face the wrath of unpredictable results.” But where’s the fun in that?

We’re about to break the rules a bit. While the official guide would have us use safe, predictable codes, we’re throwing caution to the wind with some unconventional choices:

Function Code 0xAB for a three-field write

File Type 0xA1 for output operations

File Type 0xA2 for input operations

Why stick to the mundane when we can spice things up? Following the official guidelines might get us a nice, neat, predictable operation—but that’s not what we’re here for.

We’re diving headfirst into exploit development, and that means shaking things up and seeing what magic (or black magic) we can create. Get ready for a wild ride!

If you’re scratching your head wondering why we’re using these codes, just take a peek at Phase 4 for the backstory. Trust me, it’ll all make sense!

Element Number: This is also known as the Register Number. It specifies the register’s position and is represented as a WORD (2 bytes). The value of this element indicates the specific register:

  • x00 corresponds to I:0.0 or O:0.0
  • x01 corresponds to I:0.1 or O:0.1 and so on.

Sub-Element: Determines the offset from the base address in the register. It specifies how many bytes to skip before selecting the specific bit or part of the register.

  • 1st Address Field: Selects the bit number within the register. For example, [ff ff] selects the entire register for the Element Number (e.g., I:0.0).
  • 2nd Address Field: Makes the bit position editable. For example, [ff ff] make entire reg. editable.
  • 3rd Address Field: Specifies the data to be written at the selected bit position. For example, [ff ff] edit the entire register with (1111 1111 1111 1111) // Force ON

This is the reason it called a “logical write” with a 3-address field, ensuring data is written precisely to the desired bit location.

That’s Why we are using same data 3-time

We have changed PLC CPU mode REM PROG -> REM RUN before closing existing Register session by sock.close(). [Explore Phase 5 🔍]

  • state == “run”: payload = {“cmd”:”\x0f”,”fnc”:”\x80″,”data”:”\x06″}
  • state == “prog”: payload = {“cmd”:”\x0f”,”fnc”:”\x80″,”data”:”\x01″}

Finally, we construct and send a malicious payload to force a digital input. This is the moment of truth where our exploit comes to life!

Python POC for Malicious Payloads: Digital Input Forcing on PLCs

POC Demo: Hacking PLCs with Python – It’s Not Just for Cyber Nerds Anymore!

Ever wondered what happens when you mix Python with a PLC? No, it’s not a new programming language, but rather a gateway to some serious digital mischief! In this demo, we’ll show you how to use Python to craft a malicious payload that forces a PLC’s digital inputs. Think of it as a hacker’s version of a magician’s trick – except instead of pulling rabbits out of hats

Conclusion

And there you have it! Our Python script has successfully forced a digital input on the PLC. This phase not only demonstrates the power of scripting in cybersecurity but also provides a practical example of exploiting PLC vulnerabilities.

Keep an eye on this space for more adventures in PLC hacking. Next up, we’ll explore additional features and refine our exploits. Until then, happy hacking!

<—Prev

Exploit # 0: learn from Starting

Next —->

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top