Summary
The runTailscalePing method of the TailscalePing class injects the hostname parameter inside a shell command, leading to a command injection and the possibility to run arbitrary commands on the server.
Details
When adding a new monitor on Uptime Kuma, we can select the "Tailscale Ping" type. Then we can add a hostname and insert a command injection payload into it. The front-end application requires that the field follow a specific pattern, this validation only happens on the front-end and can be removed by removing the attribute pattern on the input element.
|
let cmd = `tailscale ping ${hostname}`; |
|
|
|
log.debug("Tailscale", cmd); |
|
|
|
return new Promise((resolve, reject) => { |
|
let timeout = interval * 1000 * 0.8; |
|
exec(cmd, { timeout: timeout }, (error, stdout, stderr) => { |
We can finally add the new monitor and observe that our command is being executed.
NOTE: When using Uptime Kuma inside a container, the "TailScale Ping" type is not visible. We can fake this information by intercepting WebSocket messages and set the isContainer option to false.
PoC
- Authenticate.
- Create a new monitor.
- Select the TailScale Ping type (if not visible, see the note in the details section).
- Insert the command injection payload inside the
hostname field. (for example $(id >&2))
- Remove the
pattern requirement on the field.
- Save and start the monitor.

Impact
An authenticated user can execute arbitrary command on the server running Uptime Kuma.
Remediation
There are other command execution in the codebase, they use a method spawn from the child_process module which does not interpret the command as a shell command, the same thing should be done here.
NOTE: The Tailscale CLI seems to support the -- sequence. It should be used between the ping subcommand and the hostname argument to avoid argument injection.
Summary
The
runTailscalePingmethod of theTailscalePingclass injects thehostnameparameter inside a shell command, leading to a command injection and the possibility to run arbitrary commands on the server.Details
When adding a new monitor on Uptime Kuma, we can select the "Tailscale Ping" type. Then we can add a hostname and insert a command injection payload into it. The front-end application requires that the field follow a specific pattern, this validation only happens on the front-end and can be removed by removing the attribute
patternon theinputelement.uptime-kuma/server/monitor-types/tailscale-ping.js
Lines 40 to 46 in dc42420
We can finally add the new monitor and observe that our command is being executed.
NOTE: When using Uptime Kuma inside a container, the "TailScale Ping" type is not visible. We can fake this information by intercepting WebSocket messages and set the
isContaineroption tofalse.PoC
hostnamefield. (for example$(id >&2))patternrequirement on the field.Impact
An authenticated user can execute arbitrary command on the server running Uptime Kuma.
Remediation
There are other command execution in the codebase, they use a method
spawnfrom thechild_processmodule which does not interpret the command as a shell command, the same thing should be done here.NOTE: The Tailscale CLI seems to support the
--sequence. It should be used between thepingsubcommand and thehostnameargument to avoid argument injection.