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.pl
index 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.

http://efficito.com -- Hosted accounting and ERP.
Robust and Flexible. No vendor lock-in.