Cross-site Scripting (XSS) - Generic in boxbilling/boxbilling

Valid

Reported on

Aug 18th 2019


Overview

Boxbilling is a free billing & client management software

Affected versions of this software are vulnerable to Cross-site Scripting (XSS). It is possible to inject JavaScript with object decoding such as <script>alert(1)</script> resulting in XSS.

Technical Description

if we look in src/bb-modules/Support/html_client file we do find the following javascript code which is responsible for making the API request. The javascript serialize the input fields and send a post request to example.com/index.php?_url=/api/client/support/ticket_create

<script type="text/javascript">
$(function() {
    $('#ticket-submit').bind('submit',function(event){
        $('.wait').show();
        bb.post(
            'client/support/ticket_create',
            $(this).serialize(),
            function(result) {
                bb.redirect('{{ 'support/ticket'|link }}' + '/' + result);
            }
        );
        return false;
    });

The request is received by ticket_create function in /src/bb-modules/Support/Api/Client.php. The only check in this function exists is for the required parameters.

public function ticket_create($data)
    {
        $required = array(
            'content'             => 'Ticket content required',
            'subject'             => 'Ticket subject required',
            'support_helpdesk_id' => 'Ticket support_helpdesk_id required',
        );
        $this->di['validator']->checkRequiredParamsForArray($required, $data);

        $helpdesk = $this->di['db']->getExistingModelById('SupportHelpdesk', $data['support_helpdesk_id'], 'Helpdesk invalid');

        $client = $this->getIdentity();

        return $this->getService()->ticketCreateForClient($client, $helpdesk, $data);
    }

Once it verifies that the provided data contains the required parameters, it combine the request with client identity and helpdesk id and sends its request to ticketCreateForClient function in src/bb-modules/support/service.php


...
...
        $ticket                      = $this->di['db']->dispense('SupportTicket');
        $ticket->client_id           = $client->id;
        $ticket->subject             = $data['subject'];
        $ticket->support_helpdesk_id = $helpdesk->id;
        $ticket->created_at          = date('Y-m-d H:i:s');
        $ticket->updated_at          = date('Y-m-d H:i:s');

        // related task with ticket
        $ticket->rel_id        = $rel_id;
        $ticket->rel_type      = $rel_type;
        $ticket->rel_task      = $rel_task;
        $ticket->rel_new_value = $rel_new_value;
        $ticket->rel_status    = $rel_status;

        $ticketId = $this->di['db']->store($ticket);

        $this->messageCreateForTicket($ticket, $client, $data['content']);

...
    }

It stores the subject and other ticket info in support_ticket table in database and sends the content to messageCreateForTicket function.

    public function messageCreateForTicket(\Model_SupportTicket $ticket, $identity, $content)
    {
        $msg                    = $this->di['db']->dispense('SupportTicketMessage');
        $msg->support_ticket_id = $ticket->id;
        if ($identity instanceof \Model_Admin) {
            $msg->admin_id = $identity->id;
        } elseif ($identity instanceof \Model_Client) {
            $msg->client_id = $identity->id;
        } else {
            throw new \Box_Exception('Identity is not valid');
        }
        $msg->content    = $content;
        $msg->ip         = $this->di['request']->getClientAddress();
        $msg->created_at = date('Y-m-d H:i:s');
        $msg->updated_at = date('Y-m-d H:i:s');

        return $this->di['db']->store($msg);
    }

The function basically stores the content value (which is the ticket body) in support_ticket_message table in the database! which makes it stored cross-site scripting

to join this conversation