Skip to content

fix: escape special_anchor in HtmlOutput JS string literal#1337

Merged
karlkleinpaste merged 1 commit into
crosswire:masterfrom
hyder365:fix/biblesync-xss-special-anchor
Jun 30, 2026
Merged

fix: escape special_anchor in HtmlOutput JS string literal#1337
karlkleinpaste merged 1 commit into
crosswire:masterfrom
hyder365:fix/biblesync-xss-special-anchor

Conversation

@hyder365

Copy link
Copy Markdown
Contributor

HtmlOutput() in src/gtk/utilities.c interpolates settings.special_anchor into a JavaScript string literal inside a <script> tag via g_strdup_printf("window.location.hash = "%s"") with no escaping.

special_anchor is set in sword_uri() (src/main/url.cc:759) from the '#' or '!' fragment of sword:// URLs. These URLs are constructed from untrusted BibleSync navigation packets at biblesync_glue.cc:179, where a LAN peer controls the bible and ref fields directly from the UDP packet.

A crafted ref containing #"alert(1);}// breaks out of the JS string literal and executes arbitrary script in the JS-enabled WebKit2 view with file:// origin, when a user has BibleSync enabled.

The fix escapes the anchor value with g_strescape() before interpolation into the script tag, neutralizing backslash, double-quote, and other JS string literal metacharacters. The unescaped value is still used for XIPHOS_HTML_JUMP_TO_ANCHOR which handles it as an HTML anchor name, not executable code.

HtmlOutput() interpolates settings.special_anchor into a JavaScript
string literal via g_strdup_printf("%s") inside a <script> tag.
The value originates from the '#' or '!' fragment of sword:// URLs,
which are constructed from untrusted BibleSync navigation packets
(biblesync_glue.cc:179) and module content hyperlinks
(wk-html.c:96).  A crafted ref containing #";alert(1);}// breaks
out of the JS string and executes arbitrary script in the
JS-enabled WebKit2 view with file:// origin.

Escape the anchor with g_strescape() before interpolation to
neutralize backslash, double-quote, and other JS string literal
metacharacters.
@karlkleinpaste karlkleinpaste merged commit 5ef0e33 into crosswire:master Jun 30, 2026
7 checks passed
hyder365 added a commit to hyder365/xiphos that referenced this pull request Jun 30, 2026
g_strescape() escapes backslash, double-quote, and C0 control bytes but
leaves '<', '>', and '/' untouched, so a sword:// fragment of
"</script><script>PAYLOAD</script>" passes through unchanged when
interpolated into the inline <script> block in HtmlOutput().  WebKit's
HTML parser ends the <script> block at the literal '</script>' and
starts a new <script>PAYLOAD</script> block, which executes arbitrary
JavaScript in the file:// origin that wk_html_set_base_uri() hardcodes.

The fragment can originate from any BibleSync UDP multicast peer on the
LAN (biblesync_glue.cc:179 constructs the sword:// URL from peer-supplied
bible and ref), from argv[1], or from a sword:// href click inside a
malicious SWORD module.

Substitute '</' with '<\/' in the already-escaped anchor (OWASP
recommendation) so the HTML parser can no longer see the end of the
script block.  g_strescape is kept in place to also handle the
backslash-and-quote break-out case from the prior fix (crosswire#1337).
hyder365 added a commit to hyder365/xiphos that referenced this pull request Jul 1, 2026
g_strescape() escapes backslash, double-quote, and C0 control bytes but
leaves '<', '>', and '/' untouched, so a sword:// fragment of
"</script><script>PAYLOAD</script>" passes through unchanged when
interpolated into the inline <script> block in HtmlOutput().  WebKit's
HTML parser ends the <script> block at the literal '</script>' and
starts a new <script>PAYLOAD</script> block, which executes arbitrary
JavaScript in the file:// origin that wk_html_set_base_uri() hardcodes.

The fragment can originate from any BibleSync UDP multicast peer on the
LAN (biblesync_glue.cc:179 constructs the sword:// URL from peer-supplied
bible and ref), from argv[1], or from a sword:// href click inside a
malicious SWORD module.

Substitute '</' with '<\/' in the already-escaped anchor (OWASP
recommendation) so the HTML parser can no longer see the end of the
script block.  g_strescape is kept in place to also handle the
backslash-and-quote break-out case from the prior fix (crosswire#1337).
karlkleinpaste pushed a commit that referenced this pull request Jul 1, 2026
g_strescape() escapes backslash, double-quote, and C0 control bytes but
leaves '<', '>', and '/' untouched, so a sword:// fragment of
"</script><script>PAYLOAD</script>" passes through unchanged when
interpolated into the inline <script> block in HtmlOutput().  WebKit's
HTML parser ends the <script> block at the literal '</script>' and
starts a new <script>PAYLOAD</script> block, which executes arbitrary
JavaScript in the file:// origin that wk_html_set_base_uri() hardcodes.

The fragment can originate from any BibleSync UDP multicast peer on the
LAN (biblesync_glue.cc:179 constructs the sword:// URL from peer-supplied
bible and ref), from argv[1], or from a sword:// href click inside a
malicious SWORD module.

Substitute '</' with '<\/' in the already-escaped anchor (OWASP
recommendation) so the HTML parser can no longer see the end of the
script block.  g_strescape is kept in place to also handle the
backslash-and-quote break-out case from the prior fix (#1337).

Co-authored-by: Devon Kirk <hyder365@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants