Bug 40748 - Remote-Code-Execution (RCE) in update_social_data.pl
Summary: Remote-Code-Execution (RCE) in update_social_data.pl
Status: RESOLVED FIXED
Alias: None
Product: Koha
Classification: Unclassified
Component: Architecture, internals, and plumbing (show other bugs)
Version: unspecified
Hardware: All All
: P1 - high critical
Assignee: Jake Deery
QA Contact: David Cook
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2025-09-03 15:09 UTC by oos3c
Modified: 2025-10-27 07:40 UTC (History)
17 users (show)

See Also:
GIT URL:
Initiative type: ---
Sponsorship status: ---
Comma delimited list of Sponsors:
Crowdfunding goal: 0
Patch complexity: ---
Documentation contact:
Documentation submission:
Text to go in the release notes:
Version(s) released in:
25.11.00,25.05.04,24.11.09,24.05.12,22.11.31
Circulation function:


Attachments
Reverse shell via remote code execution (240.50 KB, image/png)
2025-09-03 15:09 UTC, oos3c
Details
Bug 40748: Fix RCE in update_social_data.pl (1.45 KB, patch)
2025-09-03 15:45 UTC, Jake Deery
Details | Diff | Splinter Review
Bug 40748: Check for HTTP URLs and skip shell when using system() (2.94 KB, patch)
2025-09-04 01:30 UTC, David Cook
Details | Diff | Splinter Review
Bug 40748: Check for HTTP URLs and skip shell when using system() (3.00 KB, patch)
2025-09-04 08:10 UTC, Jake Deery
Details | Diff | Splinter Review
Bug 40748: Fix RCE in update_social_data.pl (1.52 KB, patch)
2025-09-04 08:40 UTC, Jonathan Druart
Details | Diff | Splinter Review
Bug 40748: Check for HTTP URLs and skip shell when using system() (3.07 KB, patch)
2025-09-04 08:40 UTC, Jonathan Druart
Details | Diff | Splinter Review
Bug 40748: [24.05.x] Fix RCE in update_social_data.pl (3.68 KB, patch)
2025-09-25 16:25 UTC, Lucas Gass (lukeg)
Details | Diff | Splinter Review

Note You need to log in before you can comment on or make changes to this bug.
Description oos3c 2025-09-03 15:09:29 UTC
Created attachment 186080 [details]
Reverse shell via remote code execution

Hi team,

I’ve discovered a security vulnerability in the latest version of Koha.
This issue allows an authenticated administrator to achieve remote code execution on the underlying host via code injection. 

The user-supplied input in the 'Babeltheque_url_update' field within the preferences is passed without sanitisation to 'system( qq{/usr/bin/wget $url -O $output_dir/social_data.csv.bz2 } ) == 0 or die "Can't get bz2 file from url $url ($?)";'.

If a cron job has been scheduled or the user runs the 'update_social_data.pl' script, the injected code is executed.

An example payload of '$(whoami > /tmp/rce.txt)' can be used to demonstrate the execution of code. 

Consequently, an attacker can insert malicious code that is executed when the associated script runs. In the example screenshot I have provided, this led to code execution under the Koha account, allowing compromise of the server.
Comment 1 Jake Deery 2025-09-03 15:45:11 UTC
Created attachment 186082 [details] [review]
Bug 40748: Fix RCE in update_social_data.pl

This patch fixes an identified RCE in the update_social_data.pl
social_data, which when executed, allows an attacker to run arbitrary
code on the system's shell.

TO TEST:

a)  In the Babeltheque_url_update syspref, wrap any bash in $(), and then
run the update_social_data.pl script.
    *)  For example, $(whoami >
/tmp/rce.log) will output to a file the username the perl script is
running as.
APPLY PATCH
b)  Run the update_social_data.pl script again. The string is now
meta-escaped, and the RCE no longer works.
Comment 2 David Cook 2025-09-04 01:19:37 UTC
Jake's patch does work, but I'm not quite comfortable with it. 

--

The quotemeta does quote the metacharacters which prevents the RCE via the $(whoami >
/tmp/rce.log) payload, so that's good.

It also quotes the metacharacters used in a valid URL: http\:\/\/localhost\:8080\/\?a\=b\&c\=d

