WP Job Portal – AI-Powered Recruitment System for Company or Job Board website <= 2.5.1 - Unauthenticated SQL Injection
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:NTechnical Details
What Changed in the Fix
Changes introduced in v2.5.2
Source Code
WordPress.org SVN# 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, orjobid(parameters used inWHEREclauses without quotes). - Authentication: Unauthenticated.
- Preconditions: None.
Code Flow
- A request is made to
admin-ajax.phpwith an action registered viawp_ajax_nopriv_. - The handler (likely in
wp-job-portal.phpor a module's controller) calls
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
@@ -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.