
PingTools Network Utilities 4.64 - OS command injection through Watcher ICMP host
8,6
High
Discovered by
Offensive Team, Fluid Attacks
Summary
Full name
PingTools Network Utilities 4.64 - OS command injection through Watcher ICMP host
Code name
State
Public
Release date
Affected product
PingTools Network Utilities
Vendor
StreamSoft
Affected version(s)
4.64
Vulnerability name
OS Command Injection
Vulnerability type
Remotely exploitable
Yes
CVSS v4.0 vector string
CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
CVSS v4.0 base score
8.6
Exploit available
Yes
CVE ID(s)
CWE
CAPEC
Description
PingTools Network Utilities 4.64 Free contains an OS command injection vulnerability in the Host field of a Watcher ICMP service. When a Watcher check runs, the application includes the supplied Host value in a ping command, serializes that command to a single string, and executes it through sh -c.
A local attacker who can create or modify a Watcher ICMP service can supply shell metacharacters in Host to alter the intended command and execute attacker-controlled commands. Those commands run with the PingTools application UID, Android sandbox access, and permissions; the issue does not grant root or access to another application's private data.
The affected field is intended to contain a hostname or IP address. Watcher is presented as a remote-resource monitoring feature, and neither the interface nor vendor documentation describes shell syntax as accepted input.
Vulnerability
Root cause
The application combines data and shell syntax. It builds ping arguments in a list, flattens the list to one space-delimited string without quoting, and then deliberately invokes sh -c with that string. The Host value is neither constrained to a hostname/IP grammar nor shell-escaped before reaching the shell.
The tested application identifies itself as package ua.com.streamsoft.pingtools, version code 464, version name 4.64 Free, with minimum SDK 24 and target SDK 33.
Source-to-sink path
Source — Watcher Host field (
WatcherManageServiceFragment.java:164-179):Only the empty value is rejected. The layout allows up to 500 characters of unrestricted
textNoSuggestions. In contrast, the adjacent port field is restricted to digits and is range-checked.Ineffective canonicalization (
gl/C5551j.java:77-82, originallygl/j.java):IDN.toASCIIis an internationalized-domain conversion routine, not a hostname allowlist or shell encoder. Values containing shell syntax can remain unchanged; if conversion throws, the catch block explicitly returns the original value. The report's conclusion therefore does not depend onIDN.toASCIIthrowing for the submitted payload.Host appended as a raw command token (
rg/C8331b.java:126-133, originallyrg/b.java):Unsafe command serialization (
pg/AbstractC7999b.java:36-44,89-90, originallypg/b.java):For the submitted service, this produces a command beginning with
ping -c 3followed by the unquoted Host value.Shell sink (
vi/C9626e0.java:169-189, originallyvi/e0.java):pg/C8002e.java:59-70confirms thatLinuxShellforwards the three elements toProcessBuilder.command(...). AlthoughProcessBuilderitself receives an argument array, its third element is interpreted as shell code because the executable is explicitlyshwith-c.Execution context: the application declares network and other permissions, including
INTERNET, but the process is still confined by the normal Android application UID and SELinux sandbox. The Watcher service is declaredandroid:exported="false", so the tested application does not expose this sink directly as an unauthenticated Android service to other apps.
Watcher is presented by the vendor as a facility for continuous monitoring of remote resources. The service editor offers ICMP, TCP, HTTP, and HTTPS checks and labels the affected value Host; it does not advertise arbitrary shell execution. The code also enforces a numeric allowlist and range for ports, demonstrating that these inputs are intended to be structured configuration rather than shell fragments.
Using a shell internally to run ping is an implementation choice. It does not make command separators in a Host value part of the documented feature. A safe implementation can invoke ping directly with a separate argument array, avoiding sh -c, and validate the value as an IP address or hostname.
Impact
A successful injection runs commands as the PingTools application UID. Within that boundary, commands can:
read and exfiltrate files and databases accessible to PingTools, including application-private Watcher/backup data;
modify or delete PingTools data and configuration;
use permissions inherited from the application, notably outbound network access;
disrupt Watcher checks or the application process; and
establish persistence inside app-controlled storage to the extent permitted by Android and available executables.
PoC
Step-by-step
On a tester-controlled machine reachable from the Android device, start a listener:
Open PingTools Network Utilities and navigate to:
Create a node, add a service, and choose service type
ICMP.In
Host, enter the following value after replacing192.168.1.23with the listener address:Save the service and Watcher task.
Open the task menu and select
Check.Observe a connection at the listener containing the PingTools process username, for example:
The effective shell program is equivalent to:
Evidence of Exploitation
Video of exploitation:
Static evidence:


