Zero-Day
8 min read
March 17, 2026

When an int Becomes a Malicious Script. Reflected XSS in CVAT (CVE-2024-47064)

When an int Becomes a Malicious Script. Reflected XSS in CVAT (CVE-2024-47064)

Sometimes developers forget that if something looks like a number and should be a number, it won’t always be one once it lands in a pentester’s hands. During another penetration test of the powerful data-annotation tool Computer Vision Annotation Tool (CVAT), I came across a classic but very dangerous Reflected Cross-Site Scripting (XSS) flaw.

The bug I found earned the official identifier CVE-2024-47064 and a GitHub security advisory (GHSA-hp6c-f34j-qjj7). The vulnerability was classified as High with a score of 8.1 (vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:N). Let’s see how the lack of strict input type validation and proper output encoding allowed injecting malicious JavaScript directly into the victim’s browser.

TL;DR - Key takeaways

  • What Went Wrong? (Endpoints and Assumptions)
  • Proof of Concept
  • What Is the Security Impact?
  • How to Fix It? (Remediation)

What Went Wrong? (Endpoints and Assumptions)

CVAT (vulnerable versions include Server: 2.16.3, Core: 15.1.1, Canvas: 2.20.8, UI: 1.64.51) has API endpoints for managing Requests. In my tests I focused on two specific addresses:

A standard request uses a numeric identifier (an int) in place of <ID>. If we try to query the server for a nonexistent request (e.g. ID 2137), the server politely returns an error in HTML format:

  • /api/requests/<ID> (GET and POST methods)
  • /api/requests/<ID>/cancel (POST method)
"There is no request with specified id: 2137"
GET/api/requests/2137
Server response

"There is no request with specified id: 2137"

Harmless: an int in the response
GET/api/requests/<script>alert(1)</script>
Server response

"There is no request with specified id: <script>alert(1)</script>"

Reflected XSS: code reflected without encoding!

Proof of Concept

Seems harmless, right? But what if, instead of a number, we enter an arbitrary string? It turns out the application accepts it without flinching and reflects it directly in the response, without filtering HTML special characters!

Exploiting the fact that our string is reflected on the page, we can attempt to inject HTML/JavaScript. Instead of a number, we place a malicious payload in the URL path (properly URL-encoded so the server interprets it correctly):

GET /api/requests/%3Cimg%20src=x%20onerror=print()%3E HTTP/1.1 Host: app.cvat.ai

The decoded value is simply: <img src=x onerror=print()>. What happens on the browser side?

The browser tries to render an image from the source x. Since no such image exists, the onerror event fires and obediently executes the print() function, popping up the system print dialog on the victim’s screen.

I achieved the same on the /cancel endpoint, using a payload that triggers a classic alert box on a mouseover event:

Reflected XSS — why does it work?

The server does not encode the output. Whatever we place in the URL as the ID parameter is pasted directly into the HTML of the response. The browser treats it as valid HTML and obediently executes the embedded script.

Attacker
Phishing link
Victim clicks
JS executed!
Endpoint #1: /api/requests/<ID>
GET /api/requests/%3Cimg%20src=x%20onerror=print()%3E

Decoded: <img src=x onerror=print()>

Effect: the browser executes print() - the print dialog

Endpoint #2: /api/requests/<ID>/cancel
POST /api/requests/<IMG SRC=x onmouseover="alert('xss')">/cancel

Event: onmouseover

Effect: alert('xss') on mouseover

Cross-Site Scripting — XSS attack visualization
Reflected XSS — the malicious payload is reflected in the server response and executed in the victim’s browser.

What Is the Security Impact?

Although this is a Reflected XSS (requiring user interaction — the UI:R parameter in the CVSS vector, e.g. clicking a crafted link sent in a phishing email or messenger), its consequences are serious. That is also why the vector indicates high confidentiality (C:H) and integrity (I:H).

