On 1. October 2025, Checkmarx Zero researcher Bruno Dias identified and reported the JavaScript package @lanyer640/mcp-runcommand-server to NPM due to malicious content. NPM has now removed this package from the public repository, but private repositories, caches, etc. may still provide it to your organization.
This package purports to be an MCP tool server and may appear legitimate to developers.
- Versions starting with 1.0.6 contain a remote reverse shell to IP `
45.115.38.27
`on port `2333
` that allows an attacker to interact with infected machines using the privileges of the installing/running user-
Installation is sufficient for infection; users do not need to take any action beyond running an `
npm install
` command to be compromised
-
Installation is sufficient for infection; users do not need to take any action beyond running an `
- While only version 1.0.6 and later are actively malicious, all versions should be treated skeptically
- Attributes of the author’s metadata and behavior strongly suggest to us that they are a threat actor
- Timeline of release, growth in popularity, and then insertion of malicious content match known attack tactics for a deception campaign
- Versions prior to 1.0.6, while missing clearly malicious content, have several security weaknesses that pose risks to adopters regardless of malicious intent
Recommended Actions
- Ensure local package caches — such as Nexus Repository, Artifactory, ProGet, Verdaccio and others — are blocking caching and installation of @lanyer640/mcp-runcommand-server
- We recommend blocking the package entirely, but it is essential to block versions starting with 1.0.6
- Configure network controls to block and alert on connections to `
45.115.38.27
`. Examine logs for connections to this IP, especially on port `2333
` to identify potentially infected systems. - Examine `
package.json
`, `npm-shrinkwrap.json
` and equivalent files, as well as `node_modules
` directories, for evidence of package installation (see below for example scripts that can assist you). - Use your SCA tool’s inventory or malicious package protection features to identify any code projects that may have included this package in their dependency trees.
- Block new installations with the Checkmarx Malicious Package Protection API (also known as MPIAPI or SCS Threat Intel API) on your developer desktops and CI/CD systems.
Identification scripts
Here are some scripts we’ve created to help identify evidence of installation of this package. Caution: these scripts have received only limited testing in lab environments. You should review, test, and modify as needed before using them in production environments and make sure you do not take corrective action without review.
Bash script for Linux and similar systems
#!/usr/bin/env bash
# find-mcp-runcommand-server-semver.sh by Checkmarx Zero
# Usage: ./find-mcp-runcommand-server-semver.sh [START_PATH] [MIN_VERSION]
# NOTE -- adapt as needed, this script has recieved limited testing
set -euo pipefail
START="${1:-/}"
MIN="${2:-1.0.6}"
semver_ge() {
# numeric-only compare: major.minor.patch
IFS='.-+' read -r a b <<<"$1"
IFS='.-+' read -r c d <<<"$2"
IFS='.' read -r a1 a2 a3 <<<"${a:-0.0.0}"
IFS='.' read -r c1 c2 c3 <<<"${c:-0.0.0}"
a1=${a1:-0}; a2=${a2:-0}; a3=${a3:-0}
c1=${c1:-0}; c2=${c2:-0}; c3=${c3:-0}
if ((a1>c1)); then return 0
elif ((a1<c1)); then return 1
elif ((a2>c2)); then return 0
elif ((a2<c2)); then return 1
elif ((a3>=c3)); then return 0
else return 1; fi
}
# Prune noisy or special filesystems for speed/safety
PRUNE='( -path */.git -o -path */node_modules/.cache -o -path /proc -o -path /sys -o -path /dev -o -path /run -o -path /Volumes -o -path /mnt -o -path /media ) -prune -o'
# Find package.json files under directories named "mcp-runcommand-server"
# Use Node to parse JSON robustly (required)
eval find "\"$START\"" -type d -name "mcp-runcommand-server" $PRUNE -type f -name package.json -path '*/mcp-runcommand-server/package.json' -print0 |
while IFS= read -r -d '' pkg; do
# read version via Node for correctness
ver=$(node -pe "try{console.log(require(process.argv[1]).version||'')}catch(e){''}" "$pkg" 2>/dev/null || true)
[[ -z "$ver" ]] && continue
if semver_ge "$ver" "$MIN"; then
printf '%s,%s\n' "$ver" "$(dirname "$pkg")"
fi
done
Powershell script for Windows systems
# find-mcp-runcommand-server-semver.ps1 by Checkmarx Zero
# Usage: .\find-mcp-runcommand-server-semver.ps1 -Start 'C:\' -MinVersion '1.0.6'
# NOTE -- adapt as needed, this script has recieved limited testing
param(
[string]$Start = 'C:\',
[string]$MinVersion = '1.0.6'
)
function Convert-ToSemVerTriple {
param([string]$Version)
# Strip prerelease/build then split
$core = ($Version -split '[-+]')[0]
$parts = $core.Split('.')
[int[]]@(
[int]($parts[0] | ForEach-Object { if ($_ -match '^\d+$'){$_} else {0} }),
[int]($parts[1] | ForEach-Object { if ($_ -match '^\d+$'){$_} else {0} }),
[int]($parts[2] | ForEach-Object { if ($_ -match '^\d+$'){$_} else {0} })
)
}
function Test-SemVerGE {
param([string]$A, [string]$B)
$aTrip = Convert-ToSemVerTriple $A
$bTrip = Convert-ToSemVerTriple $B
if ($aTrip[0] -gt $bTrip[0]) { return $true }
elseif ($aTrip[0] -lt $bTrip[0]) { return $false }
elseif ($aTrip[1] -gt $bTrip[1]) { return $true }
elseif ($aTrip[1] -lt $bTrip[1]) { return $false }
else { return ($aTrip[2] -ge $bTrip[2]) }
}
$results = New-Object System.Collections.Generic.List[object]
# Search directories named "mcp-runcommand-server" and read package.json
Get-ChildItem -Path $Start -Directory -Recurse -ErrorAction SilentlyContinue |
Where-Object { $_.Name -ieq 'mcp-runcommand-server' } |
ForEach-Object {
$pkg = Join-Path $_.FullName 'package.json'
if (Test-Path $pkg) {
try {
$json = Get-Content $pkg -Raw -ErrorAction Stop | ConvertFrom-Json
$ver = $json.version
if ($ver -and (Test-SemVerGE -A $ver -B $MinVersion)) {
$results.Add([pscustomobject]@{
Version = $ver
Path = $_.FullName
}) | Out-Null
}
} catch { }
}
}
# Output CSV to console
$results | Sort-Object Path | Format-Table -AutoSize
# To export: $results | Export-Csv -NoTypeInformation -Path .\lanyer640_mcp-runcommand-server_matches.csv
Breakdown of risk
Version 1.0.6 of @lanyer640/mcp-runcommand-server contains an altered `run_command` that calls a`startServer()` function which connects to an attacker-controlled machine via a hard-coded IP address and port. A reverse shell — a tactic that makes an outgoing connection but allows an attacker to remotely execute shell commands on the infected host — is opened and persisted once the connection is made.
The included `preinstall` script in the package definition, which runs whenever the module is installed via tools like `npm` or `yarn`, ensures that the reverse shell is loaded and activated immediately upon installation. This means that simply installing the package is enough to infect a host without any further action.
The package itself is relatively new, and began life as a seemingly-trustworthy MCP tool server. While it had security weaknesses that could lead to exploitation, it does not appear to have been actively malicious until the changes associated with 1.0.6. This does not appear to be a case of a legitimate author being compromised, as so many malicious infections are. Instead, multiple signals about the maintainer account and the nature of the package strongly suggest that this package was produced with the intention of getting developers to adopt the tool while it is safe, and then deliberately add this malicious reverse shell capability in an update.
As Dias puts it: “in older versions (e.g., 1.0.5) the package didn’t contain a hardcoded backdoor, but it executes received commands with `shell: true` without any form of authentication or authorization. This makes it vulnerable to RCE and any client that talks to the MCP server can run arbitrary system commands.” As a result we strongly recommend blocking all versions of this package, not just the malicious revisions.
We reported this package to NPM, and they responded promptly: it is no longer available in the public repository. However, many organizations use a private repository system to cache NPM packages for installation. It’s important that responders identify any installed or cached copies to prevent future infection.