DEV Community

Cover image for I Started With One OPC UA `GetEndpoints` Request. Then I Found Anonymous Writes.
404Saint
404Saint

Posted on

I Started With One OPC UA `GetEndpoints` Request. Then I Found Anonymous Writes.

By RUGERO Tesla (@404Saint).

[*] Target Server: opc.tcp://127.0.0.1:4840
[*] Sending unauthenticated GetEndpointsRequest...
[+] Received 5 exposed endpoints.
Enter fullscreen mode Exit fullscreen mode

Five endpoints from a single unauthenticated GetEndpointsRequest. That was the starting point for this OPC UA laboratory. The response advertised four cryptographically protected configurations and one endpoint using:

SecurityMode:   None
SecurityPolicy: None
Enter fullscreen mode Exit fullscreen mode

The server was therefore not enforcing a single security posture at the discovery layer. A client could select the unprotected endpoint even though Sign and SignAndEncrypt configurations were available.

OPC UA endpoint discovery

Endpoint discovery showing the five configurations advertised by the laboratory server.

That observation alone was not treated as a downgrade vulnerability. No active downgrade attack was performed. The finding was simpler: an unencrypted endpoint was explicitly available for client selection.

The next step was to use it.

From GetEndpoints to ActivateSession

OPC UA establishes application sessions through a defined sequence:

OpenSecureChannel
        ↓
CreateSession
        ↓
ActivateSession
Enter fullscreen mode Exit fullscreen mode

Using the SecurityPolicy=None endpoint, an anonymous identity token was supplied during ActivateSession.

The server accepted the session:

[+] SecureChannel established
[+] Session activation succeeded

