Parking meters in major US cities started growing tumors in 2022 — small stickers placed over legitimate QR codes, redirecting drivers from official city payment systems to credential-harvesting sites. The FBI issued a formal warning. Cities pulled and replaced signage. The stickers cost a few cents to print.

QR code phishing — commonly called quishing — exploits the same gap that has always made social engineering effective: the gap between what something appears to be and what it actually is. A QR code on a parking meter, a conference badge, a restaurant table, or an email attachment carries no visible indication of its destination. And for much of the history of enterprise email security, it carried that destination invisibly past every security scanner.

This post covers how quishing works technically, the real campaigns that targeted Microsoft 365 at scale, the physical attack vectors, and the detection and defense controls available to defenders in 2024 and beyond.


Why QR Codes Bypass Email Security

A standard email security gateway works by parsing message content for URLs, evaluating those URLs against reputation databases, following redirect chains, and sandboxing destination pages. This pipeline is mature and effective against URL-based phishing.

A quishing email works differently:

  1. The attacker creates a QR code containing a malicious URL.
  2. The QR code is rendered as an image (PNG, JPEG, or SVG) and attached to or embedded in the email.
  3. The email body may contain no suspicious text or URLs — just an image of a QR code and legitimate-looking copy (“Scan to verify your account”).
  4. The email security gateway scans the text body, finds no malicious URLs, and delivers the message.
  5. The user scans the QR code with their phone — bypassing the corporate proxy, DNS filtering, and endpoint controls on their laptop.

The fundamental problem: the URL is encoded as a visual pattern inside an image. Without OCR and QR decoding applied to image attachments, the URL is invisible to text-based scanning.


Real Incidents and Campaigns

FBI Warning: Parking Meter Fraud (January 2022)

On January 18, 2022, the FBI issued PSA I-011822-PSA documenting that fraudulent QR code stickers were being placed over legitimate QR codes at parking meters in multiple US cities including Austin, Texas, and San Antonio, Texas. The City of San Antonio confirmed the stickers and issued public warnings. Victims who scanned the counterfeit codes were directed to sites harvesting payment card data.

The attack requires zero technical infrastructure beyond a printer: generate a QR code pointing to a phishing site, print it as a sticker, and place it over a legitimate meter QR code. Physical inspection of the sticker (looking for signs of overlay) is the only pre-visit detection method.

Microsoft 365 Credential Harvesting Campaigns (2023–2024)

Beginning in mid-2023, threat intelligence vendors documented high-volume quishing campaigns targeting enterprise Microsoft 365 credentials. Key characteristics:

  • Volume: Cofense reported a 587% increase in QR code phishing attacks in Q3 2023 versus Q1 2023.
  • Targeting: Executives and finance personnel were disproportionately targeted (per Abnormal Security research).
  • Lure themes: Multi-factor authentication reset notifications, DocuSign document reviews, HR benefit enrollment deadlines.
  • URL evasion: Attackers embedded QR codes pointing to URLs hosted on Azure Blob Storage, Bing redirect URLs (bing.com/ck/a?...), or legitimate URL shorteners to inherit their reputation.
  • Mobile exploitation: Victims scanned with mobile phones, landing on Microsoft login clones optimized for mobile screens, often with real-time session cookie capture (adversary-in-the-middle via frameworks like Evilginx2).

Generating a QR Code (Demonstrating How Easy This Is)

The following code generates a legitimate QR code using the Python qrcode library. The same operation, pointed at a malicious URL, is what attackers use:

 1# qr_demo.py
 2# Shows how trivially easy QR code generation is — no special skill required
 3# pip install qrcode[pil]
 4
 5import qrcode
 6from qrcode.image.styledpil import StyledPilImage
 7from qrcode.image.styles.moduledrawers import RoundedModuleDrawer
 8
 9# Attacker would substitute a phishing URL here
