CVE-2024-23831 (HTML, published 2024-02-02T16:15Z)
Description: value: LedgerSMB is a free web-based double-entry accounting system. When a LedgerSMB database administrator has an active session in /setup.pl, an attacker can trick the admin into clicking on a link which automatically submits a request to setup.pl without the admin's consent.  This request can be used to create a new user account with full application (/login.pl) privileges, leading to privilege escalation.  The vulnerability is patched in versions 1.10.30 and 1.11.9.

Severity: nan (CVSS v3 base score: 7.5)
Weakness: CWE-352 \u2014 Cross-Site Request Forgery (CSRF)
The web application does not, or can not, sufficiently verify whether a well-formed, valid, consistent request was intentionally provided by the user who submitted the request.
Repository: https://github.com/ledgersmb/LedgerSMB
Files changed:
UI/setup/begin_backup.html
UI/setup/confirm_operation.html
UI/setup/credentials.html
UI/setup/edit_user.html
UI/setup/migration_step.html
UI/setup/new_user.html
UI/setup/select_coa.html
UI/setup/template_info.html
UI/setup/upgrade_info.html
lib/LedgerSMB.pm
lib/LedgerSMB/Middleware/SessionStorage.pm
lib/LedgerSMB/PSGI.pm
lib/LedgerSMB/Scripts/setup.pm
lib/LedgerSMB/Template/UI.pm
Commit message: Fix missing CSRF mitigation


Vulnerable code:
use HTTP::Status qw( HTTP_OK );
    my $cookie      = $req->cookies->{$self->cookie};
    my $session     = $self->store->decode($cookie);
Copyright (C) 2011-2022 The LedgerSMB Core Team
                        )

Fixed code:
  <input type="hidden" name="csrf_token" value="[% csrf_token %]" />
  <input type="hidden" name="csrf_token" value="[% csrf_token %]" />
                  <input type="hidden" name="csrf_token" value="[% csrf_token %]" />
      <input type="hidden" name="csrf_token" value="[% csrf_token %]" />
          <input type="hidden" name="csrf_token" value="[% csrf_token %]" />
      <input type="hidden" name="csrf_token" value="[% csrf_token %]" />
        <input type="hidden" name="csrf_token" value="[% csrf_token %]" />
  <input type="hidden" name="csrf_token" value="[% csrf_token %]" />
  <input type="hidden" name="csrf_token" value="[% csrf_token %]" />
  <input type="hidden" name="csrf_token" value="[% csrf_token %]" />
=item verify_csrf()

This method verifies the C<csrf_token> value in request parameters (held in
C<$self->{csrf_token}>) against the value in the session object.  When one is
not defined or they are not equal, this function returns a PSGI triplet to be
used as the response resulting in a 400 -- Bad Request.

When the CSRF token matches, C<undef> is returned indicating processing is to
continue.

use HTTP::Status qw( HTTP_OK HTTP_BAD_REQUEST );

sub verify_csrf {
    my ($self) = @_;
    my $got = $self->{csrf_token};
    my $want = $self->{_req}->env->{'lsmb.session'}->{csrf_token};
    if (not ($got and $want and $got eq $want)) {
        $logger->debug( "CSRF have '$got'; want '$want'" );
        return [ HTTP_BAD_REQUEST,
                 [ 'Content-Type' => 'text/plain; charset=ascii' ],
                 [ 'Bad request: CSRF token failure' ] ];
    }
    return undef;
}

use String::Random;
    my $cookie             = $req->cookies->{$self->cookie};
    my $session            = $self->store->decode($cookie);
    $session->{csrf_token} //= String::Random->new->randpattern('.' x 23);
            enable '+LedgerSMB::Middleware::SessionStorage',
                domain      => 'setup',
                cookie      => "$cookie~setup",
                cookie_path => '/',
                secret      => $secret,
                duration    => 60*60*24*90;
    if (my $csrf = $request->verify_csrf) {
        return $csrf;
    }
    if (my $csrf = $request->verify_csrf) {
        return $csrf;
    }
    if (my $csrf = $request->verify_csrf) {
        return $csrf;
    }
    if (my $csrf = $request->verify_csrf) {
        return $csrf;
    }
    if (my $csrf = $request->verify_csrf) {
        return $csrf;
    }
    if (my $csrf = $request->verify_csrf) {
        return $csrf;
    }
    if (my $csrf = $request->verify_csrf) {
        return $csrf;
    }
    if (my $csrf = $request->verify_csrf) {
        return $csrf;
    }
    if (my $csrf = $request->verify_csrf) {
        return $csrf;
    }
            if (my $csrf = $request->verify_csrf) {
                return $csrf;
            }
    if (my $csrf = $request->verify_csrf) {
        return $csrf;
    }
    if (my $csrf = $request->verify_csrf) {
        return $csrf;
    }
    if (my $csrf = $request->verify_csrf) {
        return $csrf;
    }
    if (my $csrf = $request->verify_csrf) {
        return $csrf;
    }
    if (my $csrf = $request->verify_csrf) {
        return $csrf;
    }
    if (my $csrf = $request->verify_csrf) {
        return $csrf;
    }
    if (my $csrf = $request->verify_csrf) {
        return $csrf;
    }
Copyright (C) 2011-2024 The LedgerSMB Core Team
                        ),
                    csrf_token => $request->{_req}->env->{'lsmb.session'}->{csrf_token},