CVE-2026-7051

Blog2Social: Social Media Auto Post & Scheduler <= 8.9.0 - Missing Authorization to Authenticated (Subscriber+) Delete Arbitrary B2S Post Records via 'postId' Parameter

mediumMissing Authorization
5.4
CVSS Score
5.4
CVSS Score
medium
Severity
8.9.1
Patched in
1d
Time to patch

Description

The Blog2Social: Social Media Auto Post & Scheduler plugin for WordPress is vulnerable to Missing Authorization in all versions up to, and including, 8.9.0. This is due to a missing ownership verification in the B2S_Post_Tools::deleteUserPublishPost() and B2S_Post_Tools::deleteUserSchedPost() functions, neither function includes a blog_user_id constraint in its database query, allowing authenticated attackers to soft-delete any user's B2S post records by supplying arbitrary sequential wp_b2s_posts.id values via the 'postId' parameter. This makes it possible for authenticated attackers to delete other users' published and scheduled social media post records, disrupting content publishing workflows.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=8.9.0
PublishedMay 12, 2026
Last updatedMay 13, 2026
Affected pluginblog2social

What Changed in the Fix

Changes introduced in v8.9.1

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-7051 (Blog2Social IDOR) ## 1. Vulnerability Summary The **Blog2Social** plugin (up to 8.9.0) contains an Insecure Direct Object Reference (IDOR) vulnerability in its post-deletion functions. The functions `B2S_Post_Tools::deleteUserPublishPost()` and `B2S_Post…

Show full research plan

Exploitation Research Plan: CVE-2026-7051 (Blog2Social IDOR)

1. Vulnerability Summary

The Blog2Social plugin (up to 8.9.0) contains an Insecure Direct Object Reference (IDOR) vulnerability in its post-deletion functions. The functions B2S_Post_Tools::deleteUserPublishPost() and B2S_Post_Tools::deleteUserSchedPost() handle the deletion (soft-deletion via a hide flag) of social media post records stored in the wp_b2s_posts table. These functions fail to verify if the requesting user owns the record being deleted, allowing any authenticated user (Subscriber level and above) to delete arbitrary records by providing their numeric ID.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Actions:
    • b2s_delete_user_publish_post (for published posts)
    • b2s_delete_user_sched_post (for scheduled posts)
  • HTTP Method: POST
  • Payload Parameter: postId (likely an array or single integer, handled in B2S_Post_Tools as an array)
  • Authentication: Authenticated (Subscriber+)
  • Preconditions:
    • The attacker must have a valid session.
    • A valid b2s_security_nonce must be obtained.
    • The target wp_b2s_posts.id must be known or guessed (sequential).

3. Code Flow

  1. Entry Point: A POST request is sent to admin-ajax.php with action=b2s_delete_user_publish_post.
  2. AJAX Routing: includes/Ajax/Post.php registers the action:
    add_action('wp_ajax_b2s_delete_user_publish_post', array($this, 'deleteUserPublishPost'));
    
  3. AJAX Handler: Ajax_Post::deleteUserPublishPost() (wrapper) extracts the postId and calls the logic tool.
  4. Vulnerable Logic (Sink): includes/B2S/Post/Tools.php:
    public static function deleteUserPublishPost($postIds = array()) {
        global $wpdb;
        // ...
        foreach ($postIds as $v) {
            // VULNERABLE: No blog_user_id check in this query
            $row = $wpdb->get_row($wpdb->prepare("SELECT id,v2_id,post_id FROM {$wpdb->prefix}b2s_posts WHERE id =%d", (int) $v));
            if (isset($row->id) && (int) $row->id == $v) {
                // Soft delete by setting 'hide' to 1
                $wpdb->update($wpdb->prefix.'b2s_posts', array('hook_action' => $hook_action, 'hide' => 1), array('id' => $v));
                // ...
            }
        }
    }
    
    The query only checks if the record exists by ID. It does not verify the blog_user_id column against get_current_user_id().

4. Nonce Acquisition Strategy

The plugin uses a global nonce b2s_security_nonce. This nonce is frequently localized for use in the admin dashboard and Blog2Social-specific pages.

  1. Check Availability: The nonce is generated for the action string 'b2s_security_nonce'.
  2. Identification: In assets/js/b2s/general.js, the nonce is accessed via jQuery('#b2s_security_nonce').val().
  3. Strategy:
    • Log in as a Subscriber.
    • Navigate to the WordPress Dashboard (/wp-admin/index.php) or any B2S page if accessible.
    • If the plugin enqueues its scripts for all authenticated users, the nonce will be in the HTML.
    • Use browser_eval to search for the hidden input:
      browser_eval("document.getElementById('b2s_security_nonce')?.value")
    • Alternatively, check for localized objects in the page source like b2s_security_nonce.

5. Exploitation Strategy

Step 1: Discover Target ID

Since IDs in wp_b2s_posts are sequential, an attacker can brute-force IDs. For a PoC, we will create a post as Admin and identify its ID in the database.

Step 2: Request Deletion

Send the following request using the http_request tool:

Request Details:

  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Method: POST
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body:
    action=b2s_delete_user_publish_post&postId[]=TARGET_ID&b2s_security_nonce=VALID_NONCE
    
    (Note: If postId[] fails, try postId=TARGET_ID as the description implies a single parameter, but the code handles an array).