10target_url = "https://legit-looking-domain.com/microsoft365/verify"
11
12qr = qrcode.QRCode(
13    version=1,
14    error_correction=qrcode.constants.ERROR_CORRECT_H,
15    box_size=10,
16    border=4,
17)
18qr.add_data(target_url)
19qr.make(fit=True)
20
21# Styled QR with rounded corners looks more professional and legitimate
22img = qr.make_image(
23    image_factory=StyledPilImage,
24    module_drawer=RoundedModuleDrawer()
25)
26img.save("malicious_qr.png")
27
28print(f"QR code generated for: {target_url}")
29print("Time to create this: approximately 10 seconds.")
30print("No technical knowledge required beyond pip install.")

This is the complete technical skill barrier for a quishing email campaign. Pairing this with a phishing-as-a-service kit that provides the credential capture page reduces the effort to minutes.


The Full Attack Chain

Email-Based Quishing

Step 1: Attacker registers a lookalike domain or uses a URL hosted on a legitimate service (Azure Blob, Google Redirect, Cloudflare Pages) to inherit the host’s reputation.

Step 2: Attacker generates a QR code image containing the phishing URL.

Step 3: A phishing email is crafted with a Microsoft 365 MFA lure:

From: IT Security Team <noreply@company-helpdesk.com>
Subject: Action Required: Verify Your Microsoft 365 MFA Device

Your Microsoft 365 account requires re-verification of your authenticator device.
Please scan the QR code below using your mobile phone within 24 hours to
avoid account suspension.

[QR CODE IMAGE]

Microsoft 365 Security Team

Step 4: Email bypasses SEG (no malicious URLs in body), arrives in inbox.

Step 5: Employee scans QR code on their phone. The URL resolves to a Microsoft login clone configured with an adversary-in-the-middle proxy (Evilginx2, Modlishka, or a commercial phishing kit).

Step 6: Employee enters credentials and MFA code. The proxy captures the session cookie in real time, bypassing MFA.

Step 7: Attacker uses the stolen session cookie to access the M365 account, set up mail forwarding rules, access OneDrive, or conduct BEC from the compromised account.

Physical QR Code Attacks

Physical attacks require no technical delivery mechanism:

Physical Attack Surfaces:
├── Parking meters (payment QR codes)
├── Restaurant table QR menus
├── Conference/event badges and check-in stations
├── Printed flyers (fake promotions, "free WiFi" QR codes)
├── Retail store QR codes (product info, reviews, discounts)
├── Healthcare waiting rooms (check-in QR codes)
└── Hotel/Airbnb property QR codes (WiFi, checkout)

Attack execution: Print a sticker with a QR pointing to a phishing page. Place over the original. Victim sees what appears to be the expected QR code and scans without suspicion.


Detection

Email: Extracting and Analyzing QR Codes from Attachments

 1# qr_extractor.py
 2# Extract and decode QR codes from email image attachments for analysis
 3# pip install pyzbar pillow
 4
 5from PIL import Image
 6from pyzbar.pyzbar import decode
 7import sys
 8import re
 9import urllib.parse
10
11def extract_qr_urls(image_path: str) -> list[str]:
12    """
13    Decode all QR codes from an image and return embedded URLs.
14    """
15    try:
16        img = Image.open(image_path)
17        decoded_objects = decode(img)
18        urls = []
19        for obj in decoded_objects:
20            data = obj.data.decode("utf-8")
21            # Check if decoded data looks like a URL
22            if re.match(r'https?://', data):
23                urls.append(data)
24                print(f"[QR DECODED] URL found: {data}")
25                parsed = urllib.parse.urlparse(data)
26                print(f"  Domain: {parsed.netloc}")
27                print(f"  Path: {parsed.path}")
28        return urls
29    except Exception as e:
30        print(f"Error processing {image_path}: {e}")
31        return []
32
33if __name__ == "__main__":
34    image_file = sys.argv[1] if len(sys.argv) > 1 else "attachment.png"
35    urls = extract_qr_urls(image_file)
36    if not urls:
37        print("No QR codes found in image.")
38    for url in urls:
39        print(f"\n[ANALYSIS NEEDED] Submit to threat intel: {url}")

Microsoft Defender for Office 365 — KQL Hunting Query

