CVE-2026-33030: Nginx UI: Unencrypted Storage of DNS API Tokens and ACME Private Keys
Summary
Nginx-UI contains an Insecure Direct Object Reference (IDOR) vulnerability that allows any authenticated user to access, modify, and delete resources belonging to other users. The application's base Model struct lacks a userid field, and all resource endpoints perform queries by ID without verifying user ownership, enabling complete authorization bypass in multi-user environments.
Severity
High - CVSS 3.1 Score: 8.8 (High)
Vector String: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H
Note: Original score was 7.5. The score was updated to 8.8 after discovering that sensitive data (DNS API tokens, ACME private keys) is stored in plaintext, which when combined with IDOR allows immediate credential theft without decryption.
Product
nginx-ui
Affected Versions
All versions up to and including v2.3.3
CWE
CWE-639: Authorization Bypass Through User-Controlled Key
Description
Exposed DNS Provider Credentials
The dns.Config structure (internal/cert/dns/configenv.go) contains API credentials:
go type Configuration struct { Credentials map[string]string json:"credentials" // API tokens here Additional map[string]string json:"additional" }
| Provider | Credential Fields | Impact if Leaked | |----------|------------------|------------------| | Cloudflare | CFAPITOKEN | Full DNS zone control | | Alibaba Cloud DNS | ALICLOUDACCESSKEY, ALICLOUDSECRETKEY | Full DNS control + potential IAM access | | Tencent Cloud DNS | TENCENTCLOUDSECRETID, TENCENTCLOUDSECRETKEY | Full DNS control | | AWS Route53 | AWSACCESSKEYID, AWSSECRETACCESSKEY | Route53 + potential AWS access | | GoDaddy | GODADDYAPIKEY, GODADDYAPISECRET | DNS record modification |
Combined Attack: IDOR + Plaintext Storage
When the IDOR vulnerability is combined with plaintext storage, attackers can directly extract API tokens from other users' resources:
Attack Chain: ┌─────────────────────────────────────────────────────────────────┐ │ 1. Attacker authenticates with low-privilege account │ │ 2. Uses IDOR to enumerate: /api/dnscredentials/1,2,3... │ │ 3. Reads plaintext API tokens directly from HTTP response │ │ 4. No decryption needed - tokens stored in cleartext │ │ 5. Uses stolen tokens to: │ │ - Modify DNS records (domain hijacking) │ │ - Issue fraudulent SSL certificates │ │ - Pivot to cloud infrastructure │ └─────────────────────────────────────────────────────────────────┘
PoC: Extracting Plaintext Credentials via IDOR
bash Attacker with low-privilege token accessing admin's DNS credential curl -H "Authorization: $ATTACKERTOKEN" \ https://nginx-ui.example.com/api/dnscredentials/1
Response contains PLAINTEXT API token (no decryption required): { "id": 1, "name": "Production Cloudflare", "provider": "cloudflare", "config": { "credentials": { "CFAPITOKEN": "yhyQ7xR...plaintexttokenvisible..." } } }
Updated CVSS Score with Plaintext Storage
The plaintext storage increases the confidentiality impact:
CVSS 3.1 Score: 8.8 (High)
Vector: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H
- Scope Changed (S:C): Impact extends to external services (DNS providers, cloud platforms) - High Confidentiality (C:H): Plaintext API tokens immediately usable - High Integrity (I:H): DNS records, certificates can be modified - High Availability (A:H): Services can be disrupted via DNS/certificate manipulation
---
Attack Scenario: Certificate Hijacking
1. Attacker creates low-privilege account on nginx-ui 2. Uses IDOR to enumerate all DNS credentials: /api/dnscredentials/1,2,3... 3. Steals Cloudflare API token from admin's credential 4. Uses token to: - Modify DNS records - Issue fraudulent Let's Encrypt certificates - Intercept traffic to victim domains
Credit
Discovered by security researcher during authorized security audit.
Recommendation
Immediate Mitigation
1. Add User Ownership to Models
go // model/model.go type Model struct { ID uint64 gorm:"primarykey" json:"id" UserID uint64 gorm:"index" json:"userid" // Add this field CreatedAt time.Time json:"createdat" UpdatedAt time.Time json:"updatedat" DeletedAt gorm.DeletedAt gorm:"index" json:"deletedat,omitempty" }
2. Filter Queries by Current User
go // api/certificate/dnscredential.go func GetDnsCredential(c gin.Context) { id := cast.ToUint64(c.Param("id")) currentUser := c.MustGet("user").(model.User)
d := query.DnsCredential dnsCredential, err := d.Where( d.ID.Eq(id), d.UserID.Eq(currentUser.ID), // Add user filter ).First()
if err != nil { cosy.ErrHandler(c, err) return } // ... }
3. Add Authorization Middleware
go // middleware/authorization.go func RequireOwnership(resourceType string) gin.HandlerFunc { return func(c gin.Context) { currentUser := c.MustGet("user").(model.User) resourceID := cast.ToUint64(c.Param("id"))
// Check if resource belongs to current user ownerID, err := getResourceOwner(resourceType, resourceID) if err != nil || ownerID != currentUser.ID { c.AbortWithStatusJSON(http.StatusForbidden, gin.H{ "message": "Access denied", }) return } c.Next() } }
Database Migration
sql -- Add userid column to all resource tables ALTER TABLE dnscredentials ADD COLUMN userid BIGINT; ALTER TABLE certs ADD COLUMN userid BIGINT; ALTER TABLE acmeusers ADD COLUMN userid BIGINT; ALTER TABLE sites ADD COLUMN userid BIGINT; ALTER TABLE streams ADD COLUMN userid BIGINT; ALTER TABLE configs ADD COLUMN userid BIGINT;
-- Set default owner for existing resources UPDATE dnscredentials SET userid = 1 WHERE userid IS NULL; UPDATE certs SET userid = 1 WHERE userid IS NULL;
-- Add foreign key constraint ALTER TABLE dnscredentials ADD CONSTRAINT fkdnscredentialsuser FOREIGN KEY (userid) REFERENCES users(id);
Long-term Improvements
1. Implement role-based access control (RBAC) 2. Add audit logging for resource access 3. Implement resource sharing functionality with explicit permissions 4. Add integration tests for authorization checks
---
Remediation for Plaintext Storage
Immediate Fix: Encrypt Sensitive Fields
Apply the same serializer:json[aes] pattern used for S3 credentials to DNS and ACME data:
model/dnscredential.go: go type DnsCredential struct { Model Name string json:"name" Config dns.Config json:"config,omitempty" gorm:"serializer:json[aes]" // Add AES encryption Provider string json:"provider" ProviderCode string json:"providercode" gorm:"index" }
model/acmeuser.go: go type AcmeUser struct { Model // ... Key PrivateKey json:"-" gorm:"serializer:json[aes]" // Add AES encryption // ... }
Data Migration
Existing plaintext data must be re-saved to trigger encryption:
go func MigrateSensitiveData() error { // Migrate DNS credentials var dnsCreds []model.DnsCredential query.DnsCredential.Find(&dnsCreds) for , cred := range dnsCreds { query.DnsCredential.Save(&cred) // Re-save triggers AES encryption }
// Migrate ACME users var acmeUsers []model.AcmeUser query.AcmeUser.Find(&acmeUsers) for , user := range acmeUsers { query.AcmeUser.Save(&user) }
return nil }
Summary of Required Changes
| File | Line | Current | Fix | |------|------|---------|-----| | model/dnscredential.go | 7 | serializer:json | serializer:json[aes] | | model/acmeuser.go | Key field | serializer:json | serializer:json[aes] |
References
- CWE-639: Authorization Bypass Through User-Controlled Key - OWASP IDOR Prevention Cheat Sheet - PortSwigger: IDOR Vulnerabilities
Disclosure Timeline
- 2026-03-13: Vulnerability discovered through source code audit - 2026-03-13: Vulnerability successfully reproduced in local Docker environment - 2026-03-13: All IDOR operations verified: READ, MODIFY, DELETE - 2026-03-13: Security advisory prepared - [Pending]: Report submitted to nginx-ui maintainers - [Pending]: CVE ID requested - [Pending]: Patch developed and tested - [Pending]: Public disclosure (21-90 days after vendor notification)
Other sources
Nginx UI is a web user interface for the Nginx web server. In versions 2.3.3 and prior, Nginx-UI contains an Insecure Direct Object Reference (IDOR) vulnerability that allows any authenticated user to access, modify, and delete resources belonging to other users. The application's base Model struct lacks a userid field, and all resource endpoints perform queries by ID without verifying user ownership, enabling complete authorization bypass in multi-user environments. At time of publication, there are no publicly available patches.
— MITRE
Affected Software
Event History
Frequently Asked Questions
What is the severity of CVE-2026-33030?
CVE-2026-33030 is considered a high severity vulnerability due to its potential impact on user privacy and data integrity.
How do I fix CVE-2026-33030?
To fix CVE-2026-33030, ensure that user access controls are implemented correctly by adding a `user_id` field to the application's model and enforcing authorization checks for resource endpoints.
Who is affected by CVE-2026-33030?
Any users of the Nginx-UI version up to 1.99 are affected by CVE-2026-33030 if they are authenticated.
What resources can be accessed through CVE-2026-33030?
CVE-2026-33030 allows authenticated users to access, modify, and delete resources belonging to other users due to insufficient access controls.
Is there a patch available for CVE-2026-33030?
Yes, a patch for CVE-2026-33030 is incorporated in the subsequent releases after version 1.99 of Nginx-UI.