Coordinated Disclosure Timeline

Summary

Audiobookshelf is vulnerable to blind server-side request forgery (SSRF) vulnerabilities.

Project

audiobookshelf

Tested Version

v2.6.0

Details

Issue 1: Blind SSRF in Auth.js (GHSL-2023-266)

The /auth/openid/config endpoint takes a user-controlled req.query.issuer GET parameter and uses it to send a GET request on line 482, which leads to an unauthenticated server-side request forgery (SSRF). The response of the request is not reflected back to the user, so it is a blind SSRF.

    router.get('/auth/openid/config', async (req, res) => {
      if (!req.query.issuer) {
        return res.status(400).send('Invalid request. Query param \'issuer\' is required')
      }
      let issuerUrl = req.query.issuer
      if (issuerUrl.endsWith('/')) issuerUrl = issuerUrl.slice(0, -1)

      const configUrl = `${issuerUrl}/.well-known/openid-configuration`
      axios.get(configUrl).then(({ data }) => {
        res.json({
          issuer: data.issuer,
          authorization_endpoint: data.authorization_endpoint,
          token_endpoint: data.token_endpoint,
          userinfo_endpoint: data.userinfo_endpoint,
          end_session_endpoint: data.end_session_endpoint,
          jwks_uri: data.jwks_uri
        })
      }).catch((error) => {
        Logger.error(`[Auth] Failed to get openid configuration at "${configUrl}"`, error)
        res.status(error.statusCode || 400).send(`${error.code || 'UNKNOWN'}: Failed to get openid configuration`)
      })
    })

This issue was found with the help of CodeQL’s Server-side request forgery query.

Impact

This issue allows for crafting GET requests on behalf of the audiobookshelf server.

PoC

  1. Start a simple python web server in a folder containing a example file called file.txt with python -m http.server 9000. This command will serve the file.txt file on http://127.0.0.1:9000/.
  2. Start audiobookshelf. We assume that it is running on example.com.
  3. Send the following request. The ? at the end of the payload will make the receiving server ignore the /.well-known/openid-configuration added path.
     curl 'http://example.com/auth/openid/config?issuer=http://127.0.0.1:9000/file.txt?'
    
  4. Observe on the python server that a request has been logged.

Issue 2: Blind SSRF in podcastUtils.js (GHSL-2023-267)

The getPodcastFeed function in server/controllers/PodcastController.js takes a user controlled rssFeed POST parameter and uses it to send a GET request in server/utils/podcastUtils.js line 221, which leads to a server side request forgery (SSRF). The response of the request is not reflected back to the user, so it is a blind SSRF. It requires an authenticated user.

module.exports.getPodcastFeed = (feedUrl, excludeEpisodeMetadata = false) => {
  Logger.debug(`[podcastUtils] getPodcastFeed for "${feedUrl}"`)
  return axios.get(feedUrl, { timeout: 12000, responseType: 'arraybuffer', headers: { Accept: 'application/rss+xml' } }).then(async (data) => {
  # code cut for readability
  }
}

This issue was found with the help of CodeQL’s Server-side request forgery query.

Impact

This issue allows for crafting GET requests on behalf of the audiobookshelf server.

Resources

  1. Start a simple python web server in a folder containing a example file called file.txt with python -m http.server 9000. This command will serve the file.txt file on http://127.0.0.1:9000/.
  2. Start audiobookshelf. We assume that it is running on example.com.
  3. Send the following request.
    curl -X POST 'http://example.com/api/podcasts/feed' -d '{"rssFeed":"http://127.0.0.1:9000/file.txt"}' -H 'Content-Type: application/json' -H 'Authorization: Bearer <token>'
    
  4. Observe on the python server that a request has been logged.

CVE

Credit

These issues were discovered and reported by GHSL team member @sylwia-budzynska (Sylwia Budzynska).

Contact

You can contact the GHSL team at securitylab@github.com, please include a reference to GHSL-2023-266 or GHSL-2023-267 in any communication regarding these issues.