Hacker Newsnew | past | comments | ask | show | jobs | submit | peterabbitcook's commentslogin

A couple years ago I worked on a service that had to communicate with a Siemens S7-1500 PLC. Based on my experience with that project, none of what I’ve read recently about unsecured industrial PLCs is surprising.

I opened Siemens TIA Portal and PLCSIM for the first time and thought “wow, I didn’t think the Windows 95 GUI library was still supported.” None of the PLC contractors we had hired knew how to enable TLS on the thing (user/pass eg admin/admin was their usual). Anecdote: I once spent hours reading the docs and clicking around trying to get it to accept an SSL certificate signed by a real CA and it wouldn’t go, but it accepted one I self-signed in openssl.

In all fairness, the people who are experts in the field of Siemens PLC programming are usually mechanical-ish engineers and security is not in their skill set or on their mind.


Most factories I know do not allow their PLC be accessed from the internet. They are usually on a separate Network. However, the "engineering" station (the computer running e.g. TIA Portal) sometimes is.

The PLC engineers I had contact with usually had an electrical engineering background. That's why they like PLCs in the first place with the ladder logic programming languages, grafcet and if they feel fancy a bit of structured text (assembly like) or structured control language (pascal like). They indeed did not know much about software security but a great deal about machine safety.

A real security nightmare are older OPC servers (OPC-DA) which is super reliant on DCOM. OPC is quite important to connect the PLCs to SCADA systems or 3rd party devices.


>They are usually on a separate Network.

Then someone plugs in a cable because boss wants something "over there" and there's already a network that runs "over there". Or optimizes to a smart switch with vlans, and then someone else optimizes to a single vlan. It's not hard to not give a shit, or not understand, network security.


That someone can be brought into an office and shown a small diagram of the approved network topology. Then they can be shown a small diagram of the current network topology. Next, they can be asked if they're the same. If they're not, they can finally be asked if they're aware that deviating from the approved network topology without consulting infosec is grounds for termination of their employment.


Bunch of assumptions about operational excellence in there. Doesn't match my experience but, it does match my desire.


You have to be the change you want to see in the world.

We often forget this, but computer software and hardware are engineering disciplines. Part of engineering is knowing when something's bad and telling the person who asked you to do that something that you won't do it. In the civil engineering world, this means you might have to just walk away from the project and blow a whistle before people get hurt or killed. It's time to normalize that in IT.


Great advice for a much younger person.


Or anyone.

It doesn't have to wait 20 years for someone to come through engineering school to happen.


We're talking about the military. Many years ago I heard a presentation by an IT guy in the marines. He stated that senior officers would regularly give him instructions that would violate some policy or other - such as giving their secure laptop direct access to the internet so they could check their personal email - as an order. That is, they could not refuse. I hope things have changed, but this fellow was dead serious at the time.


Report that shit to your security officer


You're assuming that it'll be noticed at all, and that the person noticing cares enough about it to make a big deal out of it - likely involving several layers of management.

In reality it'll likely first be noticed ten years down the line, by someone who lets out a big sigh, mutters something about "incompetent dipshits not updating documentation", and moves on with their day.


I'm not assuming anything.

I'm saying that's what you do in order to solve the issue. You have to actually try, and you have to do actual engineering.

If the local planning commission submits a call for proposals for a bridge to cross a 400 foot chasm over sharp rocks, and they insist that it absolutely, positively must be made out of popsicle sticks, local civil engineering firms aren't going to take up the project, because that's insane.

Why do we give the management of these places a pass for PLC and SCADA systems that could give massive problems - up to and including the loss of human life - if they're hacked?


Isn't this the industry expectation in that kind of equipment? If it was signed by a real CA the cert. could expire and render the equipment unable to communicate.


That’s a tough question. If your PLC is on an airgapped LAN, admin/admin is not great security hygiene but you’ve reduced most of the risk by airgapping.

On my project the service I wrote was doing bidirectional communication with the PLC over OPCUA. The server running this pod was connected to the internet, so it was critical to have proper TLS for the OPCUA client/server. Rotating LetsEncrypt certs on the system every 45 days is a lot of toil, but using a self-signed cert that expires in 2040 from some dev laptop doesn’t pass muster in most organizations either.

That’s just the OPCUA path.. In these projects You also typically see WinCC HMIs that can talk to the PLC from anywhere on the network without TLS. And also SIMATIC Web Server pages - minimally secured by default, and the amount of info you can grok about the system in Chrome Dev Tools is troubling.