6. Test Data Setup

  1. Admin User: Log in as Admin.
  2. Create Post: Create a standard WordPress post.
  3. Blog2Social Action: Use the Blog2Social "Site Sharing" feature to "Share on Networks". This will create an entry in the wp_b2s_posts table.
  4. Identify ID:
    wp db query "SELECT id FROM wp_b2s_posts ORDER BY id DESC LIMIT 1;"
    
  5. Subscriber User: Create a user with the subscriber role.

7. Expected Results

  • Response: The server should return a JSON response: {"result": true, "postId": [TARGET_ID], ...}.
  • Database Change: The record in wp_b2s_posts with id = TARGET_ID should now have the hide column set to 1.

8. Verification Steps

After the exploit request, use WP-CLI to verify the "soft delete" state:

wp db query "SELECT id, hide FROM wp_b2s_posts WHERE id = TARGET_ID;"

If successful, the output will show hide as 1.

9. Alternative Approaches

If b2s_delete_user_publish_post is restricted by capability checks in the AJAX wrapper (not visible in truncated source), try the scheduled post endpoint:

  • Action: b2s_delete_user_sched_post
  • Constraint: The record must have publish_date = '0000-00-00 00:00:00' to pass the SELECT query in deleteUserSchedPost.

If the Subscriber cannot see the nonce on the default dashboard, navigate to:

  • /wp-admin/admin.php?page=blog2social (if permitted)
  • Or inspect the Profile page to see if scripts are loaded.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Blog2Social plugin for WordPress is vulnerable to an Insecure Direct Object Reference (IDOR) flaw due to missing ownership verification in its post-deletion functions. Authenticated attackers with Subscriber-level permissions can soft-delete arbitrary published or scheduled social media post records by providing sequential record IDs via AJAX endpoints, disrupting other users' content workflows.

Vulnerable Code

// includes/B2S/Post/Tools.php:27 (deleteUserSchedPost)
$row = $wpdb->get_row($wpdb->prepare("SELECT b.id,b.post_id,b.post_for_relay,b.post_for_approve,b.sched_details_id,d.network_id,d.network_type FROM {$wpdb->prefix}b2s_posts b LEFT JOIN {$wpdb->prefix}b2s_posts_network_details d ON (d.id = b.network_details_id) WHERE b.id =%d AND b.publish_date = %s", (int) $v, "0000-00-00 00:00:00"));

---

// includes/B2S/Post/Tools.php:118 (deleteUserPublishPost)
public static function deleteUserPublishPost($postIds = array()) {
    global $wpdb;
    $resultPostIds = array();
    $blogPostId = 0;
    $count = 0;
    foreach ($postIds as $v) {
        $row = $wpdb->get_row($wpdb->prepare("SELECT id,v2_id,post_id FROM {$wpdb->prefix}b2s_posts WHERE id =%d", (int) $v));
        if (isset($row->id) && (int) $row->id == $v) {
            $hook_action = (isset($row->v2_id) && (int) $row->v2_id > 0) ? 0 : 4; //oldItems
            $wpdb->update($wpdb->prefix.'b2s_posts', array('hook_action' => $hook_action, 'hide' => 1), array('id' => $v));

Security Fix

--- /home/deploy/wp-safety.org/data/plugin-versions/blog2social/8.9.0/includes/Ajax/Get.php	2026-04-14 11:54:18.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/blog2social/8.9.1/includes/Ajax/Get.php	2026-05-05 12:19:12.000000000 +0000
@@ -54,7 +54,7 @@
 
     public function getBlogPostStatus() {
 
-        if (!current_user_can('read') || !check_ajax_referer('b2s_security_nonce', 'b2s_security_nonce', false)) {
+        if (!current_user_can('edit_posts') || !check_ajax_referer('b2s_security_nonce', 'b2s_security_nonce', false)) {
             echo wp_json_encode(array('result' => false, 'error' => 'nonce'));
             wp_die();
         }
@@ -70,7 +70,7 @@
 
     public function scrapeUrl() {
 
-        if (!current_user_can('read') || !check_ajax_referer('b2s_security_nonce', 'b2s_security_nonce', false)) {
+        if (!current_user_can('edit_posts') || !check_ajax_referer('b2s_security_nonce', 'b2s_security_nonce', false)) {
             echo wp_json_encode(array('result' => false, 'error' => 'nonce'));
             wp_die();
         }
... (truncated)

Exploit Outline

To exploit this vulnerability, an attacker must have an authenticated session (Subscriber level or higher). First, the attacker retrieves a valid 'b2s_security_nonce', which is commonly available in the admin dashboard source code. Next, the attacker sends a POST request to '/wp-admin/admin-ajax.php' with the 'action' parameter set to 'b2s_delete_user_publish_post' (for published records) or 'b2s_delete_user_sched_post' (for scheduled records). The payload must include 'postId[]' containing the target record's numeric ID. Because the underlying SQL queries in B2S_Post_Tools do not verify that the record's 'blog_user_id' matches the current user's ID, the plugin will proceed to mark the record as hidden ('hide' => 1) in the database regardless of ownership.

Check if your site is affected.

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