Document dyndns command-injection fix and two remaining follow-ups
README's public-access table and "previously resolved" summary now reflect the dyndns injection fix and the TSIG-permission regression it uncovered. Adds two open TODO items found during that audit: the dyndns app's lack of real per-user auth binding, and the fact that access logging for every real vhost on this host is currently a no-op.
This commit is contained in:
@@ -243,7 +243,7 @@ Audited 2026-07-20 — which public hostnames require credentials:
|
|||||||
| Host | Requires credentials? | Notes |
|
| Host | Requires credentials? | Notes |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `jayfield.org` / `www.jayfield.org` | No (intentionally public static site) | Was accidentally 401'ing every visitor via a stray `.htaccess` — fixed, see `TODO.md` |
|
| `jayfield.org` / `www.jayfield.org` | No (intentionally public static site) | Was accidentally 401'ing every visitor via a stray `.htaccess` — fixed, see `TODO.md` |
|
||||||
| `dyndns.jayfield.org` | Yes — HTTP Basic Auth | Vhost-level `<Directory>` block, intentional |
|
| `dyndns.jayfield.org` | Yes — HTTP Basic Auth | Vhost-level `<Directory>` block, intentional. Had an OS command injection in its update scripts (fixed 2026-07-20, see `TODO.md`); the app itself still has no real per-user auth binding (open `TODO.md` item) |
|
||||||
| `mail.jayfield.org` | N/A — root is `403` (no content served there) | |
|
| `mail.jayfield.org` | N/A — root is `403` (no content served there) | |
|
||||||
| `mail.jayfield.org/rspamd` | Yes — rspamd's own login page | |
|
| `mail.jayfield.org/rspamd` | Yes — rspamd's own login page | |
|
||||||
| `cloud.jayfield.org` (Nextcloud) | Yes — redirects to `/login` | |
|
| `cloud.jayfield.org` (Nextcloud) | Yes — redirects to `/login` | |
|
||||||
@@ -364,4 +364,19 @@ not a MITM, a self-inflicted interaction between two otherwise-correct
|
|||||||
pieces of config. Fixed 2026-07-20 by moving it behind an Apache reverse
|
pieces of config. Fixed 2026-07-20 by moving it behind an Apache reverse
|
||||||
proxy on its own subdomain, `portainer.jayfield.org`, same pattern as
|
proxy on its own subdomain, `portainer.jayfield.org`, same pattern as
|
||||||
`web.jayfield.org`/`cloud.jayfield.org`; see "Portainer" above and
|
`web.jayfield.org`/`cloud.jayfield.org`; see "Portainer" above and
|
||||||
`TODO.md` for the full details.)
|
`TODO.md` for the full details.
|
||||||
|
Also: an audit of which public hostnames require credentials (prompted by
|
||||||
|
the Portainer/HSTS work above) turned up an OS command injection in the
|
||||||
|
dyndns update scripts (`/var/www/dyndns/nsupdate*.php` built a shell
|
||||||
|
command by directly interpolating unsanitized `$_GET` input into
|
||||||
|
`exec()`) — gated by the vhost's Basic Auth, so not anonymously
|
||||||
|
exploitable, but a real privilege escalation for anyone with valid dyndns
|
||||||
|
credentials. Fixed 2026-07-20 by rewriting the update path to use
|
||||||
|
`proc_open()` with array-form arguments (no shell involved at all) plus
|
||||||
|
strict hostname/IP validation. That same audit also surfaced (a) a
|
||||||
|
regression from the 2026-07-19 TSIG key hardening that had silently
|
||||||
|
broken legitimate dyndns updates — fixed with a POSIX ACL scoped to just
|
||||||
|
that key file, not group membership, since the `bind` group also owns
|
||||||
|
`rndc.key` — and (b) that access logging for every real vhost on this
|
||||||
|
host is currently a no-op, and (c) that the dyndns app has no real
|
||||||
|
per-user auth binding. (b) and (c) are left open — see `TODO.md`.)
|
||||||
|
|||||||
@@ -3,6 +3,36 @@
|
|||||||
Outstanding hardening items (see `README.md` for the full service overview).
|
Outstanding hardening items (see `README.md` for the full service overview).
|
||||||
Nothing here is urgent; all are low-risk, no-downtime changes.
|
Nothing here is urgent; all are low-risk, no-downtime changes.
|
||||||
|
|
||||||
|
- [ ] **dyndns app has no real per-user auth binding** — found 2026-07-20,
|
||||||
|
while fixing the command injection below.
|
||||||
|
`index.php`'s `pass == '_NO_PASS_'` check is a hardcoded magic
|
||||||
|
string, not a real per-user credential — there's nothing tying the
|
||||||
|
single shared Apache Basic Auth identity to *which* hostname label
|
||||||
|
gets updated. Any caller with valid dyndns credentials can update
|
||||||
|
any of the zone's 5 hostnames (`jens`, `kack`, `test`, `uschi`,
|
||||||
|
`vpn`), not just their own. Low severity today since apparently only
|
||||||
|
one person (`jens`) has credentials, but worth fixing before adding
|
||||||
|
any second dyndns user. Fix: either move to per-host TSIG-style
|
||||||
|
tokens, or add a static host-allowlist per `.htpasswd` user in the
|
||||||
|
PHP.
|
||||||
|
|
||||||
|
- [ ] **dyndns/default-vhost access logging is a no-op** — found
|
||||||
|
2026-07-20, while investigating the command injection below.
|
||||||
|
`dyndns.jayfield.org`, `jayfield.org`, `www.jayfield.org`,
|
||||||
|
`mail.jayfield.org`, `web.jayfield.org`, and `cloud.jayfield.org`
|
||||||
|
have no `CustomLog` directive of their own, so requests to them
|
||||||
|
should fall back to the global `CustomLog ${APACHE_LOG_DIR}/access.log`
|
||||||
|
in `apache2.conf` — but that file had 0 lines when checked. Only
|
||||||
|
`default-ssl.conf`/`000-default.conf` explicitly set `CustomLog`.
|
||||||
|
Net effect: there's currently no access-log trail for any real
|
||||||
|
vhost on this host, which is exactly the evidence that would be
|
||||||
|
needed to rule out past abuse of things like the dyndns injection
|
||||||
|
above. Needs root-causing (global `CustomLog` not actually wired up
|
||||||
|
to these vhosts? logs going somewhere else? log rotation wiping it
|
||||||
|
right before checks?) and likely a per-vhost `CustomLog` added to
|
||||||
|
each site, matching the `default-ssl.conf`/`000-default.conf`
|
||||||
|
pattern.
|
||||||
|
|
||||||
- [x] **OS command injection in the dyndns update scripts** — done 2026-07-20
|
- [x] **OS command injection in the dyndns update scripts** — done 2026-07-20
|
||||||
`/var/www/dyndns/nsupdate.php`, `nsupdate_ipv4.php`, and
|
`/var/www/dyndns/nsupdate.php`, `nsupdate_ipv4.php`, and
|
||||||
`nsupdate_ipv6.php` (used by both `index.php` and `nic/update.php`,
|
`nsupdate_ipv6.php` (used by both `index.php` and `nic/update.php`,
|
||||||
|
|||||||
Reference in New Issue
Block a user