[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fZVMhvUFsdObtKbbRWP9a7FIPIIbK1rcuXXpc6qlURHI":3},{"id":4,"url_slug":5,"title":6,"description":7,"plugin_slug":8,"theme_slug":9,"affected_versions":10,"patched_in_version":11,"severity":12,"cvss_score":13,"cvss_vector":14,"vuln_type":15,"published_date":16,"updated_date":17,"references":18,"days_to_patch":20,"patch_diff_files":21,"patch_trac_url":9,"research_status":27,"research_verified":28,"research_rounds_completed":29,"research_plan":30,"research_summary":31,"research_vulnerable_code":32,"research_fix_diff":33,"research_exploit_outline":34,"research_model_used":35,"research_started_at":36,"research_completed_at":37,"research_error":9,"poc_status":9,"poc_video_id":9,"poc_summary":9,"poc_steps":9,"poc_tested_at":9,"poc_wp_version":9,"poc_php_version":9,"poc_playwright_script":9,"poc_exploit_code":9,"poc_has_trace":28,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":28,"source_links":38},"CVE-2026-4338","activitypub-unauthenticated-information-epxosure","ActivityPub \u003C 8.0.2 - Unauthenticated Information Epxosure","The ActivityPub plugin for WordPress is vulnerable to Sensitive Information Exposure in all versions up to 8.0.2 (exclusive). This makes it possible for unauthenticated attackers to extract sensitive user or configuration data.","activitypub",null,"\u003C8.0.2","8.0.2","high",7.5,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:N\u002FUI:N\u002FS:U\u002FC:H\u002FI:N\u002FA:N","Exposure of Sensitive Information to an Unauthorized Actor","2026-03-18 00:00:00","2026-04-15 18:59:57",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F35a55336-2cd1-41a8-b9ef-09ab4a8dd632?source=api-prod",29,[22,23,24,25,26],"activitypub.php","includes\u002Ffunctions-post.php","includes\u002Fscheduler\u002Fclass-post.php","includes\u002Ftransformer\u002Fclass-post.php","readme.txt","researched",false,3,"# Exploitation Research Plan: CVE-2026-4338 (ActivityPub Information Exposure)\n\n## 1. Vulnerability Summary\nThe ActivityPub plugin for WordPress is vulnerable to **Unauthenticated Sensitive Information Exposure** in versions up to 8.0.2. The vulnerability exists within the plugin's custom REST API endpoints (registered in `activitypub.php`). Specifically, the controllers responsible for serving ActivityPub objects (like `Rest\\Post_Controller` and `Rest\\Outbox_Controller`) fail to adequately verify post visibility, status, or user permissions before transforming and serving data. This allows an unauthenticated attacker to retrieve content from private posts, password-protected posts, or restricted user profiles by querying the ActivityPub REST API namespace.\n\n## 2. Attack Vector Analysis\n- **Endpoints:** \n    - Single Post: `\u002Fwp-json\u002Factivitypub\u002F1.0\u002Fposts\u002F\u003Cid>`\n    - Actor Outbox: `\u002Fwp-json\u002Factivitypub\u002F1.0\u002Factors\u002F\u003Cuser_id>\u002Foutbox`\n    - Actor Profile: `\u002Fwp-json\u002Factivitypub\u002F1.0\u002Factors\u002F\u003Cuser_id>`\n- **HTTP Method:** `GET`\n- **Authentication:** Unauthenticated (No cookies or credentials required).\n- **Payload:** No specific payload is required beyond the ID of the sensitive resource.\n- **Preconditions:** The ActivityPub plugin must be active. A post must exist that is set to `private` or has a password.\n\n## 3. Code Flow\n1. **Entry Point:** An unauthenticated `GET` request is made to a route registered in `activitypub.php` via `rest_init()`, such as `Rest\\Post_Controller`.\n2. **Route Handling:** The `Rest\\Post_Controller::get_item()` method (inferred) is invoked.\n3. **Data Retrieval:** The controller fetches the post using `get_post($id)`. \n4. **Vulnerable Sink:** The controller fails to call `is_post_disabled()` (from `includes\u002Ffunctions-post.php`) or check `current_user_can('read_post', $post_id)`.\n5. **Transformation:** The post object is passed to `Activitypub\\Transformer\\Post::to_object()` (in `includes\u002Ftransformer\u002Fclass-post.php`).\n6. **Output:** The transformer converts the full post content (including private content) into an ActivityPub `Note` or `Article` JSON object, which is then returned to the unauthenticated requester.\n\n## 4. Nonce Acquisition Strategy\nThis vulnerability involves **unauthenticated information exposure** via `GET` requests to REST API endpoints.\n- **Nonce Requirement:** In WordPress, REST API `GET` routes typically do not require a nonce unless they are specifically protected by a `permission_callback` that checks for one. \n- **Bypass:** Since the vulnerability is categorized as unauthenticated exposure, the `permission_callback` is either missing, returns `true`, or fails to check the user's authorization to view the specific object.\n- **Verification:** If an initial request returns a 401\u002F403 error, the `Rest\\Post_Controller` may be checked for its `permission_callback` implementation. However, for \"discovery\" endpoints in ActivityPub, nonces are rarely used.\n\n## 5. Exploitation Strategy\n\n### Step 1: Discover Post IDs\nIdentify IDs of posts on the target site. Standard WordPress behavior often leaks these via `\u002Fwp-json\u002Fwp\u002Fv2\u002Fposts` for public posts, but for private posts, an attacker may simply brute-force IDs (e.g., 1 to 100).\n\n### Step 2: Extract Private Post Content\nQuery the ActivityPub post endpoint for a known private post.\n- **Request:**\n  ```http\n  GET \u002Fwp-json\u002Factivitypub\u002F1.0\u002Fposts\u002F\u003CPRIVATE_POST_ID> HTTP\u002F1.1\n  Host: TARGET_HOST\n  Accept: application\u002Factivity+json\n  ```\n\n### Step 3: Extract Password-Protected Content\nQuery the ActivityPub post endpoint for a post that is password-protected.\n- **Request:**\n  ```http\n  GET \u002Fwp-json\u002Factivitypub\u002F1.0\u002Fposts\u002F\u003CPASSWORD_POST_ID> HTTP\u002F1.1\n  Host: TARGET_HOST\n  Accept: application\u002Factivity+json\n  ```\n\n### Step 4: Extract Outbox History\nQuery the author's outbox to see a timeline of activities, which may include summaries or links to content otherwise hidden.\n- **Request:**\n  ```http\n  GET \u002Fwp-json\u002Factivitypub\u002F1.0\u002Factors\u002F\u003CUSER_ID>\u002Foutbox HTTP\u002F1.1\n  Host: TARGET_HOST\n  Accept: application\u002Factivity+json\n  ```\n\n## 6. Test Data Setup\nTo verify the vulnerability, the following data must be prepared in the test environment using WP-CLI:\n\n1. **Create a Private Post:**\n   ```bash\n   wp post create --post_type=post --post_title=\"Sensitive Internal Memo\" --post_content=\"This is secret content only for admins.\" --post_status=private --post_author=1\n   ```\n   *Note the resulting ID (e.g., ID 5).*\n\n2. **Create a Password-Protected Post:**\n   ```bash\n   wp post create --post_type=post --post_title=\"Protected Strategy\" --post_content=\"The password is required to see this strategy.\" --post_status=publish --post_password=\"password123\" --post_author=1\n   ```\n   *Note the resulting ID (e.g., ID 6).*\n\n3. **Ensure Plugin is Configured:**\n   Enable the ActivityPub plugin if not already active.\n   ```bash\n   wp plugin activate activitypub\n   ```\n\n## 7. Expected Results\n- **Success:** The REST API response for the private post (ID 5) returns a JSON object with `type: \"Note\"` or `type: \"Article\"` containing the string `\"This is secret content only for admins.\"` in the `content` field.\n- **Success:** The REST API response for the password-protected post (ID 6) returns the post content without requiring the password.\n- **Failure (Patched):** The REST API returns a `403 Forbidden`, `401 Unauthorized`, or a `404 Not Found` error, or the JSON object contains a generic \"Private\" message instead of the content.\n\n## 8. Verification Steps\n1. **Direct HTTP Check:** Use the `http_request` tool to perform the `GET` requests described in Section 5.\n2. **Content Verification:** Parse the JSON response body and search for the specific strings created in the Test Data Setup.\n3. **Log Check:** Check the WordPress error logs or access logs to confirm the requests were handled by the ActivityPub REST handlers.\n\n## 9. Alternative Approaches\n- **Content Negotiation:** If the `\u002Fwp-json\u002Factivitypub\u002F1.0\u002Fposts\u002F` route is not directly accessible, try requesting the standard permalink of a private post with the header `Accept: application\u002Factivity+json`. The plugin's `includes\u002Ffunctions-request.php` logic might hijack the request and serve the JSON representation.\n- **User Discovery:** Query `\u002Fwp-json\u002Factivitypub\u002F1.0\u002Factors\u002F1` to see if sensitive metadata (like the user's slug or internal IDs) is exposed for users who have not opted into federation.","The ActivityPub plugin for WordPress exposes sensitive content from non-public posts (drafts, pending, scheduled, or private) via its custom REST API endpoints. Unauthenticated attackers can bypass standard WordPress visibility restrictions and password protections by accessing the ActivityPub JSON representation of restricted resources.","\u002F\u002F includes\u002Ffunctions-post.php lines 36-42\n\tif (\n\t\t$is_local_or_private ||\n\t\t! \\post_type_supports( $post->post_type, 'activitypub' ) ||\n\t\t'private' === $post->post_status ||\n\t\t! empty( $post->post_password )\n\t) {\n\t\t$disabled = true;\n\t}\n\n---\n\n\u002F\u002F includes\u002Ftransformer\u002Fclass-post.php line 596\n\t\t\u002F\u002F Remove Content from drafts.\n\t\tif ( ! $this->is_preview() && 'draft' === \\get_post_status( $this->item ) ) {\n\t\t\t$this->content = \\__( '(This post is being modified)', 'activitypub' );\n\n\t\t\treturn $this->content;\n\t\t}","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Factivitypub\u002F8.0.1\u002Fincludes\u002Ffunctions-post.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Factivitypub\u002F8.0.2\u002Fincludes\u002Ffunctions-post.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Factivitypub\u002F8.0.1\u002Fincludes\u002Ffunctions-post.php\t2026-02-05 09:09:42.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Factivitypub\u002F8.0.2\u002Fincludes\u002Ffunctions-post.php\t2026-03-17 11:34:48.000000000 +0000\n@@ -29,10 +29,14 @@\n \t$visibility          = \\get_post_meta( $post->ID, 'activitypub_content_visibility', true );\n \t$is_local_or_private = in_array( $visibility, array( ACTIVITYPUB_CONTENT_VISIBILITY_LOCAL, ACTIVITYPUB_CONTENT_VISIBILITY_PRIVATE ), true );\n \n+\t\u002F\u002F Only 'publish' is public. 'inherit' is allowed only for attachments.\n+\t$is_public_status = 'publish' === $post->post_status ||\n+\t\t( 'inherit' === $post->post_status && 'attachment' === $post->post_type );\n+\n \tif (\n \t\t$is_local_or_private ||\n \t\t! \\post_type_supports( $post->post_type, 'activitypub' ) ||\n-\t\t'private' === $post->post_status ||\n+\t\t! $is_public_status ||\n \t\t! empty( $post->post_password )\n \t) {\n \t\t$disabled = true;\n@@ -40,14 +44,17 @@\n \n \t\u002F*\n \t * Check for posts that need special handling.\n-\t * Federated posts changed to local\u002Fprivate need Delete activity.\n+\t * Federated posts changed to local\u002Fprivate or non-public status need Delete activity.\n \t * Deleted posts restored to public need Create activity.\n \t *\u002F\n \t$object_state = get_wp_object_state( $post );\n \n \tif (\n \t\tACTIVITYPUB_OBJECT_STATE_DELETED === $object_state ||\n-\t\t( $is_local_or_private && ACTIVITYPUB_OBJECT_STATE_FEDERATED === $object_state )\n+\t\t(\n+\t\t\tACTIVITYPUB_OBJECT_STATE_FEDERATED === $object_state &&\n+\t\t\t( $is_local_or_private || ! $is_public_status )\n+\t\t)\n \t) {\n \t\t$disabled = false;\n \t}\n\ndiff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Factivitypub\u002F8.0.1\u002Fincludes\u002Ftransformer\u002Fclass-post.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Factivitypub\u002F8.0.2\u002Fincludes\u002Ftransformer\u002Fclass-post.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Factivitypub\u002F8.0.1\u002Fincludes\u002Ftransformer\u002Fclass-post.php\t2026-03-05 08:08:28.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Factivitypub\u002F8.0.2\u002Fincludes\u002Ftransformer\u002Fclass-post.php\t2026-03-17 11:34:48.000000000 +0000\n@@ -359,10 +359,10 @@\n \t\t}\n \n \t\t\u002F*\n-\t\t * Remove attachments from the Fediverse if a post was federated and then set back to draft.\n+\t\t * Remove attachments from the Fediverse if a post was federated and then unpublished.\n \t\t * Except in preview mode, where we want to show attachments.\n \t\t *\u002F\n-\t\tif ( ! $this->is_preview() && 'draft' === \\get_post_status( $this->item ) ) {\n+\t\tif ( ! $this->is_preview() && 'publish' !== \\get_post_status( $this->item ) ) {\n \t\t\t$this->attachment = array();\n \n \t\t\treturn $this->attachment;\n@@ -543,8 +543,8 @@\n \t\t\treturn $this->summary;\n \t\t}\n \n-\t\t\u002F\u002F Remove Teaser from drafts.\n-\t\tif ( ! $this->is_preview() && 'draft' === \\get_post_status( $this->item ) ) {\n+\t\t\u002F\u002F Remove Teaser from unpublished posts.\n+\t\tif ( ! $this->is_preview() && 'publish' !== \\get_post_status( $this->item ) ) {\n \t\t\t$this->summary = \\__( '(This post is being modified)', 'activitypub' );\n \n \t\t\treturn $this->summary;\n@@ -593,8 +593,8 @@\n \t\t\treturn $this->content;\n \t\t}\n \n-\t\t\u002F\u002F Remove Content from drafts.\n-\t\tif ( ! $this->is_preview() && 'draft' === \\get_post_status( $this->item ) ) {\n+\t\t\u002F\u002F Remove Content from unpublished posts.\n+\t\tif ( ! $this->is_preview() && 'publish' !== \\get_post_status( $this->item ) ) {\n \t\t\t$this->content = \\__( '(This post is being modified)', 'activitypub' );\n \n \t\t\treturn $this->content;","The exploit targets the ActivityPub REST API namespace (default: `\u002Fwp-json\u002Factivitypub\u002F1.0\u002F`). An unauthenticated attacker sends a GET request to either the post endpoint (`\u002Fposts\u002F\u003CID>`) or the user's outbox endpoint (`\u002Factors\u002F\u003CUSER_ID>\u002Foutbox`). Because the plugin's internal visibility check only explicitly blocked 'private' status and 'draft' content in the transformer, posts with other non-public statuses (like 'pending' or 'future') or password-protected posts were served in full. By setting the `Accept` header to `application\u002Factivity+json`, the attacker receives the JSON representation of the sensitive post, including its full content and attachments.","gemini-3-flash-preview","2026-04-18 02:27:46","2026-04-18 02:28:23",{"type":39,"vulnerable_version":40,"fixed_version":11,"vulnerable_browse":41,"vulnerable_zip":42,"fixed_browse":43,"fixed_zip":44,"all_tags":45},"plugin","8.0.1","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Factivitypub\u002Ftags\u002F8.0.1","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Factivitypub.8.0.1.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Factivitypub\u002Ftags\u002F8.0.2","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Factivitypub.8.0.2.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Factivitypub\u002Ftags"]