CVE-2026-42684

WP Job Portal – AI-Powered Recruitment System for Company or Job Board website <= 2.5.1 - Unauthenticated SQL Injection

highImproper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
7.5
CVSS Score
7.5
CVSS Score
high
Severity
2.5.2
Patched in
4d
Time to patch

Description

The WP Job Portal – AI-Powered Recruitment System for Company or Job Board website plugin for WordPress is vulnerable to SQL Injection in versions up to, and including, 2.5.1 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
High
Confidentiality
None
Integrity
None
Availability

Technical Details

Affected versions<=2.5.1
PublishedMay 23, 2026
Last updatedMay 26, 2026
Affected pluginwp-job-portal

What Changed in the Fix

Changes introduced in v2.5.2

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# WP Job Portal <= 2.5.1 - Unauthenticated SQL Injection (CVE-2026-42684) ## Vulnerability Summary The **WP Job Portal** plugin for WordPress is vulnerable to an unauthenticated SQL injection. The vulnerability exists because several model functions (e.g., in `modules/job/model.php` or `modules/job…

Show full research plan

WP Job Portal <= 2.5.1 - Unauthenticated SQL Injection (CVE-2026-42684)

Vulnerability Summary

The WP Job Portal plugin for WordPress is vulnerable to an unauthenticated SQL injection. The vulnerability exists because several model functions (e.g., in modules/job/model.php or modules/jobapply/model.php) construct SQL queries by concatenating user-supplied parameters (like jobid, catid, or typeid) using esc_sql() but without surrounding the values in single quotes. Since esc_sql() only escapes characters like quotes and backslashes, it provides no protection for numeric SQL contexts. An attacker can inject arbitrary SQL commands by breaking the query logic.

Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: wpjobportal_job_list (inferred) or any AJAX action that handles job filtering/display.
  • Vulnerable Parameter: catid, typeid, or jobid (parameters used in WHERE clauses without quotes).
  • Authentication: Unauthenticated.
  • Preconditions: None.

