Stopping Supply Chain Attacks With PreflightâââSpectral
- CodeCov and Supply Chain Attacks
- Introducing: Preflight
- How Does Preflight Work?
- Getting Preflight
- Flying With Preflight
- So how does it look in action?
- Codecov Revisited
- Dealing With Change
- Checking Scripts And Binaries
- Creating New Hashes
- Malware lookup
- Summary
TL;DR: weâve just released Preflight, a CI tool against supply chain attacks that stops attacks like the recent Codecov hack: https://github.com/spectralops/preflightItâs been a few weeks since the CodeCov hack sent ripples across the software development industry. One of these ripples was an increased awareness of supply chain attacks and the risks they present. That said, itâs easy to see that this awareness is not yet implemented as a best practice in the field.
Here are a few examples:
These are all different forms of remotely running shell scripts and blindly executing them. Believe it or not, these examples are live right now in each of these projects, weeks after the CodeCov breach was discovered and mitigated. An attack in which, as you may remember, this very script was altered to contain malicious code.
But the CodeCov breach is only a symptom of a bigger, more complex issueâââsupply chain attacks.
CodeCov and Supply Chain Attacks
The above examples of remote CodeCov bash execution may vanish over time. However, other instances of remote shell script execution are prevalent even across projects and repositories on GitHub.
These create an attack vector for attackers and malefactors. There are numerous types of attacks in this space, with different motivations behind them.
-
Malicious scripts
Since an install script, or a âquick toolâ script, is by itself runnable software? The script can be replaced with any other piece of code that performs an attackerâs goal
-
Swapping software
A script that we pull can still feel like it behaves nicely. But then, it would let us install a different software package than what we originally thought it would.
-
Tricking into old updates
To avoid being detected for giving you a tainted binary? An attacker may simply push an older version of the same software youâre installing, assuming that this older version contains a famous exploit they can use.
-
Tricking into future updates or âupdate jackingâ
An attacker might want to point the script at their own update package. Which, in terms of server, is well into the future. So rollback attempts would fail. For the same goal, an attacker can replace a specific update in the past, present, or future (as long as they can cause an updater to trigger).
At the time of writing, these were all live:
Granted, we havenât discovered anything new hereâââjust a best practice still waiting to become a best practice. But what would that best practice be?
According to CodeCov and their own analysis of their breach:
The answer seems quite simpleâââvalidate and verify remotely executed bash scripts.
Introducing: Preflight
Weâve built Preflight as a tool to implement that advice into a best practice that is easy to use and integrate into your DevOps workflow. With a few more features and the icing on top, preflight is now a platform for checking, validating, and running 3rd party components aimed to help you block supply chain attacks.
preflight helps you verify scripts and executables and mitigate supply chain attacks.
How Does Preflight Work?
What happens when you execute a â curl | sh
" karate-chop? Here's one breakdown of such a chain in very general terms, highlighting the major links in the chain:
When we pull a shell script from a website, weâre trusting the tool we use (curl), and the OS for SSL (CA chain), and the domain (and by derivative our DNS services), and the human operating the domain, and the code hosted on the domain, and the human operating the hosting and code. Thatâs quite a few links in a chain.
With Preflight weâre moving the focus of the chain of trust to a single place: the end of the chain. A signature of the executable which you want to run. The signature is produced and maintained by you, and only you.
Although there are many ways to verify trust throughout the entire chain? These would be of more value to software producers (think software factory) to enable forensics, traceability, and more.
For our use case, and as depicted in the original Codecov hack? We can get all the benefits of securing our usage of the 3rd party software as consumers. Which is simpler than considering the entire chain of supply on the producer side.
Getting Preflight
First of all, itâs the chicken and the egg. How do you pull a legit preflight
binary from us without verifying it with preflight
? Having that preflight
solves this exact problem?
The best way is to grab the source, compile it yourself, and use your own binary which you put in a place that you trust. You will usually have several options to do that safely:
- Put it on your own S3 bucket
- Drop it on your own Artifactory or similar
- Push it directly into your repos (it should be as small as 4mb and seldom change so Git should work nicely with it)
If you want to just get started quickly on your workstation, you can download a release or install preflight
with homebrew:
$ brew tap spectralops/tap && brew install preflight
Flying With Preflight
The curl | sh workflow is packaged as a tool, verified with our own signature that we can create with Preflight. In addition, Preflight can verify any runnable against several of providers such as VirusTotal and others to detect malware automatically
So how does it look in action?
Someone changed the script or binary youâre running. Abort!
$ curl -L https://XXX | preflight run sha256=1ce...2244a6e86 âď¸ Preflight starting â Preflight failed: Digest does not match. Expected: <...> Actual: <...> Information: It is recommended to inspect the modified file contents.
A hash is verified, but it is actually vulnerable. Abort!
$ curl -L https://XXX | preflight run sha256=1ce...2244a6e86 âď¸ Preflight starting using file lookup: malshare.current.sha256.txt â Preflight failed: Digest matches but marked as vulnerable. Digest matches but marked as vulnerable. Information: Vulnerability: Hash was found in a vulnerable digest list More: malshare.current.sha256.txt
All ok, letâs fly.
$ curl -L https://XXX | preflight run sha256=1ce...2244a6e86 âď¸ Preflight starting â
Preflight verified ... actual script output ...
Codecov Revisited
A perfect example of Preflight in action would be CodeCov.
First, letâs create a hash. Before creating it, be sure to review the script manually and see that itâs not doing anything funny:
$ curl -s https://codecov.io/bash | ./preflight create sha256=d6aa3207c4908d123bd8af62ec0538e3f2b9f257c3de62fad4e29cd3b59b41d9
Now, weâre going to take
sha256=d6aa3207c4908d123bd8af62ec0538e3f2b9f257c3de62fad4e29cd3b59b41d9
And use this to secure our pulls from CodeCov. In this case, preflight
is checked safely into your repo under ci/preflight
.
BEFORE (insecure):
steps: - curl -s https://codecov.io/bash | sh
AFTER (safe, yay!):
steps: - curl -s https://codecov.io/bash | ./ci/preflight run sha256=d6aa3207c4908d123bd8af62ec0538e3f2b9f257c3de62fad4e29cd3b59b41d9
Dealing With Change
When updating an old binary or script to a new updated version, there will be at least two (2) valid digests âliveâ. Just replacing the single digest used will fail for the older runnable which may still be running somewhere.
$ preflight <hash list|https://url/to/hash-list>
To support updates and rolling/auto-updates of scripts and binaries we need to validate against <old hash>
+ <new hash>
at all times, until everyone upgrades to the new script. Preflight validates against a list of hashes
. Or better: give it a live URL of valid hashes and it will validate against it.
curl .. | ./ci/preflight run sha256=d6aa3207c4908d123bd8af62ec0538e3f2b9f257c3de62fad4e29cd3b59b41d9,sha256=<new hash>,...
Or to a live URL:
curl .. | ./ci/preflight run https://dl.example.com/hashes.txt
Use this when:
- You employ multiple digests verbatim and when your runnables change often (but not too often)
- You use a URL when your runnables change often. Remember to follow the chain of trust. This will now mean that:
- Your hash list URL is now a source of trust
- Visually: weâre swapping the chain of trust like so
curl <foreign trust> | ./ci/preflight <own trust>
Checking Scripts And Binaries
If you want to use Preflight as another tool for improving your Unix pipes chops, you can with the âcheckâ command:
Piping:
$ curl -s https://example.com/some-script | preflight check sha256=d6aa3207c4<...>b4 | sh
Note that preflight check
is built in a way that allows you to continue to pipe to the next process:
- If a check passes, the checked script or binary content will be dumped to
STDOUT
- If a check fails, youâll get an
exit(1)
, and an error message
Executables:
$ preflight check sha256=d6aa3207c4<...>b4 ./my-script.sh
In this case:
- If a check passes, youâll get an
exit(0)
and no output (so you can compose with other tooling) - If a check fails, youâll get an
exit(1)
and an error message
Creating New Hashes
You can easily create new hashes with preflight. The default is a SHA256
hash, but you could also create a sha256
, sha1
, and md5
hash.
$ preflight create test.sh sha256=fe6d02cf15642ff8d5f61cad6d636a62fd46a5e5a49c06733fece838f5fa9d85
Though not recommended, you can create other kinds (weaker kinds) of hashes for legacy/compatibility reasons:
$ preflight create test.sh --digest md5 md5=cb62874fea06458b2b0cabf2322c9d55
Malware lookup
preflight
comes with lookup providers. This is optional and you can enable them by using environment variables:
File Lookup
You can download a daily list of malware signatures from malshare.com or any equivalent service. Here is a direct link to such a list.
Then:
With this configured preflight will search for all digest types in this file before approving.
Here is a full example for your CI, combining preflight
with Malshare:
env: PF_FILE_LOOKUP: malshare.current.sha256.txt steps: - wget https://www.malshare.com/daily/malshare.current.sha256.txt - curl https://... | preflight <sha>
Result:
$ PF_FILE_LOOKUP=malshare.current.sha256.txt preflight run fe6d02cf15642ff8d5f61cad6d636a62fd46a5e5a49c06733fece838f5fa9d85 test.sh âď¸ Preflight starting using file lookup: malshare.current.sha256.txt â Preflight failed: Digest matches but marked as vulnerable. Information: Vulnerability: Hash was found in a vulnerable digest list More: malshare.current.sha256.txt
VirusTotal Lookup
You can use the virus total community API access to lookup your hashes.
With this configured, preflight
will automatically create the VirusTotal lookup provider and validate the digest with it.
Here is a full example for your CI, combining preflight with VirusTotal:
env: PF_VT_TOKEN: {{secrets.PF_VT_TOKEN}} steps: - curl https://... | preflight <sha>
Result:
$ PF_VT_TOKEN=xxx preflight check e86d4eb1e888bd625389f2e50644be67a6bdbd77ff3bceaaf182d45860b88d80 kx-leecher.exe âď¸ Preflight starting using VirusTotal â Preflight failed: Digest matches but marked as vulnerable. Information: Vulnerability: VirusTotal stats - malicious: 40, suspicious 0 More: https://www.virustotal.com/gui/file/e86d4eb1e888bd625389f2e50644be67a6bdbd77ff3bceaaf182d45860b88d80/detection
Summary
While thereâs a lot of interest in protecting the use of 3rd party libraries? Frequentlyâââespecially in CI systemsâââweâre actually running 3rd party tools to get a certain job done. This is another chance for attackers to aim at our supply chain. Using Preflight, we can ensure we employ 3rd party tools safely and securely.
Preflight is open source, and weâre accepting pull requests so feel free to visit our repo on Github