Skip to content

Latest commit

 

History

History
148 lines (121 loc) · 7.77 KB

0xFF-0027-Beacon-Traffic-Based-On-Common-User-Agents-Visiting-Limited-Domains.md

File metadata and controls

148 lines (121 loc) · 7.77 KB

Beacon Traffic Based on Common User Agent Only Visiting Limited Number of Rare Domains

Metadata

ID: 0xFF-0027-Beacon-Traffic-Based-On-Common-User-Agents-Visiting-Limited-Domains

OS: WindowsEndpoint, WindowsServer, Linux, MacOS

FP Rate: Medium


ATT&CK Tags

Tactic Technique Subtechnique Technique Name
TA0011 - Command and Control T1071 001 Application Layer Protocol - Web Protocols

Utilized Data Sources

Log Provider Event ID Event Name ATT&CK Data Source ATT&CK Data Component
Zscaler Proxy Network Traffic Network Traffic Content

Technical description of the attack

This query searches web proxy logs for a specific type of beaconing behavior by joining a number of sources together:

  • Traffic by actual web browsers - by looking at traffic generated by a UserAgent that looks like a browser and is used by multiple users to visit a large number of domains.
  • Users that make requests using one of these actual browsers, but only to a small set of domains - none of which are common domains.
  • The traffic is beacon-like, meaning that it occurs during many different hours of the day (i.e., periodic).

Permission required to execute the technique

User

Detection description

Attackers often attempt to masquerade beaconing traffic to a C2 server as genuine browser traffic by setting the UserAgent equal to the UserAgent used by a common web browser such as Edge or Chrome. This query attempts to detect this behavior where the attacker spoofs a common UserAgent value, but one that is not actively used by the user to browse the internet. If such a UserAgent is detected making periodic queries to a domain that is not common, this will trigger an alert.

Considerations

False Positives

This rule might trigger false positives, either because of legitimate applications exhibiting beacon-like behavior or because a user uses a specific browser only occasionally and only to visit a single domain.

Suggested Response Actions

Investigate if the domain contacted is a legitimate business domain. If it is, consider allow-listing the domain to avoid further detections. If not, it should be investigated if the user is actively visiting this domain using a rarely used browser or if this could be an attacker command and control channel.

Detection Blind Spots

Attackers might use domain-fronting techniques to masquerade the actual host names used for beaconing.

References


Detection

Language: Kusto

Platform: Sentinel

Query:

let timeframe = 2*1d;
let RuleId = "0027";
let DedupFields = dynamic(["TimeGenerated"]); // Timeframe during which to search for beaconing behavior.
let lookback = 7d; // Lookback period to find if browser was used for other domains by user.
let min_requests=50; // Minimum number of requests to consider it beacon traffic.
let min_hours=8; // Minimum number of different hours during which connections were made to consider it beacon traffic.
let trusted_user_count=10; // If visited by this many users a domain is considered 'trusted'.
let max_sites=3; // Maximum number of different sites visited using this user-agent.
// Environment-specific query to obtain 'browser-like' traffic from proxy logs.
let BrowserTraffic = (p:timespan) {
CommonSecurityLog
| where DeviceVendor == "Zscaler" and DeviceProduct == "NSSWeblog"
| where (RequestClientApplication startswith "Mozilla/" and RequestClientApplication contains "Gecko")
};
let CommonDomains = BrowserTraffic(timeframe)
| summarize source_count=dcount(SourceUserName) by DestinationHostName
| where source_count>trusted_user_count
| project DestinationHostName;
let CommonUA = BrowserTraffic(timeframe)
| summarize source_count=dcount(SourceUserName), host_count=dcount(DestinationHostName) by RequestClientApplication
| where source_count>trusted_user_count and host_count > 100 // Normal browsers are browsers used by many people and visiting many different sites.
| project RequestClientApplication;
// Find browsers that are common, i.e., many users use them and they use them to visit many different sites.
// But some users only use the browser to visit a very limited set of sites.
// These are considered suspicious - since they might be an attacker masquerading a beacon as a legitimate browser.
let SuspiciousBrowers = BrowserTraffic(timeframe)
| where RequestClientApplication in(CommonUA)
| summarize BrowserHosts=make_set(DestinationHostName),request_count=count() by RequestClientApplication, SourceUserName
| where array_length(BrowserHosts) <= max_sites and request_count >= min_requests
| project RequestClientApplication, SourceUserName,BrowserHosts;
// Just reporting on suspicious browsers gives too many false positives.
// For example users that have the browser open on the login screen of 1 specific application.
// In the suspicious browsers we can search for beacon-like behavior.
// Get all browser traffic by the suspicious browsers.
let PotentialAlerts=SuspiciousBrowers
| join BrowserTraffic(timeframe) on RequestClientApplication, SourceUserName
// Find beaconing-like traffic - i.e., contacting the same host in many different hours.
| summarize hour_count=dcount(bin(TimeGenerated,1h)), BrowserHosts=any(BrowserHosts), request_count=count() by RequestClientApplication, SourceUserName, DestinationHostName
| where hour_count >= min_hours and request_count >= min_requests
// Remove common domains like login.microsoft.com.
| join kind=leftanti CommonDomains on DestinationHostName
// Begin environment-specific filter.
// End environment-specific filter.
| summarize RareHosts=make_set(DestinationHostName), TotalRequestCount=sum(request_count), BrowserHosts=any(BrowserHosts) by RequestClientApplication, SourceUserName
// Remove browsers that visit any common domains.
| where array_length(RareHosts) == array_length(BrowserHosts);
// Look back for 7 days to see the browser was not used to visit more hosts.
// This is to get rid of someone that started up the browser a long time ago and left only a single tab open.
PotentialAlerts
| join BrowserTraffic(lookback) on SourceUserName, RequestClientApplication
| summarize RareHosts=any(RareHosts),BrowserHosts1d=any(BrowserHosts),BrowserHostsLookback=make_set(DestinationHostName) by SourceUserName, RequestClientApplication
| where array_length(RareHosts) == array_length(BrowserHostsLookback)
// Begin de-duplication logic.
| extend DedupFieldValues=pack_all()
| mv-apply e=DedupFields to typeof(string) on (
    extend DedupValue=DedupFieldValues[tostring(e)]
    | order by e // Sorting is required to ensure make_list is deterministic.
    | summarize DedupValues=make_list(DedupValue)
)
| extend DedupEntity=strcat_array(DedupValues, "|")
| project-away DedupFieldValues, DedupValues
| join kind=leftanti (
    SecurityAlert
    | where AlertName has RuleId and ProviderName has "ASI"
    | where TimeGenerated >= ago(timeframe)
    | extend DedupEntity = tostring(parse_json(tostring(parse_json(ExtendedProperties)["Custom Details"])).DedupEntity[0])
    | project DedupEntity
) on DedupEntity
// End de-duplication logic.

Version History

Version Date Impact Notes
1.5 2023-05-04 minor Updated broken URL in documentation.
1.4 2023-02-27 minor Provided a default for proxy_query in case it is not defined.
1.3 2022-08-26 minor Entity mapping added.
1.2 2022-02-22 minor Use ingestion_time for event selection and include de-duplication logic.
1.1 2021-02-19 major Added lookback mechanism.
1.0 2021-02-16 major Initial version.