Yet, wget still works, so that's good. But it got me thinking... it still works because the input is still being interpreted via the shell. If for some reason someone eventually removed the quotemeta or quotemeta itself changed, we'd be back to square one...

I'm going to suggest two things. One: we do some data checks to see if we're getting a HTTP URL. Two: we skip the shell all together by using the "system PROGRAM LIST" syntax.
Comment 3 David Cook 2025-09-04 01:30:54 UTC
Created attachment 186111 [details] [review]
Bug 40748: Check for HTTP URLs and skip shell when using system()

This change checks for HTTP(S) URLs and skips the shell when using
system() so that shell metacharacters aren't interpreted.

Test plan:
0. Apply the patch
1. Insert the following into the Babeltheque_url_update system preference:
$(whoami > /tmp/rce.log)
2. koha-shell kohadev
3. perl misc/cronjobs/social_data/update_social_data.pl
4. Note the script says the following:
$(whoami > /tmp/rce.log) is not a HTTP URL. Aborting.
5. Note also that /tmp/rce.log is not created
6. Change Babeltheque_url_update syspref to something like the following:
http://localhost:8080
7. perl misc/cronjobs/social_data/update_social_data.pl
8. Note that the wget succeeds but the bunzip2 fails (as expected) since
we haven't fetched a bzipped file
9. Try to create a payload that passes the HTTP URL test with shell
metacharacters and note that they're not interpetted and instead
just used as part of a URL string.
Comment 4 Jake Deery 2025-09-04 08:06:01 UTC
Hi David,

Thanks for the patch, looks grand to me! I must admit, I was tossing and turning in my sleep last night thinking this patch of mine is ugly and probably breaks wget.

Top job for getting a better patch in so quickly! It is greatly appreciated.

Ta,
Jake
Comment 5 Jake Deery 2025-09-04 08:10:53 UTC
Created attachment 186114 [details] [review]
Bug 40748: Check for HTTP URLs and skip shell when using system()

This change checks for HTTP(S) URLs and skips the shell when using
system() so that shell metacharacters aren't interpreted.

Test plan:
0. Apply the patch
1. Insert the following into the Babeltheque_url_update system preference:
$(whoami > /tmp/rce.log)
2. koha-shell kohadev
3. perl misc/cronjobs/social_data/update_social_data.pl
4. Note the script says the following:
$(whoami > /tmp/rce.log) is not a HTTP URL. Aborting.
5. Note also that /tmp/rce.log is not created
6. Change Babeltheque_url_update syspref to something like the following:
http://localhost:8080
7. perl misc/cronjobs/social_data/update_social_data.pl
8. Note that the wget succeeds but the bunzip2 fails (as expected) since
we haven't fetched a bzipped file
9. Try to create a payload that passes the HTTP URL test with shell
metacharacters and note that they're not interpetted and instead
just used as part of a URL string.

Signed-off-by: Jake Deery <jake.deery@openfifth.co.uk>
Comment 6 Jonathan Druart 2025-09-04 08:40:22 UTC
Created attachment 186116 [details] [review]
Bug 40748: Fix RCE in update_social_data.pl

This patch fixes an identified RCE in the update_social_data.pl
social_data, which when executed, allows an attacker to run arbitrary
code on the system's shell.

TO TEST:

a)  In the Babeltheque_url_update syspref, wrap any bash in $(), and then
run the update_social_data.pl script.
    *)  For example, $(whoami >
/tmp/rce.log) will output to a file the username the perl script is
running as.
APPLY PATCH
b)  Run the update_social_data.pl script again. The string is now
meta-escaped, and the RCE no longer works.

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Comment 7 Jonathan Druart 2025-09-04 08:40:25 UTC
Created attachment 186117 [details] [review]
Bug 40748: Check for HTTP URLs and skip shell when using system()

This change checks for HTTP(S) URLs and skips the shell when using
system() so that shell metacharacters aren't interpreted.