// Hunt for emails containing QR code images in Microsoft 365 Defender
// Requires Defender for Office 365 Plan 2 with Advanced Hunting

EmailAttachmentInfo
| where Timestamp > ago(7d)
| where FileType in~ ("png", "jpg", "jpeg", "gif", "bmp", "svg")
| where FileName has_any ("qr", "code", "scan", "verify", "mfa", "auth")
| join kind=inner (
    EmailEvents
    | where Timestamp > ago(7d)
    | where DeliveryAction == "Delivered"
    | where Subject has_any ("verify", "authenticate", "mfa", "action required",
                              "suspended", "unusual sign-in", "security alert")
) on NetworkMessageId
| project Timestamp, SenderFromAddress, RecipientEmailAddress, Subject,
          FileName, FileType, FileSize, NetworkMessageId
| order by Timestamp desc

// Secondary query: Hunt for clicks on URLs extracted from QR codes
// (requires QR extraction pipeline feeding into custom URL logs)
UrlClickEvents
| where Timestamp > ago(7d)
| where ActionType == "ClickAllowed"
| where Url matches regex @"https?://[^/]*\.(blob\.core\.windows\.net|r\.bing\.com|t\.co|bit\.ly)"
| where IPAddress !startswith "10." and IPAddress !startswith "192.168."
// External mobile IP clicking on URL shortener/redirect — characteristic of QR scan on mobile
| project Timestamp, Url, IPAddress, AccountUpn, IsClickedThrough

IOC Patterns for Quishing Campaigns

Indicators of Compromise (QR Phishing):
- Email subjects: "Action Required", "Verify MFA", "Security Alert", "DocuSign"
- Attachment filenames containing: qr, code, scan, verify, auth
- Image dimensions: QR codes are commonly square (300x300 to 1000x1000 px)
- URLs encoded in QR codes hosted on:
  - *.blob.core.windows.net (Azure Blob Storage)
  - *.googleusercontent.com (Google redirect abuse)
  - r.bing.com/* (Bing click redirect)
  - bit.ly, tinyurl.com, t.co (URL shorteners)
- Destination pages mimicking: Microsoft login, DocuSign, Adobe Sign, Okta

Defense and Mitigation Controls

Email Security

1. Enable QR Code Analysis in Your SEG

Microsoft Defender for Office 365 (Plan 1+): Enable Safe Links with QR code scanning — this was specifically added in late 2023 in response to quishing campaigns. Proofpoint and Mimecast also offer QR code extraction and URL analysis capabilities in their enterprise products.

2. Image Attachment Policies

Apply heightened scrutiny to emails with image-only bodies (no text URLs) from external senders. Consider quarantining image-only external emails for review, especially when combined with urgency-related subject lines.

3. Anti-Phishing Policies: Impersonation Protection

Configure Microsoft 365 anti-phishing policies to protect against executive impersonation, which is frequently paired with quishing campaigns.

Mobile Device Management

4. MDM Safe Browsing Enforcement

Enforce safe browsing on managed mobile devices using Intune, Jamf, or equivalent MDM solutions. Microsoft Defender for Endpoint on iOS and Android provides URL reputation checking that applies even when a QR code is scanned.

5. QR Code Scanning App Policy

Configure managed device policies to use a QR scanner app that previews the decoded URL before opening. iOS built-in camera (since iOS 12) displays the URL before navigating — ensure users know to read it. Block QR scanner apps that auto-navigate without preview.

Physical Security

6. Physical QR Code Inspection Protocol

For organizations using QR codes on physical materials (events, offices), inspect signage regularly for overlaid stickers. Use QR codes with tamper-evident features. Display the expected destination URL alongside every physical QR code (“This code takes you to parkingpayment.cityname.gov”).

User Training

7. Train Users to Preview QR Destinations

Security awareness training must specifically cover quishing. Train users to:

  • Always preview the URL before following a QR code.
  • Verify the domain matches the expected service.
  • Never scan a QR code received in an email attachment as the primary action — navigate directly instead.
  • Report suspicious QR codes on physical infrastructure to security teams.


References