Skip to main content
  1. posts/

Overriding ESX Host Acceptance Level with Secure Boot Enabled

 Author
Author
Christian Mohn
IT veteran, podcaster, author, and blogger from Bergen, Norway.
Table of Contents

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
  -> CommunitySupported

Secure 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.

Note

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 successfully

If 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
VMwareCertified
Being able to run unsigned code on a Secure Boot enabled host is a big issue, even if it does require root access to perform. This is a way to gain persistence in VCF environments, simply by using built-in tools to bypass the hypervisors own protections.

I’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:

Related