Summary
A stored cross-site scripting (XSS) vulnerability exists in the Audiobookshelf mobile application that allows arbitrary JavaScript execution through malicious library metadata. Attackers with library modification privileges can execute code in victim users' browsers/WebViews, potentially leading to session hijacking, data exfiltration, and unauthorized access to native device APIs.
Details
- Affected Component:
components/cards/ItemSearchCard.vue
- Affected Versions: Mobile app v0.11.0-beta and earlier
- Affected Platforms: Mobile app (Android/iOS via Capacitor)
- Server Compatibility:
- Confirmed vulnerable: Server v2.11.0 and earlier
- Status on later versions: Not exploitable due to API changes, assuming the server is non-malicious
Description:
The ItemSearchCard component uses Vue's v-html directive to render search result highlights without sanitizing metadata. While the component attempts to wrap matching text in <strong> tags, it does not escape the surrounding content from library item metadata.
Vulnerable Code Location:
- File:
components/cards/ItemSearchCard.vue
- Template:
<p v-html="matchHtml" />
computed: {
matchHtml() {
// matchText contains raw, unsanitized metadata (title, author, series, etc.)
var matchSplit = this.matchText.toLowerCase().split(this.search.toLowerCase().trim())
var html = ''
// Reconstructs string with <strong> tags but doesn't escape parts
html += `${firstPart}<strong class="text-warning">${actualWasThere}</strong>`
// firstPart and remaining parts are not HTML-escaped
return html
}
}
Attack Vector:
- Attacker with library privileges adds item with malicious metadata (e.g., book title:
Book Title<img src=x onerror=alert(localStorage.token||JSON.stringify(window.$nuxt.$store.state.user))>)
- Victim searches for text that matches part of the malicious field (e.g., searches for "Book")
- Search results render the unsanitized HTML via
v-html
- XSS payload executes in victim's browser/WebView
Proof of Concept: (tested on local instance of v2.11.0 server and v0.11.0-beta Android APK downloaded from GitHub Releases installed on emulator)
Set the book title of an audiobook to include the exploit: Harmless Book Title<img src=x onerror=alert(localStorage.token||JSON.stringify(window.$nuxt.$store.state.user))>
When the user searches for the book title (e.g. by typing in harm), the XSS triggers. This triggers on the mobile client (and outdated web client v2.11.0 and earlier).
The token has been leaked (and a real attack could involve sending it to an external server for the attacker to use).
Important Note:
Requires server version v2.11.0 or earlier to be exploitable. Newer server versions no longer return the matchText and matchKey fields in search API responses, which prevents the vulnerable code path from executing. However, the vulnerable code still exists in the client and could become exploitable again if:
- The API is changed to restore these fields
- The component is used with different data sources
- Older server versions are still in use
Impact
Severity Justification
CVSS 3.1 Vector: CVSS:3.1/AV:N/AC:H/PR:H/UI:R/S:C/C:L/I:L/A:N
CVSS Score: 4.0 (Medium)
- Attack Vector (AV:N): Network - exploitable remotely
- Attack Complexity (AC:H): High - primarily only exploitable when an older server version is used
- Privileges Required (PR:H): High - requires library modification access (but not administrator privileges)
- User Interaction (UI:R): Required - victim must search
- Scope (S:C): Changed - escapes sandboxing in WebView
- Confidentiality (C:L): Low- can steal tokens and session data (including admin user token)
- Integrity (I:L): Low- can modify app/server state and data (especially if admin user is attacked)
- Availability (A:N): None - can crash app but limited DoS impact
Real-World Impact
Attackers can execute arbitrary code (ex. steal sensitive data from localStorage, notably access tokens, granting admin access if the admin user is successfully attacked)
Attack Prerequisites
-
Attacker Requirements:
- Library management permissions (ability to add/edit library items) AND server v2.11.0 or earlier
- OR compromised Audiobookshelf server
-
Victim Requirements:
- Search for text matching the malicious field (ItemSearchCard, mobile client, server v2.11.0 or earlier required)
-
Environment:
- ItemSearchCard: Mobile app v0.11.0-beta or earlier, server v2.11.0 or earlier
Patches
Possible Fixes
Option 1: Use text interpolation and CSS for highlighting:
<template>
- <p v-html="matchHtml" />
+ <p>{{ firstPart }}<strong class="text-warning">{{ matchPart }}</strong>{{ lastPart }}</p>
</template>
Option 2: Sanitize HTML before rendering:
import DOMPurify from 'dompurify'
computed: {
matchHtml() {
// ... existing logic ...
const unsafeHtml = `${firstPart}<strong>${match}</strong>${lastPart}`
return DOMPurify.sanitize(unsafeHtml, {
ALLOWED_TAGS: ['strong'],
ALLOWED_ATTR: ['class']
})
}
}
Fixed in advplyr/audiobookshelf-app@b2c2b62
Summary
A stored cross-site scripting (XSS) vulnerability exists in the Audiobookshelf mobile application that allows arbitrary JavaScript execution through malicious library metadata. Attackers with library modification privileges can execute code in victim users' browsers/WebViews, potentially leading to session hijacking, data exfiltration, and unauthorized access to native device APIs.
Details
components/cards/ItemSearchCard.vueDescription:
The
ItemSearchCardcomponent uses Vue'sv-htmldirective to render search result highlights without sanitizing metadata. While the component attempts to wrap matching text in<strong>tags, it does not escape the surrounding content from library item metadata.Vulnerable Code Location:
components/cards/ItemSearchCard.vue<p v-html="matchHtml" />Attack Vector:
Book Title<img src=x onerror=alert(localStorage.token||JSON.stringify(window.$nuxt.$store.state.user))>)v-htmlProof of Concept: (tested on local instance of v2.11.0 server and v0.11.0-beta Android APK downloaded from GitHub Releases installed on emulator)
Set the book title of an audiobook to include the exploit:
Harmless Book Title<img src=x onerror=alert(localStorage.token||JSON.stringify(window.$nuxt.$store.state.user))>When the user searches for the book title (e.g. by typing in
harm), the XSS triggers. This triggers on the mobile client (and outdated web client v2.11.0 and earlier).The token has been leaked (and a real attack could involve sending it to an external server for the attacker to use).
Important Note:
Requires server version v2.11.0 or earlier to be exploitable. Newer server versions no longer return the
matchTextandmatchKeyfields in search API responses, which prevents the vulnerable code path from executing. However, the vulnerable code still exists in the client and could become exploitable again if:Impact
Severity Justification
CVSS 3.1 Vector:
CVSS:3.1/AV:N/AC:H/PR:H/UI:R/S:C/C:L/I:L/A:NCVSS Score: 4.0 (Medium)
Real-World Impact
Attackers can execute arbitrary code (ex. steal sensitive data from localStorage, notably access tokens, granting admin access if the admin user is successfully attacked)
Attack Prerequisites
Attacker Requirements:
Victim Requirements:
Environment:
Patches
Possible Fixes
Option 1: Use text interpolation and CSS for highlighting:
Option 2: Sanitize HTML before rendering:
Fixed in advplyr/audiobookshelf-app@b2c2b62