All your domains are belong to us

Published by Garnet Research (@research) Garnet Research (@research)
By @research 

An underrated but effective attack vector:
using expiring domains to take over npm packages.

One of the largest threat surfaces in the open source software supply chain is also one of the most obvious: package maintainers and their accounts. Citing a study from late 2021, 2,812 maintainer profiles on npm had expired domains, which allowed someone to utilize this attack vector and hijack 8,494 packages.

While the focus on security is steadily gaining each year, some elementary attack vectors are still being overlooked. Security researcher Lance Vick made this very clear in May 2022, and went so far as to make a spreadsheet of all the packages on npm in which the maintainer had an expired domain to raise awareness on the subject.

Too many cooks in the kitchen

We have discussed previously the premise of super dependency in open source ecosystems and the fact that you expose yourself to the influence of thousands of developers whom you don’t know and should not necessarily trust by default. In the context of this post, we are not going to talk about the technical side of dependencies, but the human and behavioural side of it – a side that is often overlooked.

Threat actors are taking advantage of the implict trust and liberal governance of open source ecosystems which allow virtually anyone to contribute. After all, this is what the entire ecology of open source fundamentally stands on.

The point to note here is that when looking at the security risks of 3rd-party dependencies, it is essential to consider that humans form a significant portion of the attack surface, and it’s not just about the code.

The larger the surface, the greater the risk

The most vulnerable target for attackers are popular packages with a large number of downstream dependents and contributors. Because of the large attack surface, threat actors are incentivized to compromise the parent package or its dependents, and as a result, affect millions of npm users.

To put this in context, let’s look at an example of an actual npm package. ms, a popular utility for converting time formats with over ~140,000 weekly downloads, has more than 50 maintainers and about ~4400 dependent packages as of June 2022. The concerning part is that over 60% of the ms package maintainers had weak auth credentials or didn’t have 2FA enforced, according to recent research.

Now imagine what would happen if a maintainer of such a package got compromised? Let’s dig in deeper…

Abandoned resources as an attack vector

An attacker can hijack a target package in various ways. One of the ways is to obtain control of the maintainer’s account–a method commonly known as Account Takeover (ATO) in the security world. In general, ATO can result from various attack paths such as credential stuffing, social engineering, or phishing. However, for this post, we’ll focus on account takeover through domain expiration.

The scary thing about this kind of supply chain attack is that it originates through exploiting legitimate mechanisms. In fact, it is a result of human error or negligence on abandoned resources, which can then be hijacked to carry out attacks. Such an attack is difficult to catch because it originates from a trusted maintainer’s account.

Relationship between package managers, repositories and maintainers in OSS