Our security policy
We have reserved the ID CVE-2026-103038 to refer to this issue from now on.
System Information
Product: PingTools Network Utilities
Affected version:
4.64 Free(versionCode464)Package:
ua.com.streamsoft.pingtoolsOperating system: Android 7.0 and later according to the application's declared minimum SDK (
minSdkVersion=24)
References
Google Play: https://play.google.com/store/apps/details?id=ua.com.streamsoft.pingtools
Vendor site and support contact: https://pingtools.org/
Solution and Workaround
Preferred solution
The vendor should preserve the Watcher ICMP feature but change its execution boundary. After normalizing the Host value with IDN.toASCII, validate it as a single hostname or IP address accepted by the product. Reject invalid values; do not try to make arbitrary input safe by removing shell characters. The validated value must remain one argument throughout command construction.
The command builder already models the ping invocation as an argument list. It should pass that list directly to a process-execution API, instead of joining it into command.h() and passing the resulting string to LinuxShell.w1("sh", "-c", ...). If LinuxShell is the application's common execution wrapper, add an overload that accepts List<String> and delegates that list unchanged to ProcessBuilder.command(...). This keeps the existing command, output collection, timeout, and Watcher-result handling while removing the shell interpreter from this path.
The intended execution model is equivalent to:
validateWatcherHost should accept only the hostname and IPv4/IPv6 forms that Watcher documents, including any legitimate internationalized hostname after normalization. Tests should cover valid hostnames and addresses as well as values beginning with -, shell separators, substitutions, expansions, and whitespace. None of the latter may reach the process launcher.
Proposed patch
This is a proposed maintainer patch for the vulnerable execution boundary. It is not a vendor patch and was not supplied by the researcher. It must be reviewed and adapted by the product maintainer before release. Its security property is that the normalized, validated Host remains one process argument and is never interpreted by a shell.
The new LinuxShell.execute(List<String>) interface must pass the list unchanged to new ProcessBuilder(arguments).start(); it must not join, quote, or pass the arguments to sh -c. validateWatcherHost must reject shell syntax, whitespace, and option-like values, accepting only the DNS and IP address forms supported by Watcher. Apply the same validation before storing values received through backup restore.
Mitigation
Add a non-shell process-execution interface that accepts an argument list and use it for every Watcher ICMP check. Keep the Host value as one list element from
WatcherServiceEntityto thepingprocess; do not serialize and reparse the command.Apply the same Host validation at each persistence boundary: the Watcher editor and the Watcher-backup import/restore path. Both routes must accept supported DNS names and IP addresses, and reject values outside that grammar before they are stored or scheduled.
Add end-to-end regression tests for normal Watcher checks using valid DNS, IPv4, IPv6, and internationalized hostnames. Add negative tests for separators, expansions, substitutions, whitespace, and option-like values; assert that the application rejects them and that no shell process is created.
Backport the correction to supported releases, publish the first fixed version, and document the upgrade path. The release notes should state that the change affects Watcher ICMP Host processing so operators can identify and update affected installations.
Credits
The vulnerability was discovered by Andrés Ramos from Fluid Attacks' Offensive Team.
Timeline
Vulnerability discovered
Vendor contacted
Public disclosure
Does your application use this vulnerable software?
During our free trial, our tools assess your application, identify vulnerabilities, and provide recommendations for their remediation.














