These two challenges require reading flag in the /flag/
path via SSRF.
(Web) Baby Simple Gocurl
1 | n.startsWith("https://") || n.startsWith("http://") ? window.open(n, "_self") : r.router.set(a.redirect_link ? n : "/portal") |
1 | r.GET("/flag/", func(c *gin.Context) { |
first, to read the Flag
, a request must be made using the 127.0.0.1
IP.
1 | r.GET("/curl/", func(c *gin.Context) { |
in this challenge, we can use the http module to send a request to the desired web service and get the response value.
Get the parameter values of url
, header_key
, and header_value
from the parameters received from the user.
1 | if c.ClientIP() != "127.0.0.1" && (strings.Contains(reqUrl, "flag") || strings.Contains(reqUrl, "curl") || strings.Contains(reqUrl, "%")) { |
but this challenge has a validation process as above. The IP of the user we currently delivered must be 127.0.0.1
, and the characters flag
, curl
, %
must not be included in the url value we sent.
1 | if c.ClientIP() != "127.0.0.1" && (strings.Contains(reqUrl, "flag") || strings.Contains(reqUrl, "curl") || strings.Contains(reqUrl, "%")) |
oops but the conditional statement is a bit weird. we can bypass this by making it operate like false && true.
1 | // https://github.com/gin-gonic/gin/blob/457fabd7e14f36ca1b5f302f7247efeb4690e49c/context.go#L768 |
There is a comment as above in the part where the ClientIP() function is defined.
(Web) Adult Simple Gocurl
1 | if strings.Contains(reqUrl, "flag") || strings.Contains(reqUrl, "curl") || strings.Contains(reqUrl, "%") { |
all the code in this challenge is the same as baby. but the difference is the conditional statement above. we can’t bypass the conditional now
1 | if reqHeaderKey != "" || reqHeaderValue != "" { |
But we can add any HTTP headers we want using header append logic
1 | => https://issues.redhat.com/browse/UNDERTOW-990?workflowName=GIT+Pull+Request+workflow+&stepId=5 |
Let’s see the above before the exploit. we can see that it sets the X-Forwarded-Prefix: /test-service
header when requesting /docs. as a result, the location to be redirected normally is /docs/index.html
, but it is redirected to /test-service/docs/index.html
.
in other words, if we send a request to a place that returns a 302 response and send the X-Forwarded-Prefix
header together, we can send the request to the desired path.
1 | [GIN-debug] redirecting request 301: / --> / |
if we send a request to http://127.0.0.1:8080//
, we can see a 302 redirect back to the normalized path after normalizing the path. we figured out how to send a request to a place with a redirect response.
1 | [GIN-debug] redirecting request 301: /flag// --> /flag// |
if we send a request like http://localhost:8080/curl/?url=http://127.0.0.1:8080//&header_key=X-Forwarded-Prefix&header_value=/flag
, the redirect is executed. We can see that during this process we normalize the path using the X-Forwarded-Prefix
header and send the request to /flag/.