Nelio Content <= 4.3.1 - Authenticated (Contributor+) Server-Side Request Forgery
Description
The Nelio Content – Editorial Calendar & Social Media Auto-Posting plugin for WordPress is vulnerable to Server-Side Request Forgery in all versions up to, and including, 4.3.1. This makes it possible for authenticated attackers, with Contributor-level access and above, to make web requests to arbitrary locations originating from the web application which can be used to query and modify information from internal services.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:NTechnical Details
What Changed in the Fix
Changes introduced in v4.3.2
Source Code
WordPress.org SVN# Research Plan: CVE-2026-39521 Nelio Content SSRF ## 1. Vulnerability Summary The **Nelio Content** plugin (up to version 4.3.1) contains an authenticated SSRF vulnerability. It fails to properly validate or sanitize the `url` parameter in its internal REST API endpoints responsible for fetching r…
Show full research plan
Research Plan: CVE-2026-39521 Nelio Content SSRF
1. Vulnerability Summary
The Nelio Content plugin (up to version 4.3.1) contains an authenticated SSRF vulnerability. It fails to properly validate or sanitize the url parameter in its internal REST API endpoints responsible for fetching remote image metadata or URL previews. Authenticated users with Contributor access or higher can exploit this to make the WordPress server send requests to internal network resources (e.g., AWS metadata service at 169.254.169.254 or internal admin panels at 127.0.0.1).
The vulnerability resides in the backend REST controller logic (inferred namespace nelio-content/v1/utils) which is triggered by the "Social Message Editor" UI when a user attempts to add an image via URL.
2. Attack Vector Analysis
- Endpoint:
POST /wp-json/nelio-content/v1/utils/get-external-image-info(inferred fromsocial-message-editor.jsand common plugin patterns). - Alternative Endpoint:
POST /wp-json/nelio-content/v1/utils/get-url-meta(if the above is not found). - Vulnerable Parameter:
url(in the JSON body). - Authentication: Required (Contributor level or higher).
- Headers:
X-WP-Nonce: Standard WordPress REST API nonce.X-Nelio-Content-Auth: Custom Nelio API token (generated vianelio_content_generate_api_auth_token()).
3. Code Flow
- Entry Point: A Contributor user accesses the Nelio Content Calendar or Social Message Editor.
- Frontend Interaction: The user clicks the "Add image from URL" button in the social message editor, which invokes the
Nelio_Content_Social_Message_Image_Url_Dialogcomponent (defined inassets/dist/js/social-message-editor.js). - API Request: The JS makes a
POSTrequest to the plugin's REST API endpoint. - Backend Processing: The plugin receives the request. In a vulnerable version, it takes the
urlparameter and passes it directly towp_remote_get()orwp_remote_request()without checking if the host is a private/internal IP address. - Sink: The WordPress server executes the HTTP request to the attacker-supplied URL.
4. Nonce & Token Acquisition Strategy
Nelio Content requires both a standard WordPress REST nonce and a custom Nelio Authentication Token.
Acquisition Steps:
- Prepare Site: Ensure the plugin is "ready" by setting a dummy site ID (otherwise the calendar page is blocked by a wizard).
wp option update nelio_content_settings '{"site_id":"12345","is_ready":true}' --format=json - Navigate: Use
browser_navigatetohttp://localhost:8080/wp-admin/admin.php?page=nelio-content. - Extract Nonce:
browser_eval("window.wpApiSettings?.nonce") - Extract Nelio Token:
Based onadmin/class-nelio-content-admin.php, the$plugin_settingsarray containsauthenticationToken.browser_eval("window.nelioContentAdmin?.authenticationToken || window.nelioContentData?.authenticationToken")
5. Exploitation Strategy
Step 1: Authentication & Discovery
- Authenticate as a Contributor.
- Extract the
nonceandauthenticationTokenusing the strategy above.
Step 2: SSRF Payload
Send a POST request to hit an internal service. We will target the WordPress login page itself on 127.0.0.1 to demonstrate the ability to reach internal ports.
- Request Type:
POST - URL:
/wp-json/nelio-content/v1/utils/get-external-image-info - Headers:
Content-Type: application/jsonX-WP-Nonce: [extracted_nonce]X-Nelio-Content-Auth: [extracted_token]
- Body:
{ "url": "http://127.0.0.1:8080/wp-login.php" }
Step 3: Response Analysis
A successful SSRF will return a 200 OK with JSON data containing either metadata from the internal page (e.g., title "Log In ‹ ... — WordPress") or an error from the image parser indicating the content was not a valid image but confirming the fetch occurred.
6. Test Data Setup
- User Creation:
wp user create attacker attacker@example.com --role=contributor --user_pass=password - Plugin Readiness:
wp option update nelio_content_settings '{"site_id":"exploited","is_ready":true}' --format=json - Internal Target: Ensure the WordPress site is listening on
127.0.0.1:8080.
7. Expected Results
- Success Response: The API returns a JSON object. If it fetches an HTML page, it might return:
{ "success": true, "data": { "title": "Log In \u2039 ...", "images": [] } } - Internal Error: If targeting a closed port (e.g.,
http://127.0.0.1:9999), it returns a connection refused error, confirming the request was attempted.
8. Verification Steps
- HTTP Logs: Check the WordPress access logs (if accessible) to see a request from
127.0.0.1to/wp-login.phptriggered by the server itself. - REST Response: Use the
http_requesttool and confirm thedatafield in the JSON response contains strings or metadata from theurlpayload.
9. Alternative Approaches
If /utils/get-external-image-info is not the endpoint:
- Search for other endpoints under
wp-json/nelio-content/v1/using thehttp_requesttool to list routes. - Try the endpoint
/wp-json/nelio-content/v1/social-message/fetch-external-image. - Try a
GETrequest to the same endpoints ifPOSTis disallowed:/wp-json/nelio-content/v1/utils/get-external-image-info?url=http://127.0.0.1:8080/.
Summary
The Nelio Content plugin for WordPress is vulnerable to authenticated Server-Side Request Forgery (SSRF) via its REST API endpoints. Authenticated users with Contributor-level access or higher can exploit this to make the WordPress server send requests to arbitrary internal or external URLs by manipulating the 'url' parameter in utility endpoints.
Security Fix
@@ -378,13 +378,13 @@ $svg_icon_file = nelio_content()->plugin_path . '/assets/dist/images/logo.svg'; if ( ! file_exists( $svg_icon_file ) ) { - return 'admin-generic'; + return 'dashicons-admin-generic'; } // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents $icon = file_get_contents( $svg_icon_file ); if ( empty( $icon ) ) { - return 'admin-generic'; + return 'dashicons-admin-generic'; } return 'data:image/svg+xml;base64,' . base64_encode( $icon ); @@ -2,6 +2,7 @@ .nelio-content-connect-profile-warning__content{max-width:40em}.nelio-content-connect-profile-warning__actions{display:flex;justify-content:flex-end;margin-top:2em}.nelio-content-connect-profile-warning__actions>*{margin-left:.5em} .nelio-content-social-message-editor{max-width:52em;width:100%}.nelio-content-social-message-editor .components-modal__header h1{width:100%}.nelio-content-social-message-editor__title{align-items:center;display:flex;width:100%}.nelio-content-social-message-editor__title-text{flex-grow:1}.nelio-content-social-message-editor__title-help{flex-grow:0}.nelio-content-social-message-editor__schedule-options{display:flex;flex-direction:row;min-height:5em}.nelio-content-social-message-editor__schedule-options .components-base-control+.components-base-control{margin:0}.nelio-content-social-message-editor__actions{align-items:center;display:flex;justify-content:flex-end;margin-top:2em}.nelio-content-social-message-editor__actions>*{margin-right:.5em}.nelio-content-social-message-editor__actions>:last-child{margin-right:0} .nelio-content-social-message-editor__recurring-dialog{display:flex;gap:.5em}.nelio-content-social-message-editor__error-summary{color:#a00;display:flex;flex-grow:1;margin-right:1.7em}.nelio-content-social-message-editor__error-summary span{margin-right:.3em} +.nelio-content-social-message-editor .components-base-control{margin-bottom:1em} .nelio-content-social-message-editor__date-selector{padding-left:.5em;padding-right:2em}.nelio-content-social-message-editor__date-selector .components-base-control,.nelio-content-social-message-editor__date-selector .components-base-control .components-base-control__field{margin-bottom:0}.nelio-content-social-message-editor__date-selector input{height:40px}.nelio-content-social-message-editor__positive-days-selector{height:40px;margin:0;width:5em}.nelio-content-social-message-editor__reset-date-button{display:inline-block;margin-left:.5em} .nelio-content-social-message-editor__medium-tags-selector{margin-bottom:1em}.nelio-content-social-message-editor__medium-tags-selector>div,.nelio-content-social-message-editor__medium-tags-selector>p{padding-left:.5em;padding-right:.5em} .nelio-content-social-message-editor__message-editor{margin-bottom:.5em;position:relative}.nelio-content-social-message-editor__message-editor textarea{height:5em;overflow-y:always;resize:none}.nelio-content-social-message-editor__message-editor--is-merged-with-social-profile-selector textarea{border-top-left-radius:0;border-top-right-radius:0;margin-top:0}
Exploit Outline
To exploit this vulnerability, an attacker must first authenticate with at least Contributor-level privileges. They must then retrieve two required security tokens: the standard WordPress REST API nonce (found in window.wpApiSettings.nonce) and a custom Nelio authentication token (found in window.nelioContentAdmin.authenticationToken). Using these tokens, the attacker sends a POST request to the endpoint `/wp-json/nelio-content/v1/utils/get-external-image-info`. The payload consists of a JSON body with a 'url' parameter set to an internal or sensitive target, such as the AWS metadata service (169.254.169.254) or internal services on loopback addresses (127.0.0.1). The server will then execute a request to that URL and return the metadata or response content to the attacker.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.