Applying the Interim Brotli CVE Workaround on SDDC Manager 9.1.1

🗓️ October 30, 2026 · Home Lab, How-To's, VMware Cloud Foundation

Broadcom has a manual interim workaround for a critical Brotli vulnerability in SDDC Manager 9.1.1. It’s all done at a shell, with edits to two files. This post walks through it on my SDDC Manager 9.1.1 lab, from the backups to the verification tests and the rollback.

What the workaround does

CVE-2026-111497 is rated Critical, and it’s documented in VMSA-2026-0008. Read the advisory before you continue. KB 460732 covers SDDC Manager 9.1.1 and 9.1.1 EP, and Broadcom is clear that the workaround is temporary and only mitigates this one CVE.

The fix is to refuse any request body that declares Content-Encoding: br, since Brotli is the only encoding the CVE affects. It’s done in two places. An nginx rule covers requests arriving on port 443, and a check in the SDDC Manager UI server covers requests sent straight to 127.0.0.1:7500, which nginx never sees. Both return an HTTP 415, and gzip and deflate are left alone.

If your SDDC Manager is on 5.2.x, 9.0.2, or 9.1.0, this isn’t your KB. Broadcom covers those versions in KB 460734.

Before you start

My lab’s SDDC Manager reports VMware Cloud Foundation 9.1.1.0200.25801071 in its About dialog, so that’s the build this post covers.

The SDDC Manager About dialog showing VMware Cloud Foundation 9.1.1.0200.25801071

Before I touched anything, I checked the SDDC Manager backup settings in VCF Operations, under Administration → SDDC Manager → D3M0 → Backup Settings. Automatic backup is enabled on a weekly schedule, and the last backup shows as Successful. The KB’s first prerequisite is to back up the SDDC Manager appliance. I didn’t take a new one, since I already had a backup from October 24th.

The SDDC Manager Backup Settings page in VCF Operations

Then I SSH’d to SDDC Manager as the vcf user and switched to root with su -. Everything below needs root, since it edits files under /etc/nginx and the UI app and restarts services.

Next, I backed up the two files the workaround modifies. The copies are what the rollback restores from. I ran these in the root shell and kept it open, because the later commands reuse the $APP variable.

APP=/opt/vmware/vcf/sddc-manager-ui-app/server/src/servers/sddcManagerServer/app.js
cp -p $APP $APP.orig-cve111497
cp -p /etc/nginx/nginx.conf /root/nginx.conf.before-cve111497

Both copies completed without an error.

The root shell after setting the APP variable and copying both files

Step 1: The nginx rule

I opened /etc/nginx/nginx.conf with vi to make two additions. The first goes in the http { } block, right after the map_hash_bucket_size 128 line, which is line 15 on mine.

vi opened the file read-only, with [readonly] in the status line at the bottom.

nginx.conf open in vi with the readonly flag in the status line

I checked why. The file is mode 0400 and owned by root, so nothing has the write bit. It isn’t immutable, and vi is the real /bin/vi, not an alias to view.

root@vcf-m01-sddcm01 [ ~ ]# ls -l /etc/nginx/nginx.conf
-r--------. 1 root root 49161 Oct 16 18:36 /etc/nginx/nginx.conf
root@vcf-m01-sddcm01 [ ~ ]# lsattr /etc/nginx/nginx.conf
--------------e------- /etc/nginx/nginx.conf
root@vcf-m01-sddcm01 [ ~ ]# type vi
vi is hashed (/bin/vi)

Root can still write to it, so I forced the save with :wq!. Afterward I ran the same three checks again. The file still shows -r--------, with no immutable flag, and vi is still /bin/vi, so the appliance’s permissions are unchanged.

The same three checks after the save, with nginx.conf still showing -r——–

This is the map I added.

# CVE-2026-111497 interim workaround: refuse request bodies that declare
# Content-Encoding: br. Brotli is the only encoding affected by this CVE.
# Remove once the Node.js runtime is upgraded to a fixed release.
map $http_content_encoding $reject_br {
    default 0;
    "~*\bbr\b" 1;
}

The map is in place right after map_hash_bucket_size 128.

The CVE-2026-111497 map block added to the http block in nginx.conf

The second goes in the server { listen 443 ssl; ... } block, just before the # certs sent to the client in SERVER HELLO comment and after the limit_req line.

# CVE-2026-111497 interim workaround: reject Brotli request bodies
# before they reach the UI server (localhost:7500). See map above.
if ($reject_br) {
    return 415;
}

Here’s the if block in place, after the limit_req line and before the # certs sent to the client comment.

The if ($reject_br) block added to the 443 server block in nginx.conf

Test the configuration and reload nginx.

nginx -t && systemctl reload nginx

nginx -t reported the syntax is ok and the test is successful, and the reload returned without an error.

nginx -t passing, followed by the nginx reload

If nginx -t fails, restore the original and contact Broadcom support.

