CSS injection using component islands and useHead in nuxt/nuxt

Valid

Reported on

Feb 6th 2023


Description

After a component island render, the resulting head is regex'd for tags. This regex is not very robust and can be tricked, allowing for CSS injection.

Proof of Concept

app.vue

<template>
  <div>
    Nuxt 3 Playground

    <Page :title="title" />
    <input v-model="title" type="text">
  </div>
</template>

<script setup lang="ts">

const title = ref()

</script>

nuxt.config.ts

export default defineNuxtConfig({
  experimental: {
    componentIslands: true
  }
})

page.server.vue

<template>
  <div>
    hello!
  </div>
</template>

<script setup lang="ts">

const props = defineProps<{title: string}>()

useHead({
  meta: {
    name: 'description',
    value: props.title ?? 'Hello!'!
  }
})

</script>

Using the payload:

><style>*{color:red}</style>

This payload works as < and > within attribute values are interpreted as closing and opening tags.

The impact here appears to be limited to CSS injection, it could possibly be raised to XSS but I could not figure this out as double quotes cannot be used.

Impact

CSS injection is mostly useful for defacement, but can also be used to steal information (see CSS keylogger).

Including user provided data within metadata is very common!

References

We are processing your report and will contact the nuxt team within 24 hours. 10 months ago
OhB00 modified the report
10 months ago
We have contacted a member of the nuxt team and are waiting to hear back 10 months ago
OhB00
10 months ago

Researcher


Try this in production mode if you're having trouble reproducing

OhB00 modified the report
10 months ago
OhB00
7 months ago

Researcher


This issue doesn't work on the new useHeadSafe function, but still works on the old function.

OhB00 modified the report
6 months ago
Daniel Roe
6 months ago

Maintainer


I think this is the purpose of useHeadSafe - sanitising user input. Wouldn't this also be a problem with any user-generated content in head?

e.g. ><script>console.log('hi')</script>

OhB00
6 months ago

Researcher


So useHeadSafe does sanitize input and it prevents users from escaping tags (like the previous title XSS bug). useHead also now does this for title, and it also does this for meta.

For example you cannot do "><script>blah" in our example because useHead is escaping the quotes within meta tags.

The issue occurs in Nuxt islands when this regex is used to parse the input back.

Essentially anywhere that you can place < or > characters without them being escaped will then be parsed by Nuxt Islands and either a link or a style can be injected.

As you know Vue will escape < and > into &gt; and &lt; to prevent XSS, however useHead only escapes " within attributes, as, in theory, you cannot perform any injection if you cannot escape the quote.

For example:

useHead({
  meta: {
    name: 'description',
    value: 'test <>"'
  },
});

The output of this will be:

<meta name="description"  value="test <>&quot;"

There is no injection bug here, but when combined with the issue in Nuxt Islands it is then possible to inject a script, as any time <style> is present within the head it will be injected.

While useHeadSafe does mitigate this issue, I don't think it makes much sense to provide useHead which does the same things but is vulnerable in a specific edge case of use. If this is the case I would recommend that the function is renamed to useHeadUnsafe as you cannot trust this function.

OhB00
4 months ago

Researcher


Fixed in 9b09b4d1127e3bbdbd5d97edd2309ad7f40463f9

OhB00 modified the report
4 months ago
Daniel Roe validated this vulnerability 4 months ago
OhB00 has been awarded the disclosure bounty
The fix bounty is now up for grabs
The researcher's credibility has increased: +7
Daniel Roe marked this as fixed in 3.7.0 with commit 9b09b4 4 months ago
The fix bounty has been dropped
This vulnerability will not receive a CVE
This vulnerability is scheduled to go public on Aug 14th 2023
Daniel Roe published this vulnerability 4 months ago
to join this conversation