Test plan:
0. Apply the patch
1. Insert the following into the Babeltheque_url_update system preference:
$(whoami > /tmp/rce.log)
2. koha-shell kohadev
3. perl misc/cronjobs/social_data/update_social_data.pl
4. Note the script says the following:
$(whoami > /tmp/rce.log) is not a HTTP URL. Aborting.
5. Note also that /tmp/rce.log is not created
6. Change Babeltheque_url_update syspref to something like the following:
http://localhost:8080
7. perl misc/cronjobs/social_data/update_social_data.pl
8. Note that the wget succeeds but the bunzip2 fails (as expected) since
we haven't fetched a bzipped file
9. Try to create a payload that passes the HTTP URL test with shell
metacharacters and note that they're not interpetted and instead
just used as part of a URL string.

Signed-off-by: Jake Deery <jake.deery@openfifth.co.uk>

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Comment 8 Jonathan Druart 2025-09-04 08:42:39 UTC
Exiting with an error code would be good if the syspref does not contain a valid URI.
Comment 9 Martin Renvoize (ashimema) 2025-09-04 14:27:49 UTC
Nice teamwork there, thanks all.
Comment 10 David Cook 2025-09-05 02:45:56 UTC
(In reply to Jonathan Druart from comment #8)
> Exiting with an error code would be good if the syspref does not contain a
> valid URI.

Mmm that's true. Good call.
Comment 11 oos3c 2025-09-05 17:59:37 UTC
Hi all,

The fix looks sufficient to me. The list form of system seems to block the command injection.

I have requested a CVE ID for this. Happy to re-test this issue more if required.
Comment 12 David Cook 2025-09-09 02:01:41 UTC
(In reply to oos3c from comment #11)
> The fix looks sufficient to me. The list form of system seems to block the
> command injection.

Thanks for that.
 
> I have requested a CVE ID for this. Happy to re-test this issue more if
> required.

Usually we ask that you wait to request the CVE until after we've pushed the fix out to all supported versions, but it sounds like you've already gone ahead.

Could you share that CVE ID number here for our reference?

Thanks!
Comment 13 oos3c 2025-09-17 12:26:28 UTC
> Usually we ask that you wait to request the CVE until after we've pushed the
> fix out to all supported versions, but it sounds like you've already gone
> ahead.
> 
> Could you share that CVE ID number here for our reference?

Apologies, I will make a note of that for future submissions. An ID hasn't been assigned yet but I will share when I receive more information.
Comment 14 Fridolin Somers 2025-09-23 12:14:40 UTC
Pushed to 24.11.x-security for 24.11.09
Comment 15 Wainui Witika-Park 2025-09-24 07:34:55 UTC
Pushed to 22.11.x-security for 22.11.31
Comment 16 Lucas Gass (lukeg) 2025-09-25 16:25:36 UTC
Created attachment 186924 [details] [review]
Bug 40748: [24.05.x] Fix RCE in update_social_data.pl

This patch fixes an identified RCE in the update_social_data.pl
social_data, which when executed, allows an attacker to run arbitrary
code on the system's shell.

TO TEST:

a)  In the Babeltheque_url_update syspref, wrap any bash in $(), and then
run the update_social_data.pl script.
    *)  For example, $(whoami >
/tmp/rce.log) will output to a file the username the perl script is
running as.
APPLY PATCH
b)  Run the update_social_data.pl script again. The string is now
meta-escaped, and the RCE no longer works.

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>

Bug 40748: Check for HTTP URLs and skip shell when using system()

This change checks for HTTP(S) URLs and skips the shell when using
system() so that shell metacharacters aren't interpreted.

Test plan:
0. Apply the patch
1. Insert the following into the Babeltheque_url_update system preference:
$(whoami > /tmp/rce.log)
2. koha-shell kohadev
3. perl misc/cronjobs/social_data/update_social_data.pl
4. Note the script says the following:
$(whoami > /tmp/rce.log) is not a HTTP URL. Aborting.
5. Note also that /tmp/rce.log is not created
6. Change Babeltheque_url_update syspref to something like the following:
http://localhost:8080
7. perl misc/cronjobs/social_data/update_social_data.pl
8. Note that the wget succeeds but the bunzip2 fails (as expected) since
we haven't fetched a bzipped file
9. Try to create a payload that passes the HTTP URL test with shell
metacharacters and note that they're not interpetted and instead
just used as part of a URL string.

Signed-off-by: Jake Deery <jake.deery@openfifth.co.uk>

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Comment 17 David Nind 2025-10-27 07:40:11 UTC
Security bug fix, no change to the manual required.