Mar 26, 2026·6 min read·3 visits
Vikunja < 2.2.1 contains an SSRF flaw in its migration module due to unvalidated third-party URLs. Authenticated attackers can exploit this to read internal network data, such as cloud metadata services. The issue is resolved in version 2.2.2 via a centralized SSRF-safe HTTP client.
A Server-Side Request Forgery (SSRF) vulnerability in Vikunja versions prior to 2.2.1 allows authenticated users to exfiltrate internal network resources during task migration operations.
Vikunja is an open-source task management platform that includes features for migrating data from external services. The migration modules interface with third-party APIs to retrieve tasks, metadata, and file attachments. This functionality exposes a Server-Side Request Forgery (SSRF) attack surface if the URLs provided by the external API are not strictly validated.
The vulnerability, tracked as CVE-2026-33675, exists in the migration helper functions responsible for fetching remote file attachments. Versions of Vikunja prior to 2.2.1 fail to restrict the destination IP addresses during these fetch operations. An attacker with privileges to initiate a migration can exploit this flaw to coerce the Vikunja server into making arbitrary HTTP requests.
Successful exploitation results in the exfiltration of internal network data. The server processes the internal response and stores it as a task attachment, which the attacker can subsequently download. This allows an authenticated user to bypass external firewalls and access internal services, including cloud provider metadata endpoints.
The vulnerability stems from the implementation of the DownloadFile and DownloadFileWithHeaders functions located in pkg/modules/migration/helpers.go. These functions are tasked with retrieving remote assets specified by the third-party service during a data migration. The code utilizes the standard Go http.Client{} struct without any custom dialer or transport configuration.
The standard http.Client{} natively follows HTTP redirects and resolves domain names to IP addresses without restrictions. When a third-party API returns a URL, the application passes this URL directly to the http.Client.Do() method. The application performs no pre-flight checks on the hostname or the resolved IP address to verify if the destination is a public, routable endpoint.
Because the application implicitly trusts the URLs provided by the external service, it acts as a confused deputy. If the external service returns a URL pointing to a private IPv4 address (RFC 1918) or a non-routable metadata IP, the standard HTTP client initiates the connection. The response body is then read into a buffer and processed as a legitimate file attachment.
Prior to the patch, the DownloadFileWithHeaders function initialized a default HTTP client for every file download. The code lacked any mechanisms to intercept the connection process or validate the target destination. The vulnerability was explicitly ignored by a security linter comment, incorrectly assuming that URLs from a migration provider API were inherently safe.
func DownloadFileWithHeaders(url string, headers http.Header) (buf *bytes.Buffer, err error) {
// Request setup omitted
hc := http.Client{}
resp, err := hc.Do(req) // #nosec G704 -- URL is from migration provider API
// Response processing omitted
}The remediation introduced in commit 93297742236e3d33af72c993e5da960db01d259e replaces the default client with a specialized SSRF-safe alternative. The utils.NewSSRFSafeHTTPClient() function returns a client configured with a custom DialContext. This dialer hooks into the connection phase, resolving the hostname and verifying that the resulting IP address is not within restricted or private ranges.
func DownloadFileWithHeaders(url string, headers http.Header) (buf *bytes.Buffer, err error) {
// Request setup omitted
hc := utils.NewSSRFSafeHTTPClient()
resp, err := hc.Do(req)
// Response processing omitted
}Exploitation requires the attacker to possess a user account on the target Vikunja instance with permissions to initiate task migrations. The attacker must also control an account on a supported third-party migration provider, such as Todoist or Trello. The initial step involves creating a crafted task within the third-party platform.
The attacker configures the file attachment URL of the crafted task to point to an internal resource. A common target in cloud environments is the metadata service at http://169.254.169.254/latest/meta-data/. Once the malicious payload is staged on the external provider, the attacker instructs the Vikunja application to begin the migration process.
The Vikunja server parses the task data from the external provider and invokes the vulnerable download function on the attacker-supplied URL. The server issues an HTTP GET request to the internal IP address, receives the sensitive data, and saves it as an attachment to the newly migrated task. The attacker completes the exfiltration by downloading the attachment from the Vikunja user interface.
The vulnerability allows an authenticated attacker to read data from services located within the same network as the Vikunja application server. This compromises the confidentiality of internal resources that are intentionally isolated from the public internet. The CVSS v3.1 base score of 6.4 reflects the medium severity, driven by the requirement for authentication and the changed scope of the attack.
In cloud deployments, the impact is elevated if the server can reach instance metadata endpoints. An attacker can retrieve temporary IAM credentials, user data scripts, and configuration variables. Access to these credentials facilitates lateral movement and privilege escalation within the broader cloud environment.
For on-premises deployments, the attacker can target internal administrative interfaces, unauthenticated databases, or unpatched internal microservices. While the vulnerability does not directly grant arbitrary code execution on the Vikunja server, the exposure of internal infrastructure presents a significant risk to the organization.
The primary remediation strategy is to upgrade the Vikunja application to version 2.2.2 or later. While version 2.2.1 addressed the specific vulnerable functions in the migration module, version 2.2.2 implements a centralized SSRF protection mechanism. This comprehensive fix applies the SSRF-safe HTTP client to all outgoing requests, including webhooks and avatar fetches.
Administrators must review the application configuration after upgrading to ensure the protections are active. The 2.2.2 release introduces an outgoingrequests configuration block in config.yml. The OutgoingRequestsAllowNonRoutableIPs setting must remain configured to false in production environments.
Organizations should implement network segmentation as a defense-in-depth measure. The Vikunja application server should reside in an isolated network segment with strict egress firewall rules. Blocking access to the 169.254.169.254 IP address at the host firewall level prevents SSRF vulnerabilities from exposing cloud metadata.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:N/A:L| Product | Affected Versions | Fixed Version |
|---|---|---|
Vikunja go-vikunja | < 2.2.1 | 2.2.1 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-918 |
| Attack Vector | Network |
| CVSS v3.1 | 6.4 (Medium) |
| EPSS Score | 0.00034 (0.03%) |
| Impact | Information Disclosure, Internal Reconnaissance |
| Exploit Status | No known public exploits (None) |
| Authentication | Required (Low Privileges) |
The web application receives a URL or similar request from an upstream component and retrieves the contents of this URL, but it does not sufficiently ensure that the request is being sent to the expected destination.