SecureChannel State: OPEN (SecurityPolicy#None)
Session State:       ACTIVE
Authentication Type: Anonymous
Enter fullscreen mode Exit fullscreen mode

OPC UA anonymous session handshake

Successful anonymous session establishment over the SecurityPolicy=None endpoint.

At this point the scope of the test changed. Endpoint discovery had established insecure exposure. Session establishment demonstrated that the exposed endpoint was actually usable without an authenticated identity. The address space was the next layer.

Walking the Address Space

OPC UA does not present an industrial control environment as a flat register table.

Its application model is an address space containing Objects, Variables, Methods, Properties, References, and associated metadata.

A recursive traversal was therefore used to enumerate the Object graph and inspect the variables reachable through the anonymous session.

The crawler followed child references and collected:

Browse Name
NodeId
Node Class
AccessLevel
UserAccessLevel
Current Value
Read Result
Enter fullscreen mode Exit fullscreen mode

Relative browse-path resolution was also tested against the standard Server object:

0:Objects
 └── 0:Server
      └── 0:ServerStatus
           └── 0:CurrentTime
Enter fullscreen mode Exit fullscreen mode

which resolved to:

ns=0;i=2258
Enter fullscreen mode Exit fullscreen mode

The traversal itself was not the interesting finding.

Most of the address space did not provide unrestricted access. Several nodes rejected reads or returned authorization-related errors.

That distinction became important when evaluating write permissions.

AccessLevel Wasn't the Final Verdict

The audit did not classify a variable as writable solely because an attribute suggested that it was. Readable variables were tested with actual WriteRequest operations.

The mutations were deliberately small and reversible:

Boolean
False → True

integer
0 → 1

Double
0.0 → 1.0

Int64
0 → 1

example bytestring
b'test123' → b'test123_probe'
Enter fullscreen mode Exit fullscreen mode

Each successful mutation was immediately restored to its original value.

The result was unambiguous:

[CRITICAL VULNERABILITY] Write ACCEPTED on tag Boolean!
Value changed: False -> True

[CRITICAL VULNERABILITY] Write ACCEPTED on tag integer!
Value changed: 0 -> 1

[CRITICAL VULNERABILITY] Write ACCEPTED on tag Double!
Value changed: 0.0 -> 1.0

[CRITICAL VULNERABILITY] Write ACCEPTED on tag Int64!
Value changed: 0 -> 1

[CRITICAL VULNERABILITY] Write ACCEPTED on tag example bytestring!
Value changed: b'test123' -> b'test123_probe'
Enter fullscreen mode Exit fullscreen mode

OPC UA address-space audit

Recursive address-space enumeration and access-level inspection from the anonymous session.

OPC UA write authorization testing

Successful write mutations against five tested variables. Every mutation was reverted after verification.

This is stronger evidence than simply reporting an exposed AccessLevel.

The server received the write request, accepted it, value changed and the original value was then restored. The finding is therefore specific to effective write authorization: the anonymous session was able to modify the tested variables without presenting an authenticated user identity.

The Security Boundaries Were Not Universally Broken

The write finding made it important to test the other security controls rather than assume that everything was misconfigured.

The endpoint configuration exposed SecurityPolicy=None, but protected configurations were also available:

SignAndEncrypt     Aes128_Sha256_RsaOaep
Sign               Aes128_Sha256_RsaOaep
SignAndEncrypt     Basic256Sha256
Sign               Basic256Sha256
Enter fullscreen mode Exit fullscreen mode

An independent X.509 certificate was then generated for the certificate validation test.

It was deliberately unrelated to the earlier test certificate:

Subject:
CN=independent-untrusted-client

Issuer:
CN=independent-untrusted-client

Self-Signed:
True

Application URI:
urn:freeopcua:client:independent-test
Enter fullscreen mode Exit fullscreen mode

The protected connection attempt used:

Basic256Sha256
SignAndEncrypt
Enter fullscreen mode Exit fullscreen mode

The server rejected the certificate:

BadCertificateUriInvalid
Enter fullscreen mode Exit fullscreen mode

OPC UA X.509 trust boundary test

Independent self-signed certificate rejected during the protected OPC UA handshake.

Invalid username/password authentication was also rejected with:

BadUserAccessDenied
Enter fullscreen mode Exit fullscreen mode

The resulting security picture was therefore more nuanced than a simple “OPC UA authentication is broken” conclusion.

Security Boundary Result
SecurityPolicy=None advertised Insecure exposure
Anonymous session Accepted
Address-space traversal Extensive visibility
Tested anonymous writes Accepted
Invalid credentials Rejected
Independent X.509 certificate Rejected
Protected endpoints Available

The significant finding was the combination of an anonymously accessible session and effective write permissions on the tested variables.

Seeing the Protocol Instead of Just the API

The packet captures were an important part of the investigation.

For example, the username authentication test produced an ActivateSessionRequest containing a UserNameIdentityToken:

UserName: admin
Password: ...
EncryptionAlgorithm:
    http://www.w3.org/2001/04/xmlenc#rsa-oaep
Enter fullscreen mode Exit fullscreen mode

The client reported BadUserAccessDenied, but the PCAP showed how the identity token was actually represented on the wire. That distinction matters in protocol research. A high-level library can tell you that an operation succeeded or failed but a packet capture can show the actual message, security mode, token type, request structure, and server response.

The laboratory therefore combined both:

Python harness
      +
OPC UA server
      +
Wireshark / PCAP
      +
manual protocol inspection
Enter fullscreen mode Exit fullscreen mode

rather than treating the client library as the protocol itself.

The Research Repository

The complete laboratory is available in the repository:

industrial-protocol-labs / opcua-research

It contains the phase-by-phase notes, Python research harnesses, packet captures, screenshots, logs, and reproduction instructions.

opcua-research/
├── captures/
├── logs/
├── notes/
├── screenshots/
└── scripts/
Enter fullscreen mode Exit fullscreen mode

The detailed notes cover:

  • OPC UA architecture and security model
  • Endpoint discovery
  • SecureChannel and session establishment
  • Address-space traversal
  • Access-level auditing
  • Effective write authorization
  • X.509 trust-boundary testing
  • Laboratory reproduction

The interesting part of this research was not any individual API call. It was following the protocol far enough to determine what an apparently ordinary anonymous session could actually do.

GetEndpoints was only the first request.

Top comments (0)