Critical OJS Vulnerability: How an “Add Reviewer” Flaw Led to an Admin Takeover

Picture this: an account called “esciassessor” has been sitting quietly on one of our clients’ OJS sites since 2022. Then one morning, out of nowhere, it becomes the most powerful admin on the site. No cracked passwords, no endless login attempts. The attacker just slipped through a tiny gap in OJS that PKP, the folks who build OJS, had already warned everyone about.

In this post, we’ll walk you through exactly what happened, real screenshots and all. We’ll explain the cause in plain English, help you check whether your journal is at risk, and show you two ways to fix it: using the terminal, or using cPanel if File Manager is all you’ve got.

A not-so-normal morning: “Hey, there’s a new admin”

It was Thursday morning, 15 October 2026, when our security plugin pinged us: an account on one client’s OJS had just become a Site Administrator. That’s the top role in OJS, basically the master key to the whole building. Whoever holds it can run every journal on the server, add users, and install plugins.

Here’s the thing: nobody on the client’s team had added any new admin that day.

Figure 1. Our security plugin spots an unknown new admin called esciassessor and locks it down right away.

The moment that alert went off, the plugin locked the account. It didn’t delete it, just kept it frozen until the team could figure out whether it was legit.

And then it got worse: a sketchy plugin shows up

Not long after, a second alert popped up. The same account was trying to sneak a file called RqcCall.inc.php into the OJS plugins folder.

Think of an OJS plugin like an app on your phone. It adds handy features, but it can also run pretty much anything on the server. This file was named to look like part of OJS’s built-in RQC plugin, so nobody would think twice. In reality, it was a “backdoor”: a hidden door that takes orders from an outside server and carries them out.

Figure 2. Our malware scanner recognizes the bad pattern in RqcCall.inc.php and quarantines it on the spot.

Since the file got quarantined the second it appeared, the attacker never got to use it. If it had slipped through, they could have controlled the server from afar whenever they liked, even after we removed their admin account.

An old account, back from the dead

Next, we pulled up the admin list on that site. Plot twist: esciassessor wasn’t a new account at all. It was registered on 20 January 2022, then sat there untouched for almost four years.

Figure 3. There it is in the admin list: esciassessor (registered 2022), right next to the main admin. It shows Blocked because our plugin had already stepped in.

Honestly, using an old account is a pretty smart move. Journal managers usually keep an eye on new sign-ups, but hardly anyone double-checks accounts that have been around for years. Old accounts look harmless and fly under the radar.

The weird part: no signs of a break-in

Every visit to a website gets written down in the server’s guest book, called the access log. Break-ins usually leave a mess in there: hundreds of password guesses, failed logins piling up, or weird file uploads.

This time? Almost spotless. From the attacker’s IP address, we only saw a handful of requests to the “add reviewer” feature, the same one editors use every single day. The server said “success” to every one of them. A few minutes later, that same IP was already strolling around the admin settings page.

15/Oct/2026 09:31  POST  …/reviewer-grid/enroll-reviewer   → 200 (success)
15/Oct/2026 09:33  GET   /index/admin/settings             → 200 (success)

Put simply, nobody smashed a window or picked a lock. The visitor walked in through the front door, and the security guard happily opened the boardroom for them. That’s what made us go digging through PKP’s official records.