To understand this attack vector, it’s important to understand the relationship between various entities involved in the supply chain. Typically, open source packages reside between two loosely coupled systems - a source repo, the package manager, and the people who maintain these package projects (aka the maintainers). Below is a conceptual overview:

  • Source Control: the system of record for code and collaboration. Typically using a version control system such as git provided through a service such as GitHub, BitBucket or GitLab – this is where the ongoing development happens. The source code for an open source project usually lives inside a git repository hosted on one of these platforms, and this repoistory can be controlled either through an individual’s personal account (e.g. dominictarr/event-stream), or an organizational account (e.g. microsoft/typescript). An owner of a source repository has full permissions to perform administrative actions and typically authenticates to the service using credentials such as email/password, tokens or external identity providers.
  • Maintainers: the humans who decide what goes in/out in terms of code, and are also in charge of the governance. In source control, there is usually a small number of maintainers who essentially serve as the gatekeepers for new code (or commits). This is usually complemented by a larger number of indiviual contributors who can make new contributions (such as bug fixes or minor additions) through submitting Pull Requests. Small packages can range from having solo maintainers (e.g. a hobbyist) and commits from a handful of other authors. In contrast, larger projects (e.g. Apache Spark) can have several maintainers and dozens of contributors handling different parts of the project, with established protocols for governance.
  • Package Manager & Registry: the upstream distribution channel for published packages is where pre-built deliverables are released for consumption for downstream users. In context of npm, the official package registry is publicly hosted (; however, there are cases where an organization can deploy a private npm registry to host internal packages (e.g. @yelp/sensitive-internal-package).

In a typical high-level workflow, maintainers collaborate in source control systems during development, and finished modules are published to package registries. Consumers of packages (which could be humans or machines, e.g. CI servers) typically download package versions from the registry server for use in downstream workflows (e.g. builds, testing) and applications.

Maintainers are ultimately the ones respobsible for contorlling what goes into the contents of the package, the process around its development, and what its dependencies are. Hence, when using an open source module, you implicitly trust anyone who has written code for the project. Even innocent contributors who inadvertently introduce bugs can be problematic, while a malicious contributor can wreak havoc.

Examining the case of npm, a package on can have multiple maintainers controlling that account.

"maintainers": [

      "name": "fkj",
      "email": "[email protected]"
        "name": "us",
        "email": "[email protected]"
Example metadata for a npm package in the registry

It can also be noted that these accounts are linked to an email address e.g. [email protected], [email protected], or [email protected].

This means that gaining access to even one of the maintainer accounts can result in attacker potentially gaining control of the entire repo and perform administrative actions.

What is domain expiration

Some security exploits can be incredibly complex to explain and can require a lot of experience to understand. However, domain expiration is a type of exploit that’s almost too simple. To illustrate, you first need to understand the procedure of acquiring a domain. First, you go to a domain registrar like Namecheap, search for the domain you want to buy, and purchase it, after which you own the domain. With this ownership, you can do various things like setting up a website or configure mail servers to set up an email account for yourself. This is exactly the type of access attackers want to gain when looking at domain expiration. It’s a common practice in the developer world for people to want their own domain and an accompanying email address, like [email protected]. And, of course, if you have your own domain and email address, you’re going to be using it. Consequently, some maintainers use their custom email addresses when creating an account on a registry such as npm. This in itself is not an issue. However, it’s quite common for people to only buy access to a domain valid for a limited period of time (usually one year at a time).

How can domain hijacking result in supply chain attacks?

A domain’s expiration date is exactly what attackers want when they perform a domain hijacking attack. When a domain expires, anyone in the world can buy it. In the past, this has even caused Google problems when someone managed to buy, after which Google had to buy it back for 12,000 dollars.

However, hackers aren’t buying expired domains because they want someone to buy it back. To them, acquiring an expired domain can be much more profitable. Think about how you reset your password – you click “forgot password” and get a reset link sent to your email inbox. Technically, if you control the domain, you control the email servers. Therefore, any new password reset requests will be routed to an attacker-controlled email inbox, and they will have full access to the maintainer’s account on the npm registry. This is what Lance Vick managed to do when he bought the domain for the maintainer of the foreach npm package. The below tweet summarises the process well:

This means that Lance could’ve easily released a new version of foreach containing malicious code (e.g. credential-stealing malware). As of writing, half a year later, the package still gets over 4.5 million weekly downloads.

Concerns around this novel attack vector were reiterated in a recent study by JFrog, stating that 3210 unique packages and 900 maintainers were found to have email domains that were expired and available for purchase by virtually anyone, making them vulnerable to this kind of a hijacking attack.

Preventing domain takeover attacks

As opposed to other supply chain security exploits, preventing domain expiration is incredibly simple, and there are three main ways to prevent it for OSS maintainers:

  • Set up auto-renewal of your domain (a standard feature supported by domain registrars)
  • Use a non-custom domain like or
  • Enable Two-factor authentication (2FA)

For consumers of packages, a good practice to reduce to risk of an exploit through a hijacked transitive dependency is by “pinning” your dependencies to absolute versions in the package.json file. In a scenario where a package being indirectly consumed gets hijacked, the attacker would only be able to deliver a malicious payload by publishing a new version of the package, which would not get downloaded automatically if an older, immutable version was specified.

On this end, we’ve published a simple tool which makes it easy for anyone to check the status of domain expiration for maintainers of an npm package.