When an authenticated user (e.g. a project administrator) clicks such a link, the malicious script runs in the context of their trusted CVAT session. In this way an attacker can:

  • Hijack session tokens or cookies — full account compromise.
  • Perform actions with the victim’s privileges (e.g. modify annotations, add malicious accounts, delete projects).
  • Redirect the victim to data-harvesting (phishing) pages.

CVSS 8.1, High

Despite the user-interaction requirement, the lack of required privileges (PR:N) and the high impact on confidentiality and integrity earned this vulnerability a score of 8.1 (High). A phishing link is all the attacker needs.

CVE-2024-47064
8.1High
Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:N
Exploitability Metrics
Attack Vector (AV)
Network (N)Adjacent (A)Local (L)Physical (P)
Attack Complexity (AC)
Low (L)High (H)
Privileges Required (PR)
None (N)Low (L)High (H)
User Interaction (UI)
None (N)Required (R)
Scope (S)
Unchanged (U)Changed (C)
Impact Metrics
Confidentiality Impact (C)
None (N)Low (L)High (H)
Integrity Impact (I)
None (N)Low (L)High (H)
Availability Impact (A)
None (N)Low (L)High (H)

How to Fix It? (Remediation)

This bug is a reminder of how important two layers of defense against XSS are:

  • Strict Input Validation: The server should check right at the start whether the supplied <ID> parameter is really an integer. If it is not, the request should be rejected immediately (returning, say, a 400 Bad Request), without processing its contents. It is always better to use whitelists of allowed data types.
  • Context-Aware Output Encoding: This is the most important line of defense. Any user-supplied data, before being displayed in an HTTP response (an HTML context), must be properly encoded. HTML control characters must be converted to the appropriate entities: <&lt;, >&gt;.

The Developer’s Golden Rule

When you send the user’s browser text that included parts of their own requests (e.g. error messages, search results), ALWAYS encode the output. The browser is a trusting beast — it will execute any code the server presents as valid HTML.

Vulnerable
No input validation
ID = <script>alert(1)</script>
Accepted as valid
No output encoding
Response: "...id: <script>alert(1)</script>"
The browser executes the script!
Secure
Input validation (whitelisting)
ID = <script>...</script>
400 Bad Request: not an int!
Output encoding (HTML entities)
Response: "...id: &lt;script&gt;...&lt;/script&gt;"
Displayed as text, not code!
Browser security — protection against XSS
Input validation and output encoding — the two key layers of defense against XSS attacks.

Summary

CVE-2024-47064 is a classic example of a Reflected XSS vulnerability, where a lack of input type validation and output encoding allowed injecting malicious JavaScript into the victim’s browser. The flaw in CVAT was rated CVSS 8.1 (High) due to the lack of required privileges and the high impact on confidentiality and integrity. It was responsibly disclosed and patched, but it remains an excellent lesson: never trust user input, and always encode your output.

Bibliography

1
CVE-2024-47064 - NIST National Vulnerability Database

The official NIST NVD entry describing the Reflected XSS vulnerability in CVAT, rated CVSS 8.1.

2
GHSA-hp6c-f34j-qjj7 - GitHub Security Advisory

The security advisory published on GitHub by the CVAT team, containing details of the XSS vulnerability and information about the fix.

3
OWASP - Cross Site Scripting (XSS)

The OWASP compendium on Cross-Site Scripting attacks — types, vectors, defense mechanisms, and examples.

4
OWASP Top 10:2021 - A03 Injection

The OWASP Top 10 category covering injection attacks, including Cross-Site Scripting (XSS), as one of the most common web application security threats.

5
CVAT - Computer Vision Annotation Tool (GitHub)

The open-source repository of the CVAT data-annotation tool for machine learning and Computer Vision projects.

Does your application reflect things it shouldn’t?

Reflected XSS is one of the most common web vulnerabilities. Our pentesters test every input parameter, every endpoint, and every injection vector. Don’t wait for someone else to find the XSS in your application.

Order a penetration test