AnalysisType: rule
Filename: proofpoint_threat_campaign.py
RuleID: "Proofpoint.ThreatCampaign"
DisplayName: "Proofpoint Active Threat Campaign Detected"
Enabled: true
LogTypes:
- Proofpoint.Event
Status: Experimental
Tags:
- Proofpoint
- Email Security
- Phishing
- Malware
- Campaign
- Initial Access:Phishing
- Execution:User Execution
- Resource Development:Develop Capabilities
Severity: High
Description: >
This rule alerts when Proofpoint identifies an email as part of an active
threat campaign. Campaign-based threats indicate coordinated attacks that
are targeting multiple organizations or users. These threats are typically
more sophisticated and require immediate attention.
Runbook: |
1. Search for all emails from this campaign in the last 30 days and verify quarantine status across the organization
2. Block all campaign IOCs (domains, IPs, file hashes) within 15 minutes and check Proofpoint Threat Insight for details
3. Initiate threat hunting within 2 hours for lateral movement and share IOCs with threat intelligence platforms
Reference: https://help.proofpoint.com/Threat_Insight_Dashboard/API_Documentation/Campaign_API
Reports:
MITRE ATT&CK:
- TA0042:T1587 # Resource Development: Develop Capabilities
- TA0001:T1566 # Initial Access: Phishing
- TA0002:T1204 # Execution: User Execution
Tests:
- Name: Active Campaign with Malware
ExpectedResult: true
Log:
messageTime: "2026-01-08T09:30:00Z"
sender: "campaign-actor@malware.net"
senderIP: "192.0.2.200"
fromAddress:
- "campaign-actor@malware.net"
toAddresses:
- "target@company.com"
recipient:
- "target@company.com"
subject: "Invoice #98765"
malwareScore: 100
phishScore: 30
spamScore: 10
impostorScore: 0
quarantineFolder: "Attachment Defense"
quarantineRule: "threat"
messageID: "<campaign-email-001@malware.net>"
threatsInfoMap:
- threatType: "attachment"
classification: "malware"
threatStatus: "active"
threat: "invoice_98765.exe"
threatID: "mal-threat-abc123"
campaignID: "46e01b8a-0e67-4464-ac5a-b87ad4cc1f2a"
- Name: Active Phishing Campaign
ExpectedResult: true
Log:
messageTime: "2026-01-08T11:00:00Z"
sender: "support@paypa1-secure.com"
senderIP: "198.51.100.88"
fromAddress:
- "support@paypa1-secure.com"
toAddresses:
- "victim@company.com"
recipient:
- "victim@company.com"
subject: "Verify Your Account Now"
malwareScore: 0
phishScore: 95
spamScore: 20
impostorScore: 15
quarantineFolder: "Phish"
quarantineRule: "phish"
messageID: "<phish-campaign-002@paypa1-secure.com>"
threatsInfoMap:
- threatType: "url"
classification: "phish"
threatStatus: "active"
threat: "http://paypa1-verify-account.com/login"
threatID: "phish-threat-xyz789"
campaignID: "credential-harvest-2026-01"
- Name: Multiple Campaigns in One Email
ExpectedResult: true
Log:
messageTime: "2026-01-08T14:00:00Z"
sender: "multi-campaign@attacker.org"
senderIP: "203.0.113.150"
fromAddress:
- "multi-campaign@attacker.org"
toAddresses:
- "employee@company.com"
recipient:
- "employee@company.com"
subject: "Urgent Security Update"
malwareScore: 85
phishScore: 80
spamScore: 15
impostorScore: 10
quarantineFolder: "Attachment Defense"
quarantineRule: "threat"
messageID: "<multi-campaign@attacker.org>"
threatsInfoMap:
- threatType: "attachment"
classification: "malware"
threatStatus: "active"
threat: "security-update.zip"
threatID: "mal-def456"
campaignID: "malware-campaign-alpha"
- threatType: "url"
classification: "phish"
threatStatus: "active"
threat: "http://fake-security-portal.com"
threatID: "phish-ghi789"
campaignID: "phish-campaign-beta"
- Name: Threat Without Campaign - No Alert
ExpectedResult: false
Log:
messageTime: "2026-01-08T15:00:00Z"
sender: "random-attacker@spam.net"
senderIP: "198.51.100.200"
fromAddress:
- "random-attacker@spam.net"
toAddresses:
- "user@company.com"
recipient:
- "user@company.com"
subject: "Click Here Now"
malwareScore: 20
phishScore: 70
spamScore: 50
impostorScore: 0
quarantineFolder: "Phish"
quarantineRule: "phish"
messageID: "<no-campaign@spam.net>"
threatsInfoMap:
- threatType: "url"
classification: "phish"
threatStatus: "active"
threat: "http://isolated-phish.com"
threatID: "phish-jkl012"
- Name: Campaign But Threat Not Active - No Alert
ExpectedResult: false
Log:
messageTime: "2026-01-08T16:00:00Z"
sender: "old-campaign@historical.com"
senderIP: "203.0.113.250"
fromAddress:
- "old-campaign@historical.com"
toAddresses:
- "user@company.com"
recipient:
- "user@company.com"
subject: "Old Threat"
malwareScore: 5
phishScore: 5
spamScore: 10
impostorScore: 0
messageID: "<cleared-campaign@historical.com>"
threatsInfoMap:
- threatType: "url"
classification: "phish"
threatStatus: "cleared"
threat: "http://old-threat.com"
threatID: "phish-mno345"
campaignID: "old-campaign-2025"
- Name: Cleared Campaign Threats Only - No Alert
ExpectedResult: false
Log:
messageTime: "2026-01-08T16:30:00Z"
sender: "remediated@historical.com"
senderIP: "198.51.100.250"
fromAddress:
- "remediated@historical.com"
toAddresses:
- "user@company.com"
recipient:
- "user@company.com"
subject: "Previously Flagged Campaign"
malwareScore: 10
phishScore: 15
spamScore: 5
impostorScore: 0
messageID: "<all-cleared@historical.com>"
threatsInfoMap:
- threatType: "attachment"
classification: "malware"
threatStatus: "cleared"
threat: "old-file.exe"
threatID: "mal-pqr678"
campaignID: "remediated-campaign-2025"
- threatType: "url"
classification: "phish"
threatStatus: "cleared"
threat: "http://remediated-phish.com"
threatID: "phish-stu901"
campaignID: "remediated-campaign-2025"
# ------ paired body: proofpoint_threat_campaign.py ------
from panther_proofpoint_helpers import proofpoint_alert_context
def rule(event):
# Check if any threats have a campaign ID
for threat in event.get("threatsInfoMap", []):
if threat.get("campaignID") and threat.get("threatStatus") == "active":
return True
return False
def severity(event):
malware_score = event.get("malwareScore", 0)
phish_score = event.get("phishScore", 0)
if malware_score >= 90 or phish_score >= 90:
return "CRITICAL"
if malware_score >= 70 or phish_score >= 70:
return "HIGH"
return "DEFAULT"
def title(event):
sender = event.get("sender", "<UNKNOWN_SENDER>")
# Get campaign ID from first threat with one
campaign_id = None
for threat in event.get("threatsInfoMap", []):
if threat.get("campaignID"):
campaign_id = threat.get("campaignID")
break
if campaign_id:
return f"Proofpoint: Active Threat Campaign Detected - {campaign_id}"
return f"Proofpoint: Active Threat Campaign - Email from {sender}"
def alert_context(event):
# Use the common helper
context = proofpoint_alert_context(event)
# Filter to only threats with campaign IDs
all_threats = context["threats"]
campaign_threats = [t for t in all_threats if "campaignID" in t]
campaign_ids = set(t.get("campaignID") for t in campaign_threats if t.get("campaignID"))
# Extend with campaign-specific fields
context.update(
{
"campaignIDs": list(campaign_ids),
"campaignCount": len(campaign_ids),
"threats": campaign_threats,
}
)
return context