daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

vuejs.md (10826B)


      1 ---
      2 title: "Vue.js"
      3 section: "Network Services"
      4 sectionSlug: "network-services-pentesting"
      5 sourcePath: "src/network-services-pentesting/pentesting-web/vuejs.md"
      6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/network-services-pentesting/pentesting-web/vuejs.md"
      7 sha: "188de82beb54e70956b2952367a0af91d26758b8"
      8 isIndex: false
      9 modified: true
     10 license: "CC-BY-NC-4.0"
     11 ---
     12 
     13 # Vue.js
     14 
     15 ## XSS Sinks in Vue.js
     16 
     17 ### v-html Directive
     18 The `v-html` directive renders **raw** HTML. `<script>` elements inserted through the underlying `innerHTML` operation are normally inert, but active attributes and elements such as an image `onerror` can still produce XSS. Never pass unsanitized user input to `v-html`.<sup>[[1]](#references)[[3]](#references)</sup>
     19 
     20 ```html
     21 <div id="app">
     22   <div v-html="htmlContent"></div>
     23 </div>
     24 <script>
     25   new Vue({
     26     el: '#app',
     27     data: {
     28       htmlContent: '<img src=x onerror=alert(1)>'
     29     }
     30   })
     31 </script>
     32 ```
     33 
     34 ### v-bind with src or href
     35 Binding a user string to URL-bearing attributes (`href`, `src`, `xlink:href`, `formaction` …) lets payloads such as `javascript:alert(1)` run when the link is followed.<sup>[[2]](#references)[[3]](#references)</sup>
     36 
     37 ```html
     38 <div id="app">
     39   <a v-bind:href="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/network-services-pentesting/pentesting-web/userInput">Click me</a>
     40 </div>
     41 <script>
     42   new Vue({
     43     el: '#app',
     44     data: {
     45       userInput: 'javascript:alert(1)'
     46     }
     47   })
     48 </script>
     49 ```
     50 
     51 ### v-on with user-controlled handlers
     52 If an attacker controls a Vue **template**, directive expressions such as `v-on:click="..."` become code in the compiled render function. Merely storing the string `alert(1)` in a data property does not execute it as JavaScript; the dangerous condition is untrusted template source.<sup>[[3]](#references)</sup>
     53 
     54 ```html
     55 <div id="app">
     56   <!-- Dangerous if an attacker controls this template expression: -->
     57   <button v-on:click="alert(document.domain)">Click me</button>
     58 </div>
     59 <script>
     60   new Vue({
     61     el: '#app',
     62     data: {}
     63   })
     64 </script>
     65 ```
     66 
     67 ### Dynamic attribute / event names
     68 User-supplied names in `v-bind:[attr]` or `v-on:[event]` expand the attack surface and can bypass application allowlists. Vue applies its own attribute/event handling and CSP still applies, so validate behavior against the deployed Vue version rather than assuming every `on*` name becomes a native handler.<sup>[[3]](#references)</sup>
     69 
     70 ```html
     71 <img v-bind:[userAttr]="payload">
     72 <!-- userAttr = 'onerror', payload = 'alert(1)' -->
     73 ```
     74 
     75 ### SSR attribute-name splitting in object `v-bind`
     76 
     77 When auditing SSR, test the **keys** of objects spread with `v-bind="attrs"`, not only their values. `@vue/server-renderer` through 3.5.41 checked dynamic attribute names against a blacklist that omitted carriage return (`U+000D`). A raw CR in an attacker-controlled key therefore reached the HTML response; the browser normalised it to LF and split one intended name into several attributes. This can create a zero-interaction XSS when an object from a CMS, form builder, API or stored JSON is spread onto an element. The published advisory listed no patched version at publication time.<sup>[[9]](#references)</sup>
     78 
     79 ```javascript
     80 // Reaches <input v-bind="attrs"> during SSR
     81 const attrs = {
     82   ['x\rautofocus\ronfocus']: 'alert(document.domain)'
     83 }
     84 // Browser parses: x="" autofocus="" onfocus="alert(document.domain)"
     85 ```
     86 
     87 This is an **SSR-only** parser differential: client-side Vue uses `setAttribute()`, which rejects such a name. Send the key through the real input path (for example, JSON `"x\rautofocus\ronfocus"`), inspect the raw response bytes before hydration, and verify whether intermediaries preserve the CR. A source-code review should trace both property names and values into `v-bind="object"` or equivalent render-function props.<sup>[[9]](#references)</sup>
     88 
     89 ### Dynamic component (`<component :is>`)
     90 Allowing an untrusted component identifier in `:is` may expose components that were not intended for the user. A string containing `<script>` is not automatically compiled as an inline template; server compromise requires a separate path that treats attacker input as template source.<sup>[[3]](#references)</sup>
     91 
     92 ```html
     93 <component :is="userChoice"></component>
     94 <!-- userChoice must be restricted to an allowlist of intended components -->
     95 ```
     96 
     97 ### Untrusted templates in SSR
     98 During server-side rendering, the template runs **on your server**; injecting user HTML can escalate XSS to full Remote Code Execution (RCE). CVEs in `vue-template-compiler` prove the risk.<sup>[[3]](#references)</sup>
     99 
    100 ```javascript
    101 // DANGER – never do this
    102 const app = createSSRApp({ template: userProvidedHtml })
    103 ```
    104 
    105 Vue CSTI payloads and version-specific compiler gadgets are documented in [Client Side Template Injection](/hacktricks/pentesting-web/client-side-template-injection-csti); keep this page focused on Vue-specific sinks and SSR boundaries.
    106 
    107 ### Filters / render functions that eval
    108 Legacy filters that build render strings or call `eval`/`new Function` on user data are another XSS vector—replace them with computed properties.<sup>[[3]](#references)</sup>
    109 
    110 ```javascript
    111 Vue.filter('run', code => eval(code))   // DANGER
    112 ```
    113 
    114 ---
    115 
    116 ## Other Common Vulnerabilities in Vue Projects
    117 
    118 ### Prototype pollution and HTML-context confusion in `vue-i18n`
    119 `vue-i18n`'s flat-JSON resolver has accepted `__proto__` paths that write to `Object.prototype`. Trace whether an attacker can supply locale objects or remote translation bundles, then look for polluted properties reaching a secondary gadget rather than treating the write alone as code execution.<sup>[[4]](#references)</sup>
    120 
    121 ```javascript
    122 handleFlatJson({ '__proto__.polluted': 'yes' })
    123 console.log(({}).polluted) // "yes" on affected versions
    124 ```
    125 
    126 Do not treat `escapeParameterHtml: true` as a sanitizer for the **final translation string**. Affected `vue-i18n` releases escaped interpolated parameters as text but could still produce executable markup when a translation placed the parameter in an HTML attribute and the result was passed to `v-html`. This is particularly relevant for remotely managed translations and low-privileged locale editors.<sup>[[10]](#references)</sup>
    127 
    128 ```html
    129 <!-- message: <img src=x onerror="{payload}"> -->
    130 <p v-html="$t('warning', { payload: 'alert(document.domain)' })"></p>
    131 ```
    132 
    133 Audit every `$t()`/`t()` flow into `v-html` and sanitize the complete rendered HTML under an explicit policy. The reviewed advisory identifies 9.14.5, 10.0.8 and 11.1.10 as the first patched release in each maintained branch.<sup>[[10]](#references)</sup>
    134 
    135 ### Open redirects with vue-router
    136 Applications sometimes use an unchecked `next` value with `window.location` or an external-navigation helper, creating an open redirect. The earlier page specifically named `router.push`, `<router-link>`, and a `javascript:` destination. Preserve those as test inputs for application wrappers and dynamic URL props, but Vue Router navigation is normally route-oriented, so do not assume the bare router accepts every external scheme without validating the deployed version and normalization path.<sup>[[3]](#references)[[6]](#references)</sup>
    137 
    138 ```javascript
    139 window.location.assign(this.$route.query.next) // DANGER: validate origin/scheme
    140 ```
    141 
    142 Prefer a server-side mapping from a short identifier to an approved destination; if a URL must be accepted, apply an allowlist rather than a denylist.<sup>[[6]](#references)</sup>
    143 
    144 ### CSRF in Axios / fetch
    145 SPAs still need deliberate CSRF defenses. `SameSite=Lax` or `Strict` blocks cookies on many cross-site requests, but coverage depends on method, navigation context, browser behavior, and whether sibling subdomains are attacker-controlled.<sup>[[7]](#references)</sup>
    146 
    147 ```javascript
    148 axios.post('/api/transfer', data, {
    149   headers: { 'X-CSRF-TOKEN': token }
    150 })
    151 ```
    152 
    153 ### Click-jacking
    154 Prevent framing with CSP `frame-ancestors`; `X-Frame-Options` is a useful legacy fallback. Sending both is defense in depth, not a requirement for standards-compliant modern browsers.<sup>[[8]](#references)</sup>
    155 
    156 ```http
    157 X-Frame-Options: DENY
    158 Content-Security-Policy: frame-ancestors 'none';
    159 ```
    160 
    161 ### Content-Security-Policy pitfalls
    162 Runtime template compilation can conflict with a strict CSP. Prefer a runtime-only build with precompiled templates so the application does not need eval-like compilation; confirm the exact Vue version and build rather than adding `'unsafe-eval'` blindly.<sup>[[3]](#references)</sup>
    163 
    164 ```http
    165 Content-Security-Policy: default-src 'self'; script-src 'self';
    166 ```
    167 
    168 ### Supply-chain attacks (node-ipc – March 2022)
    169 The sabotage of **node-ipc**—pulled by Vue CLI—showed how a transitive dependency can run arbitrary code on dev machines. Pin versions and audit often.<sup>[[5]](#references)</sup>
    170 
    171 ```bash
    172 npm ci --ignore-scripts   # safer install
    173 ```
    174 
    175 ---
    176 
    177 ## Hardening Checklist
    178 
    179 1. **Sanitise** every string before it hits `v-html` (DOMPurify).
    180 2. **Whitelist** allowed schemes, attributes, components, and events; never spread attacker-controlled property names into SSR elements.
    181 3. **Avoid `eval`** and dynamic templates altogether.
    182 4. **Patch dependencies weekly** and monitor advisories.
    183 5. **Send strong HTTP headers** (CSP, HSTS, XFO, CSRF).
    184 6. **Lock your supply chain** with audits, lockfiles, and signed commits.
    185 
    186 
    187 ## References
    188 
    189 - [1] [Vue XSS Guide: Examples and Prevention](https://www.stackhawk.com/blog/vue-xss-guide-examples-and-prevention/)
    190 - [2] [Vue JS Security](https://medium.com/@isaacwangethi30/vue-js-security-6e246a7613da)
    191 - [3] [Security | Vue.js](https://vuejs.org/guide/best-practices/security)
    192 - [4] [Vue I18n Allows Prototype Pollution in handleFlatJson (GHSA-p2ph-7g93-hw3m)](https://github.com/advisories/GHSA-p2ph-7g93-hw3m)
    193 - [5] [Alert: peacenotwar module sabotages npm developers in the node-ipc package to protest the invasion of Ukraine](https://snyk.io/blog/peacenotwar-malicious-npm-node-ipc-package-vulnerability/)
    194 - [6] [OWASP - Unvalidated Redirects and Forwards Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html)
    195 - [7] [MDN - Secure cookie configuration and `SameSite`](https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides/Cookies)
    196 - [8] [MDN - Clickjacking defenses](https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/Clickjacking)
    197 - [9] [XSS in `@vue/server-renderer` via missing CR in attribute-name blacklist](https://github.com/vuejs/core/security/advisories/GHSA-g2v6-rqmx-r4w6)
    198 - [10] [`vue-i18n`'s `escapeParameterHtml` does not prevent DOM-based XSS through tag attributes](https://github.com/advisories/GHSA-x8qp-wqqm-57ph)