Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion pkg/fuzz/analyzers/xss/analyzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ func classifyAttributeContext(attrName, attrValue, tagName string) XSSContext {
}

if _, ok := urlAttrs[attrName]; ok {
trimmed := strings.TrimSpace(strings.ToLower(attrValue))
trimmed := stripC0Controls(strings.TrimSpace(strings.ToLower(attrValue)))
if strings.HasPrefix(trimmed, "javascript:") ||
strings.HasPrefix(trimmed, "vbscript:") ||
strings.HasPrefix(trimmed, "data:text/html") ||
Expand All @@ -408,6 +408,20 @@ func classifyAttributeContext(attrName, attrValue, tagName string) XSSContext {
return ContextHTMLAttribute
}

// stripC0Controls removes all C0 control characters (0x00-0x1F) and DEL (0x7F)
// from the string. Per the WHATWG URL spec, browsers silently strip these
// characters from URI schemes before interpretation, so an attacker can bypass
// naive prefix checks by inserting them (e.g. "java\x00script:" is treated as
// "javascript:" by browsers).
func stripC0Controls(s string) string {
return strings.Map(func(r rune) rune {
if r <= 0x1F || r == 0x7F {
return -1 // drop the character
}
return r
}, s)
}

// containsMarker does a case-insensitive substring check.
// markerLower must already be lowercased by the caller.
func containsMarker(text, markerLower string) bool {
Expand Down
56 changes: 56 additions & 0 deletions pkg/fuzz/analyzers/xss/analyzer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,62 @@ func TestAnalyzeReflectionContext(t *testing.T) {
expected: ContextHTMLAttributeURL,
},

// === C0 control character bypass in javascript: URI ===
{
name: "NULL byte in javascript: URI scheme",
body: "<a href=\"java\x00script:alert('FUZZ1337MARKER')\">xss</a>",
marker: marker,
expected: ContextScript,
},
{
name: "form feed (0x0C) in javascript: URI scheme",
body: "<a href=\"java\x0cscript:alert('FUZZ1337MARKER')\">xss</a>",
marker: marker,
expected: ContextScript,
},
{
name: "vertical tab (0x0B) in javascript: URI scheme",
body: "<a href=\"java\x0bscript:alert('FUZZ1337MARKER')\">xss</a>",
marker: marker,
expected: ContextScript,
},
{
name: "bell (0x07) in javascript: URI scheme",
body: "<a href=\"\x07javascript:alert('FUZZ1337MARKER')\">xss</a>",
marker: marker,
expected: ContextScript,
},
{
name: "backspace (0x08) in javascript: URI scheme",
body: "<a href=\"java\x08script:alert('FUZZ1337MARKER')\">xss</a>",
marker: marker,
expected: ContextScript,
},
{
name: "DEL (0x7F) in javascript: URI scheme",
body: "<a href=\"java\x7fscript:alert('FUZZ1337MARKER')\">xss</a>",
marker: marker,
expected: ContextScript,
},
{
name: "multiple mixed C0 control chars in javascript: URI",
body: "<a href=\"\x01j\x02a\x03v\x04a\x05s\x06c\x07r\x08i\x0ep\x0ft:alert('FUZZ1337MARKER')\">xss</a>",
marker: marker,
expected: ContextScript,
},
{
name: "C0 chars in vbscript: URI in href",
body: "<a href=\"\x00vb\x0bscript:msgbox(FUZZ1337MARKER)\">click</a>",
marker: marker,
expected: ContextScript,
},
{
name: "C0 chars in data:text/html URI in iframe src",
body: "<iframe src=\"\x0cdata:text/html,<h1>FUZZ1337MARKER</h1>\">",
marker: marker,
expected: ContextScript,
},

// === ScriptData Context (non-executable script) ===
{
name: "reflection in script type=application/json",
Expand Down