Vulnerability Report: codetabs Proxy Endpoint Non-Blind SSRF
Vulnerability Submission Assessment
Vulnerability Chain Exists: Yes
Submission Verdict
| Attribute |
Value |
| Vulnerability Type |
A10: Server-Side Request Forgery (SSRF) |
| Exploitation Method |
Unauthenticated |
| CVSS 3.1 |
8.6 High (AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:L/A:L) |
| Submit for Report |
Yes |
Rationale
Based on the rule in 4.5.1 SSRF Direct Impact Determination: "SSRF via proxy/import functionality with response echo → Submit", and the rule in Step 2: SSRF Specific Determination: "Able to access internal services and return data → Submit", this vulnerability meets the submission criteria.
Additionally, the quick-reference appendix explicitly lists SSRF + Cloud Metadata / Internal Network Echo as high-severity and reportable.
Details
The vulnerability exists in a publicly accessible proxy endpoint. An attacker can specify an arbitrary target address via the quest parameter without authentication. The server then issues an HTTP request to that address and returns the response body directly.
This means an attacker can read data from internal HTTP services, localhost services, and cloud metadata endpoints — going beyond mere port scanning and constituting a full data-exfiltration SSRF.
Next Steps
Proceed to generate the complete vulnerability report.
1. Product Overview
codetabs is a multi-functional HTTP API service implemented in Go, providing capabilities such as code statistics, proxying, header retrieval, geolocation, weather, and random number generation. The project exposes multiple /v1/ routes via http.ServeMux, among which /v1/proxy/ is responsible for forwarding requests to target URLs and returning the results.
2. Vulnerability Description
| Attribute |
Value |
| Vulnerability Type |
CWE-918: Server-Side Request Forgery (SSRF) |
| CVSS 3.1 |
8.6 High (AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:L/A:L) |
| Vulnerable File |
project/codetabs/proxy/proxy.go (22–103) |
| Prerequisites |
No login required, no special privileges needed |
| Entry Point |
/v1/proxy/ (public endpoint, unauthenticated) |
- Core Impact: An attacker can leverage the server's network position to access internal networks, localhost, or cloud metadata addresses and directly read the response content.
- Environmental Constraints: The target service must have
proxy.Router enabled, and the server must have network reachability to the target address.
- Default Trigger Conditions: The route
/v1/proxy/ is registered by default. In the current version, isBanned() always returns false, meaning exploitation conditions are satisfied by default.
3. Affected Scope
- Affected Versions: The version corresponding to the current project code, at minimum any version containing the implementation in
project/codetabs/proxy/proxy.go.
- Unaffected Versions: Versions that do not expose the
/v1/proxy/ route, or that have added target address allowlisting, internal/metadata address blocking, or have disabled arbitrary proxy capability.
- Trigger Conditions and Defaults:
- Route enabled: Enabled by default.
- Authentication requirement: None.
- Target restrictions: No host, IP, port, or protocol restrictions.
- Redirect restrictions: Not configured.
4. Vulnerability Details
4.1 Code Audit Analysis
The proxy route is registered in main.go:
mux.HandleFunc("/v1/proxy/", mw(proxy.Router, "proxy", c))
Although the middleware calls isBanned(r) for the proxy service, the current implementation of isBanned() always returns false:
func isBanned(r *http.Request) bool {
return false
}
This means there is no effective access control in the current version, and any external user can request this endpoint.
In proxy.Router(), the application reads the attacker-controlled quest value directly from the query parameters:
r.ParseForm()
p.quest = r.Form.Get("quest")
Subsequently, in doProxyRequest(), only the protocol prefix is stripped and re-prepended:
p.quest = "http://" + u.RemoveProtocolFromURL(p.quest)
RemoveProtocolFromURL() merely removes the http:// or https:// prefix and does not validate whether the target address belongs to an internal network, loopback address, or cloud metadata address:
func RemoveProtocolFromURL(url string) string {
if strings.HasPrefix(url, "https://") {
return url[8:]
}
if strings.HasPrefix(url, "https:/") {
return url[7:]
}
if strings.HasPrefix(url, "http://") {
return url[7:]
}
if strings.HasPrefix(url, "http:/") {
return url[6:]
}
return url
}
Finally, the request is issued directly by the server:
var netClient = &http.Client{
Timeout: time.Second * 10,
}
resp, err := netClient.Get(p.quest)
No CheckRedirect is set here, so Go's default behavior of automatically following redirects applies, which can be used to assist SSRF via redirect chains.
The response logic also performs no security filtering:
Key code snippet:
if strings.Contains(contentType, "application/json") {
json.NewDecoder(resp.Body).Decode(&data)
if data != nil {
w.Header().Set("Content-Type", "application/json")
u.SendJSONToClient(w, data, 200)
return
}
}
reader := bufio.NewReader(resp.Body)
for {
line, err := reader.ReadBytes('\n')
if err != nil {
if err != io.EOF {
log.Printf("Error PROXY-2: %s => %v\n", p.quest, err)
}
w.Write([]byte(fmt.Sprintf("%v", string(line))))
return
}
w.Write([]byte(fmt.Sprintf("%v", string(line))))
}
The vulnerability chain can be summarized as:
External request to /v1/proxy/?quest=<target address>
→ Server reads quest
→ Strips protocol prefix and prepends http://
→ Server issues GET request to attacker-specified address
→ Target response content is returned directly to attacker
→ Non-blind SSRF achieved
4.2 PoC Construction
This endpoint is designed as a "proxy content fetcher," making it an ideal SSRF verification point. Constructing a PoC only requires controlling the quest parameter — no additional authentication or special request headers are needed.
Key payload design points:
- The target address does not need to include a protocol; the server will automatically prepend
http://.
- Localhost addresses can be used directly, e.g.,
127.0.0.1:80/.
- Cloud metadata addresses can be used, e.g.,
169.254.169.254/latest/meta-data/.
- If the target returns JSON, the endpoint will parse and re-return the JSON, demonstrating response echo.
- If the target returns plain text, the endpoint outputs the text line by line, suitable for reading internal service pages or debug endpoints.
- Since Go follows redirects by default, an externally controlled site returning
302 Location: http://127.0.0.1:8080/ can also be used to assist SSRF.
5. Proof of Concept (Reproduction)
5.1 Environment Setup
- Target: codetabs
- Frontend address:
http://127.0.0.1:3000/
- Reproduction method: Direct access to the proxy endpoint
- Authentication state: Not logged in
5.2 Reproduction Steps
Step 1: Confirm the proxy endpoint is accessible
Access http://127.0.0.1:3000/v1/proxy/?quest=127.0.0.1:80/. The server will issue a request to 127.0.0.1:80 on localhost.
If a web service exists on the target port, the page content will appear directly in the response; if no service exists, an error indicating the resource is unavailable will be returned.
Step 2: Verify ability to read cloud metadata or internal network resources
Access http://127.0.0.1:3000/v1/proxy/?quest=169.254.169.254/latest/meta-data/.
If the runtime environment can reach the cloud metadata endpoint, the response will directly display metadata paths or specific content.
Similarly, this can be replaced with an internal HTTP service address, e.g., 10.0.0.5:8080/ or 192.168.1.10:8080/health.
Step 3: Verify that response content is visible to the attacker
Point quest at an internal endpoint that returns JSON, e.g., 127.0.0.1:8080/debug.
After the server requests that address, if the response Content-Type is application/json, the data will be parsed and re-output as JSON.
If the response is plain text, it will be output verbatim. This directly proves the vulnerability is a non-blind SSRF rather than mere connectivity probing.
5.3 Result Verification
| Verification Item |
Result |
Request quest=127.0.0.1:80/ |
Server issues request to localhost; if an HTTP service is running, its page content is returned |
Request quest=169.254.169.254/latest/meta-data/ |
If the environment can reach the cloud metadata address, content is directly readable |
| Request internal JSON endpoint |
Response data is re-encoded as JSON by the server and returned |
| Request text-based internal page |
Response body is returned line by line; attacker can view content directly |
| Redirect-assisted SSRF |
Since CheckRedirect is not set on the client, automatic redirect following is supported by default |
5.4 Attack Chain Diagram
Attacker
→ Accesses /v1/proxy/?quest=<internal or metadata address>
→ Server issues request
→ Internal target returns response
→ Proxy endpoint returns response content to attacker
→ SSRF confirmed
6. POC
A Python verification script has been generated in the repository: poc_codetabs_proxy_ssrf.py
Command-line examples:
python .\poc_codetabs_proxy_ssrf.py --base-url http://127.0.0.1:3000 --target-url 127.0.0.1:80/
python .\poc_codetabs_proxy_ssrf.py --base-url http://127.0.0.1:3000 --target-url 169.254.169.254/latest/meta-data/
Equivalent raw request examples:
curl "http://127.0.0.1:3000/v1/proxy/?quest=127.0.0.1:80/"
curl "http://127.0.0.1:3000/v1/proxy/?quest=169.254.169.254/latest/meta-data/"
7. Remediation Recommendations
-
Remove arbitrary proxy capability
If the business does not require fetching from arbitrary targets, take down the /v1/proxy/ route entirely.
-
Enforce a strict allowlist
Only allow access to explicitly allowlisted domains; prohibit users from submitting arbitrary URLs.
Allowlist validation should be performed after URL parsing, based on the normalized hostname and resolved IP addresses.
-
Block access to internal, loopback, link-local, and cloud metadata addresses
The following targets must be blocked:
127.0.0.0/8
10.0.0.0/8
172.16.0.0/12
192.168.0.0/16
169.254.0.0/16
::1, fc00::/7, fe80::/10
- Cloud provider metadata-specific addresses
-
Restrict protocols and ports
Only allow https; prohibit dangerous protocols such as file, gopher, and ftp.
Only allow access to ports required by the business.
-
Disable automatic redirect following
Set CheckRedirect on http.Client to prevent bypassing allowlists or probing strategies via 302 redirects.
-
Do not echo target responses directly
If proxy capability must be retained, consider returning only a fixed-format summary rather than the full response body, to avoid turning it into a "data-exfiltration SSRF."
-
Audit and strengthen access control
The current isBanned() always returns false and provides no real protection. Rate limiting, authentication, logging/alerting, and source control should be added.
Remediation Example
parsedURL, err := url.ParseRequestURI(userInput)
if err != nil {
return errors.New("invalid url")
}
host := parsedURL.Hostname()
ips, err := net.LookupIP(host)
if err != nil || len(ips) == 0 {
return errors.New("unresolvable host")
}
for _, ip := range ips {
if isPrivateOrLoopback(ip) || isMetadataIP(ip) {
return errors.New("forbidden target")
}
}
client := &http.Client{
Timeout: 5 * time.Second,
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
This report contains no screenshots as required; evidence is based on source code location, exploitation path, and an executable PoC.
Vulnerability Report: codetabs Proxy Endpoint Non-Blind SSRF
Vulnerability Submission Assessment
Vulnerability Chain Exists: Yes
Submission Verdict
Rationale
Based on the rule in
4.5.1 SSRF Direct Impact Determination: "SSRF via proxy/import functionality with response echo → Submit", and the rule inStep 2: SSRF Specific Determination: "Able to access internal services and return data → Submit", this vulnerability meets the submission criteria.Additionally, the quick-reference appendix explicitly lists
SSRF + Cloud Metadata / Internal Network Echoas high-severity and reportable.Details
The vulnerability exists in a publicly accessible proxy endpoint. An attacker can specify an arbitrary target address via the
questparameter without authentication. The server then issues an HTTP request to that address and returns the response body directly.This means an attacker can read data from internal HTTP services, localhost services, and cloud metadata endpoints — going beyond mere port scanning and constituting a full data-exfiltration SSRF.
Next Steps
Proceed to generate the complete vulnerability report.
1. Product Overview
codetabs is a multi-functional HTTP API service implemented in Go, providing capabilities such as code statistics, proxying, header retrieval, geolocation, weather, and random number generation. The project exposes multiple
/v1/routes viahttp.ServeMux, among which/v1/proxy/is responsible for forwarding requests to target URLs and returning the results.2. Vulnerability Description
project/codetabs/proxy/proxy.go(22–103)/v1/proxy/(public endpoint, unauthenticated)proxy.Routerenabled, and the server must have network reachability to the target address./v1/proxy/is registered by default. In the current version,isBanned()always returnsfalse, meaning exploitation conditions are satisfied by default.3. Affected Scope
project/codetabs/proxy/proxy.go./v1/proxy/route, or that have added target address allowlisting, internal/metadata address blocking, or have disabled arbitrary proxy capability.4. Vulnerability Details
4.1 Code Audit Analysis
The proxy route is registered in
main.go:Although the middleware calls
isBanned(r)for theproxyservice, the current implementation ofisBanned()always returnsfalse:This means there is no effective access control in the current version, and any external user can request this endpoint.
In
proxy.Router(), the application reads the attacker-controlledquestvalue directly from the query parameters:Subsequently, in
doProxyRequest(), only the protocol prefix is stripped and re-prepended:RemoveProtocolFromURL()merely removes thehttp://orhttps://prefix and does not validate whether the target address belongs to an internal network, loopback address, or cloud metadata address:Finally, the request is issued directly by the server:
No
CheckRedirectis set here, so Go's default behavior of automatically following redirects applies, which can be used to assist SSRF via redirect chains.The response logic also performs no security filtering:
u.SendJSONToClient(w, data, 200).w.Write(...)andw.Write(...).Key code snippet:
The vulnerability chain can be summarized as:
4.2 PoC Construction
This endpoint is designed as a "proxy content fetcher," making it an ideal SSRF verification point. Constructing a PoC only requires controlling the
questparameter — no additional authentication or special request headers are needed.Key payload design points:
http://.127.0.0.1:80/.169.254.169.254/latest/meta-data/.302 Location: http://127.0.0.1:8080/can also be used to assist SSRF.5. Proof of Concept (Reproduction)
5.1 Environment Setup
http://127.0.0.1:3000/5.2 Reproduction Steps
Step 1: Confirm the proxy endpoint is accessible
Access
http://127.0.0.1:3000/v1/proxy/?quest=127.0.0.1:80/. The server will issue a request to127.0.0.1:80on localhost.If a web service exists on the target port, the page content will appear directly in the response; if no service exists, an error indicating the resource is unavailable will be returned.
Step 2: Verify ability to read cloud metadata or internal network resources
Access
http://127.0.0.1:3000/v1/proxy/?quest=169.254.169.254/latest/meta-data/.If the runtime environment can reach the cloud metadata endpoint, the response will directly display metadata paths or specific content.
Similarly, this can be replaced with an internal HTTP service address, e.g.,
10.0.0.5:8080/or192.168.1.10:8080/health.Step 3: Verify that response content is visible to the attacker
Point
questat an internal endpoint that returns JSON, e.g.,127.0.0.1:8080/debug.After the server requests that address, if the response
Content-Typeisapplication/json, the data will be parsed and re-output as JSON.If the response is plain text, it will be output verbatim. This directly proves the vulnerability is a non-blind SSRF rather than mere connectivity probing.
5.3 Result Verification
quest=127.0.0.1:80/quest=169.254.169.254/latest/meta-data/CheckRedirectis not set on the client, automatic redirect following is supported by default5.4 Attack Chain Diagram
6. POC
A Python verification script has been generated in the repository:
poc_codetabs_proxy_ssrf.pyCommand-line examples:
python .\poc_codetabs_proxy_ssrf.py --base-url http://127.0.0.1:3000 --target-url 127.0.0.1:80/python .\poc_codetabs_proxy_ssrf.py --base-url http://127.0.0.1:3000 --target-url 169.254.169.254/latest/meta-data/Equivalent raw request examples:
curl "http://127.0.0.1:3000/v1/proxy/?quest=127.0.0.1:80/"curl "http://127.0.0.1:3000/v1/proxy/?quest=169.254.169.254/latest/meta-data/"7. Remediation Recommendations
Remove arbitrary proxy capability
If the business does not require fetching from arbitrary targets, take down the
/v1/proxy/route entirely.Enforce a strict allowlist
Only allow access to explicitly allowlisted domains; prohibit users from submitting arbitrary URLs.
Allowlist validation should be performed after URL parsing, based on the normalized hostname and resolved IP addresses.
Block access to internal, loopback, link-local, and cloud metadata addresses
The following targets must be blocked:
127.0.0.0/810.0.0.0/8172.16.0.0/12192.168.0.0/16169.254.0.0/16::1,fc00::/7,fe80::/10Restrict protocols and ports
Only allow
https; prohibit dangerous protocols such asfile,gopher, andftp.Only allow access to ports required by the business.
Disable automatic redirect following
Set
CheckRedirectonhttp.Clientto prevent bypassing allowlists or probing strategies via 302 redirects.Do not echo target responses directly
If proxy capability must be retained, consider returning only a fixed-format summary rather than the full response body, to avoid turning it into a "data-exfiltration SSRF."
Audit and strengthen access control
The current
isBanned()always returnsfalseand provides no real protection. Rate limiting, authentication, logging/alerting, and source control should be added.Remediation Example
This report contains no screenshots as required; evidence is based on source code location, exploitation path, and an executable PoC.