
Research
Security News
Lazarus Strikes npm Again with New Wave of Malicious Packages
The Socket Research Team has discovered six new malicious npm packages linked to North Korea’s Lazarus Group, designed to steal credentials and deploy backdoors.
Application Security
Bradley Meck Farias
April 19, 2023
Code signing! We hear excitement about this a lot but it is something we haven't really discussed on our blog. Luckily, npm has been working on a way to provide this to all registry users, regardless of things like security vendors through an npm RFC they made. This feature has been released and is on the GitHub blog. We wish a huge round of congratulations to them on shipping this monumental feature.
There are often misunderstandings about what code signing provides. Code signing does not inherently make code safer to execute; code signing allows checks to be performed and information to be preserved about a built piece of software; these are called attestations. We want to discuss what checks and information are provided by npm's code signing.
The feature being shipped is referred to as "provenance" in their documents because that is what it is providing data about. Provenance is creating a clear link to where something came from, it is not about what something is. In order to provide this data, npm is using the sigstore standard workflow which correlates custom publishing data such as JS package contents with well known and known computing infrastructure metadata. For example, a GitHub action container may have metadata associated with it such as why the container is running and that would be attached to any package published from inside of that container.
The provenance provided is based upon GitHub actions currently, but can be expected to expand over time. The npm CLI introduced a npm --provenance
flag that will send environmental data to the registry to verify it came from a known location.
Hearing environmental data, you might be quick to think spoofing the data will be simple; however, by using dedicated and well known infrastructure like GitHub Actions, the data cannot be entirely spoofed. Even if the environment variables of the npm
process are tampered with, the actual data from the build machine itself cannot be. Imaging the following attempt to impersonate a maintainer with a good reputation:
require('child_process').spawnSync(
'npm',
['publish', '--provenance', '--access', 'public'],
{
env: {
...process.env,
...REPUTABLE_ACTOR_VARIABLES
}
}
)
This will not work; although this would tamper with the environment seen by npm
, it would not be able to remove the connection details themselves from the machine for network reasons; the remote server for signing can see the remote IP causing the publish and cross reference with GitHub actions to get untainted data. The data currently gathered in an unforgeable manner is available for further reference.
This means that code signing does not work on a local machine since it would not have a trusted source of untampered data. GitHub is providing a trusted infrastructure and committing to provide the proper environment and metadata in a tamper proof way for npm
to use. If a local machine was allowed to provide the necessary metadata it could provide false data as it isn't considered trusted by the npm
registry to begin with.
What data is provided in these attestations is roughly as follows (and a few more not listed):
package.json
and infrastructure metadata. This prevents attempts to spoof publishing into a different registry owned by the same GH user.npm publish
. Due to things like allowing network access, timer access, etc. this does not mean the build is reproducible.All together, this data provides a picture of what was being built and by whom. The data is obtained from GitHub's infrastructure running the GitHub action and not from the process doing the npm publish
. It does not make any guarantees about what the code may do or what actions were done to create the package's code that is published.
npm publish --provenance
?#https://registry.npmjs.org/$PACKAGE/$VERSION#dist
. This can be used to cross reference against the public rekor log of sigstore and includes a signature signed by npm's own keyAll told, there are a few discretely new things going on but all the old behaviors of a normal publish remain. No checks are performed on the code itself that is pushed to the registry, nor are any checks performed on the code placed onto your computer. The only check is that the bundled package tarball is the same as the one that is associated with the metadata about provenance.
This data allows a few things for tooling going forward:
Code signing discretely ties data to a specific package bundle as an opaque blob. There is no analysis during uploading or downloading of the bundle internals. No workflow is provided to compare the history of a package. No cross referencing with other packages for things like dependency confusion or typosquating is performed. Code signing is specifically tied to the opaque blob being downloaded from the internet without extracting information from within that blob such as file contents.
This is a great source of data for Socket to be able to leverage and one we will excitedly use to enhance the ecosystem further:
There is a library sigstore-js to do this provided by npm/GitHub but we can go over some of the details of how it works so you can walk through it yourself. This is gonna be a bit rough so feel free to skip ahead.
We have provided links for ease of use along the way here.
https://registry.npmjs.org/${name}/${constraint}
(see example)@latest
) of the package using doc.version
, its tarball location from doc.dist.tarball
, the expected tarball integrity at doc.dist.integrity
, and its npm attestations using doc.dist.attestations.url
.https://registry.npmjs.org/-/npm/v1/attestations/${name}@${version}
and for each check the certs attestation.bundle.verificationMaterial
. (see example)predicateType === 'https://slsa.dev/provenance/v0.2'
. This attestation contains data about the tarball in attestation.bundle.verificationMaterial.tlogEntries
including a logIndex
that can be cross referenced using the public Rekor API at https://rekor.sigstore.dev/api/v1/log/entries?logIndex=${logIndex}
(see example, or more readable UI) with relevant data under ${UUID}.attestation.data
as BASE64 JSON. NPM includes a signed result of Rekor entry that under attestation.bundle.dsseEnvelope
if you want to verify it and use that.rekorEntry.subject.name
as PURL (e.g. pkg:npm/%40socketsecurity/cli@0.5.1
). and make sure it matches the package name and version.rekorEntry.subject.digest
. Whew! If they match we know they are talking about the same tarball. This has disambiguated the package and the tarball contents without overlap at this point.rekorEntry.predicate.materials
defines what GitHub commit / repository was being pulled to publish.rekorEntry.predicate.invocation
defines what GitHub Action actually executed which is different since a GitHub action may actually come from a different repository than the one being published. This contains useful information about the environment including the GitHub user that pressed the merge button, release button, etc. that caused the action to execute.This is a great feature that finally allows real connection of what created a tarball on npm. Using this feature, it is practical for things like the file explorer at Socket to provide a concrete link not just to the code as it exists exactly on the npm registry but also to what is on GitHub potentially allowing easier human auditing of files. Additionally we can finally do better social graph connections to GitHub rather than what are often npm publishes via bot API keys these days. We are always looking forward on how to enhance our product based upon this and hope to have some features out soon around the capabilities this gives us. During that time, we hope to see some more adoption by the community in part driven by wanting those features available on packages.
Subscribe to our newsletter
Get notified when we publish new security blog posts!
Try it now
Research
Security News
The Socket Research Team has discovered six new malicious npm packages linked to North Korea’s Lazarus Group, designed to steal credentials and deploy backdoors.
Security News
Socket CEO Feross Aboukhadijeh discusses the open web, open source security, and how Socket tackles software supply chain attacks on The Pair Program podcast.
Security News
Opengrep continues building momentum with the alpha release of its Playground tool, demonstrating the project's rapid evolution just two months after its initial launch.