This Week In Security: Deeper Dive Into SolarWinds, Bouncy Castle, And Docker Images

Merry Christmas and happy holidays! I took Christmas day off from writing the security roundup, coming in a day early with this week’s installment, dodging New year’s day. The SolarWinds story has continued to dominate the news, so lets dive into it a bit deeper.

Microsoft has published their analysis of Solorigate, and the details are interesting. The added code was carefully written to blend in with the rest of the code, using the name OrionImprovementBusinessLayer.Initialize, which sounds like a perfectly boring-yet-legitimate function. The actual backdoor is obfuscated using zip compression and base64 encoding.

Once this bootstrap code begins, it runs a series of checks before actually doing anything malicious. It waits 2 weeks after installation to do anything, and then checks the system domain name for any indication it’s running in a test environment. It then checks for certain security applications, like Wireshark, and refuses to run if they are detected. This series of checks all seem to be an effort to avoid detection, and to only run in a deployed environment. Even the Command and Control URL that the backdoor uses is constructed to appear benign. Beyond this, it seems that the malware simply waited for instructions, and didn’t take any automated actions. All the attacks were performed manually.

One of the side-effects of the sudden attention given to SolarWinds devices is that a whole slew of other problems will be found and fixed, like CVE-2020-10148, an authentication bypass. The most surprising finding, however, is a *second* backdoor in the SolarWinds code, nicknamed Supernova. It’s possible that this was an earlier backdoor from the same actors as Solarigate, but the current theory is that it’s a backdoor installed by yet another, unrelated attacker.

Pi-hole Logs Vulnerability

If you have a Raspberry Pi running the Pi-hole software, you might want to patch a newly discovered vulnerability in the administrative interface. The issue, CVE-2020-35659, is a cross site scripting vulnerability, where viewing the logfile could allow arbitrary JS to run. The payload is JS embedded in a DNS name, which gets triggered by the log view. While it takes user interaction to view the log file, it’s frighteningly easy to get the malicious DNS request in the log. All it takes is a single resource request in any website visited by any device on the network. The PoC hasn’t been published yet, to give everyone time to update. This isn’t a sophisticated attack, so once the rest of the details are released, it should be easy to adapt the sample for real-world attacks. That said, it’s unclear how useful it is to be able to run arbitrary JS in the context of a Pi-hole.

Bouncy Castle Bypass Bug

“Don’t roll your own encryption” is still a worthy principle, but it doesn’t mean that well-known implementations can’t have problems. In this case, Bouncy Castle’s Java implementation has a coding mistake in the OpenBSDBcrypt routines. doCheckPassword is the vulnerable function, and it has a particular problem. So first, know that this routine compares Unix password hashes, which are base64 encoded, in the form of $y$j9T$fUtLoMA0qexwXogYTTY0K.$/jkWehjtTOASsLbYP5CVBxIiEY903Mukb7wtjjpIx4A. Now, take a look at the vulnerable Java code, and see if you see the problem:

boolean isEqual = sLength == newBcryptString.length();
for (int i = 0; i != sLength; i++)
{
    isEqual &= (bcryptString.indexOf(i) == newBcryptString.indexOf(i));
}
return isEqual;

Java isn’t my “first” programming language, but this isn’t particularly hard code to understand, so let’s walk through it. The first line declares the boolean variable isEqual, which serves as a state storage for the loop. This will always return true, because earlier code, not shown here, already checks for a length of 60. The meat of this snippet is the for loop, which iterates from 0 to 59. The problem is the use of indexOf(i). The programmer apparently thought this method would return the character at index i, comparing the two strings one character at a time. The problem is that indexOf actually does a search for the specified character, and returns the location where it was first found, or -1 if it doesn’t exist. When there is a single integer parameter given to this method, it indicates the character to search for — as a unicode value.

So the above snippet is actually comparing the location of unicode 0 (U+0000) in the two strings, and then comparing the location of unicode 1 (U+0001), through Unicode 59 (U+0059). Unicode is a descendent of ASCII, and inherits its first 128 characters directly from ASCII. Hence, characters 0 through 31 are control codes that will never be part of a password hash. Characters 32-35, 37-45, and 58 and 59 are all symbols that will never be part of the string. 36 is the “$” character, and while that character does appear in the compared strings, it will always be in the same position, as Unix password hashes use it as a separator symbol. Thus, the set of characters that this broken implementation actually checks are the period, the slash, and 0-9. And even then, only the first appearance of each are checked. Since “2” is part of the string indicating that the hash is using bcrypt, it’s also effectively ignored, as indexOf() only returns the *first* location a character is found. That leaves us only 11 out of 64 characters that are actually checked, and only their first appearance.

Researchers at Synopsys discovered this bug back in October. In their testing, they determined that every password that used Bouncy Castle’s broken bcrypt implementation was vulnerable to attack. They estimate that about 20% of such hashes can be bypassed in under 1000 guesses. Version 1.67 was released in November, addressing the issue. Ironically, the vulnerability was introduced in a set of changes adding constant-time comparisons. Not only is the code broken as discussed, it’s also not time-constant. It took almost an entire year for someone to notice the problem, because the function *almost* does the right thing.

This and That

Threatnix reports on a new phishing campaign, primarily targeting Facebook credentials. This particular story is interesting because it’s the first time I remember GitHub pages being used to host such a campaign. A bit of sleuthing let the researchers download the list of phished credentials, totalling over 600k.

Friends don’t let friends run untrusted Docker images, at least according to Prevasio. Researchers there put together a process to test all four million images on Docker Hub for problems. The results shouldn’t be surprising. About half of those images contain known vulnerabilities. Over 6,000 of those images tested were classified as malicious or “potentially harmful”.

Possibly related to Solarigate, The US’s CISA has published Sparrow, a tool for detecting compromised Azure infrastructure. Because it was written by government employees, the code is in the public domain.

13 thoughts on “This Week In Security: Deeper Dive Into SolarWinds, Bouncy Castle, And Docker Images

  1. It would seem that the higher the level of abstraction a language offers the greater the probability that it can facilitate a security flaw based on a conceptual error made by the programmer.

    1. Are you saying I shouldn’t write my LISP-like security and networking framework ( .NET for humans!) on top of Visual Basic? It implements a very clever authentication token passing system based on the Mandelbrot set and quantum simulation such that should it be damaged in transit, it can be reconstructed from just one bit.

  2. The Bouncy Castle bug smells a bit like programming by Stack Overflow. Happens to me all the time when hacking away in an unfamiliar language, but a bit surprised to see this slip into a prominent open source project.

    My guess is that the contributor of this code doesn’t write Java often and banged at it until it compiled and passed unit tests (it will correctly return true/false for most not specially crafted passwords). Except for the indexOf, the source code reads very much like efficient C code that doesn’t want to rely on the optimizing compiler.

    1. “Let’s port this constant-speed C code to Java. How hard can it be?”
      “Oh, no pointers! Let’s treat the strings as a char array then. Easy peasy.”
      “Oh, you can’t access string characters with [i]? Let me Google index of char in string.”
      “It compiles! It passes the tests! git commit”

Leave a Reply to Jonathan Bennett Cancel reply

Please be kind and respectful to help make the comments section excellent. (Comment Policy)

This site uses Akismet to reduce spam. Learn how your comment data is processed.