Blog2Social: Social Media Auto Post & Scheduler <= 8.9.0 - Missing Authorization to Authenticated (Subscriber+) Delete Arbitrary B2S Post Records via 'postId' Parameter
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:LTechnical Details
What Changed in the Fix
Changes introduced in v8.9.1
Source Code
WordPress.org SVN# 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 inB2S_Post_Toolsas an array) - Authentication: Authenticated (Subscriber+)
- Preconditions:
- The attacker must have a valid session.
- A valid
b2s_security_noncemust be obtained. - The target
wp_b2s_posts.idmust be known or guessed (sequential).
3. Code Flow
- Entry Point: A POST request is sent to
admin-ajax.phpwithaction=b2s_delete_user_publish_post. - AJAX Routing:
includes/Ajax/Post.phpregisters the action:add_action('wp_ajax_b2s_delete_user_publish_post', array($this, 'deleteUserPublishPost')); - AJAX Handler:
Ajax_Post::deleteUserPublishPost()(wrapper) extracts thepostIdand calls the logic tool. - Vulnerable Logic (Sink):
includes/B2S/Post/Tools.php:
The query only checks if the record exists by ID. It does not verify thepublic 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)); // ... } } }blog_user_idcolumn againstget_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.
- Check Availability: The nonce is generated for the action string
'b2s_security_nonce'. - Identification: In
assets/js/b2s/general.js, the nonce is accessed viajQuery('#b2s_security_nonce').val(). - 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_evalto 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:
(Note: Ifaction=b2s_delete_user_publish_post&postId[]=TARGET_ID&b2s_security_nonce=VALID_NONCEpostId[]fails, trypostId=TARGET_IDas the description implies a single parameter, but the code handles an array).
6. Test Data Setup
- Admin User: Log in as Admin.
- Create Post: Create a standard WordPress post.
- Blog2Social Action: Use the Blog2Social "Site Sharing" feature to "Share on Networks". This will create an entry in the
wp_b2s_poststable. - Identify ID:
wp db query "SELECT id FROM wp_b2s_posts ORDER BY id DESC LIMIT 1;" - Subscriber User: Create a user with the
subscriberrole.
7. Expected Results
- Response: The server should return a JSON response:
{"result": true, "postId": [TARGET_ID], ...}. - Database Change: The record in
wp_b2s_postswithid = TARGET_IDshould now have thehidecolumn set to1.
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 theSELECTquery indeleteUserSchedPost.
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.
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
@@ -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.