high · 8.1Jul 24, 2026

Poweradmin: IDOR Broken Access Control in DNS Record Edit

Rohit Hatagale
AI Security Researcher, SecureLayer7

Any authenticated Poweradmin user who owns at least one DNS zone can overwrite records in zones they have no access to, by sending a victim's record ID alongside their own zone ID in a crafted POST…

Packagepoweradmin/poweradmin
Ecosystemcomposer
Affected>= 3.0.0, < 3.9.11
Fixed in3.9.11
Poweradmin: IDOR Broken Access Control in DNS Record Edit

The problem

Poweradmin's record-edit flow in RecordManager::editRecord() checks zone ownership against a zid value from the POST body, but then applies the database write to a rid value also from the POST body. Nothing on the server ties the record ID back to the zone ID, so the permission gate and the write target completely different objects.

The backend SQL is WHERE id = ? with no zone column in the predicate, meaning any record in the entire database is reachable. An attacker with a single owned zone (zone id 3, for example) can overwrite content, type, ttl, and the disabled flag of any record in any other zone.

In a shared or multi-tenant install this is a full cross-tenant DNS takeover.

Proof of concept

A working proof-of-concept for this issue in poweradmin/poweradmin, with the exact payload below.

http
POST /zones/3/edit HTTP/1.1
Host: poweradmin.example.com
Cookie: PHPSESSID=<attacker-session>
Content-Type: application/x-www-form-urlencoded

_token=<valid-csrf-token>&serial=<current-soa-serial>&commit=1&record[5][rid]=5&record[5][zid]=3&record[5][name]=pwned&record[5][type]=A&record[5][content]=6.6.6.6&record[5][ttl]=3600&record[5][prio]=0

# record[5][rid]=5  -> victim's record (lives in zone 2, owned by admin)
# record[5][zid]=3  -> attacker's own zone (passes the ownership check)
# _token + serial   -> attacker's own valid CSRF token and SOA serial; nothing looks anomalous

The root cause is a confused-deputy pattern (CWE-639, authorization bypass through user-controlled key). The ownership check reads $record['zid'] from the request, and the write reads $record['rid'] from the same request. Because neither value is validated against the other, the attacker can point them at different rows entirely.

The fix in commits 12651fa, 88f443d, and e021067 (released as v3.9.11 / v4.2.5) stops trusting the client-supplied zid. Instead, editRecord() now derives the real zone from the record ID on the server side using getZoneIdFromRecordId(), matching the approach already used by the delete path.

The hidden-field marker scheme added to the edit template also prevents submitting record rows that were not rendered by the server.

The fix

Upgrade to Poweradmin 3.9.11 (3.x LTS) or 4.2.5 (stable). Both releases bind record edits to the zone the record actually belongs to, server-side. No config changes are required; it is a drop-in upgrade. Custom themes that fork edit.html must be re-synced because zone saves now depend on new hidden marker fields added by the patch.

Reported by Saif Salah.

References: [1][2][3][4][5][6]

Related research