English | 한국어
| Version | Supported |
|---|---|
| Latest | Yes |
Please report security vulnerabilities through GitHub Security Advisories:
Do NOT open a public issue for security vulnerabilities.
- Description: A clear description of the vulnerability
- Steps to Reproduce: Detailed steps to reproduce the issue
- Impact: The potential impact of the vulnerability
- Affected Components: Which modules or files are affected
- Environment:
- Operating System and version
- Python version
- FreeRDP version
- Desktop Environment
- Display Server (X11/Wayland)
| Step | Timeframe |
|---|---|
| Acknowledgment | Within 48 hours |
| Assessment | Within 7 days |
| Fix | Within 30 days |
The following areas are considered in scope for security reports:
- Command injection in subprocess calls: Unsanitized input passed to shell commands
- Credential exposure in config files: Passwords or secrets stored in plaintext
- RDP session hijacking: Unauthorized access to active RDP sessions
- Path traversal in UNC conversion: Manipulation of Windows/Linux path translation
- Privilege escalation via backend management: Unauthorized privilege gain through container or VM backends
The following are considered out of scope:
- Attacks requiring physical access to the machine
- Social engineering attacks
- Vulnerabilities in third-party dependencies (report these to the upstream project)
This project follows these security practices:
- Input validation on subprocess arguments: All arguments passed to subprocess calls are validated and sanitized
- Askpass preferred over plaintext passwords: Interactive password prompts (askpass) are used instead of storing passwords in plaintext
- XDG-compliant file permissions: Configuration files follow XDG Base Directory Specification with appropriate file permissions
- No secrets in code or git: Secrets, credentials, and API keys are never committed to the repository
- TLS-only RDP: SecurityLayer=2 (TLS) enforced on the RDP channel; NLA disabled only because RDP is bound to 127.0.0.1
- Windows build pinning: Feature updates blocked via registry policy; security updates install normally
- Container-isolated RDP: RDP port is bound to 127.0.0.1 only; not exposed to the network
winpodx treats the Windows guest as a semi-trusted component, not a trusted extension of the host. This section documents how that trust boundary is drawn and enforced, specifically for the app-discovery channel introduced in v0.1.7.
winpodx provisions the Windows guest itself inside a rootless Podman container (or, optionally, Docker / libvirt). Because the host controls the image, the unattended-install answer file, and the OEM post-install scripts, a freshly provisioned guest starts in a known-good state.
However, JSON emitted by scripts/windows/discover_apps.ps1 is treated as
semi-trusted input: it is not attacker-controlled network data, but it is
also not implicitly trusted code. The guest can be corrupted through entirely
legitimate Windows-side activity, for example:
- A malicious
.lnkdropped into the user's Start Menu folder. - A rogue
AppxManifest.xmlfrom a sideloaded UWP package. - Supply-chain compromise of a Windows application the user installed themselves (installer trojan, auto-update hijack, etc.).
Any of these can cause the discovery script to emit hostile metadata. The host side must therefore validate everything the guest sends before it is allowed to influence the host filesystem or any executed command line.
The host enforces the trust boundary with a layered set of guards. Each of these rejects malformed guest output before it can be written to disk or interpolated into a subprocess argument list:
_CONTAINER_NAME_RE— the container name pulled from the host config is scrubbed against an allowlist regex before it is passed as a subprocess arg._AUMID_RE— the UWP launch URI must match^[A-Za-z0-9._-]+![A-Za-z0-9._-]+$, i.e. a barePackageFamilyName!AppId. Anything else is refused._WM_CLASS_RE— the WM class hint used for desktop-entryStartupWMClass=must be a safe Linux identifier.- Slug regex — every slug derived from a guest-supplied app name is
sanitized to
[a-z0-9_-]{1,64}before it is used as a filesystem path component. _MAX_APPS(500) — a hard cap on the total number of discovered entries accepted per run._MAX_ICON_BYTES(1 MiB per icon) — a per-icon size cap._MAX_PATH_LEN— a cap on the length of guest-provided path strings.- Host-side stdout cap (64 MiB) — the subprocess reader bounds the total bytes consumed from the guest, so a misbehaving guest cannot OOM the host.
- PNG magic byte +
QImage.loadFromDatasanity check — every icon must begin with the PNG signature and must successfully parse through Qt's image decoder before it is written under~/.local/share/icons/hicolor/. _safe_rmtree— refuses to delete any path outside~/.local/share/winpodx/apps/._is_within— refuses to write any path outside the winpodx user data directory.- List-args subprocess only — all subprocess invocations pass an argv
list;
shell=Trueis never used on guest-derived strings.
Secrets flow host -> guest only. The RDP password is generated on the host
by core/compose.py generate_password, written into the guest through
unattended-install compose environment variables, and rotated on a
host-controlled schedule.
The discovery JSON carries no credentials in either direction. The
guest -> host channel is strictly typed as {app metadata, icon bytes} and
nothing else, so a compromised guest cannot exfiltrate host secrets through
this channel because the channel has no field that could carry them.
If an attacker fully compromises the Windows guest, the bounded impact via the discovery channel is:
Possible:
- Bogus
.desktopentries appearing in the user's app menu (with names already sanitized through the slug regex). - A PNG crafted specifically to exercise the
QImageparser. - Broken launches for malicious AUMIDs that fail
_AUMID_REvalidation (the launch simply never happens).
NOT possible via the discovery channel:
- Host code execution outside the PNG parser itself (which runs inside Qt's or the stdlib's own image-decoding sandbox).
- Filesystem writes outside
~/.local/share/winpodx/apps/and~/.local/share/icons/hicolor/. - Command injection into FreeRDP, Podman, Docker, or virsh argument lists.
- Credential exfiltration (no credentials traverse the guest -> host direction).
Additionally, the Windows guest runs under rootless Podman by default, so even a full guest-kernel RCE does not grant host root. A guest-compromise attacker is confined to the unprivileged user namespace that hosts the guest container.
We appreciate responsible disclosure and will credit reporters in release notes (unless anonymity is preferred).