SurfLink < 2.6.0 - Missing Authorization to Authenticated (Subscriber+) 410 Gone URL Import via 'surfl_import_410' AJAX Action
Description
The SurfLink - Ultimate Link Manager plugin for WordPress is vulnerable to unauthorized data modification due to a missing capability check on the ajax_import_410() function in all versions up to 2.6.0. This is due to a missing capability check (current_user_can()) and missing nonce verification (check_ajax_referer()) in the ajax_import_410() function, while all other AJAX handlers in the same class (ajax_add_single_410, ajax_save_editted_410, ajax_delete_410, ajax_bulk_410_delete, ajax_empty_410, ajax_export_410) properly implement both authorization and nonce checks. This makes it possible for authenticated attackers, with Subscriber-level access and above, to import arbitrary URLs into the 410 Gone database table via the surfl_import_410 AJAX action. Injected URLs will cause the site to return HTTP 410 Gone responses to all visitors accessing those paths, potentially causing denial of service for legitimate pages and SEO damage through search engine delisting.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:NTechnical Details
What Changed in the Fix
Changes introduced in v2.6.0
Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2026-3552 (SurfLink Missing Authorization) ## 1. Vulnerability Summary The **SurfLink – Link Manager & Backup Restore** plugin (versions < 2.6.0) contains a missing authorization vulnerability in its 410 Gone URL import functionality. While most AJAX handlers in th…
Show full research plan
Exploitation Research Plan: CVE-2026-3552 (SurfLink Missing Authorization)
1. Vulnerability Summary
The SurfLink – Link Manager & Backup Restore plugin (versions < 2.6.0) contains a missing authorization vulnerability in its 410 Gone URL import functionality. While most AJAX handlers in the plugin properly enforce capabilities and check nonces, the ajax_import_410 function (associated with the surfl_import_410 AJAX action) fails to call current_user_can() or check_ajax_referer(). This allows any authenticated user with Subscriber-level access or higher to inject arbitrary URLs into the plugin's 410 redirect table. Once injected, visitors accessing these URLs will receive an HTTP 410 Gone response, effectively allowing an attacker to take down legitimate pages or posts and damage the site's SEO.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - AJAX Action:
surfl_import_410(Action hook:wp_ajax_surfl_import_410) - Vulnerable Parameter: Likely
urlsorimport_data(carrying newline-separated or CSV-formatted URLs). - Authentication: Required (Subscriber level or higher).
- Preconditions: The plugin must be active. The "Redirects" or "410 Manager" module may need to be enabled in the SurfLink Module Manager (though AJAX actions are often registered regardless of module status).
3. Code Flow
- Registration: The plugin registers the AJAX action in its loader class (likely
SURFL_Loaderor a specialized 410 handler class):add_action('wp_ajax_surfl_import_410', [$this, 'ajax_import_410']); - Trigger: A Subscriber user sends a POST request to
admin-ajax.phpwithaction=surfl_import_410. - Missing Security: The
ajax_import_410()function begins execution without calling:check_ajax_referer(...)(Missing CSRF protection).current_user_can('manage_options')(Missing Authorization).
- Data Processing: The function retrieves URL data from
$_POST['urls'](or a similar parameter), splits it, and inserts it into the database table (likely{$wpdb->prefix}surfl_410). - Impact: The plugin's redirection engine monitors requests. When a visitor hits an injected path, the plugin sends a
header("HTTP/1.1 410 Gone")and terminates the request.
4. Nonce Acquisition Strategy
According to the vulnerability report, the surfl_import_410 action does not verify a nonce. Therefore, no nonce is required for exploitation.
However, to maintain a stealthy profile or if the environment has additional security layers, nonces for related actions (which share the same class) can be found by:
- Shortcode/Page: The 410 Manager is an admin-only feature, but scripts might be localized on the settings page.
- Navigation: Use the browser to navigate to the SurfLink dashboard.
- Extraction: Since the vulnerable action is
surfl_import_410, related nonces are likely stored in a global JS object. Based on the naming convention inclass-surfl-br-loader.php(which usessurfl_br_ajax), look forwindow.surfl_ajaxorwindow.surfl_redirects_ajax. - JS Variable:
browser_eval("window.surfl_ajax?.nonce")(inferred identifier).
5. Exploitation Strategy
The goal is to inject a critical path (like the homepage or a specific post) into the 410 list.
Step-by-Step Plan:
- Authentication: Use the
http_requesttool to authenticate as a Subscriber user and capture cookies. - Payload Identification: Since the exact parameter name is not in the provided snippet, we will attempt the common pattern for "Import" actions: a POST parameter named
urlscontaining the target path. - The Exploit Request:
- URL:
http://<target>/wp-admin/admin-ajax.php - Method:
POST - Headers:
Content-Type: application/x-www-form-urlencoded - Body:
action=surfl_import_410&urls=/important-page/
- URL:
- Alternative Payload: If
urlsfails, tryimport_dataordata.
6. Test Data Setup
- Target Post: Create a legitimate page or post that we will "delete" via the exploit.
wp post create --post_type=page --post_title="Business Critical Page" --post_name="critical-page" --post_status=publish
- Attacker User: Create a Subscriber user.
wp user create attacker attacker@example.com --role=subscriber --user_pass=password123
- Verify Access: Confirm
/critical-page/returns a200 OKstatus before the exploit.
7. Expected Results
- The
admin-ajax.phprequest should return a successful JSON response (e.g.,{"success":true}or a count of imported URLs). - Navigating to
http://<target>/critical-page/should now result in an HTTP 410 Gone response instead of the page content. - The WordPress admin panel for SurfLink (410 Manager) should show
/critical-page/in its list.
8. Verification Steps
- HTTP Status Check:
Usehttp_request(GET) on the targeted path and verify the status code is410. - Database Inspection:
Use WP-CLI to check the plugin's 410 table (identifying the table name first):wp db tables | grep surflwp db query "SELECT * FROM wp_surfl_410 WHERE url LIKE '%critical-page%';"(Table name inferred aswp_surfl_410).
9. Alternative Approaches
- Bulk DOS: Attempt to import a large list of URLs (e.g.,
/,/wp-admin/,/wp-login.php,/shop/) to prove the extent of the Denial of Service. - File-based Import: If the
ajax_import_410expects a file upload rather than a POST parameter, usemultipart/form-datato upload a.csvor.txtfile containing the paths. This is suggested by the "Import/Export" feature description inreadme.txt. - Parameter Brute-force: If
urlsis incorrect, usebrowser_navigateto the SurfLink 410 Manager as an Admin to inspect theNetworktab during a legitimate import to find the exact parameter names.
Summary
The SurfLink plugin for WordPress is vulnerable to unauthorized data modification due to missing capability and nonce checks in its 410 Gone URL import handler. Authenticated attackers with Subscriber-level access can exploit this to inject arbitrary URLs into the plugin's 410 redirect table, causing legitimate pages to return '410 Gone' errors, leading to denial of service and SEO damage.
Vulnerable Code
// Within the class responsible for 410 Management (likely SURFL_Loader or similar) // Registered via: add_action('wp_ajax_surfl_import_410', [$this, 'ajax_import_410']); public function ajax_import_410() { // Vulnerability: Missing check_ajax_referer() and current_user_can() calls found in sibling functions $urls = isset($_POST['urls']) ? $_POST['urls'] : ''; if (!empty($urls)) { $url_list = explode("\n", $urls); foreach ($url_list as $url) { // Logic to insert $url into the {$wpdb->prefix}surfl_410 table } wp_send_json_success(); } wp_send_json_error(); }
Security Fix
@@ -342,6 +342,11 @@ public function ajax_import_410() { + $this->check_nonce(); + + if (!current_user_can('manage_options')) { + wp_send_json_error(['message' => esc_html__('Unauthorized', 'surflink')]); + } + $urls = isset($_POST['urls']) ? $_POST['urls'] : ''; if (!empty($urls)) { // ... processing logic ...
Exploit Outline
The exploit targets the 'surfl_import_410' AJAX action which is missing authorization and CSRF protection. 1. Authenticate to the WordPress target as a user with at least Subscriber-level privileges. 2. Construct a POST request to the '/wp-admin/admin-ajax.php' endpoint. 3. Include the following parameters in the POST body: - 'action': Set to 'surfl_import_410'. - 'urls': A list of target URLs or paths (e.g., the homepage '/' or critical posts) separated by newlines. 4. Submit the request. Because the vulnerable function does not verify a nonce or check for 'manage_options' capabilities, the URLs will be successfully imported into the plugin's 410 Gone database. 5. Confirm the exploit by visiting the targeted paths as an unauthenticated visitor; the server will now return an HTTP 410 Gone response, making the content inaccessible.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.