Table of Contents
First up is some clever wizardry from the [Aqua Nautilus] research team, who discovered a timing attack that leaks information about private npm packages. The setup is this, npm hosts both public and private node.js packages. The public ones are available to everyone, but the private packages are “scoped”, meaning they live within a private namespace, “@owner/packagename” and are inaccessible to the general public. Trying to access the package results in an HTTP 404 error — the same error as trying to pull a package that doesn’t exist.
The clever bit is to keep trying, and really pay attention to the responses. Use npm’s API to request info on your target package, five times in a row. If the package name isn’t in use, all five requests will take the expected amount of time. That request lands at the service’s backend, a lookup is performed, and you get the response. On the flipside if your target package does exist, but is privately scoped, the first request returns with the expected delay, and the other four requests return immediately. It appears that npm has front-end that can cache a 404 response for a private package. That response time discrepancy means you can map out the private package names used by a given organization in their private scope.
Now this is all very interesting, but it turns into a plausible attack when combined with typosquatting and dependency confusion issues. Those attacks are two approaches to the same goal, get a node.js deployment to run a malicious package instead of the legitimate one the developer intended. One depends on typos, but dependency confusion just relies on a developer not explicitly defining the scope of a package.
Siemens Left the Keys in the Ignition
Siemens released version 12 of their TIA portal nearly 10 years ago, and with this update added asymmetric cryptography between the portal and their SIMATIC S7-1200 and S7-1500 products. To put that in plain English, their industrial controllers started using HTTPS to talk to the controller software. This was a good thing. Unfortunately, the private key for that HTTPS connection lives on the controller hardware, and every unit shipped had the same key.
The real fun is how the team at Claroty discovered all this. Industrial controllers get programmed in a specific, high-level language, that is strictly for automation logic. Pointers, memory management, and the rest of our normal vulnerable vectors don’t show up here. Those programs get compiled to a custom bytecode that runs in a controlled environment on the controller. So naturally, our researchers reverse engineered the bytecode and wrote their own compiler, to have access to those under-the-hood features. It was still a challenge to defeat the security controls built-in to that environment, but they eventually found a function that could set a pointer to an arbitrary value, allowing kernel reads and writes. Including reading the universal key.
And that brings us to the first real attack that having this secret-but-shared key enables. Depending on the configuration, it’s sometimes possible to download the configuration blob without authentication. One of the items contained in that dataset is the encrypted password hash, which was encrypted using — you guessed it — that universal key. With the key in hand, an attacker can download the hash, decrypt, and then authenticate using the hash.
And then, having the secret key to an HTTPS certificate allows all the normal shenanigans one would think of: capturing traffic and decrypting, or performing a man-in-the-middle attack. The issues have been fixed with the latest firmware and portal updates, and Siemens has issued an advisory owning up to the problems. See the video below for the first-hand story of escaping The Matrix and getting to real code execution on these machines.
(Hackaday’s parent company, Supplyframe, is owned by Siemens.)
Valid PNG, Valid PHP
There’s always something weirdly fun about a file that is valid as multiple, wildly different file formats. In this case, it’s the PNG file that is also valid PHP. The security angle here is that many websites allow you to upload PNGs and then view them. If that PNG could actually, sneakily be a PHP file too, you have an instant webshell. Now there are likely some insecure sites where it’s just as simple as uploading a script, but many uploader libraries will at least check that the file is valid for the specified mimetype. Simply, it has to be a valid PNG
if set to
image/png. But is that enough?
PNG files can contain comments. What does a web server do when it serves a
.php file that also contains binary PNG data? In many cases, it treats it like any other file, sending the raw data until it finds a
<?php tag, where it might just start executing PHP script. The next question is how does one create such a file that the PHP script persists through compression, resizing, and other sanitizing steps? The first approach discussed is using tEXt chunks, which are usually used for storing title, author, etc. In many cases these bits of embedded text will persist through any modifications.
The other, harder approach, is to embed text as image data directly. There is the PLTE chunk, where custom palette entries can be defined. These can have any value, but must have a multiple-of-three length, and maximum 768 characters. The even harder one is to use the actual IDAT chunks, AKA actual valid pixel data. This is deep magic indeed.
Zoneminder — It Could Have Been Worse
This one hits close to home, as it’s a trio of vulnerabilities in a project I recommend and occasionally sling code for. [Trenches of IT] contacted the project on the 30th with a trio of vulnerabilities that had the potential to be really ugly when put together. Up first is a Log Injection flaw, where a web user can push arbitrary data into the ZM logs, with no rate limiting. Need the cameras to stop recording? Fill the drive up with log messages. Both the message itself and the component that sent it can be set arbitrarily.
Up next is a CSRF bypass. Cross-Site Request Forgery is an attack where an end user’s browser takes an action the user didn’t intend, just by making an HTTP request. ZM uses CSRF tokens to protect against such issues, which is a random token, supplied by the web service, that must be included in any POST requests. The bypass in the case of ZM is that some actions, like deleting a recording, can be made as GET requests, which don’t require a token. Whoops.
The last issue has the potential to be the worst, as it’s a Cross-Site Scripting (XSS) issue. That log injection issue also enables this one, as the “file” field can contain arbitrary HTML code, including a script tag. Put the three issues together and a user with view-only privileges can inject a delete or disable command that will trigger when an admin views the logs. That’s bad, but at least it’s not a pre-auth vulnerability. It was fixed in 1.36.27, released on the 7th. If you happen to be stuck in the 1.34 series of releases, this a few other issues are outstanding and no further releases are planned for that deprecated version. Thanks to [Trenches of IT] for finding and reporting!
Microsoft’s Gift That Keeps Giving
Remember the Exchange 0-day, the one that was sort of a workaround of an older problem with the autodiscover endpoint? Well it still hasn’t been patched, and the recommended workarounds have been updated multiple times, as they’ve been trivially bypassed. It might just be time to just pull the plug on autodiscover, and just block all outside access to IIS while you’re at it.
Python and Userland Execution
Finally a red-team quick-tip. You’ve popped shell on a server, and you want to pull some tools in to start exploring the environment. Root is mounted read-only, and your temp directories are all mounted
noexec. How do you get a binary on to the system and run it? If Python is installed, then you could use
ulexecve. [Vincent Berg] wrote the tool and has the story. How it works is equally impressive, as it downloads and parses a given binary, manually loading it into memory and generating the jump buffer. Finally, it jumps code execution into the new binary, which never needs to be written to disk. Tricky.