Skip to content

Commit 60519b0

Browse files
committed
fix response splitting fps. Improve print and report of reflections
1 parent 3616ed9 commit 60519b0

File tree

2 files changed

+100
-64
lines changed

2 files changed

+100
-64
lines changed

pkg/report.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ type (
2020
URL string `json:"-"`
2121
Identifier string `json:"identifier"`
2222
Reason string `json:"reason"`
23-
Occurrences []string `json:"occurrences,omitempty"`
23+
Reflections []string `json:"reflections,omitempty"`
2424
Request reportRequest `json:"request"`
2525
SecondRequest *reportRequest `json:"secondRequest,omitempty"`
2626
}

pkg/requests.go

Lines changed: 99 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -47,89 +47,121 @@ func getRespSplit() string {
4747
return "\\r\\n" + RESP_SPLIT_HEADER + ": " + RESP_SPLIT_VALUE
4848
}
4949

50+
func getHeaderReflections(header http.Header, headersWithPoison []string) []string {
51+
var parts []string
52+
for _, name := range headersWithPoison {
53+
if val, ok := header[name]; ok {
54+
// strings.Join if a header has multiple values
55+
parts = append(parts, fmt.Sprintf("%s: %s", name, strings.Join(val, ",")))
56+
}
57+
}
58+
return parts
59+
}
60+
5061
func checkPoisoningIndicators(repResult *reportResult, repCheck reportCheck, success string, body string, poison string, statusCode1 int, statusCode2 int, sameBodyLength bool, header http.Header, recursive bool) []string {
5162
headersWithPoison := []string{}
52-
if strings.Contains(Config.ReasonTypes, "header") && header != nil && poison != "" && poison != "http" && poison != "https" && poison != "nothttps" && poison != "1" { // dont check for reflection of http/https/nothttps (used by forwarded headers), 1 (used by DOS) or empty poison
63+
// Response splitting check
64+
if strings.Contains(repCheck.Identifier, "response splitting") {
5365
for x := range header {
5466
if x == RESP_SPLIT_HEADER && header.Get(x) == RESP_SPLIT_VALUE {
5567
repCheck.Reason = "HTTP Response Splitting"
56-
}
57-
if strings.Contains(header.Get(x), poison) {
58-
headersWithPoison = append(headersWithPoison, x)
68+
break
5969
}
6070
}
61-
}
71+
if repCheck.Reason == "" {
72+
return headersWithPoison // no response splitting header found, return empty slice
73+
}
74+
// Other checks
75+
} else {
6276

63-
if repCheck.Reason == "" {
64-
// check for reflection in body
65-
if strings.Contains(Config.ReasonTypes, "body") && poison != "" && poison != "http" && poison != "https" && poison != "nothttps" && poison != "1" && strings.Contains(body, poison) { // dont check for reflection of http/https/nothttps (used by forwarded headers), 1 (used by DOS) or empty poison
66-
repCheck.Reason = fmt.Sprintf("Reflection Body: Response Body contained poison value %s %d times", poison, strings.Count(body, poison))
67-
repCheck.Occurrences = findOccurrencesWithContext(body, poison, 25)
68-
// check for reflection in headers
69-
} else if len(headersWithPoison) > 0 {
70-
repCheck.Reason = fmt.Sprintf("Reflection Header: Response Header(s) %s contained poison value %s", strings.Join(headersWithPoison, ", "), poison)
71-
// check for different status code
72-
} else if strings.Contains(Config.ReasonTypes, "status") && statusCode1 >= 0 && statusCode1 != Config.Website.StatusCode && statusCode1 == statusCode2 {
73-
// check if status code should be ignored
74-
for _, status := range Config.IgnoreStatus {
75-
if statusCode1 == status || Config.Website.StatusCode == status {
76-
PrintVerbose("Skipped Status Code "+strconv.Itoa(status)+"\n", Cyan, 1) // TODO is it necessary to check if default status code changed?
77-
return headersWithPoison
77+
if strings.Contains(Config.ReasonTypes, "header") && header != nil && poison != "" && poison != "http" && poison != "https" && poison != "nothttps" && poison != "1" { // dont check for reflection of http/https/nothttps (used by forwarded headers), 1 (used by DOS) or empty poison
78+
for x := range header {
79+
if x == RESP_SPLIT_HEADER && header.Get(x) == RESP_SPLIT_VALUE {
80+
repCheck.Reason = "HTTP Response Splitting"
81+
}
82+
if strings.Contains(header.Get(x), poison) {
83+
headersWithPoison = append(headersWithPoison, x)
7884
}
7985
}
86+
}
8087

81-
if !recursive {
82-
var tmpWebsite WebsiteStruct
83-
var err error
84-
85-
// try up to 3 times
86-
count := 3
87-
for i := range count {
88-
Print(fmt.Sprintln("Status Code", statusCode1, "differed from the default", Config.Website.StatusCode, ", sending verification request", i+1, "from up to 3"), Yellow)
89-
tmpWebsite, err = GetWebsite(Config.Website.Url.String(), true, true)
90-
if err == nil {
91-
Print(fmt.Sprintln("The verification request returned the Status Code", tmpWebsite.StatusCode), Yellow)
92-
break
88+
if repCheck.Reason == "" {
89+
// check for reflection in body
90+
if strings.Contains(Config.ReasonTypes, "body") && poison != "" && poison != "http" && poison != "https" && poison != "nothttps" && poison != "1" && strings.Contains(body, poison) { // dont check for reflection of http/https/nothttps (used by forwarded headers), 1 (used by DOS) or empty poison
91+
if len(headersWithPoison) > 0 {
92+
repCheck.Reason = fmt.Sprintf("Reflection Body and Header: Response Body contained poison value %s %d times and Response Header(s) %s contained poison value %s", poison, strings.Count(body, poison), strings.Join(headersWithPoison, ", "), poison)
93+
} else {
94+
repCheck.Reason = fmt.Sprintf("Reflection Body: Response Body contained poison value %s %d times", poison, strings.Count(body, poison))
95+
}
96+
repCheck.Reflections = findOccurrencesWithContext(body, poison, 25)
97+
repCheck.Reflections = append(repCheck.Reflections, getHeaderReflections(header, headersWithPoison)...)
98+
// check for reflection in headers
99+
} else if len(headersWithPoison) > 0 {
100+
repCheck.Reason = fmt.Sprintf("Reflection Header: Response Header(s) %s contained poison value %s", strings.Join(headersWithPoison, ", "), poison)
101+
repCheck.Reflections = getHeaderReflections(header, headersWithPoison)
102+
// check for different status code
103+
} else if strings.Contains(Config.ReasonTypes, "status") && statusCode1 >= 0 && statusCode1 != Config.Website.StatusCode && statusCode1 == statusCode2 {
104+
// check if status code should be ignored
105+
for _, status := range Config.IgnoreStatus {
106+
if statusCode1 == status || Config.Website.StatusCode == status {
107+
PrintVerbose("Skipped Status Code "+strconv.Itoa(status)+"\n", Cyan, 1) // TODO is it necessary to check if default status code changed?
108+
return headersWithPoison
93109
}
94110
}
95-
if err != nil {
96-
repResult.HasError = true
97-
msg := fmt.Sprintf("%s: couldn't verify if status code %d is the new default status code, because the verification encountered the following error %d times: %s", repCheck.URL, statusCode1, count, err.Error())
98-
repResult.ErrorMessages = append(repResult.ErrorMessages, msg)
111+
112+
if !recursive {
113+
var tmpWebsite WebsiteStruct
114+
var err error
115+
116+
// try up to 3 times
117+
count := 3
118+
for i := range count {
119+
Print(fmt.Sprintln("Status Code", statusCode1, "differed from the default", Config.Website.StatusCode, ", sending verification request", i+1, "from up to 3"), Yellow)
120+
tmpWebsite, err = GetWebsite(Config.Website.Url.String(), true, true)
121+
if err == nil {
122+
Print(fmt.Sprintln("The verification request returned the Status Code", tmpWebsite.StatusCode), Yellow)
123+
break
124+
}
125+
}
126+
if err != nil {
127+
repResult.HasError = true
128+
msg := fmt.Sprintf("%s: couldn't verify if status code %d is the new default status code, because the verification encountered the following error %d times: %s", repCheck.URL, statusCode1, count, err.Error())
129+
repResult.ErrorMessages = append(repResult.ErrorMessages, msg)
130+
} else {
131+
Config.Website = tmpWebsite
132+
}
133+
return checkPoisoningIndicators(repResult, repCheck, success, body, poison, statusCode1, statusCode2, sameBodyLength, header, true)
99134
} else {
100-
Config.Website = tmpWebsite
135+
repCheck.Reason = fmt.Sprintf("Changed Status Code: Status Code %d differed from %d", statusCode1, Config.Website.StatusCode)
101136
}
102-
return checkPoisoningIndicators(repResult, repCheck, success, body, poison, statusCode1, statusCode2, sameBodyLength, header, true)
103-
} else {
104-
repCheck.Reason = fmt.Sprintf("Changed Status Code: Status Code %d differed from %d", statusCode1, Config.Website.StatusCode)
105-
}
106-
// check for different body length
107-
} else if strings.Contains(Config.ReasonTypes, "length") && Config.CLDiff != 0 && success != "" && sameBodyLength && len(body) > 0 && compareLengths(len(body), len(Config.Website.Body), Config.CLDiff) {
108-
if !recursive {
109-
var tmpWebsite WebsiteStruct
110-
var err error
111-
112-
// try up to 3 times
113-
count := 3
114-
for range count {
115-
tmpWebsite, err = GetWebsite(Config.Website.Url.String(), true, true)
116-
if err == nil {
117-
break
137+
// check for different body length
138+
} else if strings.Contains(Config.ReasonTypes, "length") && Config.CLDiff != 0 && success != "" && sameBodyLength && len(body) > 0 && compareLengths(len(body), len(Config.Website.Body), Config.CLDiff) {
139+
if !recursive {
140+
var tmpWebsite WebsiteStruct
141+
var err error
142+
143+
// try up to 3 times
144+
count := 3
145+
for range count {
146+
tmpWebsite, err = GetWebsite(Config.Website.Url.String(), true, true)
147+
if err == nil {
148+
break
149+
}
118150
}
119-
}
120-
if err != nil {
121-
repResult.HasError = true
122-
msg := fmt.Sprintf("%s: couldn't verify if body length %d is the new default body length, because the verification request encountered the following error %d times: %s", repCheck.URL, statusCode1, count, err.Error())
123-
repResult.ErrorMessages = append(repResult.ErrorMessages, msg)
151+
if err != nil {
152+
repResult.HasError = true
153+
msg := fmt.Sprintf("%s: couldn't verify if body length %d is the new default body length, because the verification request encountered the following error %d times: %s", repCheck.URL, statusCode1, count, err.Error())
154+
repResult.ErrorMessages = append(repResult.ErrorMessages, msg)
155+
} else {
156+
Config.Website = tmpWebsite
157+
}
158+
return checkPoisoningIndicators(repResult, repCheck, success, body, poison, statusCode1, statusCode2, sameBodyLength, header, true)
124159
} else {
125-
Config.Website = tmpWebsite
160+
repCheck.Reason = fmt.Sprintf("Changed Content Length: Length %d differed more than %d bytes from normal length %d", len(body), Config.CLDiff, len(Config.Website.Body))
126161
}
127-
return checkPoisoningIndicators(repResult, repCheck, success, body, poison, statusCode1, statusCode2, sameBodyLength, header, true)
128162
} else {
129-
repCheck.Reason = fmt.Sprintf("Changed Content Length: Length %d differed more than %d bytes from normal length %d", len(body), Config.CLDiff, len(Config.Website.Body))
163+
return headersWithPoison
130164
}
131-
} else {
132-
return headersWithPoison
133165
}
134166
}
135167

@@ -139,6 +171,10 @@ func checkPoisoningIndicators(repResult *reportResult, repCheck reportCheck, suc
139171
Print(msg, Green)
140172
msg = "Reason: " + repCheck.Reason + "\n"
141173
Print(msg, Green)
174+
if len(repCheck.Reflections) > 0 {
175+
msg = "Reflections: " + strings.Join(repCheck.Reflections, " ... ") + "\n"
176+
Print(msg, Green)
177+
}
142178
msg = "Curl 1st Request: " + repCheck.Request.CurlCommand + "\n"
143179
Print(msg, Green)
144180
msg = "Curl 2nd Request: " + repCheck.SecondRequest.CurlCommand + "\n\n"

0 commit comments

Comments
 (0)