This is a forged badge inside the building, not a broken front door
CVE-2026-41101 is an improper access control flaw in Microsoft Word for Android that Microsoft classifies as a spoofing issue. Public metadata is thin, but the authoritative records point to Word for Android versions before 16.0.19822.20190 as affected, with the fixed build at 16.0.19822.20190. The attacker must already have local access and low privileges on the same Android device, and Microsoft says no user interaction is required once that foothold exists.
Microsoft's 7.1 HIGH score is defensible in pure CVSS math, but it overstates enterprise urgency. This is not remotely reachable, not unauthenticated, not a server-side trust boundary break, and not an RCE. In real fleets this behaves like a post-compromise mobile client abuse case with per-device blast radius, so the right operational bucket is LOW unless you have a very large Android M365 estate handling sensitive approvals or regulated document workflows.
3 steps from start to impact.
Land on the Android device with ADB or a malicious app
- Attacker has local access to the target Android device
- A vulnerable Word for Android build is installed
- Attacker can execute code or interact within the same device profile
- This is already post-initial-access
- MDM, Play Protect, app allow-listing, and blocked sideloading cut down the reachable population
- Corporate Android adoption is usually a subset of the total Office estate
Abuse the Word access control bug to spoof trusted state
- Local authenticated context on the device
- Word for Android before 16.0.19822.20190
- No public proof-of-concept was found
- Microsoft did not publish deep technical details
- Application sandboxing and work-profile separation can limit adjacent data access
Leverage the spoofed view to mislead the user or tamper with trusted content
- Target user relies on Word mobile for business documents
- Relevant files or account context are present on the device
- Blast radius is usually one device or one user session
- Mobile Office use is often secondary to desktop workflows
- MAM containerization and conditional access reduce downstream abuse
The supporting signals.
| In-the-wild status | No public exploitation evidence found in the sources reviewed, and not listed in CISA KEV. |
|---|---|
| Public PoC availability | None found. GitHub Advisory shows "No known source code" for this CVE. |
| EPSS | 0.044% (14th percentile) per GitHub Advisory Database/FIRST-linked EPSS data. |
| KEV status | Not KEV-listed as of this assessment. |
| CVSS vector | CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N — the key down-pressure is AV:L + PR:L, meaning the attacker already has a foothold on the handset. |
| Affected versions | NVD maps the vulnerable Android package range as Word for Android before 16.0.19822.20190. |
| Fixed version | 16.0.19822.20190 or later; for managed fleets this should arrive through Google Play / Managed Google Play distribution. |
| Exposure and scanning reality | Inference: this is a client-side Android app, not an internet-facing service, so Shodan/Censys-style exposure counting is effectively N/A. Your real exposure source is MDM app inventory, not perimeter scanning. |
| Disclosure date | Published 2026-05-12 during Microsoft's May 2026 release cycle. |
| Researcher / reporter | Microsoft's public advisory and NVD entry reviewed here do not credit a reporting researcher or external organization. |
noisgate verdict.
The decisive factor is attacker position: this bug requires local authenticated access on the Android device, which means the attacker is already past your primary control plane. That sharply narrows both the exposed population and the blast radius compared with remotely reachable Office bugs.
Why this verdict
- Start from Microsoft's 7.1, then subtract for attacker position:
AV:L/PR:Lmeans the adversary already has a local foothold on the handset, which is classic post-compromise friction. - Not remotely reachable: this is an Android client app, not an internet-facing service, so the reachable population is your installed mobile Word base rather than every exposed host on the perimeter.
- Spoofing, not code execution: even if exploitation is real, the likely outcome is trust abuse or document-context manipulation, not broad host takeover or wormable spread.
- Weak exploitation signal: no KEV listing, no public exploitation evidence in reviewed sources, no public PoC located, and EPSS is very low at 0.044%.
Why not higher?
To justify MEDIUM or HIGH operationally, this would need either remote reachability, active exploitation, or a larger fleet-wide blast radius. Instead, the chain starts with prior local access on an Android device and ends in a spoofing outcome inside a single app context.
Why not lower?
It is not IGNORE because Microsoft shipped a fix, the vulnerable app is massively deployed in absolute terms, and once an attacker is on-device there may be meaningful trust and document-handling consequences. The absence of required user interaction after foothold also keeps it above pure paperwork.
What to do — in priority order.
- Force app auto-update — Use Managed Google Play or equivalent EMM controls to push Word for Android to 16.0.19822.20190+. For a LOW verdict there is no SLA (treat as backlog hygiene), so enforce this in the next normal mobile app maintenance cycle rather than breaking glass.
- Block sideloading and debug abuse — Disable unknown-source installs, restrict
ADB/developer options where policy allows, and keep Play Protect enforced. This matters because the exploit chain already assumes local device access, and for LOW there is no SLA (treat as backlog hygiene). - Use app protection and work profiles — Apply Intune/MAM controls so corporate data in Word stays containerized and conditional on device compliance. That limits what a local foothold can actually reach, and for LOW this is backlog hygiene with no formal deadline.
- Inventory vulnerable Android builds — Query MDM for the Word package and flag versions below 16.0.19822.20190. This is the only reliable enterprise exposure view because network scanners will not tell you much here; for LOW, do it through routine hygiene reporting.
- A WAF does not help because this is not a web-exposed server flaw.
- Perimeter vulnerability scanning is weak here because the target is a mobile client app, not a listening service.
- MFA alone does not fix a local app access-control bug once the attacker already has code or session access on the device.
Crowdsourced verification payload.
Run this on an auditor workstation, CI job, or admin jump box against a CSV export from Intune/EMM/mobile inventory that includes app package/version data. Invoke it as python3 verify_cve_2026_41101.py mobile_apps.csv; no admin rights are required on the workstation, but you need permission to export device app inventory. Expected columns are flexible, but the script looks for a Word Android package/product plus a version such as 16.0.19822.20180.
#!/usr/bin/env python3
# verify_cve_2026_41101.py
# Audit Microsoft Word for Android versions against CVE-2026-41101.
# Input: CSV export from MDM/EMM/app inventory.
# Output: VULNERABLE / PATCHED / UNKNOWN
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN, 3=usage/file error
import csv
import re
import sys
from typing import Optional, Tuple
FIXED_VERSION = (16, 0, 19822, 20190)
TARGET_PACKAGES = {
'com.microsoft.office.word',
}
TARGET_NAME_HINTS = (
'word for android',
'microsoft word',
)
def parse_version(v: str) -> Optional[Tuple[int, ...]]:
if not v:
return None
nums = re.findall(r'\d+', v)
if not nums:
return None
return tuple(int(x) for x in nums)
def compare_versions(a: Tuple[int, ...], b: Tuple[int, ...]) -> int:
max_len = max(len(a), len(b))
a2 = a + (0,) * (max_len - len(a))
b2 = b + (0,) * (max_len - len(b))
if a2 < b2:
return -1
if a2 > b2:
return 1
return 0
def row_matches_word_android(row: dict) -> bool:
hay = ' '.join(str(v or '').strip().lower() for v in row.values())
if any(pkg in hay for pkg in TARGET_PACKAGES):
return True
return any(hint in hay for hint in TARGET_NAME_HINTS)
def get_version_from_row(row: dict) -> Optional[str]:
preferred = [
'version', 'app_version', 'application_version', 'package_version',
'installed_version', 'version_name', 'build', 'app build'
]
lowered = {str(k).strip().lower(): v for k, v in row.items()}
for key in preferred:
if key in lowered and str(lowered[key]).strip():
return str(lowered[key]).strip()
for k, v in lowered.items():
if 'version' in k and str(v).strip():
return str(v).strip()
return None
def get_device_name(row: dict) -> str:
preferred = ['device', 'device_name', 'managed_device_name', 'hostname', 'serial', 'user']
lowered = {str(k).strip().lower(): v for k, v in row.items()}
for key in preferred:
if key in lowered and str(lowered[key]).strip():
return str(lowered[key]).strip()
return 'unknown-device'
def main() -> int:
if len(sys.argv) != 2:
print('UNKNOWN - usage: python3 verify_cve_2026_41101.py <inventory.csv>')
return 3
path = sys.argv[1]
try:
with open(path, newline='', encoding='utf-8-sig') as f:
reader = csv.DictReader(f)
if not reader.fieldnames:
print('UNKNOWN - CSV has no header row')
return 2
matched = 0
vulnerable = []
patched = []
unknown = []
for row in reader:
if not row_matches_word_android(row):
continue
matched += 1
device = get_device_name(row)
version_str = get_version_from_row(row)
parsed = parse_version(version_str or '')
if not parsed:
unknown.append((device, version_str or ''))
continue
if compare_versions(parsed, FIXED_VERSION) < 0:
vulnerable.append((device, version_str))
else:
patched.append((device, version_str))
except FileNotFoundError:
print(f'UNKNOWN - file not found: {path}')
return 3
except Exception as e:
print(f'UNKNOWN - failed to parse CSV: {e}')
return 2
if matched == 0:
print('UNKNOWN - no Microsoft Word for Android entries found in inventory')
return 2
fixed_str = '.'.join(str(x) for x in FIXED_VERSION)
print(f'Checked {matched} Word for Android record(s); fixed version is {fixed_str}')
if vulnerable:
print('VULNERABLE')
for device, version in vulnerable[:25]:
print(f' {device}: {version}')
if len(vulnerable) > 25:
print(f' ... and {len(vulnerable) - 25} more vulnerable record(s)')
if unknown:
print(f' Note: {len(unknown)} matching record(s) had unknown version data')
return 1
if patched and not unknown:
print('PATCHED')
for device, version in patched[:10]:
print(f' {device}: {version}')
if len(patched) > 10:
print(f' ... and {len(patched) - 10} more patched record(s)')
return 0
print('UNKNOWN')
if patched:
print(f' {len(patched)} matching record(s) are at or above {fixed_str}')
if unknown:
print(f' {len(unknown)} matching record(s) are missing usable version data')
for device, version in unknown[:10]:
display = version if version else '<empty>'
print(f' {device}: {display}')
return 2
if __name__ == '__main__':
sys.exit(main())
If you remember one thing.
Sources
- Microsoft Security Update Guide - CVE-2026-41101
- NVD - CVE-2026-41101
- GitHub Advisory Database - GHSA-6449-fjcw-2pg6
- Google Play - Microsoft Word: Edit Documents
- Microsoft Intune - Add and Assign Managed Google Play Apps to Android Enterprise Devices
- Microsoft Support - May 2026 updates for Microsoft Office
- Trend Micro - May 2026 Microsoft CVE review table
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.