> admin/admin is not great security hygiene but you’ve reduced most of the risk by airgapping.

admin/admin is on a post-it stuck to the monitor because everyone we hire is perfectly capable of rooting a machine they have physical access to.


Yeah, but deliberately rooting a machine you have no business touching means you are instantly getting fired.

Having an actual password prevents people "borrowing" a key to the equipment broom closet from "optimizing" some config values they really shouldn't be messing around with.


I'm not sure if you're speaking from personal experience, but most I've interacted with don't have to worry about the self-signed vs. LetsEncrypt debate. They just don't do it. Also there would be no way to do LetsEncrypt as the system is air gapped.


You can do DNS challenges for air gapped networks as long as the TXT records resolve publicly.


So then you have a signed CSR right? How would you get the certificate onto the box?


Just my opinion but I believe a self-signed is more secure than Let’s encrypt. When a self-signed cert changes and no one in the org knows why that is alarming. Getting certificate error messages every month and half every numbs the org to the alarm.


> Rotating LetsEncrypt certs on the system every 45 days is a lot of toil

What is unique about your system using LetsEncrypt that you can't automate certbot to handle this task as it was designed and intended to be done?


On an airgapped system that is is turned on once and needs to keep running for many, many years? Industrial equipment is a world of its own and internet best practices just don't transfer directly.

Some PLCs run extremely expensive machines. Some machines can't afford to have their control systems stutter or fail because that can lead to physical damage and production outages of enormous proportions. A PLC that stops communicating because a certificate just expired is absolutely not acceptable in some plants.


Running certbot on your web server is easy.

Running certbot on a random PLC isn't happening.


My guess would be something like the CA using some feature that was newer than what the equipment would support (e.g. ECC signatures but the equipment only supporting RSA), not an intentional "no real CAs" decision.


also you can't pin the user/pw to the machine with a note, because someone might need remote access. better stick with admin/admin


My mind was blown when I realized that the way tftp works is that as the machine is booting it asks the network if anyone has some software for it to run.


