This Week in Security: The Github Supply Chain Attack, Ransomware Decryption, and Paragon
Last Friday Github saw a supply chain attack hidden in a popular Github Action. To understand this, we have to quickly cover Continuous Integration (CI) and Github Actions. CI essentially …read more


Last Friday Github saw a supply chain attack hidden in a popular Github Action. To understand this, we have to quickly cover Continuous Integration (CI) and Github Actions. CI essentially means automatic builds of a project. Time to make a release? CI run. A commit was pushed? CI run. For some projects, even pull requests trigger a CI run. It’s particularly handy when the project has a test suite that can be run inside the CI process.
Doing automated builds may sound straightforward, but the process includes checking out code, installing build dependencies, doing a build, determining if the build succeeded, and then uploading the results somewhere useful. Sometimes this even includes making commits to the repo itself, to increment a version number for instance. For each step there are different approaches and interesting quirks for every project. Github handles this by maintaining a marketplace of “actions”, many of which are community maintained. Those are reusable code snippets that handle many CI processes with just a few options.
One other element to understand is “secrets”. If a project release process ends with uploading to an AWS store, the process needs an access key. Github stores those secrets securely, and makes them available in Github Actions. Between the ability to make changes to the project itself, and the potential for leaking secrets, it suddenly becomes clear why it’s very important not to let untrusted code run inside the context of a Github Action.
And this brings us to what happened last Friday. One of those community maintained actions, tj-actions/changed-files
, was modified to pull an obfuscated Python script and run it. That code dumps the memory of the Github runner process, looks for anything there tagged with isSecret
, and writes those values out to the log. The log, that coincidentally, is world readable for public repositories, so printing secrets to the log exposes them for anyone that knows where to look.
Researchers at StepSecurity have been covering this, and have a simple search string to use: org:changeme tj-actions/changed-files Action
. That just looks for any mention of the compromised action. It’s unclear whether the compromised action was embedded in any other popular actions. The recommendation is to search recent Github Action logs for any mention of changed-files
, and start rotating secrets if present.
Linux Supply Chain Research
The folks at Fenrisk were also thinking about supply chain attacks recently, but specifically in how Linux distributions are packaged. They did find a quartet of issues in Fedora’s Pagure web application, which is used for source code management for Fedora packages. The most severe of them is an argument injection in the logging function, allowing for arbitrary file write.
The identifier
option is intended to set the branchname for a request, but it can be hijacked in a request, injecting the output flag: http://pagure.local/test/history/README.md?identifier=--output=/tmp/foo.bar
. That bit of redirection will output the Git history to the file specified. Git history consists of a git hash, and then the short commit message. That commit message has very little in the way of character scrubbing, so Bash booleans like ||
can be used to smuggle a command in. Add the cooked commit to your local branch of something, query the URL to write the file history to your .bashrc
file, and then attempt to SSH in to the Pagure service. The server does the right thing with the SSH connection, refusing to give the user a shell, but not before executing the code dropped into the .bashrc
file. This one was disclosed in April 2024, and was fixed within hours of disclosure by Red Hat.
Pagure was not the only target, and Fenrisk researchers also discovered a critical vulnerability in OpenSUSE’s Open Build Service. It’s actually similar to the Fedora Pagure issue. Command options can be injected into the wget command used to download the package source file. The --output-document
argument can be used to write arbitrary data to a file in the user’s home directory, but there isn’t an obvious path to executing that file. There are likely several ways this could be accomplished, but the one chosen for this Proof of Concept (PoC) was writing a .proverc
file in the home directory. Then a second wget argument is injected, using --use-askpass
to trigger the prove
binary. It loads from the local rc file, and we have arbitrary shell code execution. The OpenSUSE team had fixes available and rolled out within a few days of the private disclosure back in June of 2024.
Breaking Ransomware Encryption
What do you do when company data is hit with Akira ransomware, and the backups were found wanting? If you’re [Yohanes Nugroho], apparently you roll up your sleeves and get to work. This particular strain of Akira has a weakness that made decryption and recovery seemingly easy. The encryption key was seeded by the current system time, and [Yohanes] had both system logs and file modification timestamps to work with. That’s the danger of using timestamps for random seeds. If you know the timestamp, the pseudorandom sequence can be derived.
It turns out, it wasn’t quite that easy. This strain of Akira actually used four separate nanosecond scale time values in determining the per-file encryption key. Values we’ll call t3 and t4 are used to seed the encryption used for the first eight bytes of each file. If there’s any hope of decrypting these files, those two values will have to be found first. Through decompiling the malware binaries, [Yohanes] knew that the malware process would start execution, then run a fixed amount of code to generate the t3 key, and a fixed amount of code before generating the t4 key. In an ideal world, that fixed code would take a fixed amount of time to run, but multi-core machines, running multi-threaded operations on real hardware will introduce variations in that timing.
The real-world result is a range of possible time offsets for both those values. Each timestamp from the log results in about 4.5 quadrillion timestamp pairs. Because the timing is more known, once t3 and t4 are discovered, finding t1 and t2 is much quicker. There are some fun optimizations that can be done, like generating a timestamp to pseudorandom value lookup table. It works well ported to CUDA, running on an RTX 4090. In the end, brute-forcing a 10 second slice of timestamps cost about $1300 dollars when renting GPUs through a service like vast.ai. The source code that made this possible isn’t pretty, but [Yohanes] has made it all available if you want to attempt the same trick.
Github and Ruby-SAML — The Rest of the Story
Last week we briefly talked about Github’s discovery of the multiple parser problem in Ruby-SAML, leading to authentication bypass. Researchers at Portswigger were also working on this vulnerability, and have their report out with more details. One of those details is that while Github had already moved away from using this library, Gitlab Enterprise had not. This was a real vulnerability on Gitlab installs, and if your install is old enough, maybe it still is.
The key here is a CDATA section wrapped in an XML comment section is only seen by one of the parsers. Include two separate assertion blocks, and you get to drive right through the difference between the two parsers.
Paragon
There’s a new player in the realm of legal malware. Paragon has reportedly targeted about 90 WhatsApp users with a zero-click exploit, using a malicious PDF attachment to compromise Android devices. WhatsApp has mitigated this particular vulnerability on the server side.
It’s interesting that apparently there’s something about the process of adding the target user to the WhatsApp group that was important to making the attack work. Paragon shares some similarities with NSO Group, but maintains that it’s being more careful about who those services are being offered to.
Bits and Bytes
We have a pair of local privilege escalation attacks. This is useful when an attacker has unprivileged access to a machine, but can use already installed software to get further access. The first is Google’s Web Designer, that starts a debug port, and exposes an account token and file read/right to the local system. The other is missing quotation marks in Plantronics Hub, which leads to the application attempting to execute C:\Program.exe
before it descends into Program Files
to look for the proper location.
This is your reminder, from Domain Guard, to clean up your DNS records. I’ve now gone through multiple IP address changes of my “static” IP Addresses. At the current rate of IPv4 exhaustion, those IPs are essentially guaranteed to be given out to somebody else. Is it a problem to have dangling DNS records? It’s definitely not a good situation, because it enables hacks from cross-site scripting vulnerabilities, to cookie stealing, to potentially defeating domain verification schemes with the errant subdomain.
MacOS has quite a fine history of null-pointer dereference vulnerabilities. That’s when a pointer is still set to NULL, or 0, and the program errantly tries to access that memory location. It used to be that a clever attacker could actually claim memory location 0, and take advantage of the bogus dereference. But MacOS put an end to that technique in a couple different ways, the most effective being disallowing 32 bit processes altogether in recent releases. It seems that arbitrary code execution on MacOS as result of a NULL Pointer Dereference is a thing of the past. And yes, we’re quite aware that this statement means that somehow, someone will figure out a way to make it happen.
And Finally, watchTowr is back with their delightful blend of humor and security research. This time it’s a chain of vulnerabilities leading to an RCE in Kentico, a proprietary web Content Management System. This vulnerability has one of my least favorite data formats, SOAP XML. It turns out Kentico’s user authentication returns an empty string instead of a password hash when dealing with an invalid username. And that means you can craft a SOAP authenticaiton token with nothing more than a valid nonce and timestamp. Whoops. The issue was fixed in a mere six days, so good on Kentico for that.