On August 4th, the LedgerSMB project was advised of a security
vulnerability in the code. Please see below our security advisory.
Reflected cross-site scripting of authenticated users in LedgerSMB
Summary:
========
LedgerSMB does not sufficiently HTML-encode error messages sent to the
browser. By sending a specially crafted URL to an authenticated user,
this flaw can be abused for remote code execution and information
disclosure.
Known vulnerable:
=================
All of:
- 1.1.0 upto 1.1.12 (including)
- 1.2.0 upto 1.2.26 (including)
- 1.3.0 upto 1.3.47 (including)
- 1.4.0 upto 1.4.42 (including)
- 1.5.0 upto 1.5.30 (including)
- 1.6.0 upto 1.6.33 (including)
- 1.7.0 upto 1.7.32 (including)
- 1.8.0 upto 1.8.17 (including)
Known fixed:
============
- 1.7.33
- 1.8.18
Details:
========
When encountering an error, LedgerSMB sends the user feedback which may
include user-provided input. This input was not sufficiently sanitized
before being included in the error report. This allows an attacker inject
a script in the error response page by send a specially crafted URL to an
authenticated user. As the error page itself does not contain any sensitive
information, a sophisticated payload in addition to targetting a sufficiently
privileged user, is required for information disclosure.
Proper audit control and separation of duties limit Integrity impact of
the attack vector.
The vulnerable code to provide this user-feedback dates back to version 1.0.
Please note that not error messages are vulnerable to this attack as not all
messages report the problematic input to the user.
Severity:
=========
CVSSv3.1 Base Score: 8.2 (High)
CVSSv3.1 Vector: AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:L/A:N
Recommendations:
================
We recommend all users to upgrade to known-fixed versions. Versions prior
to 1.7 are end-of-life and will not receive security fixes from the
LedgerSMB project.
Users who cannot upgrade, may apply the included patches or are advised
to contact a vendor for custom support.
There are no workarounds available for this vulnerability.
References:
===========
CVE-2021-3694 (LedgerSMB)
https://ledgersmb.org/cve-2021-3694-cross-site-scripting https://huntr.dev/bounties/ef7f4cf7-3a81-4516-b261-f5b6ac21430c/Reported by:
============
ranjit-git, user of the
huntr.dev platform
Patches:
========
Patch for LedgerSMB 1.8 (1.8.0 upto 1.8.17, including):
-------------------------------------------------------
[[[
diff --git a/lib/LedgerSMB/PSGI/Util.pm b/lib/LedgerSMB/PSGI/Util.pm
index 11c01918f..bf443d886 100644
--- a/lib/LedgerSMB/PSGI/Util.pm
+++ b/lib/LedgerSMB/PSGI/Util.pm
@@ -24,6 +24,7 @@ use strict;
use warnings;
use Carp;
+use HTML::Escape;
use HTTP::Status qw( HTTP_OK HTTP_INTERNAL_SERVER_ERROR HTTP_SEE_OTHER
HTTP_BAD_REQUEST );
@@ -41,7 +42,7 @@ Returns a standard error representation for HTTP status 500
sub internal_server_error {
- my ($msg, $title, $company, $dbversion) = @_;
+ my ($msg, $title, $company, $dbversion) = map { escape_html($_ // '') } @_;
$title //= 'Error!';
$msg =~ s/\n/<br>/g;
diff --git a/old/lib/LedgerSMB/oldHandler.pm b/old/lib/LedgerSMB/oldHandler.pm
index 1db966406..848eeb75c 100644
--- a/old/lib/LedgerSMB/oldHandler.pm
+++ b/old/lib/LedgerSMB/oldHandler.pm
@@ -57,6 +57,7 @@ use LedgerSMB::Sysconfig;
use Cookie::Baker;
use Digest::MD5;
+use HTML::Escape;
use Log::Log4perl;
use Try::Tiny;
@@ -184,14 +185,17 @@ sub handle {
sub _error {
my ($form, $msg, $status) = @_;
$msg = "? _error" if !defined $msg;
+ my $html_msg = escape_html($msg);
+ my $html_dbversion = escape_html($form->{dbversion});
+ my $html_company = escape_html($form->{company});
$status = 500 if ! defined $status;
print qq|Status: $status ISE
Content-Type: text/html; charset=utf-8
<html>
-<body><h2 class="error">Error!</h2> <p><b>$msg</b></p>
-<p>dbversion: $form->{dbversion}, company: $form->{company}</p>
+<body><h2 class="error">Error!</h2> <p><b>$html_msg</b></p>
+<p>dbversion: $html_dbversion, company: $html_company</p>
</body>
</html>
|;
]]]
Patch for LedgerSMB 1.7 (1.7.0 upto 1.7.32, including):
-------------------------------------------------------
[[[
diff --git a/lib/LedgerSMB/PSGI/Util.pm b/lib/LedgerSMB/PSGI/Util.pm
index 2d6195d69..b716a01c4 100644
--- a/lib/LedgerSMB/PSGI/Util.pm
+++ b/lib/LedgerSMB/PSGI/Util.pm
@@ -24,6 +24,7 @@ use strict;
use warnings;
use Carp;
+use HTML::Escape;
use HTTP::Status qw( HTTP_OK HTTP_INTERNAL_SERVER_ERROR HTTP_SEE_OTHER
HTTP_UNAUTHORIZED );
@@ -41,7 +42,7 @@ Returns a standard error representation for HTTP status 500
sub internal_server_error {
- my ($msg, $title, $company, $dbversion) = @_;
+ my ($msg, $title, $company, $dbversion) = map { escape_html($_ // '') } @_;
$title //= 'Error!';
$msg =~ s/\n/<br>/g;
diff --git a/old/bin/
old-handler.pl b/old/bin/
old-handler.plindex 24fa7a0a0..87864fc7e 100644
--- a/old/bin/
old-handler.pl+++ b/old/bin/
old-handler.pl@@ -187,14 +187,16 @@ $form->{dbh}->disconnect() if defined $form->{dbh};
sub _error {
my ($form, $msg, $status) = @_;
$msg = "? _error" if !defined $msg;
+ my $html_msg = escape_html($msg);
+ my $html_dbversion = escape_html($form->{dbversion});
+ my $html_company = escape_html($form->{company});
$status = 500 if ! defined $status;
print qq|Status: $status ISE
Content-Type: text/html; charset=utf-8
-
<html>
-<body><h2 class="error">Error!</h2> <p><b>$msg</b></p>
-<p>dbversion: $form->{dbversion}, company: $form->{company}</p>
+<body><h2 class="error">Error!</h2> <p><b>$html_msg</b></p>
+<p>dbversion: $html_dbversion, company: $html_company</p>
</body>
</html>
|;
]]]
--
Bye,
Erik.
Robust and Flexible. No vendor lock-in.