Well, what else can it do, really? It has to boot with pretty much zero knowledge about the external world (maybe except asking the user for the current date and time). Sure, you can hardcode an outdated list of CAs (it's always outdated because the system can be booted 10/20/100 years after it was made) in but that just opens you to unexpected obsolescence, and you usually can't put too much stuff in the bootloader anyway.

Not really dissimilar from the human upbringing: leave a baby with "bad" guardians, and it will grow up corrupted. That's a feature, not a bug: if you knew what behaviour exactly you wanted (other than "whatever Simon says"), you would just bake it in in the first place, right?


If we pretend we're revising TFTP boot in 1995, let's have it get up to 20 boot options from the server and their md5 hashes, and if it's not set to auto it waits for the user to pick one. It then verifies the hash as it downloads. Also it uses TCP for the download.


Nah, you won't sell the cow like that. You need to add more reliance on the public CA infrastructure and third-party code signing. Also, "ask user"? Ask the TPM instead — I mean, why would the computer's owner trust the computer's user, right?


> In all fairness, the people who are experts in the field of Siemens PLC programming are usually mechanical-ish engineers and security is not in their skill set or on their mind.

Stuxnet was over a decade ago.

There should be a simple rule that everyone with the ability to understand things like PLCs should be able to grasp: your equipment does not touch the internet or external storage, period.

Those who can't grasp this concept should be shown the door with a recommendation that they find a less mentally-taxing line of work.


The Stuxnet PLCs weren't touching either, though.

The worm was designed to jump across network air gaps via USB flash drives, and spread across isolated networks to other hosts. Finally, it targeted what was likely going to be a service technician laptop, which had to connect to the PLC via a data cable to update and adjust its configuration.

An attack like this can only be avoided if you never transfer any data from the outside to the inside. But that means you won't ever be able to install any form of software update or upload new config files without manually typing them in - or even reinstall the OS on any machine...


Oh I wish that was possible.

Fact is if the customer wants to put their worksite management on AWS, you inevitably expose the OPC server and/or PLC to a Intranet. This is then inevitably connected to the internet in some way. The customer may or may not put barriers between them, but that's not up to us to decide.

This becomes especially a problem if they have multiple sites across a country or countries.

We have a customer that runs a factory on god forsaken WIFI, then dares to complain about a bad connection to said equipment. Thankfully, they have their own internal IT department.


I work with logistics automation software. Most PLCs I've worked with are legacy and don't even support TLS. Doesn't surprise me at all. I honestly am baffled how old some of these sites are and never got a security upgrade.

Now with the EU cyber resiliency act things are moving but I think it's infeasible for many sites to meet modern security standards, because of both hardware and software.


What are your feelings about using initContainers and wait-for-it to skirt the thundering-herd problem?


It made me think of the “step on all the colored tiles without crossing your path” puzzles in Links Awakening!

I wonder how the optimal path changes if turns are penalized as well as steps - in real life turning a lawnmower takes a lot more effort than moving the same spot twice in a straight line.


So under most brewing conditions a nonzero amount of this microbe survive, but that count is 3 orders of magnitude lower what might be termed a “therapeutic dose?”

What even is a good experimental design for probiotic tea? Brew -> agitate in hydrochloric acid & digestive enzymes for X minutes -> mix with billions of existing gut microbes (that may kill this microbe or out-compete it for resources) and incubate at body temperature -> count the number of new colonies of this particular microbe?


The author does mention that a synthetic digestive system was used in another experiment, so you may be correct!


I think I’m a little skeptical.

#pragma is a real keyword in C but # is just a comment in python - why not use a pythonic @decorator?

Vendoring a library like this into an app seems like a lot more toil than just writing the native multiprocessing python code, or using something built for number crunching like arrayfire or numpy


Because (even if you could decorate for-loops, or instead decorated functions containing a loop and nothing else) the resulting behavior would be completely unlike the behavior of all other @decorators in Python. It would also be nearly impossible to implement this functionality as a decorator without introducing tons of fragility.

The preprocessor/import-hook hack that allows macro-like behavior in Python does arbitrary transforms at the text/code-load level, while decorators are evaluated at runtime and run regular Python code. Decorators can be composed/wrapped by other functions and decorators, whereas implementing Lucen-like functionality with a decorator would necessarily match on the name/symbol of the decorator and couldn't indirect "through" it.

Heck, even if you disallowed wrapping and matched on the decorator name during preprocessing (basically stripping it out and rewriting decorated code strings at load time), even identifying the correct decorator as a transformation target would require reimplementing a ton of Python's import/scope/name resolution behavior by hand at code-transformation time. If your decorator was called lucen.parallelize(), consider the difference between "import lucen; @lucen.parallelize", "from lucen import parallelize; @parallelize", "from lucen import parallelize as pl; parallelize = 123; @pl", and so on. You'd have to handle all of those cases, and many more (e.g. decorated functions defined inside other classes/functions) at the code-as-string or AST level. You couldn't run the decorator at runtime, because a) syntactic information is gone by then, and b) because then you'd need to unimport/reimport module code which had already been run with arbitrary global side effects.

More information on the methods available to get preprocessor-like functionality in Python:

Custom source encoding text transformers can be loaded via .pth files: https://pydong.org/posts/PythonsPreprocessor/

Import hooks can be configured at runtime, and run during subsequent import statements. They were originally designed to allow customized module discovery, but nothing stops you from using them like Lucen does: to intercept imports of local Python files and transform/replace the code imported at load time: https://peps.python.org/pep-0302/


You can @decorate a for-loop?

I wish there were easier ways to play with syntax in Python, but there we are.


Though I guess using @decorator semantics would make it look a lot like python-numba syntax


just wanted to make sure ... even if someone someday wishes to revert the decision of using lucen they will be okay with just removing the import hooks - and run everything as native -- and when they have time, they can clear the comments but till then they can work without thinking


Where to stop is a pretty fuzzy question I suppose.

In my world, I think the worst possible outcome would be a java.util.json library that does 50-95% of what I currently do with GSON or Jackson. In that scenario I still need an external dependency, and I can either ignore java.util.json or turn a codebase into an error-prone mix of both.

Maybe a good set of design considerations for java.util.json would be “what does this API need to run a CRUD app written in modern java?” Parse requests from the web, send responses to the web, and serialize/deserialize data from X database (e.g. if noSQL or jsonb). In my head “in modern java” would mean that JSON is converted to records at application boundaries


> Maybe a good set of design considerations for java.util.json would be “what does this API need to run a CRUD app written in modern java?”

Reading the JEP, that is not the motivation. The target is more a short script or some REPL interaction that reads JSON data from a web service and does something with it. A CRUD app likely already uses a web framework that comes with a full JSON library.


Fick’s Law. The rate of absorption is proportional to the concentration gradient.


Reading this gives me considerable pause - I can’t think of many classes within the codebase I work on that don’t have @Slf4j at the top…

Since there wasn’t a link to the source code in that post, can you help me understand this - for the SLF4J baseline is your logger impl a console appender, a file appender, or a network service like an OTel collector? Does any of that matter for GC context?


All common logging backends create a LogEvent or similar object for each logging call, and logging calls also typically construct new strings, which usually means a new StringBuilder object, its internal array (multiple ones if it grows), the final array it is copied to, and the String object that wraps that array.

These are typically short-lived objects and therefore cheap. Nevertheless, continually creating many such objects increases GC pressure, in particular if the logging happens in code that doesn't otherwise create many objects.


Cheap, not free, and even pretty simple to accidentally fool the GC on the lifetime of these objects.

Consider, for example, if you have a log message like this

    logger.info("Hello {}", myOldObject);
if "myOldObject" is large enough or contains references to large things or has just been around for a while, it may be a part of OldGen at this point. And if that's the case, the LogEvent objects will end up automatically promoted to OldGen. Meaning the only time those can be be claimed is in an expensive major collection. The end result is that these things will ultimately fill up old gen and trigger more of the expensive old gen collections.

That's why it can be faster in some circumstances to write the more wordy

    if (logger.isInfoEnabled()) {
      logger.info("Hello {}", myOldObject.toString());
    }
Nothing saves you, however, if your string being logged is too long. It can be autopromoted to old gen if you are trying to log a 10mb string.


> And if that's the case, the LogEvent objects will end up automatically promoted to OldGen.

Why do you think this would happen? There's no mechanism that makes young gen objects that reference old gen objects (or are referenced by old gen objects) get promoted faster. You have to survive a certain number of collections.


Ah shoot, you're right this wouldn't be what I'm thinking.

I was thinking of "GC Nepotism" [1]. That's the case where an object in old gen pointing to an object in new gen will automatically promote that new gen object into old gen. This can be particularly problematic with graph structures.

[1] https://psy-lob-saw.blogspot.com/2016/03/gc-nepotism-and-lin...


> All common logging backends create a LogEvent or similar object for each logging call, and logging calls also typically construct new strings, which usually means a new StringBuilder object, its internal array (multiple ones if it grows), the final array it is copied to, and the String object that wraps that array.

Which then gets discarded because that was a Log.verbose and your minimum log level in production is WARN.

Which is why many libraries have moved towards making your log message returned by a lambda. One constant lambda allocation (so, not a lot, an invokedynamic is absolutely fuck all.) that allows you to straight up skip allocating a full string that most likely is interpolating things and attempting to reach for context present on other threads is strictly better in 99.9% of the cases. The GC pressure is kept minimal and most importantly, constant.


> Which then gets discarded because that was a Log.verbose and your minimum log level in production is WARN.

This isn't true for the LogEvent or equivalent object, which only gets created after the log level is tested to be applicable by the logger implementation.

For call-site object allocation, you can wrap the logging call into an if statement that checks for the corresponding log level. The lambda allocation isn't constant if it captures anything from the surrounding scope, which will generally be the case for logging calls. (Unless by "constant" you mean that it's a single allocation per execution.)


In the Java-verse it’s also do-able with Guice. I’ve tried it with Dagger but bailed (square peg / round hole).

I think I prefer Springboot AOP, especially with SpEL.

The term “cross cutting concerns” is thrown around a lot when discussing AOP. Took me a while to appreciate just how powerful it is in this context - sprinkle an AOP annotation here or there to avoid massive refactors in a large codebase, or avoid rewriting classes in a way that makes your classes themselves “cross cut concerns.”


I’ve dealt with a question that rhymes with this.

Sonarqube or CodeQL reports might tell me what percentage of a repo is duplicated code, and a large percentage of that is in src/test/java

I find that a lot of the time this is not just some flippant observation but a clue that I should be using a mechanism like @ParameterizedTest instead of @Test, so I rewrite those tests in a way that makes them easier to set-up, define parameters/constraints, inputs, and outputs. Sometimes it does get a little convoluted as you either use a lot of naked Arguments.of() or define test-class-scoped nested records to encapsulate test parameters, inputs, expected outputs, etc.


Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: