The MFA bypass hiding in your Google Workspace
You bought MFA so a stolen password would not be enough. Then Google shipped a feature that hands attackers a password that is enough, and it does not appear in any report you are watching.
That feature is the app-specific password. In 2025 a Russian state-sponsored group made it the entire attack.
What an app-specific password actually is
An app-specific password (ASP) is a 16-character code Google generates so a legacy application can reach Gmail, Calendar, or Contacts when 2-Step Verification is on. Old IMAP clients, scan-to-email copiers, scripts that never learned OAuth. The ASP exists so those things keep working after you turn on MFA.
Read that again. The ASP exists specifically to let an application authenticate without the second factor. That is not a bug. It is the designed behavior. Four properties make it dangerous:
- It bypasses MFA by definition. Once issued, the code authenticates with no second factor, ever.
- It is persistent. It stays valid until someone manually revokes it or the account password changes.
- It is invisible. ASP creation and use do not land in the standard Google Workspace audit logs. There is no admin alert, no report, no trail in the places you look.
- Users create them alone. Any user with 2SV can mint one without an administrator ever knowing.
A credential that defeats your strongest control, lasts indefinitely, and leaves no footprint. If you were designing a backdoor, you would be proud of this one.
How APT29 used it
The group Google tracks as UNC6293, assessed to be linked to APT29 (the Russian SVR, also called Cozy Bear), did not exploit a vulnerability. There was no CVE, no malware, no zero-day. They used the feature as built.
The operation was patient social engineering. Operators posed as U.S. State Department officials and built rapport with their targets, who were academics, researchers, and government-adjacent figures, over a series of legitimate-looking emails. At the right moment they walked the target through “connecting” a tool to their account. The steps the target followed were the real steps to create an app-specific password. The target generated the 16-character code and handed it over.
From there the attacker had durable, MFA-free access to the mailbox. No phishing page to detect. No malicious binary to flag. No anomalous login to trip a rule, because from Google’s side a valid ASP was used exactly as ASPs are meant to be used. The victim’s MFA was fully intact and completely irrelevant.
This is what identity-based attacks look like now. The exploit is the feature. The target is the human. The credential is one your tooling was never built to see.
Why you cannot just go look
The obvious response is “I will check who has ASPs.” You will quickly find there is no screen for that. The Admin Console does not surface a domain-wide view. The audit logs do not record it. A per-user manual check across hundreds or thousands of accounts is not an audit, it is a wish.
The visibility does exist, but only through the Admin SDK Directory API. Specifically the asps endpoint, which lists the app-specific passwords for a given user, including when each was created and last used. Google gives you the data programmatically. It just never built the dashboard.
So I built the dashboard.
The detection tool
I wrote a Python auditor that does the thing the console will not. It authenticates as a service account with domain-wide delegation, enumerates every user in the domain, and queries the asps endpoint for each one in parallel. It reports:
- Which users have app-specific passwords at all
- How many each one has, and the application name attached to each
- When each ASP was created and when it was last used, so dormant credentials stand out
- Whether the account is an admin, because an ASP on an admin is your worst case
Output comes in HTML for a readout, CSV for the spreadsheet crowd, and JSON for piping into a SIEM. It can also revoke, one ASP at a time or all of a user’s at once, so detection and remediation live in the same tool.
The whole thing is read-only by default. Enumeration needs nothing more than admin.directory.user.readonly and admin.directory.user.security. You can run it today, on your own domain, and know within minutes whether this blind spot is empty or not.
It is open source under Apache-2.0: github.com/saasvista/asp-auditor. Setup, scopes, and usage are in the README.
What to do after you run it
Finding the ASPs is step one. Then:
- Triage by privilege. Admin accounts with ASPs first, then anyone with access to sensitive data. An ASP on a super admin is a standing emergency.
- Revoke what has no business reason. Most ASPs are forgotten relics of an app nobody uses anymore. Kill them.
- Migrate the legitimate ones to OAuth. Almost every modern client supports “Sign in with Google.” OAuth gives you scoped, revocable, visible access. ASPs give you none of that.
- Run it on a schedule and alert on new ones. A net-new ASP appearing on a high-value account is a signal worth waking up for. This is detection content, not a one-time scan.
- Enroll your highest-risk users in Advanced Protection. Google’s Advanced Protection Program blocks ASP creation entirely. For executives and admins, that is the right default.
The larger point
Multi-factor authentication is necessary and it is not sufficient. The attacks that matter now do not break your controls, they route around them, through features built for convenience and credentials your monitoring cannot see. Defending identity means knowing every path into an account, including the legacy ones nobody talks about, and watching the places your vendor’s dashboard does not cover.
App-specific passwords are one of those paths. Now you can see it. Go look.
The ASP Security Auditor is open source at github.com/saasvista/asp-auditor. Built during the 2025 UNC6293 campaign and maintained as the threat is ongoing.