In June 2026, Austin Gadient, CTO and co-founder of Vali Cyber did a presentation of Zero Lock at the Norwegian VMUG
One of the things that caught my eye in that presentation, is the technique he has discovered for overriding an ESX hosts Acceptance Level (This has, as far as I know, been disclosed to VMware a long time ago).
Every VIB (vSphere Installation Bundle) carries an Acceptance Level, and so does every host, running from strictest to loosest:
VMware Certified → VMware Accepted → Partner Supported → Community Supported.
The level reflects who tested and stands behind the VIB, from VMware’s own QA down to an individual on the internet, and Community Supported VIBs are unsigned. (Broadcom: Understanding Acceptance Levels for VIBs and Hosts, Working with Acceptance Levels)
A host only installs VIBs whose Acceptance Level is at least as high as the host’s own, so the host-level setting is a floor. Set the host to Partner Supported and a Community Supported VIB gets refused. Drop the host to Community Supported and it’s allowed (Manage the Acceptance Levels of Hosts and VIBs). That’s why lowering the Acceptance Level is such a big deal, it is the gate that decides whether unsigned code gets onto the host at all, whether that code came from an attacker or from an admin just trying to install a legitimate community tool.
With UEFI Secure Boot enabled, ESX validates the boot chain and its VIBs cryptographically. Broadcom’s own documentation is explicit that under Secure Boot “the VIB verifier verifies every VIB package that is installed on the system.” (UEFI Secure Boot for ESXi Hosts).
The Acceptance Level itself lives at a specific ConfigStore address: component esx_update, group software, key acceptance_level. esxcli software acceptance set is a front door with a guard on it. configstorecli is a side door into the same room. (William Lam: Introduction to configstorecli)
Secure Boot and Acceptance Levels are tightly linked because Secure Boot’s whole job is verifying that everything loaded during boot, including VIBs, is properly signed. Community Supported VIBs are unsigned, so the two features directly conflict.
In practice, when Secure Boot is enabled on a host, you are not supposed to be able to lower the Acceptance Level down to Community Supported. It gets blocked because doing so would mean loading unsigned code on a host that’s supposed to guarantee everything in the boot chain is cryptographically verified. If you try to install a Community Supported VIB (like an unsupported driver or fling) on a Secure Boot-enabled host, it will fail the signature check.
Acceptance Level Override#
Everything below was tested against ESX 9.1, the latest release at the time of writing.
With Secure Boot off, there is nothing to prevent someone with root access to change the ESX Host Acceptance Level. The plain, fully supported esxcli software acceptance set --level=CommunitySupported just works, and any whitelist-clean community VIB installs cleanly.
Austin’s demo showed that esxcli normally refuses to lower a host’s Acceptance Level while Secure Boot is on, but the Acceptance Level is ultimately just a value sitting in ESX’s ConfigStore, and configstorecli can write to that store directly, bypassing the esxcli guard.
With Secure Boot genuinely enforcing, confirmed with secureBoot.py -s, the guard does its job first:
esxcli software acceptance set --level=CommunitySupported
-> [AcceptanceConfigError] Secure Boot enabled: Cannot change acceptance level to community.The gap here is architectural: Secure Boot enforcement lives in esxcli’s logic (via the API), not in the config store it’s writing to. configstorecli writes to that same store directly, with no awareness of Secure Boot at all so the value changes exactly as it would if the guard had said yes. The Acceptance Level was never actually protected; only the one path to it was.
So how do you work around that? Well, fairly easily by bypassing the esxcli command altogether, and changing the Acceptance Level directly in the ConfigStore by importing a properly formatted json file:
echo '{"level": "COMMUNITY_SUPPORTED"}' > acceptance.json && \
configstorecli config current set -c esx_update -g software -k acceptance_level -infile ./acceptance.json
-> Set: completed successfully
esxcli software acceptance get
-> CommunitySupportedSecure Boot’s guarantee that only signed code runs on the host depends entirely on every write path to that config value enforcing the same check,and in this case, one of them doesn’t. The ConfigStore write defeats the Acceptance Level guard on a Secure Boot host, and the host now reports CommunitySupported despite Secure Boot being on. In reality, the only thing that checks that the acceptance level isn’t set to Community Supported, is esxcli itself.
esxcli is a python script that works as a wrapper for the management API (VMOMI/vim). Each namespace (like software.acceptance) corresponds to a managed object type on the API side. The actual behavior, like validation logic, Secure Boot checks, whatever a given command does lives server-side, in whatever backend implements that managed object, not in the Python CLI code itself.
Demo#
This demo shows the entire process of changing the ESX host Acceptance Level, with Secure Boot enabled, and installing a non-signed Community Supported VIB. In this case I created a small VIB file that changes the DCUI and SSH Logon banners. The SSH login banner is changed via /etc/issue and changing the DCUI is done by running configstorecli at install time, and then every time the host boots from a /etc/init.d/ hook that the VIB itself created.
CommunitySupported (unsigned) VIBs are restricted to a whitelist of installable paths, enforced by a schema on the host itself (/usr/share/esximage/schemas/vib20-extensibility.rng), separate from the Acceptance Level check. The allowed paths include things like /etc/vmware/ (custom firewall rulesets are the canonical use case), /etc/init.d/ (boot scripts), and /opt/ (a general third-party dumping ground). Anything outside that set requires PartnerSupported or higher.
One of the issues that can be exploited here, is that you can in fact write to /etc/init.d/ and create your own boot scripts, that run on boot even with Secure Boot enabled.
This looks like it contradicts the earlier claim that Secure Boot verifies every installed VIB, it doesn’t, but the two checks operate at different layers. Secure Boot verifies the boot chain: the bootloader, the kernel, and the VIBs recorded as part of the trusted image profile. It doesn’t continuously re-audit arbitrary files already sitting on disk. Once the ConfigStore bypass gets a VIB installed outside that trusted path, anything it drops into /etc/init.d/ just runs like any other boot script. Secure Boot has no mechanism that walks back and checks it after the fact.
This is proven by setting the Acceptance Level back to VMware Certified after the fact:
echo '{"level": "VMWARE_CERTIFIED"}' > acceptance.json && \
configstorecli config current set -c esx_update -g software -k acceptance_level -infile ./acceptance.json
-> Set: completed successfullyIf you do this via esxcli it will fail with an error since you have a Community Supported VIB installed. configstorecli happily ignores that when called directly. Setting it back even survives reboot, and the Community Supported VIB runs without issues:
[root@localhost:~] esxcli software vib list
Name Version Vendor Acceptance Level Install Date Platforms
----------------------------- -------------------------------------------- ---------- ------------------ ------------ ---------
atlantic 1.0.3.0-16vmw.910.0.25370933 VMW VMwareCertified 2026-07-20 host
...
...
vsipfwlib 9.1.0.0200-9.1.25524171 VMware VMwareCertified 2026-07-20 host
tools-light 13.1.0-0.25370933 VMware VMwareCertified 2026-07-20 host
vninja-demo 1.1.1-1.0 vninja.net CommunitySupported 2026-07-31 host
[root@localhost:~] esxcli software acceptance get
VMwareCertifiedI’ve talked about Rogue VMs in the past, and I do have a working Proof of Concept VIB that allows a Rogue VM to start on boot bypassing the persistence issue that was highlighted in that talk, namely that /etc/rc.local.d/local.sh isn’t run at boot time when Secure Boot is on. There is no need to put anything in there, when you can just install a VIB that ensures that it gets executed at boot time anyway. I’ll cover that PoC in a follow-up post later on.
Credits and sources#
This post draws on the following talk, prior research, and official documentation:
- Austin Gadient, CTO and co-founder of Vali Cyber, for discovering the
configstorecliacceptance-level bypass technique, presenting it in the ZeroLock talk at VMUG Norway, June 2026, and for directly explaining the payload-path whitelist mechanism when I reached out. - William Lam, for his write-ups on Introduction to the new ESXi Configuration Store CLI (configstorecli), still the best reference for understanding ConfigStore and how
configstorecliinteracts with it, and on Creating a Custom VIB for ESXi 8.x, which I adapted for building the Community Supported VIB used in this post. - Broadcom TechDocs, for the official documentation referenced throughout this post: Understanding Acceptance Levels for VIBs and Hosts, Working with Acceptance Levels, Manage the Acceptance Levels of Hosts and VIBs, and UEFI Secure Boot for ESXi Hosts.