cp -p /root/nginx.conf.before-cve111497 /etc/nginx/nginx.conf
nginx -t

Step 2: The UI server check

nginx can’t see requests sent straight to 127.0.0.1:7500, so the UI server gets the same rule. I opened the app.js file that $APP points to and found this line, which is line 137 on my build.

winston.debug('Mounting app middleware');

I added the following block directly below it, before the app.use(bodyParser.json({... call.

// CVE-2026-111497: reject Brotli request bodies before any parser runs.
// Other encodings (gzip, deflate) are left as they are.
app.use((req, res, next) => {
   if (/\bbr\b/i.test(req.headers['content-encoding'] || '')) {
      return res.status(415).json({ message: 'Brotli request bodies are not accepted', status: 415 });
   }
   next();
});

Here’s the middleware in place between the winston.debug line and the bodyParser.json call.

The Brotli check added to app.js between the winston.debug line and the bodyParser call

Then I restarted the UI service, which returned without an error.

systemctl restart sddc-manager-ui-app

After that I ran Broadcom’s wait loop. It polls the UI until it answers with something other than a 503 or no response at all.

for i in $(seq 1 40); do
  c=$(curl -s -o /dev/null -w '%{http_code}' -X POST http://127.0.0.1:7500/ui/api/ping \
      -H 'Content-Type: application/json' --data-binary '{"a":1}')
  [ "$c" != 503 ] && [ "$c" != 000 ] && break
  sleep 6
done
echo "ui_code=$c"

The loop ended with ui_code=403. That’s the UI answering, not the 503 or 000 the loop waits out, so the UI was back up.

The wait loop finishing with ui_code=403

Verify

Broadcom gives four tests to run on SDDC Manager. The first two should return 415, and the last two should not.

TestWhat to expect
Brotli body, straight to the UI on port 7500415
Brotli body, through nginx415
Plain JSON, through nginxNot 415. A normal response such as 401 or 403 if you aren’t logged in
gzip bodyNot 415

Here are the Brotli tests. The first goes direct to the UI server, and the second goes through nginx.

curl -s -o /dev/null -w '%{http_code}' -X POST http://127.0.0.1:7500/ui/api/ping \
  -H 'Content-Type: application/json' -H 'Content-Encoding: br' --data-binary '{"a":1}'

curl -k -s -o /dev/null -w '%{http_code}' -X POST https://127.0.0.1/ui/api/ping \
  -H 'Content-Type: application/json' -H 'Content-Encoding: br' --data-binary '{"a":1}'

Both returned 415, the first direct to the UI on port 7500 and the second through nginx.

Both Brotli tests returning 415

The plain JSON test is the second command again without the Content-Encoding header.

curl -k -s -o /dev/null -w '%{http_code}' -X POST https://127.0.0.1/ui/api/ping \
  -H 'Content-Type: application/json' --data-binary '{"a":1}'

Broadcom’s gzip test builds a gzip file and sends it straight to the UI on port 7500. I sent a real gzip body through nginx instead.

printf '{"a":1}' | gzip | curl -k -s -o /dev/null -w '%{http_code}' -X POST https://127.0.0.1/ui/api/ping \
  -H 'Content-Type: application/json' -H 'Content-Encoding: gzip' --data-binary @-

Both returned 403 instead of 415. The UI is rejecting the unauthenticated request, which is the normal answer, so plain JSON and gzip bodies still get through the new rule.

The plain JSON and gzip tests both returning 403

Finally, confirm the configuration is in place.

nginx -T 2>/dev/null | grep -c reject_br
grep -c 'CVE-2026-111497' $APP

The first should return 2, one for the map and one for the if, and the second should return 1.

Mine came back 2 and 1.

The reject_br count of 2 and the app.js count of 1

If any test returns something else, don’t continue. Roll back and contact Broadcom support.

Rolling back

Restore both files from the backups, then reload nginx and restart the UI service. The first line sets $APP again, in case you’re in a new shell.

APP=/opt/vmware/vcf/sddc-manager-ui-app/server/src/servers/sddcManagerServer/app.js
cp -p $APP.orig-cve111497 $APP
cp -p /root/nginx.conf.before-cve111497 /etc/nginx/nginx.conf
nginx -t && systemctl reload nginx
systemctl restart sddc-manager-ui-app

Wrapping up

The workaround did what the KB says it should. Brotli request bodies get a 415 both straight to the UI server and through nginx, and plain JSON and gzip bodies still get past the new rule to the normal 403 for an unauthenticated request. The only snag was that nginx.conf is mode 0400 on SDDC Manager, so vi opened it read-only and I had to save with :wq!. The mode was unchanged afterward.

Keep in mind that this is a stopgap and not a fix. The comment in the nginx rule says to remove it once the Node.js runtime is upgraded to a fixed release, and nothing here replaces patching when Broadcom ships one. Keep an eye on KB 460732 for updates. Until then, the two backups from the start are what take it back out.

-virtualex-

Comments