Code Flow

  1. A request is made to admin-ajax.php with an action registered via wp_ajax_nopriv_.
  2. The handler (likely in wp-job-portal.php or a module's controller) calls
Research Findings
Static analysis — not yet PoC-verified

Summary

The WP Job Portal plugin for WordPress is vulnerable to unauthenticated SQL injection because it concatenates user-supplied numeric parameters into SQL queries using esc_sql() but without surrounding single quotes. This allows attackers to break the SQL context and execute arbitrary database commands to extract sensitive information.

Vulnerable Code

// File: modules/jobapply/model.php
// Lines 58-63 in version 2.5.1
if ($wpjobportal_searchjobcategory && is_numeric($wpjobportal_searchjobcategory))
    $wpjobportal_inquery .= " AND job.jobcategory = " . esc_sql($wpjobportal_searchjobcategory);
if ($wpjobportal_searchjobtype && is_numeric($wpjobportal_searchjobtype))
    $wpjobportal_inquery .= " AND job.jobtype = " . esc_sql($wpjobportal_searchjobtype);
if ($wpjobportal_searchjobstatus && is_numeric($wpjobportal_searchjobstatus))
    $wpjobportal_inquery .= " AND job.jobstatus = " . esc_sql($wpjobportal_searchjobstatus);

---

// File: modules/job/model.php
// Lines 63-73 in version 2.5.1
    $query = "SELECT DISTINCT job.id AS jobid,job.tags AS jobtags,job.title,job.created,job.city,
        CONCAT(job.alias,'-',job.id) AS jobaliasid,job.noofjobs,job.isfeaturedjob,job.status,
        cat.cat_title,company.id AS companyid,company.name AS companyname,company.logofilename, jobtype.title AS jobtypetitle,job.endfeatureddate,job.startpublishing,job.stoppublishing,
        job.params,CONCAT(company.alias,'-',company.id) AS companyaliasid,LOWER(jobtype.title) AS jobtypetit,
        job.salarymax,job.salarymin,job.salarytype,srtype.title AS srangetypetitle,jobtype.color AS jobtypecolor,job.currency
        FROM `" . wpjobportal::$_db->prefix . "wj_portal_jobs` AS job
        ".wpjobportal::$_company_job_table_join." JOIN `" . wpjobportal::$_db->prefix . "wj_portal_companies` AS company ON company.id = job.companyid
        LEFT JOIN `" . wpjobportal::$_db->prefix . "wj_portal_categories` AS cat ON cat.id = job.jobcategory
        LEFT JOIN `" . wpjobportal::$_db->prefix . "wj_portal_salaryrangetypes` AS srtype ON srtype.id = job.salaryduration
        LEFT JOIN `" . wpjobportal::$_db->prefix . "wj_portal_jobtypes` AS jobtype ON jobtype.id = job.jobtype
        LEFT JOIN `" . wpjobportal::$_db->prefix . "wj_portal_jobcities` AS jobcity ON jobcity.jobid = job.id
        LEFT JOIN `" . wpjobportal::$_db->prefix . "wj_portal_cities` AS city ON city.id = jobcity.cityid
        LEFT JOIN `" . wpjobportal::$_db->prefix . "wj_portal_states` AS state ON state.countryid = city.countryid
        LEFT JOIN `" . wpjobportal::$_db->prefix . "wj_portal_countries` AS country ON country.id = city.countryid
        WHERE job.id =". esc_sql($wpjobportal_jobid);

---

// File: modules/job/model.php
// Lines 25-27 in version 2.5.1
                FROM `" . wpjobportal::$_db->prefix . "wj_portal_jobs` AS job
                LEFT JOIN `" . wpjobportal::$_db->prefix . "wj_portal_categories` AS cat ON job.jobcategory = cat.id
                LEFT JOIN `" . wpjobportal::$_db->prefix . "wj_portal_jobtypes` AS jobtype ON job.jobtype = jobtype.id
                LEFT JOIN `" . wpjobportal::$_db->prefix . "wj_portal_companies` AS company ON job.companyid = company.id
                WHERE job.status = 1 AND DATE(job.startpublishing) <= CURDATE() AND DATE(job.stoppublishing) >= CURDATE()
                ORDER BY created DESC LIMIT " . esc_sql($wpjobportal_noofjobs);

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/wp-job-portal/2.5.1/modules/job/model.php /home/deploy/wp-safety.org/data/plugin-versions/wp-job-portal/2.5.2/modules/job/model.php
--- /home/deploy/wp-safety.org/data/plugin-versions/wp-job-portal/2.5.1/modules/job/model.php	2026-04-21 04:30:56.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/wp-job-portal/2.5.2/modules/job/model.php	2026-04-27 04:23:12.000000000 +0000
@@ -2740,6 +2740,16 @@
                     $wpjobportal_valarray[$uf->field] = isset($wpjobportal_searchajax[$uf->field]) ? $wpjobportal_searchajax[$uf->field] : '';
                 }
                 if (isset($wpjobportal_valarray[$uf->field]) && $wpjobportal_valarray[$uf->field] != null) {
+
+                    // ==========================================
+                    // SECURITY FIX: ESCAPE VALUES AT THE TOP
+                    // ==========================================
+                    // esc_sql() natively handles both strings and arrays.
+                    // If it receives an array (like in checkbox/multiple cases),
+                    // it safely loops through and escapes all nested values automatically.
+                    $wpjobportal_valarray[$uf->field] = esc_sql($wpjobportal_valarray[$uf->field]);
+                    // ==========================================
+
                     switch ($uf->userfieldtype) {
                         case 'text':
                         case 'email':

Exploit Outline

An unauthenticated attacker can exploit this vulnerability by sending a crafted request to endpoints that trigger job search or filtering logic, typically via AJAX actions like 'wpjobportal_job_list'. By providing a malicious payload in parameters such as 'catid', 'typeid', or 'jobid' (e.g., '1 OR (SELECT 1 FROM (SELECT(SLEEP(5)))a)'), the attacker can execute time-based or boolean-based SQL injection. Since these parameters are not wrapped in quotes in the SQL statement, esc_sql() fails to sanitize characters that alter the SQL logic (like whitespace, OR, AND, etc.), allowing the attacker to exfiltrate database contents without authentication.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.