-
Notifications
You must be signed in to change notification settings - Fork 114
Description
Title
[Security] Server-Side Request Forgery (SSRF) in webapp-manager favicon downloader script (common.py)
Description
Summary
The helper script /usr/lib/webapp-manager/common.py in the WebApp Manager package is vulnerable to Server-Side Request Forgery (SSRF) when executed directly from the command line.
The script accepts an arbitrary URL via sys.argv[1] and performs HTTP requests to that URL and derived paths using the requests library. No validation is performed to prevent requests to internal network resources, localhost, or private IP ranges.
This allows an attacker to trick a user into executing the script with a malicious URL, resulting in unintended server-side HTTP requests to internal services.
The vulnerability is present in the current version of WebApp Manager shipped with Linux Mint Cinnamon editions.
Affected Component
- File:
/usr/lib/webapp-manager/common.py - Entry point:
if __name__ == "__main__": download_favicon(sys.argv[1]) - Dependency:
requestslibrary
Vulnerability Details
The script is designed as a utility to download the best available favicon for a given website URL. When run directly, it accepts a single command-line argument (the target URL) and proceeds as follows:
-
URL Normalization
url = normalize_url(url) (scheme, netloc, path, _, _, _) = urllib.parse.urlparse(url) root_url = "%s://%s" % (scheme, netloc)
The normalization extracts scheme and netloc but does not restrict allowed hosts or schemes beyond basic parsing.
-
Primary Request
The script performs a GET request to the provided URL:response = requests.get(url, timeout=3)
-
HTML Parsing and Link Extraction
If the response is successful, the HTML is parsed with BeautifulSoup to extract various favicon-related links (rel="icon",apple-touch-icon, etc.). -
Derived Requests
For each discovered link, the script constructs a new URL (relative links are resolved againstroot_url) and performs additional GET requests:response = requests.get(link, timeout=3)
-
Fallback Mechanisms
The script also attempts/favicon.icoand a Google favicon API endpoint derived from the original URL.
All these requests are made without any restrictions on destination hosts or ports.
Proof of Concept
The vulnerability can be demonstrated with the following commands on a standard Linux Mint Cinnamon installation:
-
Attempt connection to localhost on closed port
/usr/bin/python3 /usr/lib/webapp-manager/common.py http://127.0.0.1:9999
Output:
HTTPConnectionPool(host='127.0.0.1', port=9999): Max retries exceeded with url: / (Caused by NewConnectionError(... Connection refused))→ Confirms attempt to connect to localhost on arbitrary port.
-
Local HTTP server verification
Start a local server:python3 -m http.server 8000
Then execute:
/usr/bin/python3 /usr/lib/webapp-manager/common.py http://127.0.0.1:8000
Server log:
127.0.0.1 - - [07/Jan/2026 17:44:35] "GET / HTTP/1.1" 200 - 127.0.0.1 - - [07/Jan/2026 17:44:35] "GET /favicon.ico HTTP/1.1" 404 -→ Confirms successful outbound requests to localhost.
-
Internal network target
/usr/bin/python3 /usr/lib/webapp-manager/common.py http://192.168.1.1
Output:
HTTPConnectionPool(host='192.168.1.1', port=80): Max retries exceeded with url: / (Caused by ConnectTimeoutError(...))→ Confirms attempt to contact private network device (common router gateway).
Impact
An attacker could exploit this vulnerability to:
- Perform port scanning on the local machine and local network
- Access internal web interfaces (e.g., router administration panels, local development servers)
- Interact with internal APIs
- In cloud environments, potentially access instance metadata services (http://169.254.169.254)
The attack requires the user to execute the script with a malicious URL (social engineering vector).
Recommended Mitigation
Implement host validation at the beginning of the CLI execution block:
import ipaddress
def is_forbidden_host(url):
try:
parsed = urllib.parse.urlparse(url)
hostname = parsed.hostname
if hostname in ['localhost', '127.0.0.1', '::1']:
return True
if hostname:
ip = ipaddress.ip_address(hostname)
return ip.is_private or ip.is_loopback or ip.is_multicast or ip.is_reserved
except Exception:
pass
return False
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: common.py <url>")
sys.exit(1)
url = sys.argv[1]
if is_forbidden_host(url):
print("Error: Requests to private/local addresses are not allowed")
sys.exit(1)
download_favicon(url)Alternative minimal approach: disable direct execution entirely:
if __name__ == "__main__":
print("This script is intended for internal use only")
sys.exit(1)Conclusion
While the vulnerable code path is not exercised during normal GUI usage of WebApp Manager, the ability to execute the script directly with arbitrary URLs constitutes a valid Server-Side Request Forgery vulnerability.
No evidence of in-the-wild exploitation has been observed.
This report is submitted responsibly to improve the security posture of a widely-used Linux Mint component.
Thank you