- Domain takeover
If an attacker wants to target an application, they can try to find the dependencies of that application. Then, by doing a domain takeover, they can control an email address associated with one of these dependencies and in turn, take control of the dependency and inject a backdoor.
Everyone can create an account with his email and push some code that could be imported from all other developers of the world. Nowadays, some libraries are widely used, like React, Axios, Express, Vue, Moment, Bootstrap and jQuery.
How can we find the libraries used?
When the application is packaged before going in production, the code could be obfuscated and minified.. So, retrieving all the dependencies name could be painful.
In some cases, the packaging process creates a .map file associated with each .js file. In case of error, the browser can add a line into the console to inform that an error occurred on the line X, but with a minified code, the code can take only 1 line; therefore the debugging process could be complex. Map files allow the developer to have more details from the error and pointing to a real line on the code before the minification process. Except for debugging an error, the map file is not needed by the user visiting the website.
The interesting point is that the list of the dependencies of the application is also referenced in map files. This list could be extracted with a simple bash command using the JQ tool, for example.
The example in the next figure is decomposed in two commands as the package name can differ if it starts with a ‘@’ or not.
Now, a list of all the dependencies used by the front-end application is created … but only the main dependencies that the developer needed.
Does a dependency have a dependency?
With the map file, we only collect the dependencies that the application needs but what about “nested dependencies”?
Any code can have dependencies, there can be multiple levels of dependencies included.
The NPM API allows getting the dependencies of a package, the list is given on the JSON data return like the following one.
So, by getting the last version of a package and then the dependencies, it’s possible to get the dependencies of the dependencies of the dependencies of the … you see the point.
So now, we really have a list of all the dependencies used by the application.
How can we find the owner of a library?
When a dependency is pushed on the NPM website, the creator must have an email to login. The library’s developer is also known as the maintainer. Each library can have multiple maintainers which makes collaboration easier.
Same as the previous part, the NPM API allows anyone to retrieve the package maintainer email for all the version of the given package.
The first step is to get the latest version, and then get the maintainers’ emails address with a simple JQ command like shown by the first command in the figure below.
The second command shows how to list all the maintainers of all the previous version.
At that point, we have a list of all the maintainers’ email address of all the libraries used by the application.
Wait, what the link between this and ‘domain takeover’?
So, let’s go back to the “domain takeover” part:
A domain takeover is the fact to buy a domain name that was previously owned by another entity but then became available on the market. After buying the domain, it can be used to get access to resources that the previous owner of the domain has.
For example, if a package named foobar on NPM was developed by Jean-Jacques from the company acme.lu. That guy used his professional address to push the package, so firstname.lastname@example.org.
However, the ACME company closed and the domain acme.lu became free to register after 1 year as no one renewed it.
Anyone can buy that domain on a registrar and create an email address email@example.com.
With this email address created, it’s possible to use the “forgot password” feature of the NPM website and get a new access to the NPM account previously used to push the foobar package.
This method could be used with all the website on which one Jean-Jacques had an account and that could lead to a total hijacking of Jean-Jacques’s identity (mail, banks, shopping, social networks, …).
However, there is a limitation which is the 2 Factor Authentication (2FA). If the 2FA is enabled, it is not possible to take over the NPM account as we need at least a token or a recovery key.
Since May 2022, the top 500 package of NPM are forced to use the 2FA but not the dependencies…
How can we check if the domain is still active?
As we identified our goal now, we should find all the email domains that are vulnerable to a domain takeover.
For that part multiple methods can be used, but one of them is to check if an NS record is present or not. If no NS is present, that implies that the domain is not referenced on the TLD root servers (.lu, .com, and so on), so a simple check can be made with a DNS request.
In the given example below, the domain possesses an NS server and some other check allowed to verify it’s not prone to domain takeover (we will not disclose a potential domain takeover on this blog post 😉).
Sometimes, for intersting or high value domains, an individual can buy the domain and wait for someone who needs it to notice. And if it is the case, that person can sell it more expensive than the original price. For example, one guy achieved to buy the google.com for 12$ and as Google really need it, an offer of 6000$ was made to get back this domain. That practice is now highly used and when a domain is bought for being sold more expensive it is called a “parked domain” or in a “parking state”.
So, if one of the domains found from the NPM maintainers emails is parked, it can also be possible to acquire it at a more expensive price than the original amount. The end results for this type of attack would be the same: own the domain and steal the NPM account to include a backdoor in the package.
Checking for parked domains was not implemented in the script, but some NS of parking companies can be found on the internet. If one NS matches the NS from the parking list, it can lead to a domain takeover.
Are there some other takeover methods?
Another interesting method is to check if the email still exists. However, this not yield any successful during the research.
A large number of the emails found were gmail.com, almost 50%. While buying gmail.com domain could be possible previously, now Google should have a better handling of their domain expiration. We can assume it’s highly unlikely to be able to buy the gmail.com domain name.
Therefore, the first idea was to include if the script a check to see if the Gmail address exists by using the tool GHunt. However it seems that it is not possible to recreate a Gmail email when it has been deleted (by the user or by Google). Google does not recycle account names, therefore, it’s impossible to recreate an email that doesn’t exist anymore.
Another possibility was to check for leaked credentials, if we are able to log in to the Gmail account, the goal is reached as we can reset the NPM password and push a backdoored version of the library.
These two methods are available for Gmail but also for all other email providers like Outlook, Yahoo, and so on.
All that story to say: it’s difficult to find a domain takeover on NPM packages, but it’s also hard to check if libraries used by our software are prone to that vulnerability.
An offensive oriented script (ExcelliumSA/MapOver), was written during this research. It could be adapted or totally changed to be included inside some security check made by a CI/CD for example. The detection of a potential domain take over can be improved by adding some additional checks and could also be automated by sending some requests to a registrar.
Do you have any questions? Would you like to know more about this subject? Contact our experts!