The culprit: the “add reviewer” flaw (pkp-lib #13482)

Turns out, the attacker used a flaw PKP had already documented as pkp-lib issue #13482, titled Validate user group IDs when enrolling/creating reviewers in editorial workflow. The issue was reported on October 8, 2026, and PKP released the code-level security patches the following day.

However, there’s one important detail: as of October 16, 2026, the official OJS download packages still don’t include this fix. So while the patch is already available, journal administrators shouldn’t assume that installing the latest downloadable version will solve this particular vulnerability. For now, applying the appropriate security patch is the recommended way to address it without waiting for the next OJS release.

Imagine a reception desk handing out visitor badges. When editors add a reviewer, they fill in a little form: who the person is, and what kind of badge they get. That badge should always say “Reviewer”.

The catch? The receptionist only checked that the badge field wasn’t empty. They never looked at what it actually said. So the attack played out like this:

  1. The attacker sent the “add reviewer” form straight to the server, skipping the normal screen.
  2. In the badge field, they typed “Site Administrator” instead of “Reviewer”.
  3. The server saw the field wasn’t empty and basically said, “Sure, go ahead.”
  4. Just like that, the attacker’s account had the master key to every journal on the server.

PKP’s patch basically retrains the receptionist. Now the server checks that the badge really is the “Reviewer” badge for that journal. It also stops trusting visitors to say which form they’re using, because that’s easy to fake.

So why call it “assessor”?

The attacker’s username has the word “assessor” in it, and the timing lined up perfectly with Indonesia’s journal accreditation season. Right now, lots of journal managers are in touch with accreditation assessors all the time.

So an account called “assessor” can look totally normal, even reassuring. Was it a coincidence or a clever disguise? We honestly can’t say. Either way, the lesson stands: judge accounts by what they can do, not by their name, especially if they have admin rights.

Is your journal at risk?

If you’re running OJS 3.3, 3.4, or 3.5, your journal may still be vulnerable to this security issue, even if you’ve already installed the latest publicly available release. Here’s where things stand as of October 16, 2026:

OJS seriesLatest available releaseRelease expected to include the fix
3.33.3.0-233.3.0-24
3.43.4.0-113.4.0-12
3.53.5.0-53.5.0-6

Not sure which version you’re on? Here’s how to check:

  1. Log in as an admin and head to the Administration menu.
  2. Click System Information.
  3. Find the OJS version line. It might use dots, like 3.3.0.21, which is the same thing as 3.3.0-21.

Important: The security fixes are already available as official PKP patches, but the updated OJS release packages listed above have not yet been published. That means you don’t have to wait for the next release to protect your journal. You can apply the appropriate patch to your current installation now, then upgrade to the fixed release once it becomes available.

Already applied the patch? Great! Your OJS version number might stay the same, but the specific vulnerability can still be fixed. What matters is whether the correct security patch has been successfully applied. You can always check the official PKP download page for the latest available releases.

Good news for Openjournaltheme clients: you’re already covered

We look after hosting and security for hundred our client journals. We’ve already built the fix into our OJS security tools, so the gap is closed automatically on every one of them. If you’re our client, you don’t need to lift a finger.

Not with us? No worries. Just pick one of the fixes below.

How to fix it yourself (if you’re not our client)

Since the OJS releases containing this security fix aren’t publicly available yet, we recommend applying PKP’s official security patch to your current installation as soon as possible. Waiting for the next OJS release could leave your journal exposed longer than necessary, especially when the vulnerability is already known and the fix is available.

The good news? You don’t need to perform a full OJS upgrade just to address this issue. PKP has provided patches for OJS 3.3, 3.4, and 3.5, which can be applied to compatible installations. Once the updated OJS releases become available, you should still plan to upgrade. Patching addresses this specific vulnerability, but it doesn’t replace regular security updates and system maintenance.

Below, we’ll walk you through two ways to apply the patch. Keep in mind that both methods involve modifying OJS core files, so make sure you’re comfortable with the technical steps before proceeding.

Before anything else: make a backup

One wrong character can break OJS, so always have a backup ready before you touch anything:

  • OJS files: at the very least the lib/pkp folder, but the whole OJS folder is even better.
  • Database: export it through phpMyAdmin or mysqldump.

If something goes wrong after patching, just put the files back from your backup. Easy.

Method 1: Using the terminal (SSH)

Important Disclaimer – Advanced Technical Procedure
This method involves making changes directly to your OJS core files through SSH. While it can be an efficient way to apply the security patch, it requires technical knowledge and experience with server administration.

We strongly recommend that only experienced developers or server administrators perform these steps. Before proceeding, make sure you understand the commands, verify that the patch matches your OJS version, and have a complete, tested backup of your files and database.

Incorrect commands or modifications may cause website errors, downtime, or data loss. These instructions are provided for informational purposes. Open Journal Theme is not responsible for technical issues, system failures, or data loss resulting from mistakes or modifications performed independently by users, to the extent permitted by applicable law.

This is the quickest and safest route, because the patch command double-checks that the code matches before changing anything.

  1. Log in to your server over SSH and go to your OJS folder. Swap in the path that matches your server.
    cd /home/yourojs/public_html
  • Make a backup of the lib/pkp folder, then step inside it.
    cp -a lib/pkp lib/pkp-backup-$(date +%F)
    cd lib/pkp
  • Download the patch file for your OJS series. Just run the one line that matches your version.

# OJS 3.3
wget -O fix-13482.diff https://github.com/pkp/pkp-lib/pull/13486.diff
# OJS 3.4
wget -O fix-13482.diff https://github.com/pkp/pkp-lib/pull/13485.diff
# OJS 3.5
wget -O fix-13482.diff https://github.com/pkp/pkp-lib/pull/13483.diff

  • Do a quick test run first. This doesn’t change any files.
    patch -p1 --dry-run < fix-13482.diff

If every line just says checking file …, you’re good to go to step 5. If you see Reversed (or previously applied) patch detected, answer n: your OJS is already patched, nice. If you see FAILED, stop right there: the patch doesn’t match your version, so upgrading or getting in touch with us is the way to go.

  • Now apply the patch for real.
    patch -p1 < fix-13482.diff
  • Head back to the OJS folder and clear the cache. If your server runs PHP-FPM with OPcache, give PHP-FPM a restart too (adjust the PHP version to match).
    cd ../..
    rm -rf cache/t_compile/*
    sudo systemctl restart php8.1-fpm
  • Give it a test. Log in as an editor, open a submission in the review stage, and add a reviewer like you normally would. If everything works, you’re all patched up.

Changed your mind? To undo the patch, run patch -p1 -R < fix-13482.diff from the lib/pkp folder, or restore the backup folder.

Method 2: Using cPanel (just File Manager, no terminal)

No terminal? No problem. You’ll just type the changes in by hand using File Manager’s built-in editor. The steps below are for OJS 3.3, which most journals in Indonesia use. You’ll be editing three files, all inside the lib/pkp folder.

  1. Back up. In File Manager, open your OJS folder, right-click the lib/pkp folder, choose Compress, and save it as a ZIP. Also export the database via phpMyAdmin → Export.
  2. Open the first file. Go to lib/pkp/classes/controllers/grid/users/reviewer/, right-click PKPReviewerGridHandler.inc.php, and choose Edit. Use Ctrl+F to search for text.
  3. Change two identical lines. Find the line below. You’ll spot it twice: first under function createReviewer, then under function enrollReviewer.

return $this->updateReviewer($args, $request);

Replace the first one (inside createReviewer) with:
return $this->saveReviewerForm(REVIEWER_SELECT_CREATE, $request);

Replace the second one (inside enrollReviewer) with:
return $this->saveReviewerForm(REVIEWER_SELECT_ENROLL_EXISTING, $request);

  • Change the updateReviewer function. Find function updateReviewer($args, $request) {. Right below it is this line:
    $selectionType = $request->getUserVar('selectionType');

Replace just that one line with the block below, then click Save Changes.
return $this->saveReviewerForm(REVIEWER_SELECT_ADVANCED_SEARCH, $request);
}
/**
 * Save the designated reviewer form.
 */
function saveReviewerForm($selectionType, $request) {

  • Open the second and third files. Both are in lib/pkp/controllers/grid/users/reviewer/form/: CreateReviewerForm.inc.php and EnrollExistingReviewerForm.inc.php. In each file, find this line:

$this->addCheck(new FormValidator($this, 'userGroupId', 'required', 'user.profile.form.usergroupRequired'));

Swap it for the block below and save. Do this in both files.
$this->addCheck(new FormValidatorCustom($this, 'userGroupId', 'required', 'user.profile.form.usergroupRequired', function($userGroupId) {
    $context = Application::get()->getRequest()->getContext();
    $userGroupDao = DAORegistry::getDAO('UserGroupDAO');
    $userGroup = $userGroupDao->getById($userGroupId, $context->getId());
    return $userGroup && $userGroup->getRoleId() == ROLE_ID_REVIEWER;
}));

  • Clear the cache. Open the cache/t_compile folder inside your OJS folder, click Select All, then Delete.
  • Test. Open your journal’s website. Blank page or an error? Restore the files from your ZIP backup. Looks normal? Log in as an editor and try adding a reviewer to a submission.

For OJS 3.4 and 3.5, the files and code are a little different (file names end in .php, not .inc.php). Open the patch link for your version in your browser. Lines starting with a minus sign (−) are old code to remove, and lines starting with a plus sign (+) are the new code to put in their place.

SeriesPatch linkFiles changed (inside lib/pkp)
3.413485.diffclasses/controllers/grid/users/reviewer/PKPReviewerGridHandler.php, controllers/grid/users/reviewer/form/CreateReviewerForm.php
3.513483.diffclasses/controllers/grid/users/reviewer/PKPReviewerGridHandler.php, controllers/grid/users/reviewer/form/CreateReviewerForm.php

Feeling unsure? Don’t force it. Our team is happy to patch it for you.

After patching: make sure no one got in already

Patching closes the door, but it doesn’t kick out anyone who already got in. If someone slipped in before you patched, you’ll need to clean up after them.

  • Check every Site Administrator account. In phpMyAdmin, open your OJS database, go to the SQL tab, and run the query below. If you spot an account you don’t recognize, disable it and take away its role.

SELECT u.user_id, u.username, u.email, u.date_registered, u.date_last_login
FROM users u
JOIN user_user_groups uug ON uug.user_id = u.user_id
JOIN user_groups ug ON ug.user_group_id = uug.user_group_id
WHERE ug.role_id = 1;

  • Check the plugins folder. In the terminal, run find plugins -name “*.php” -mtime -14. In File Manager, sort files by the Last Modified column. Keep an eye out for new PHP files you didn’t install.
  • Change the passwords of all admin and editor accounts.
  • Check the access log for enroll-reviewer requests from IP addresses that don’t belong to your editorial team. In cPanel, you’ll find this log under Raw Access.

A reminder for OJS journal managers

Don’t wait until your journal is next.

Attacks aren’t always loud. Sometimes, one old account and one unpatched flaw are enough. If an official security patch is available, you don’t need to wait for the next OJS release.

Not comfortable applying security patches through SSH or editing OJS core files? The Open Journal Theme team can help you assess your journal, address the vulnerability, and plan the next steps.

Address security risks

OJS Support & Security

Get help identifying vulnerabilities and protecting your journal from suspicious access.

Explore Support & Security Services

Long-term OJS care

Managed OJS Hosting

Leave server maintenance and day-to-day technical operations to an OJS-focused team.

Explore Hosting Services
Not sure which service your journal needs?

Talk to our OJS specialists about your setup and security concerns.

Consult Our Team

Found this article helpful? Share it with a fellow journal manager who might need it.

References

About the Author

Leave a Comment

Your email address will not be published. Required fields are marked *

Open Journal Theme

Need More Services  or Question?

Openjournaltheme.com started in 2016 by a passionate team that focused to provide affordable OJS, OMP,  OPS,  Dspace, Eprints products and services. Our mission to help publishers to be more focus on their content research rather than tackled by many technical OJS issues.

Under the legal company name :
Inovasi Informatik Sinergi Inc.

Secure Payment :

All the client’s financial account data is stored in the respective third-party site (such as Paypal, Wise and Direct Payment).
*Payment on Credit card can be done by request
Your financial account is guaranteed protection. We never keep any of the clients’ financial data.

← Index