The post Cisco Talos Unveils “Living-off-the-Land” Tactics Threatening macOS appeared first on Daily CyberSecurity.
Visualização de leitura
10 ChatGPT AI Prompts L1 SOC Analysts Can Use in Their Daily Work
Discover 10 practical ChatGPT prompts SOC analysts can use to speed up triage, analyze threats, improve documentation, and enhance incident response workflows.
The post 10 ChatGPT AI Prompts L1 SOC Analysts Can Use in Their Daily Work appeared first on TechRepublic.
New Infostealer Campaign Targets Users via Spoofed Software Installers
Introduction
Findings
behash:"4acaac53c8340a8c236c91e68244e6cb"
/api/v3/files/09a8b930c8b79e7c313e5e741e1d59c39ae91bc1f10cdefa68b47bf77519be57/execution_parents
signature:"Peastaking plenipotence ductileness chilopodous codicillary."
signature:"© 2026 Eosinophil LLC"
exports:15Mmm95ml1RbfjH1VUyelYFCf exports:2dlSKEtPzvo1mHDN4FYgv
behash:5ddb604194329c1f182d7ba74f6f5946
IOCs
import "pe"
rule win_dll_sideload_eosinophil_infostealer_jan26
{
meta:
author = "VirusTotal"
description = "Detects malicious DLLs (CoreMessaging.dll) from an infostealer campaign impersonating Malwarebytes, Logitech, and others via DLL sideloading."
reference = "https://blog.virustotal.com/2026/01/malicious-infostealer-january-26.html"
date = "2026-01-16"
behash = "4acaac53c8340a8c236c91e68244e6cb"
target_entity = "file"
hash = "606baa263e87d32a64a9b191fc7e96ca066708b2f003bde35391908d3311a463"
condition:
(uint16(0) == 0x5A4D and uint32(uint32(0x3C)) == 0x00004550 and pe.is_dll()) and
pe.exports("15Mmm95ml1RbfjH1VUyelYFCf") and pe.exports("2dlSKEtPzvo1mHDN4FYgv")
}
| sha256 | description |
|---|---|
| 6773af31bd7891852c3d8170085dd4bf2d68ea24a165e4b604d777bd083caeaa | malwarebytes-windows-github-io-X.X.X.zip |
| 4294d6e8f1a63b88c473fce71b665bbc713e3ee88d95f286e058f1a37d4162be | malwarebytes-windows-github-io-X.X.X.zip |
| 5591156d120934f19f2bb92d9f9b1b32cb022134befef9b63c2191460be36899 | malwarebytes-windows-github-io-X.X.X.zip |
| 42d53bf0ed5880616aa995cad357d27e102fb66b2fca89b17f92709b38706706 | malwarebytes-windows-github-io-X.X.X.zip |
| 5aa6f4a57fb86759bbcc9fc6c61b5f74c0ca74604a22084f9e0310840aa73664 | malwarebytes-windows-github-io-X.X.X.zip |
| 84021dcfad522a75bf00a07e6b5cb4e17063bd715a877ed01ba5d1631cd3ad71 | malwarebytes-windows-github-io-X.X.X.zip |
| ca8467ae9527ed908e9478c3f0891c52c0266577ca59e4c80a029c256c1d4fce | malwarebytes-windows-github-io-X.X.X.zip |
| 9619331ef9ff6b2d40e77a67ec86fc81b050eeb96c4b5f735eb9472c54da6735 | malwarebytes-windows-github-io-X.X.X.zip |
| a2842c7cfaadfba90b29e0b9873a592dd5dbea0ef78883d240baf3ee2d5670c5 | malwarebytes-windows-github-io-X.X.X.zip |
| 4705fd47bf0617b60baef8401c47d21afb3796666092ce40fbb7fe51782ae280 | malwarebytes-windows-github-io-X.X.X.zip |
| 580d37fc9d9cc95dc615d41fa2272f8e86c9b4da2988a336a8b3a3f90f4363c2 | malwarebytes-windows-github-io-X.X.X.zip |
| d47fd17d1d82ea61d850ccc2af3bee54adce6975d762fb4dee8f4006692c5ef7 | malwarebytes-windows-github-io-X.X.X.zip |
| 606baa263e87d32a64a9b191fc7e96ca066708b2f003bde35391908d3311a463 | CoreMessaging.dll DLL loaded by DLL SideLoading |
| fd855aa20467708d004d4aab5203dd5ecdf4db2b3cb2ed7e83c27368368f02bb | CoreMessaging.dll DLL loaded by DLL SideLoading |
| a0687834ce9cb8a40b2bb30b18322298aff74147771896787609afad9016f4ea | CoreMessaging.dll DLL loaded by DLL SideLoading |
| 4235732440506e626fd4d0fffad85700a8fcf3e83ba5c5bc8e19ada508a6498e | CoreMessaging.dll DLL loaded by DLL SideLoading |
| cd1fe2762acf3fb0784b17e23e1751ca9e81a6c0518c6be4729e2bc369040ca5 | CoreMessaging.dll DLL loaded by DLL SideLoading |
| f798c24a688d7858efd6efeaa8641822ad269feeb3a74962c2f7c523cf8563ff | CoreMessaging.dll DLL loaded by DLL SideLoading |
| 0698a2c6401059a3979d931b84d2d4b011d38566f20558ee7950a8bf475a6959 | CoreMessaging.dll DLL loaded by DLL SideLoading |
| 1b3bee041f2fffcb9c216522afa67791d4c658f257705e0feccc7573489ec06f | CoreMessaging.dll DLL loaded by DLL SideLoading |
| 231c05f4db4027c131259d1acf940e87e15261bb8cb443c7521294512154379b | CoreMessaging.dll DLL loaded by DLL SideLoading |
| ec2e30d8e5cacecdf26c713e3ee3a45ebc512059a64ba4062b20ca8bec2eb9e7 | CoreMessaging.dll DLL loaded by DLL SideLoading |
| 58bd2e6932270921028ab54e5ff4b0dbd1bf67424d4a5d83883c429cadeef662 | CoreMessaging.dll DLL loaded by DLL SideLoading |
| 57ed35e6d2f2d0c9bbc3f17ce2c94946cc857809f4ab5c53d7cb04a4e48c8b14 | CoreMessaging.dll DLL loaded by DLL SideLoading |
| cfcf3d248100228905ad1e8c5849bf44757dd490a0b323a10938449946eabeee | CoreMessaging.dll DLL loaded by DLL SideLoading |
| f02be238d14f8e248ad9516a896da7f49933adc7b36db7f52a7e12d1c2ddc6af | CoreMessaging.dll DLL loaded by DLL SideLoading |
| f60802c7bec15da6d84d03aad3457e76c5760e4556db7c2212f08e3301dc0d92 | CoreMessaging.dll DLL loaded by DLL SideLoading |
| 02dc9217f870790b96e1069acd381ae58c2335b15af32310f38198b5ee10b158 | CoreMessaging.dll DLL loaded by DLL SideLoading |
| f9549e382faf0033b12298b4fd7cd10e86c680fe93f7af99291b75fd3d0c9842 | CoreMessaging.dll DLL loaded by DLL SideLoading |
| 92f4d95938789a69e0343b98240109934c0502f73d8b6c04e8ee856f606015c8 | CoreMessaging.dll DLL loaded by DLL SideLoading |
| 66fba00b3496d61ca43ec3eae02527eb5222892186c8223b9802060a932a5a7a | CoreMessaging.dll DLL loaded by DLL SideLoading |
| e5dd464a2c90a8c965db655906d0dc84a9ac84701a13267d3d0c89a3c97e1e9b | CoreMessaging.dll DLL loaded by DLL SideLoading |
| 35211074b59417dd5a205618fed3402d4ac9ca419374ff2d7349e70a3a462a15 | CoreMessaging.dll DLL loaded by DLL SideLoading |
| 6863b4906e0bd4961369b8784b968b443f745869dbe19c6d97e2287837849385 | CoreMessaging.dll DLL loaded by DLL SideLoading |
| a83c478f075a3623da5684c52993293d38ecaa17f4a1ddca10f95335865ef1e2 | CoreMessaging.dll DLL loaded by DLL SideLoading |
| 43e2936e4a97d9bc43b423841b137fde1dd5b2f291abf20d3ba57b8f198d9fab | CoreMessaging.dll DLL loaded by DLL SideLoading |
| f001ae3318ba29a3b663d72b5375d10da5207163c6b2746cfae9e46a37d975cf | CoreMessaging.dll DLL loaded by DLL SideLoading |
| c67403d3b6e7750222f20fa97daa3c05a9a8cce39db16455e196cd81d087b54d | CoreMessaging.dll DLL loaded by DLL SideLoading |
| 5ee9d4636b01fd3a35bd8e3dce86a8c114d8b0aa6b68b1d26ace7ef0f85b438a | Payload dropped by one of the malicious DLLs |
| e84b0dadb0b6be9b00a063ed82c8ddba06a2bd13f07d510d14e6fd73cd613fba | Payload dropped by one of the malicious DLLs |
New Infostealer Campaign Targets Users via Spoofed Software Installers
Introduction
Findings
behash:"4acaac53c8340a8c236c91e68244e6cb"
/api/v3/files/09a8b930c8b79e7c313e5e741e1d59c39ae91bc1f10cdefa68b47bf77519be57/execution_parents
signature:"Peastaking plenipotence ductileness chilopodous codicillary."
signature:"© 2026 Eosinophil LLC"
exports:15Mmm95ml1RbfjH1VUyelYFCf exports:2dlSKEtPzvo1mHDN4FYgv
behash:5ddb604194329c1f182d7ba74f6f5946
IOCs
import "pe"
rule win_dll_sideload_eosinophil_infostealer_jan26
{
meta:
author = "VirusTotal"
description = "Detects malicious DLLs (CoreMessaging.dll) from an infostealer campaign impersonating Malwarebytes, Logitech, and others via DLL sideloading."
reference = "https://blog.virustotal.com/2026/01/malicious-infostealer-january-26.html"
date = "2026-01-16"
behash = "4acaac53c8340a8c236c91e68244e6cb"
target_entity = "file"
hash = "606baa263e87d32a64a9b191fc7e96ca066708b2f003bde35391908d3311a463"
condition:
(uint16(0) == 0x5A4D and uint32(uint32(0x3C)) == 0x00004550 and pe.is_dll()) and
pe.exports("15Mmm95ml1RbfjH1VUyelYFCf") and pe.exports("2dlSKEtPzvo1mHDN4FYgv")
}
| sha256 | description |
|---|---|
| 6773af31bd7891852c3d8170085dd4bf2d68ea24a165e4b604d777bd083caeaa | malwarebytes-windows-github-io-X.X.X.zip |
| 4294d6e8f1a63b88c473fce71b665bbc713e3ee88d95f286e058f1a37d4162be | malwarebytes-windows-github-io-X.X.X.zip |
| 5591156d120934f19f2bb92d9f9b1b32cb022134befef9b63c2191460be36899 | malwarebytes-windows-github-io-X.X.X.zip |
| 42d53bf0ed5880616aa995cad357d27e102fb66b2fca89b17f92709b38706706 | malwarebytes-windows-github-io-X.X.X.zip |
| 5aa6f4a57fb86759bbcc9fc6c61b5f74c0ca74604a22084f9e0310840aa73664 | malwarebytes-windows-github-io-X.X.X.zip |
| 84021dcfad522a75bf00a07e6b5cb4e17063bd715a877ed01ba5d1631cd3ad71 | malwarebytes-windows-github-io-X.X.X.zip |
| ca8467ae9527ed908e9478c3f0891c52c0266577ca59e4c80a029c256c1d4fce | malwarebytes-windows-github-io-X.X.X.zip |
| 9619331ef9ff6b2d40e77a67ec86fc81b050eeb96c4b5f735eb9472c54da6735 | malwarebytes-windows-github-io-X.X.X.zip |
| a2842c7cfaadfba90b29e0b9873a592dd5dbea0ef78883d240baf3ee2d5670c5 | malwarebytes-windows-github-io-X.X.X.zip |
| 4705fd47bf0617b60baef8401c47d21afb3796666092ce40fbb7fe51782ae280 | malwarebytes-windows-github-io-X.X.X.zip |
| 580d37fc9d9cc95dc615d41fa2272f8e86c9b4da2988a336a8b3a3f90f4363c2 | malwarebytes-windows-github-io-X.X.X.zip |
| d47fd17d1d82ea61d850ccc2af3bee54adce6975d762fb4dee8f4006692c5ef7 | malwarebytes-windows-github-io-X.X.X.zip |
| 606baa263e87d32a64a9b191fc7e96ca066708b2f003bde35391908d3311a463 | CoreMessaging.dll DLL loaded by DLL SideLoading |
| fd855aa20467708d004d4aab5203dd5ecdf4db2b3cb2ed7e83c27368368f02bb | CoreMessaging.dll DLL loaded by DLL SideLoading |
| a0687834ce9cb8a40b2bb30b18322298aff74147771896787609afad9016f4ea | CoreMessaging.dll DLL loaded by DLL SideLoading |
| 4235732440506e626fd4d0fffad85700a8fcf3e83ba5c5bc8e19ada508a6498e | CoreMessaging.dll DLL loaded by DLL SideLoading |
| cd1fe2762acf3fb0784b17e23e1751ca9e81a6c0518c6be4729e2bc369040ca5 | CoreMessaging.dll DLL loaded by DLL SideLoading |
| f798c24a688d7858efd6efeaa8641822ad269feeb3a74962c2f7c523cf8563ff | CoreMessaging.dll DLL loaded by DLL SideLoading |
| 0698a2c6401059a3979d931b84d2d4b011d38566f20558ee7950a8bf475a6959 | CoreMessaging.dll DLL loaded by DLL SideLoading |
| 1b3bee041f2fffcb9c216522afa67791d4c658f257705e0feccc7573489ec06f | CoreMessaging.dll DLL loaded by DLL SideLoading |
| 231c05f4db4027c131259d1acf940e87e15261bb8cb443c7521294512154379b | CoreMessaging.dll DLL loaded by DLL SideLoading |
| ec2e30d8e5cacecdf26c713e3ee3a45ebc512059a64ba4062b20ca8bec2eb9e7 | CoreMessaging.dll DLL loaded by DLL SideLoading |
| 58bd2e6932270921028ab54e5ff4b0dbd1bf67424d4a5d83883c429cadeef662 | CoreMessaging.dll DLL loaded by DLL SideLoading |
| 57ed35e6d2f2d0c9bbc3f17ce2c94946cc857809f4ab5c53d7cb04a4e48c8b14 | CoreMessaging.dll DLL loaded by DLL SideLoading |
| cfcf3d248100228905ad1e8c5849bf44757dd490a0b323a10938449946eabeee | CoreMessaging.dll DLL loaded by DLL SideLoading |
| f02be238d14f8e248ad9516a896da7f49933adc7b36db7f52a7e12d1c2ddc6af | CoreMessaging.dll DLL loaded by DLL SideLoading |
| f60802c7bec15da6d84d03aad3457e76c5760e4556db7c2212f08e3301dc0d92 | CoreMessaging.dll DLL loaded by DLL SideLoading |
| 02dc9217f870790b96e1069acd381ae58c2335b15af32310f38198b5ee10b158 | CoreMessaging.dll DLL loaded by DLL SideLoading |
| f9549e382faf0033b12298b4fd7cd10e86c680fe93f7af99291b75fd3d0c9842 | CoreMessaging.dll DLL loaded by DLL SideLoading |
| 92f4d95938789a69e0343b98240109934c0502f73d8b6c04e8ee856f606015c8 | CoreMessaging.dll DLL loaded by DLL SideLoading |
| 66fba00b3496d61ca43ec3eae02527eb5222892186c8223b9802060a932a5a7a | CoreMessaging.dll DLL loaded by DLL SideLoading |
| e5dd464a2c90a8c965db655906d0dc84a9ac84701a13267d3d0c89a3c97e1e9b | CoreMessaging.dll DLL loaded by DLL SideLoading |
| 35211074b59417dd5a205618fed3402d4ac9ca419374ff2d7349e70a3a462a15 | CoreMessaging.dll DLL loaded by DLL SideLoading |
| 6863b4906e0bd4961369b8784b968b443f745869dbe19c6d97e2287837849385 | CoreMessaging.dll DLL loaded by DLL SideLoading |
| a83c478f075a3623da5684c52993293d38ecaa17f4a1ddca10f95335865ef1e2 | CoreMessaging.dll DLL loaded by DLL SideLoading |
| 43e2936e4a97d9bc43b423841b137fde1dd5b2f291abf20d3ba57b8f198d9fab | CoreMessaging.dll DLL loaded by DLL SideLoading |
| f001ae3318ba29a3b663d72b5375d10da5207163c6b2746cfae9e46a37d975cf | CoreMessaging.dll DLL loaded by DLL SideLoading |
| c67403d3b6e7750222f20fa97daa3c05a9a8cce39db16455e196cd81d087b54d | CoreMessaging.dll DLL loaded by DLL SideLoading |
| 5ee9d4636b01fd3a35bd8e3dce86a8c114d8b0aa6b68b1d26ace7ef0f85b438a | Payload dropped by one of the malicious DLLs |
| e84b0dadb0b6be9b00a063ed82c8ddba06a2bd13f07d510d14e6fd73cd613fba | Payload dropped by one of the malicious DLLs |
How Researchers Collect Indicators of Compromise
As security researchers, we actively monitor the latest CVEs and their publicly available exploits to create signatures. Beyond CVEs, we also hunt for malware on platforms such as MalwareBazaar, which enhances our visibility into attacks occurring across networks.
Threat Hunting Power Up | Enhance Campaign Discovery With Validin and Synapse
Tracking threat actor infrastructure has become increasingly complex. Modern adversaries rotate domains, reuse hosting, and replicate infrastructure templates across operations, making it difficult to connect isolated indicators to broader activity. Checking an IP, a domain, or a certificate in isolation can often return little of value when adversaries hide behind short-lived domains and churned TLS certificates.
As a result, analysts can struggle to see how infrastructure evolves over time or to identify shared traits like favicon hashes, header patterns, or registration overlaps that can link related assets.
To help address this, SentinelLABS is sharing a Synapse Rapid Power-Up for Validin. Developed in-house by SentinelLABS engineers, the sentinelone-validin power-up provides commands to query for and model DNS records, HTTP crawl data, TLS certificates, and WHOIS information, enabling analysts to quickly search, pivot through, and investigate network infrastructure for time-aware, cross-source analysis.
In this post, we explore two real-world case studies to demonstrate how an analyst can use the power-up to discover and expand their knowledge of threats.
Case Study 1 | LaundryBear APT: Body Hash Pivots
When Microsoft published indicators for LaundryBear (aka Void Blizzard), a Russian APT targeting NATO and Ukraine, the threat report included just three domains. Using the power-up’s HTTP body hash pivots, we can expand this seed set to over 30 related domains, revealing the full scope of the campaign’s infrastructure.
Initial Enrichment of Known Indicators
We begin with the s1.validin.enrich command, which serves as a unified entry point for all Validin data sources. Rather than running separate commands for DNS history, HTTP crawls, certificates, and WHOIS records, this single command executes comprehensive enrichment across all four datasets simultaneously.
The resulting node graph immediately reveals initial pivot opportunities—shared nameservers in DNS records, certificate SAN relationships, registration timing patterns, and HTTP fingerprint clusters—providing multiple investigative paths forward.
This rapid reconnaissance phase surfaces the most promising leads before committing to expensive deep pivots, helping analysts choose the optimal next step based on what patterns emerge from the enriched graph.
// Tag the published spear-phishing domain [inet:fqdn=<phising domain> +#research.laundrybear.seed] // enrich the initial domain inet:fqdn#research.laundrybear.seed | s1.validin.enrich --wildcard // display all unique fqdns related to this seed inet:fqdn#research.laundrybear.seed -> inet:fqdn | uniq

Pivoting from Crawlr Data
The Validin crawler (Crawlr) is a purpose-built, large-scale web crawler operated by Validin that continuously scans internet infrastructure. Querying Validin through the sentinelone-validin power-up provides access to pre-existing crawl observations, allowing instant analysis without active scanning.
The crawler data for our seed domains was already downloaded during the initial s1.validin.enrich command. This created inet:http:request nodes in Synapse containing multiple HTTP fingerprints stored as custom properties: body hashes (SHA1), favicon hashes (MD5), certificate fingerprints, banner hashes, and CSS class hashes.
Each fingerprint type serves as a pivot point: body hashes reveal identical content, favicon hashes expose shared branding, certificate fingerprints uncover SSL infrastructure, and class hashes detect configuration patterns. Together, these pivots transform the initial three seed domains into a comprehensive infrastructure map.
The query starts with our tagged seed domains, pivots to any related FQDNs discovered during enrichment, follows URL relationships, and lands on the actual HTTP request nodes captured by Validin’s crawler. Each inet:http:request node serves as a rich pivot point connecting to multiple content fingerprints and infrastructure properties.
// List all http requests to all the subdomains inet:fqdn#research.laundrybear.seed -> inet:fqdn -> inet:url -> inet:http:request


HTTP Pivot Discovery
Validin’s Laundry Bear Infrastructure analysis identified synchronized HTTP responses across threat actor infrastructure. We can reach the same discovery using Storm’s HTTP pivot with statistical output.
When executing inet:http:request, the command prints detailed occurrence statistics to the Storm console: how many times each HTTP fingerprint from the input http response (body SHA1, favicon MD5, banner hashes, certificate fingerprints, header patterns) appears in Validin’s database. For example, a body hash might appear on 21 IPs and 55 hostnames, a favicon hash might match 18 IPs and 52 hostnames, while a certificate fingerprint on 15 IPs and 48 hostnames.
The size of these counts is the critical indicator. High counts in the thousands indicate benign infrastructure like CDNs and can be dismissed from consideration. Very low counts (1-5) suggest isolated infrastructure. However, when a particular hash appears in the Validin crawler database with a moderate count (15-55 hosts with the same hash fingerprint in this case), it can indicate synchronized infrastructure provisioning: the exact pattern that characterized Laundry Bear’s coordinated buildout. In short, the --dry-run flag transforms expensive full-graph pivots into rapid statistical reconnaissance.
// Collect all the hash:sha1 indicators gathered in the previous step and perform a "dry run" with the s1.validin http.pivot to check statistics inet:fqdn#research.laundrybear.seed -> inet:fqdn -> inet:url -> inet:http:request -> hash:sha1 | uniq | s1.validin.http.pivot --dry-run

Materializing and Summarizing Pivots in Synapse
After identifying promising body hash pivots through --dry-run statistics, we need to materialize the actual infrastructure and summarize the results. Consider this command:
// materialize and summarize apex domains form single pivot hash:sha1=38c47d338a9c5ab7ccef7413edb7b2112bdfc56f | s1.validin.http.pivot --yield // pivot to apex domains | +inet:fqdn -> inet:fqdn +:iszone=true | uniq

Omitting the --dry-run flag is critical here; removing the flag allows the Storm command to create and persist all discovered nodes in the Cortex. The full infrastructure graph (HTTP requests, certificates, DNS records) is ingested, making it available for future pivots and correlation with other intelligence sources. The final filtering and deduplication produces a concise summary: “This body hash appears across N distinct apex domains”, transforming raw occurrence statistics into actionable threat intelligence.
Results:
- 10 apex domains with identical body hash visible immediately (JavaScript redirect)
- Most are Microsoft/corporate service typosquats used for credential phishing
- 22 IP address shared by multiple domains, revealing hosting infrastructure
Note that the query here identifies ten domains in total, two more than reported in Validin’s analysis of the LaundryBear infrastructure. The extra context here comes from our additional use of the official synapse-psl power-up, which ingests and maintains the Mozilla Public Suffix List and ensures that inet:fqdn:zone correctly identifies true organizational boundaries.
Tagging Discovered Infrastructure
Once we’ve identified related infrastructure through hash pivots, we need to tag these findings for tracking and future analysis. Storm provides inline tagging capabilities that mark nodes during the pivot workflow by appending this snippet at the end of a query that produces output to be tagged.
// Tagging ... [+#research.laundrybear.infra]
This workflow expanded three published indicators to 55 domains and 21 IP addresses through body hash pivots, revealing the campaign’s infrastructure scope.
Case Study 2 | FreeDrain: Large-Scale Pivot Operations
FreeDrain, an industrial-scale cryptocurrency phishing network, used 38,000+ lure pages across gitbook.io, webflow.io, and github.io. The campaign templated infrastructure with reused favicons, redirector domains, and phishing pages hosted on Azure and AWS S3—an ideal scenario for demonstrating the power-up’s capabilities.
Discovering Bulk Registration Patterns
During the initial investigation, SentinelLABS identified a set of redirect domains used by FreeDrain operators to funnel victims from legitimate hosting platforms to attacker-controlled phishing infrastructure. These domains were tagged as #research.freedrain.href in our Cortex. To understand the operational infrastructure behind these redirectors in Synapse, we can enrich them through Validin’s WHOIS data:
// Enrich the domains with whois data inet:fqdn#research.freedrain.href | s1.validin.whois
This enrichment ingests historical WHOIS records for each redirect domain, creating inet:whois:rec nodes with registration dates, expiration information, and critically, registrar relationships.
When exploring the enriched graph in Synapse’s UI, several nodes immediately stand out (highlighted in yellow in our default workspace setup), indicating CNO (Computer Network Operations) tags from previous investigations.

The highlighting reveals that the shared registrar is already tagged as #cno.infra.dns.bulk, a designation in our environment for DNS registrars known to facilitate bulk domain registrations used in threat operations.
This isn’t a coincidental infrastructure overlap: we’ve immediately connected FreeDrain to a known malicious service provider that’s appeared in previous campaigns. The pre-existing #cno. tag transforms this from a simple infrastructure observation into a high-confidence attribution signal: FreeDrain operators are using the same operational resources as other documented threat actors.
Before pivoting broadly, we can survey the registrar landscape within our FreeDrain redirect domain pool to understand infrastructure diversity:
inet:fqdn#research.freedrain.href | s1.validin.whois --yield | :registrar -> * | uniq

To isolate the domains registered through this bulk provider, we can filter directly by registrar name:
inet:fqdn#research.freedrain.href -> inet:whois:rec | +:registrar='<registrar name>'
The FreeDrain case demonstrates how Validin’s WHOIS enrichment can transform large-scale phishing investigations. Starting with a handful of tagged redirect domains, a single enrichment command and two pivot queries exposed the full operational infrastructure—hundreds of domains provisioned through bulk registrars.
This is possible due to Synapse’s ability to correlate new campaign data with historical intelligence: the pre-existing #cno.infra.dns.bulk tags immediately identified FreeDrain’s infrastructure as part of a known malicious service ecosystem, providing attribution context that wouldn’t exist in isolated analysis.
Passive DNS
Domain Enrichments
The sentinelone-validin power-up allows us to enrich a domain and pivot to find details, not only about its current registration but the history of the records. With the –wildcard option, Validin returns the DNS records for all related subdomains.
// Enrich the domain with its registration information, and also include subdomains [inet:fqdn=<target domain>] | s1.validin.dns --wildcard

Certificate Transparency
Certificates often include multiple domains in Subject Alternative Name (SAN) fields, revealing infrastructure relationships. The following query can help us to quickly find all certificates issued for a domain and its subdomains:
// Find all domains that share certificates with our target inet:fqdn=<target domain> | s1.validin.certs --wildcard --yield

WHOIS
The sentinelone-validin power-up is able to ingest historical WHOIS registration data, creating several node types that enable temporal analysis of infrastructure:
inet:whois:rec– WHOIS records with registration dates (:created), expiration (:expires), last update (:updated), and registrar informationinet:whois:rar– Registrar entities referenced by WHOIS recordsinet:whois:recns– Nameserver associations for each domain registrationinet:whois:contact– Contact records for domain roles (registrant, admin, tech, billing) including name, organization, email, phone, and postal address details
Multiple historical records are created per domain, allowing analysts to track changes in infrastructure over time. For example, domains registered on the same day can indicate batch infrastructure provisioning. A query such as:
inet:fqdn=<target domain> | s1.validin.whois
returns all relevant WHOIS records for the target domain, allowing the analyst to pivot to other domains, registrars, or contacts that share temporal or structural relations.
HTTP Crawler
One of the initial challenges we sought to address with the sentinelone-validin power-up was how to improve visibility into infrastructure that exists even as domains and hosting change. Validin’s crawler collects fingerprints such as page content, headers and favicons that persist across domain rotation and infrastructure churn. Leveraging these fingerprints allows analysts to identify patterns and connections that might otherwise be overlooked.
Host Response Fingerprinting
Validin’s HTTP crawler captures and fingerprints multiple aspects of web server responses—favicons, body content, HTTP headers, TLS banners, and HTML structure. The power-up parses these fingerprints and models them as pivotable properties in Synapse, enabling infrastructure clustering through content similarity.
inet:fqdn=<target domain> | s1.validin.http --yield

The crawler extracts:
- Favicon hashes (MD5) – Often forgotten when templating infrastructure
- Body hashes (SHA1) – Detect identical or templated page content
- Banner hashes – Fingerprint server software and configuration
- Header hashes – Identify shared backend infrastructure
- HTML class hashes – Cluster pages with similar structure
Downloading and Parsing Content
The power-up includes a built-in download capability that retrieves actual file content for deeper analysis. The s1.validin.download command fetches HTTP response bodies, TLS certificates, and favicon images from Validin’s storage, creating file:bytes nodes in the Cortex.
Combined with Synapse’s fileparser.parse, this enables the extraction of embedded indicators such as URLs, email addresses, file hashes, IP addresses, and other IOCs hidden in page content or certificate metadata:
In Synapse, inet:http:request is a GUID node representing a unique HTTP request event in the hypergraph. Each GUID node has a deterministic identifier usually derived from the request’s properties, enabling implicit deduplication and efficient correlation of network artifacts across the graph.
// Download and parse HTTP responses, certificates, and favicons inet:http:request=| s1.validin.download --yield | fileparser.parse
Hash-Based Pivoting
Traditional graph-based investigation requires loading all data into Synapse first, then querying relationships within your local graph. This works well for known datasets but becomes expensive when exploring unknown infrastructure: an analyst may ingest thousands of nodes only to discover they represent CDN infrastructure, shared hosting, or other benign patterns.
The HTTP pivot capability offers an alternative workflow: querying hashes directly via Validin’s API before loading data into Synapse. This enables selective enrichment, allowing the analyst to explore and evaluate potential pivot paths externally before committing to graph expansion.
// Pivot from a favicon hash to find related domains hash:md5=| s1.validin.http.pivot --category FAVICON_HASH // Pivot from an HTTP request node's embedded hashes inet:http:request= | s1.validin.http.pivot --yield // Discover related infrastructure via body hash hash:sha1= | s1.validin.http.pivot --first-seen 2024/01/01
This approach provides flexibility: assess pivot scope first, then selectively load relevant data.The --dry-run flag shows result counts without creating nodes, letting the analyst preview results before ingestion.
The HTTP pivot command supports multiple content fingerprints:
- FAVICON_HASH – MD5 of favicon images
- BODY_SHA1 – SHA1 of HTTP response bodies
- BANNER_0_HASH – First banner hash
- CLASS_0_HASH / CLASS_1_HASH – Content classification hashes
- CERT_FINGERPRINT / CERT_FINGERPRINT_SHA256 – Certificate fingerprints
- HEADER_HASH – HTTP header pattern hashes
Multi-Source Comprehensive Enrichment
The sentinelone-validin power-up combines multiple data sources in ways that traditional tools cannot. Using this capability, the analyst can retrieve DNS history, HTTP crawl results, certificates and WHOIS records for a domain, all in one query. For example:
inet:fqdn=<target domain> | s1.validin.enrich
This single command populates the Cortex with a rich node graph, giving the analyst a unified view of the target infrastructure and enabling deeper correlation and pivoting across multiple sources.
Try It Yourself
SentinelLABS has open-sourced the sentinelone-validin power-up so other Synapse users can leverage these capabilities:
// Load and configure pkg.load --path /path/to/s1-validin.yaml s1.validin.setup.apikey// Start investigating inet:fqdn=<target domain> | s1.validin.enrich
Minimal Working Example (Docker Compose)
Before deploying the power-up to a production environment, test the environment locally. It is possible to test the power-up with the open-source version of Vertex Cortex as follows
- Use the compose file at docker-compose.yml to bring up a local Cortex
- Load the sentinelone-validin package
- Open a Storm shell connected to it
# From the repository root docker compose up -d cortex docker compose --profile tools run --rm loadpkg docker compose --profile tools run --rm storm
- The Storm shell connects to cortex and you can run the examples from this post, for example:
inet:fqdn=<target domain> | s1.validin.enrich
Storm Query Tips
- Deduplicate: add
| uniqafter broad pivots. - Bound results: use
--first-seen/--last-seenons1.validin.*commands. - Control fanout: add
| limitduring exploration to keep result sets manageable. - Tag iteratively: tag intermediate sets (e.g.,
+#rep.investigation.2026_1.stage1) to branch workflows and compare clusters.
Conclusion
Using the LaundryBear and FreeDrain campaigns as case studies, we’ve explored how the sentinelone-validin power-up leverages Validin’s multi-source enrichment and HTTP fingerprinting to reveal wider campaign infrastructure within Synapse, from just a handful of indicators.
The integration makes it easier to follow how infrastructure changes over time, trace shared resources across campaigns, and connect what might first appear as isolated indicators. With this richer context available directly in Synapse, analysts can move from collection to understanding with greater speed and confidence in their conclusions.
The SentinelLABS team welcomes feedback and pull requests on the sentinelone-validin GitHub repository to help refine and extend its capabilities for the wider research community.
Resources
- sentineone-validin Power-Up
- Synapse Documentation
- Validin Platform
- Storm Query Language Guide
- LaundryBear Infrastructure Analysis
- FreeDrain: Uncovering an Industrial-Scale Crypto Theft Network

VTPRACTITIONERS{ACRONIS}: Tracking FileFix, Shadow Vector, and SideWinder
Introduction
How VT plays a role in hunting for analysts
Acronis - Success Story
Hunting Exploits Like It’s 2017-0199 (SideWinder Edition)
/*
Checks if the file is tagged with CVE-2017-0199 or CVE-2017-11882
and originates from one of the targeted countries
and the file type is a Word document, RTF, or MS-Office file
*/
import "vt"
rule hunting_cve_maldocs {
meta:
author = "Acronis Threat Research Unit (TRU)"
description = "Hunting for malicious Word/RTF files exploiting CVE-2017-0199 or CVE-2017-11882 from specific countries"
distribution = "TLP:CLEAR"
version = "1.2"
condition:
// Match if the file has CVE-2017-0199 or CVE-2017-11882 in the tags
for any tag in vt.metadata.tags :
(
tag == "cve-2017-0199" or
tag == "cve-2017-11882"
)
// Originates from a specific country?
and
(
// Removed CN due to spam submissions of related maldocs
vt.metadata.submitter.country == "PK" or
vt.metadata.submitter.country == "LK" or
vt.metadata.submitter.country == "BD" or
vt.metadata.submitter.country == "NP" or
vt.metadata.submitter.country == "MM" or
vt.metadata.submitter.country == "MV" or
vt.metadata.submitter.country == "AF"
)
// Is it a DOC, DOCX, or RTF?
and
(
vt.metadata.file_type == vt.FileType.DOC or
vt.metadata.file_type == vt.FileType.DOCX or
vt.metadata.file_type == vt.FileType.RTF
)
// Different TA spotted using .ru TLD (excluding it for now)
and not (
for any url in vt.behaviour.memory_pattern_urls : (
url contains ".ru"
)
)
and vt.metadata.new_file
}
/*
Sidewinder related malicious documents exploiting CVE 2017-0199 used during 2025 campaign
*/
import "vt"
rule apt_sidewinder_documents
{
meta:
author = "Acronis Threat Research Unit (TRU)"
description = "Sidewinder related malicious documents exploiting CVE 2017-0199"
distribution = "TLP:CLEAR"
version = "1.0"
strings:
$a1 = {62544CB1F0B9E6E04433698E85BFB534278B9BDC5F06589C011E9CB80C71DF23}
$a2 = {E20F76CDABDFAB004A6BA632F20CE00512BA5AD2FE8FB6ED9EE1865DFD07504B0304140000}
condition:
filesize < 5000KB
and any of ($a*)
and vt.metadata.new_file
// Getting spammy samples from a CN submitter
and not vt.metadata.submitter.country == "CN"
} Uncovering Shadow Vector’s SVG-Based Crimeware Campaign in Colombia
<!--
This YARA rule detects potentially malicious SVG files that are likely being used for crimeware campaigns targeting Colombia.
The rule identifies SVG images that contain legal or judicial terms commonly used in phishing scams,
along with embedded external links that could be used to deliver a payload.
-->
import "vt"
rule crimeware_svg_colombia {
meta:
author = "Acronis Threat Research Unit (TRU)"
description = "Detects potentially malicious SVG files that are likely being used for crimeware campaigns targeting Colombia"
distribution = "TLP:CLEAR"
version = "1.1"
// Reference hashes
hash1 = "6d4a53da259c3c8c0903b1345efcf2fa0d50bc10c3c010a34f86263de466f5a1"
hash2 = "2aae8e206dd068135b16ff87dfbb816053fc247a222aad0d34c9227e6ecf7b5b"
hash3 = "4cfeab122e0a748c8600ccd14a186292f27a93b5ba74c58dfee838fe28765061"
hash4 = "9bbbcb6eae33314b84f5e367f90e57f487d6abe72d6067adcb66eba896d7ce33"
hash5 = "60e87c0fe7c3904935bb1604bdb0b0fc0f2919db64f72666b77405c2c1e46067"
hash6 = "609edc93e075223c5dc8caaf076bf4e28f81c5c6e4db0eb6f502dda91500aab4"
hash7 = "4795d3a3e776baf485d284a9edcf1beef29da42cad8e8261a83e86d35b25cafe"
hash8 = "5673ad3287bcc0c8746ab6cab6b5e1b60160f07c7b16c018efa56bffd44b37aa"
hash9 = "b3e8ab81d0a559a373c3fe2ae7c3c99718503411cc13b17cffd1eee2544a787b"
hash10 = "b5311cadc0bbd2f47549f7fc0895848adb20cc016387cebcd1c29d784779240c"
hash11 = "c3319a8863d5e2dc525dfe6669c5b720fc42c96a8dce3bd7f6a0072569933303"
hash12 = "cb035f440f728395cc4237e1ac52114641dc25619705b605713ecefb6fd9e563"
hash13 = "cf23f7b98abddf1b36552b55f874ae1e2199768d7cefb0188af9ee0d9a698107"
hash14 = "f3208ae62655435186e560378db58e133a68aa6107948e2a8ec30682983aa503"
strings:
// SVG
$svg = "<svg xmlns=" ascii fullword
// Documents containing legal or judicial terms
$s1 = "COPIA" nocase
$s2 = "CITACION" nocase
$s3 = "JUZGADO" nocase
$s4 = "PENAL" nocase
$s5 = "JUDICIAL" nocase
$s6 = "BOGOTA" nocase
$s7 = "DEMANDA" nocase
// When image loads it retrieves payload from external website using HTTPS
$href1= "href='https://" nocase
$href2 = "href=\"https://" nocase
condition:
$svg
and filesize < 3MB
and 3 of ($s*)
and any of ($href*)
and vt.metadata.submitter.country == "CO"
}
rule crimeware_shadow_vector_svg
{
meta:
description = "Detects malicious SVG files associated with Shadow
Vector's Colombian campaign"
author = "Acronis Threat Research Unit (TRU)"
file_type = "SVG"
malware_family = "Shadow Vector"
threat_category = "Crimeware / Malicious Image / Embedded Payload"
tlp = "TLP:CLEAR"
strings:
$svg_tag1 = "<?xml" ascii
$svg_tag2 = "<svg" ascii
$svg_tag3 = "<!DOCTYPE svg" ascii
$svg_tag4 = "http://www.w3.org/2000/svg" ascii
//used by Shadow Vector (possibly generated in batch)
$judicial = "juzgado" ascii nocase
$judicial_1 = "citacion" ascii nocase
$judicial_2 = "judicial" ascii nocase
$judicial_3 = "despacho" ascii nocase
$generado = "Generado" ascii nocase
condition:
filesize < 3MB and
3 of ($svg_tag*) and
(1 of ($judicial*) and $generado)
}
FileFix in the wild!
Anatomy of an attack- where do we start?
- The commands used to copy text into the victims clipboard
- The commands used to construct the malicious payload
import "vt"
rule ClickFix
{
strings:
$clipboard = /(navigator\.clipboard\.writeText|document\.execCommand\(\"copy\"\))/
$pay01 = /(powershell|cmd|mshta|msiexec|pwsh)/gvfi
condition:
vt.net.url.new_url and
$clipboard and
any of ($pay*)
}
$pay03 = /powershell.{,80}iex/Analyzing payloads
Looking for a *Fix
VT Tips (based on the success story)
Supercharge Your Web-Content YARA Rules
entity:url (content:"navigator.clipboard.writeText" or content:"document.execCommand(\"copy\")") (content:"String.fromCharCode" or content:"atob")
Dissect Payloads with Advanced Content Queries
- Custom function names
- Specific variable names
- Uncommon comments
- Unique sequences of commands or API calls
behavior:"[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String("
Don't forget about the infrastructure
import "vt"
rule URLs_Downloading_Decoy_RTF_SideWinder {
meta:
target_entity = "url"
author = "Virustotal"
description = "This YARA rule identify new URLs downloading the decoy file related to SideWinder"
condition:
vt.net.url.downloaded_file.sha256 == "1955c6914097477d5141f720c9e8fa44b4fe189e854da298d85090cbc338b35a"
and vt.net.url.new_url
} !pip install vt-py nest_asyncio
import getpass, vt, json, nest_asyncio
nest_asyncio.apply()
cli = vt.Client(getpass.getpass('Introduce your VirusTotal API key: '))
FILEHASH = "1955c6914097477d5141f720c9e8fa44b4fe189e854da298d85090cbc338b35a"
RELATIONS = "itw_urls"
all_itw_urls = []
async for itemobj in cli.iterator(f'/files/{FILEHASH}/{RELATIONS}', limit=0):
all_itw_urls.append(itemobj.to_dict()) The great forgotten one: VT Diff
Conclusion
VTPRACTITIONERS{ACRONIS}: Tracking FileFix, Shadow Vector, and SideWinder
Introduction
How VT plays a role in hunting for analysts
Acronis - Success Story
Hunting Exploits Like It’s 2017-0199 (SideWinder Edition)
/*
Checks if the file is tagged with CVE-2017-0199 or CVE-2017-11882
and originates from one of the targeted countries
and the file type is a Word document, RTF, or MS-Office file
*/
import "vt"
rule hunting_cve_maldocs {
meta:
author = "Acronis Threat Research Unit (TRU)"
description = "Hunting for malicious Word/RTF files exploiting CVE-2017-0199 or CVE-2017-11882 from specific countries"
distribution = "TLP:CLEAR"
version = "1.2"
condition:
// Match if the file has CVE-2017-0199 or CVE-2017-11882 in the tags
for any tag in vt.metadata.tags :
(
tag == "cve-2017-0199" or
tag == "cve-2017-11882"
)
// Originates from a specific country?
and
(
// Removed CN due to spam submissions of related maldocs
vt.metadata.submitter.country == "PK" or
vt.metadata.submitter.country == "LK" or
vt.metadata.submitter.country == "BD" or
vt.metadata.submitter.country == "NP" or
vt.metadata.submitter.country == "MM" or
vt.metadata.submitter.country == "MV" or
vt.metadata.submitter.country == "AF"
)
// Is it a DOC, DOCX, or RTF?
and
(
vt.metadata.file_type == vt.FileType.DOC or
vt.metadata.file_type == vt.FileType.DOCX or
vt.metadata.file_type == vt.FileType.RTF
)
// Different TA spotted using .ru TLD (excluding it for now)
and not (
for any url in vt.behaviour.memory_pattern_urls : (
url contains ".ru"
)
)
and vt.metadata.new_file
}
/*
Sidewinder related malicious documents exploiting CVE 2017-0199 used during 2025 campaign
*/
import "vt"
rule apt_sidewinder_documents
{
meta:
author = "Acronis Threat Research Unit (TRU)"
description = "Sidewinder related malicious documents exploiting CVE 2017-0199"
distribution = "TLP:CLEAR"
version = "1.0"
strings:
$a1 = {62544CB1F0B9E6E04433698E85BFB534278B9BDC5F06589C011E9CB80C71DF23}
$a2 = {E20F76CDABDFAB004A6BA632F20CE00512BA5AD2FE8FB6ED9EE1865DFD07504B0304140000}
condition:
filesize < 5000KB
and any of ($a*)
and vt.metadata.new_file
// Getting spammy samples from a CN submitter
and not vt.metadata.submitter.country == "CN"
} Uncovering Shadow Vector’s SVG-Based Crimeware Campaign in Colombia
<!--
This YARA rule detects potentially malicious SVG files that are likely being used for crimeware campaigns targeting Colombia.
The rule identifies SVG images that contain legal or judicial terms commonly used in phishing scams,
along with embedded external links that could be used to deliver a payload.
-->
import "vt"
rule crimeware_svg_colombia {
meta:
author = "Acronis Threat Research Unit (TRU)"
description = "Detects potentially malicious SVG files that are likely being used for crimeware campaigns targeting Colombia"
distribution = "TLP:CLEAR"
version = "1.1"
// Reference hashes
hash1 = "6d4a53da259c3c8c0903b1345efcf2fa0d50bc10c3c010a34f86263de466f5a1"
hash2 = "2aae8e206dd068135b16ff87dfbb816053fc247a222aad0d34c9227e6ecf7b5b"
hash3 = "4cfeab122e0a748c8600ccd14a186292f27a93b5ba74c58dfee838fe28765061"
hash4 = "9bbbcb6eae33314b84f5e367f90e57f487d6abe72d6067adcb66eba896d7ce33"
hash5 = "60e87c0fe7c3904935bb1604bdb0b0fc0f2919db64f72666b77405c2c1e46067"
hash6 = "609edc93e075223c5dc8caaf076bf4e28f81c5c6e4db0eb6f502dda91500aab4"
hash7 = "4795d3a3e776baf485d284a9edcf1beef29da42cad8e8261a83e86d35b25cafe"
hash8 = "5673ad3287bcc0c8746ab6cab6b5e1b60160f07c7b16c018efa56bffd44b37aa"
hash9 = "b3e8ab81d0a559a373c3fe2ae7c3c99718503411cc13b17cffd1eee2544a787b"
hash10 = "b5311cadc0bbd2f47549f7fc0895848adb20cc016387cebcd1c29d784779240c"
hash11 = "c3319a8863d5e2dc525dfe6669c5b720fc42c96a8dce3bd7f6a0072569933303"
hash12 = "cb035f440f728395cc4237e1ac52114641dc25619705b605713ecefb6fd9e563"
hash13 = "cf23f7b98abddf1b36552b55f874ae1e2199768d7cefb0188af9ee0d9a698107"
hash14 = "f3208ae62655435186e560378db58e133a68aa6107948e2a8ec30682983aa503"
strings:
// SVG
$svg = "<svg xmlns=" ascii fullword
// Documents containing legal or judicial terms
$s1 = "COPIA" nocase
$s2 = "CITACION" nocase
$s3 = "JUZGADO" nocase
$s4 = "PENAL" nocase
$s5 = "JUDICIAL" nocase
$s6 = "BOGOTA" nocase
$s7 = "DEMANDA" nocase
// When image loads it retrieves payload from external website using HTTPS
$href1= "href='https://" nocase
$href2 = "href=\"https://" nocase
condition:
$svg
and filesize < 3MB
and 3 of ($s*)
and any of ($href*)
and vt.metadata.submitter.country == "CO"
}
rule crimeware_shadow_vector_svg
{
meta:
description = "Detects malicious SVG files associated with Shadow
Vector's Colombian campaign"
author = "Acronis Threat Research Unit (TRU)"
file_type = "SVG"
malware_family = "Shadow Vector"
threat_category = "Crimeware / Malicious Image / Embedded Payload"
tlp = "TLP:CLEAR"
strings:
$svg_tag1 = "<?xml" ascii
$svg_tag2 = "<svg" ascii
$svg_tag3 = "<!DOCTYPE svg" ascii
$svg_tag4 = "http://www.w3.org/2000/svg" ascii
//used by Shadow Vector (possibly generated in batch)
$judicial = "juzgado" ascii nocase
$judicial_1 = "citacion" ascii nocase
$judicial_2 = "judicial" ascii nocase
$judicial_3 = "despacho" ascii nocase
$generado = "Generado" ascii nocase
condition:
filesize < 3MB and
3 of ($svg_tag*) and
(1 of ($judicial*) and $generado)
}
FileFix in the wild!
Anatomy of an attack- where do we start?
- The commands used to copy text into the victims clipboard
- The commands used to construct the malicious payload
import "vt"
rule ClickFix
{
strings:
$clipboard = /(navigator\.clipboard\.writeText|document\.execCommand\(\"copy\"\))/
$pay01 = /(powershell|cmd|mshta|msiexec|pwsh)/gvfi
condition:
vt.net.url.new_url and
$clipboard and
any of ($pay*)
}
$pay03 = /powershell.{,80}iex/Analyzing payloads
Looking for a *Fix
VT Tips (based on the success story)
Supercharge Your Web-Content YARA Rules
entity:url (content:"navigator.clipboard.writeText" or content:"document.execCommand(\"copy\")") (content:"String.fromCharCode" or content:"atob")
Dissect Payloads with Advanced Content Queries
- Custom function names
- Specific variable names
- Uncommon comments
- Unique sequences of commands or API calls
behavior:"[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String("
Don't forget about the infrastructure
import "vt"
rule URLs_Downloading_Decoy_RTF_SideWinder {
meta:
target_entity = "url"
author = "Virustotal"
description = "This YARA rule identify new URLs downloading the decoy file related to SideWinder"
condition:
vt.net.url.downloaded_file.sha256 == "1955c6914097477d5141f720c9e8fa44b4fe189e854da298d85090cbc338b35a"
and vt.net.url.new_url
} !pip install vt-py nest_asyncio
import getpass, vt, json, nest_asyncio
nest_asyncio.apply()
cli = vt.Client(getpass.getpass('Introduce your VirusTotal API key: '))
FILEHASH = "1955c6914097477d5141f720c9e8fa44b4fe189e854da298d85090cbc338b35a"
RELATIONS = "itw_urls"
all_itw_urls = []
async for itemobj in cli.iterator(f'/files/{FILEHASH}/{RELATIONS}', limit=0):
all_itw_urls.append(itemobj.to_dict()) The great forgotten one: VT Diff
Conclusion
VTPRACTITIONERS{SEQRITE}: Tracking UNG0002, Silent Lynx and DragonClone
Introduction
How VT plays a role in hunting for analysts
Seqrite - Success Story
UNG0002
malware_config:30819f300d06092a864886f70d010101050003818d003081890281810096cc4e6ad9aee91ca69b7b44465e17412626a11c7855b7a69daad00f48c0ea98f0e389a0a1c4b74332bf0d603a6e53e05ee734c9a289ff172204bfc9430ed4d6041402d02b526e902b95f6f219598cb1b6391403fa627ab36dbe88646620369e7ec89bdc31f1a2b0bedba1852d5e7656d3b297f9d39f357816f0677563bc496b020301000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
type:zip AND (metadata:"lnk" AND metadata:".vbs" AND metadata:".pdf") and submitter:HK
Silent Lynx
-
Looking at the usage of encoded Telegram Bot based payload inside the C++ implant. Using either content or malware_config modifiers when extracted from the config could help us to identify new samples.
-
Spawning Powershell.exe LOLBIN.
-
VT search enablers for checking for malicious email files, if uploaded from Central Asian Geosphere.
-
ISO-oriented first-stagers.
-
Multiple behavioral overlaps between YoroTrooper & Silent Lynx and further hunting hypothesis developed by us.
DragonClone
VT Tips (based on the success story)
Hunt by Malware Configuration
Don't Overlook LNK File Metadata
Track Actors via Leaked Bot Tokens
Leverage Code-Signing Certificates
Utilize YARA and Sigma Rules
Conclusion
VTPRACTITIONERS{SEQRITE}: Tracking UNG0002, Silent Lynx and DragonClone
Introduction
How VT plays a role in hunting for analysts
Seqrite - Success Story
UNG0002
malware_config:30819f300d06092a864886f70d010101050003818d003081890281810096cc4e6ad9aee91ca69b7b44465e17412626a11c7855b7a69daad00f48c0ea98f0e389a0a1c4b74332bf0d603a6e53e05ee734c9a289ff172204bfc9430ed4d6041402d02b526e902b95f6f219598cb1b6391403fa627ab36dbe88646620369e7ec89bdc31f1a2b0bedba1852d5e7656d3b297f9d39f357816f0677563bc496b020301000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
type:zip AND (metadata:"lnk" AND metadata:".vbs" AND metadata:".pdf") and submitter:HK
Silent Lynx
-
Looking at the usage of encoded Telegram Bot based payload inside the C++ implant. Using either content or malware_config modifiers when extracted from the config could help us to identify new samples.
-
Spawning Powershell.exe LOLBIN.
-
VT search enablers for checking for malicious email files, if uploaded from Central Asian Geosphere.
-
ISO-oriented first-stagers.
-
Multiple behavioral overlaps between YoroTrooper & Silent Lynx and further hunting hypothesis developed by us.
DragonClone
VT Tips (based on the success story)
Hunt by Malware Configuration
Don't Overlook LNK File Metadata
Track Actors via Leaked Bot Tokens
Leverage Code-Signing Certificates
Utilize YARA and Sigma Rules
Conclusion
Advanced Threat Hunting: Automating Large-Scale Operations with LLMs
Last week, we were fortunate enough to attend the fantastic LABScon conference, organized by the SentinelOne Labs team. While there, we presented a workshop titled 'Advanced Threat Hunting: Automating Large-Scale Operations with LLMs.' The main goal of this workshop was to show attendees how they could automate their research using the VirusTotal API and Gemini. Specifically, we demonstrated how to integrate the power of Google Colab to quickly and efficiently generate Jupyter notebooks using natural language.
It goes without saying that the use of LLMs is a must for every analyst today. For this reason, we also want to make life easier for everyone who uses the VirusTotal API for research.
The Power of the VirusTotal API and vt-py
The VirusTotal API is the programmatic gateway to our massive repository of threat intelligence data. While the VirusTotal GUI is great for agile querying, the API unlocks the ability to conduct large-scale, automated investigations and access raw data with more pivoting opportunities.
To make interacting with the API even easier, we recommend using the vt-py library. It simplifies much of the complexity of HTTP requests, JSON parsing, and rate limit management, making it the go-to choice for Python users.
From Natural Language to Actionable Intelligence with Gemini
To bridge the gap between human questions and API queries, we can leverage the integrated Gemini in Google Colab. We have created a "meta Colab" notebook that is pre-populated with working real code snippets for interacting with the VirusTotal API to retrieve different information such as campaigns, threat actors, malware, samples, URLs among others (which we will share soon). This provides Gemini with the necessary context to understand your natural language requests and generate accurate Python code to query the VirusTotal API. Gemini doesn't call the API directly; it creates the code snippet for you to execute.
For Gemini to generate accurate and relevant code, it needs context. Our meta Colab notebook is filled with examples that act as a guide. For complex questions, it will be nice to provide the exact field names that you want to work with. This context generally falls into two categories:
- Reference Documentation: We include detailed documentation directly in the Colab. For example, we provide a comprehensive list of all available file search modifiers for the VirusTotal Intelligence search endpoint. This gives Gemini the "vocabulary" it needs to construct precise queries.
- Working Code Examples: The notebook is pre-populated with dozens of working vt-py code snippets for common tasks like retrieving file information, performing an intelligence search, or getting relationships. This gives Gemini the "grammar" and correct patterns for interacting with our API.
Example of code snippet context that we have included in our meta colab:
query_results_with_behaviors = []
query = "have:sigma have:yara have:ids have:malware_config fs:1d+ have:bundled_file tag:overlay"
RELATIONS = "behaviours"
async for itemobj in cli.iterator('/intelligence/search',params={'query': query, 'relationships': RELATIONS, 'relationship_attributes[%s]'%(RELATIONS): '\*'},limit=0):
query_results_with_behaviors.append(itemobj.to_dict())
res_with_behaviors = list(query_results_with_behaviors)
Practical Example: Hunting for Malicious LNK Files
One of the exercises in our workshop focused on analyzing malicious LNK files submitted to VirusTotal in a specific period of time to limit results. The goal was to discover common execution techniques and interesting command-line patterns.
Here’s how you could approach this with natural language queries in our meta Colab:
1. Retrieve all LNK file objects matching the criteria.
You can start by asking Gemini:
Create a code snippet to retrieve all LNK files submitted from January 1st, 2025, until April 1st, 2025, with 5 or more positives.
Code generated:
query_results = []
# Query for LNK files submitted between January 1st, 2025, and April 1st, 2025, with 5 or more positives.
query = "fs:2025-01-01+ fs:2025-04-01- p:5+ type:lnk"
async for itemobj in cli.iterator('/intelligence/search',params={'query': "%s"%(query)},limit=0):
query_results.append(itemobj.to_dict())
lnk_files = list(query_results)
print(f"Found {len(lnk_files)} LNK files matching the criteria.")
2. Flatten the data and visualize it.
Next, you can ask Gemini to process the retrieved data and create visualizations:
Flatten the data in a dataframe and create a bar chart depicting the most common TargetFileDOSName values.
Code generated:
import pandas as pd
import plotly.express as px
# Extract the 'attributes' dictionary from each object in our results
attributes_list = [result.get('attributes', {}) for result in lnk_files]
# Use json_normalize to create a flat DataFrame
df_flat = pd.json_normalize(attributes_list)
# Check if 'exiftool.TargetFileDOSName' column exists and is not empty
if 'exiftool.TargetFileDOSName' in df_flat.columns and not df_flat['exiftool.TargetFileDOSName'].empty:
# Count the occurrences of each TargetFileDOSName
target_file_counts = df_flat['exiftool.TargetFileDOSName'].value_counts().reset_index()
target_file_counts.columns = ['TargetFileDOSName', 'Count']
# Get the top 10 most common TargetFileDOSName values for visualization
top_target_files = target_file_counts.head(10)
# Create an interactive bar chart
fig = px.bar(top_target_files,
x='Count',
y='TargetFileDOSName',
orientation='h',
title='Top 10 Most Common TargetFileDOSName in LNK files',
labels={'Count': 'Number of Occurrences', 'TargetFileDOSName': 'Target File DOS Name'})
fig.show()
else:
print("The 'exiftool.TargetFileDOSName' column was not found or is empty in the flattened data.")
print("DataFrame shape:", df_flat.shape)
df_flat.head()
This will generate a code snippet using pandas and Plotly to give you a clear view of the most frequent target files in the malicious LNKs.
3. Visualize relationships with a Sankey diagram.
To dig deeper and understand the relationships between the infected machines and the malware, you can ask for a more advanced visualization:
Create a Sankey diagram connecting MachineID (source) to TargetFileDOSName (target), with link widths representing the number of occurrences. Include only MachineID values with 10 or more occurrences, grouping the rest as "Other", and make the visualization interactive with hover details.
Code generated:
import plotly.graph_objects as go
import pandas as pd
# Ensure df_flat is available from previous steps
# If not, you might need to re-run the cell that creates df_flat.
# Check if necessary columns exist in df_flat
if 'lnk_info.machine_id' in df_flat.columns and 'exiftool.TargetFileDOSName' in df_flat.columns:
# Calculate the frequency of each machine ID and target file name pair
pair_frequencies = df_flat.groupby(['lnk_info.machine_id', 'exiftool.TargetFileDOSName']).size().reset_index(name='frequency')
# Identify MachineIDs with less than 10 occurrences
machine_id_counts = pair_frequencies.groupby('lnk_info.machine_id')['frequency'].sum().reset_index(name='count')
other_machine_ids = machine_id_counts[machine_id_counts['count'] < 10]['lnk_info.machine_id'].tolist()
# Replace infrequent MachineIDs with 'Other'
pair_frequencies['source_node'] = pair_frequencies['lnk_info.machine_id'].apply(lambda x: 'Other' if x in other_machine_ids else x)
# Aggregate frequencies for the 'Other' node and other valid nodes
sankey_data = pair_frequencies.groupby(['source_node', 'exiftool.TargetFileDOSName'])['frequency'].sum().reset_index()
# Create unique labels for the Sankey nodes
all_nodes = pd.concat([sankey_data['source_node'], sankey_data['exiftool.TargetFileDOSName']]).unique()
label_to_index = {label: i for i, label in enumerate(all_nodes)}
# Create the Sankey diagram
fig = go.Figure(data=[go.Sankey(
node=dict(
pad=15,
thickness=20,
line=dict(color="black", width=0.5),
label=all_nodes,
color="blue"
),
link=dict(
source=sankey_data['source_node'].map(label_to_index),
target=sankey_data['exiftool.TargetFileDOSName'].map(label_to_index),
value=sankey_data['frequency'],
label=sankey_data.apply(lambda row: f"{row['source_node']} -> {row['exiftool.TargetFileDOSName']}: {row['frequency']}", axis=1)
)
)])
fig.update_layout(title_text="MachineID to TargetFileDOSName Sankey Diagram (Infrequent MachineIDs grouped as 'Other')", font_size=10)
fig.show()
else:
print("Required columns 'lnk_info.machine_id' or 'exiftool.TargetFileDOSName' not found in the DataFrame.")
This will create a powerful visualization that shows how different machines are being targeted with specific files, allowing us identify patterns for each machineID metadata.
4. Analyze the behavior of the LNK files.
Finally, you can investigate the behavior of the LNK files themselves, for example, by looking at how they are executed:
Create a pie chart depicting the distribution of window behaviors for LNK target processes (hidden, minimized,...).
Code generated:
import plotly.express as px
import pandas as pd
# Check if the relevant column exists
if 'lnk_info.header.show_window_str' in df_flat.columns and not df_flat['lnk_info.header.show_window_str'].empty:
# Count the occurrences of each window behavior string
window_behavior_counts = df_flat['lnk_info.header.show_window_str'].value_counts().reset_index()
window_behavior_counts.columns = ['Window Behavior', 'Count']
# Create an interactive pie chart
fig = px.pie(window_behavior_counts,
names='Window Behavior',
values='Count',
title='Distribution of LNK Target Process Window Behaviors',
hover_data={'Count': True})
fig.show()
else:
print("The 'lnk_info.header.show_window_str' column was not found or is empty in the flattened data. Cannot create the pie chart.")
This allows you to quickly identify if the LNK files are trying to hide their execution, a common use case for malware.
5. Geographic Threat Mapping of Submissions
Understanding the geographical distribution of malware submissions can provide crucial insights into whether a campaign is widespread or targeting a specific region. By analyzing the submission metadata for our set of LNK files, an analyst can quickly identify potential hotspots, which could indicate a geographically focused attack. This is achieved by fetching the submissions relationship for each file, extracting the country of origin for each submission, and plotting this data on a world map (please note that submissions doesn’t mean targeted countries, but in some cases could be related).
Using the LNK files identified previously, create a code snippet that retrieves the submissions relationship for each file. Then, count the number of submissions from each country and display the results on a choropleth world map to visualize the submission hotspots.
In this case, Gemini uses the step-by-step execution, which basically is a set of consecutive executions. The code generated was:
Step 1
query_results_with_submissions = []
# Define the search query string for LNK files
query = "fs:2025-01-01+ fs:2025-04-01- p:5+ type:lnk"
# Define the relationship to request
RELATIONS = "submissions"
# Define the relationship attributes to retrieve (all attributes)
ATTRIBUTES_RELATIONS = "*"
# Use cli.iterator to perform an intelligence search with the specified query, relationships, and relationship attributes.
async for itemobj in cli.iterator('/intelligence/search', params={'query': query, 'relationships': RELATIONS, 'relationship_attributes[%s]'%(RELATIONS): ATTRIBUTES_RELATIONS}, limit=0):
query_results_with_submissions.append(itemobj.to_dict())
# Convert query_results_with_submissions to a list called lnk_files_with_submissions
lnk_files_with_submissions = list(query_results_with_submissions)
# Print the number of LNK files found with submission data.
print(f"Found {len(lnk_files_with_submissions)} LNK files with submission data matching the criteria.")
Step 2
import pandas as pd
# Initialize an empty list to store submission countries
submission_countries = []
# Iterate through each item (LNK file) in the lnk_files_with_submissions list
for item in lnk_files_with_submissions:
# Check if 'relationships' and 'submissions' exist and are not empty
if 'relationships' in item and 'submissions' in item['relationships'] and item['relationships']['submissions']['data']:
# Iterate through each submission object
for submission in item['relationships']['submissions']['data']:
# Extract the 'country' from 'submission['attributes']' and append it to the list
if 'country' in submission['attributes']:
submission_countries.append(submission['attributes']['country'])
# Convert the submission_countries list into a pandas Series
submission_countries_series = pd.Series(submission_countries)
# Count the occurrences of each country and store in a DataFrame
country_counts = submission_countries_series.value_counts().reset_index()
country_counts.columns = ['country', 'count']
print(f"Total unique countries found: {len(country_counts)}")
print("Top 5 submission countries:")
print(country_counts.head())
Step 3
!pip install pycountry
import plotly.express as px
import pandas as pd
import pycountry
# Function to get ISO-3 code from country name
def get_iso3(country_name):
try:
return pycountry.countries.search_fuzzy(country_name)[0].alpha_3
except:
return None
# Apply the function to get ISO-3 codes
country_counts['iso_alpha'] = country_counts['country'].apply(get_iso3)
# Create a world heatmap using Plotly Express with ISO-3 codes
fig = px.choropleth(country_counts.dropna(subset=['iso_alpha']), # Drop rows where ISO code couldn't be found
locations="iso_alpha", # Use ISO-3 codes for locations
locationmode='ISO-3', # Specify ISO-3 location mode
color="count",
hover_name="country",
color_continuous_scale="Viridis",
title="Submission Country Heatmap")
# Update layout to increase figure size
fig.update_layout(
autosize=False,
width=1000, # Adjust width as needed
height=600 # Adjust height as needed
)
fig.show()
More Examples
Here are some other examples of how you can use natural language to query the VirusTotal API:
This specific question is actually a really good example of how Google Colab will run the step-by-step process. As you can see, we asked different questions in the same prompt, for that reason Gemini will do it in different steps.
Retrieve all the samples from the /intelligence/search endpoint with the following query: fs:2025-08-01+ fs:2025-08-14- p:5+ tag:crx have:codeinsight. Get the bundled_files and create a new dataframe with them too, but store only those having 5 or more malicious value. Then identify the top 5 magika of those bundled files obtained
Code generated:
Step 1
query_results_with_bundled_files = []
# Define the search query string
query = "fs:2025-08-01+ fs:2025-08-14- p:5+ tag:crx have:codeinsight"
# Define the relationship to request
RELATIONS = "bundled_files"
# Define the relationship attributes to retrieve (all attributes)
ATTRIBUTES_RELATIONS = "*"
# Use cli.iterator to perform an intelligence search with the specified query, relationships, and relationship attributes.
async for itemobj in cli.iterator('/intelligence/search', params={'query': query, 'relationships': RELATIONS, 'relationship_attributes[%s]'%(RELATIONS): ATTRIBUTES_RELATIONS}, limit=0):
query_results_with_bundled_files.append(itemobj.to_dict())
# Convert query_results_with_bundled_files to a list called crx_files_with_bundled_files
crx_files_with_bundled_files = list(query_results_with_bundled_files)
# Print the number of found CRX files with bundled file data.
print(f"Found {len(crx_files_with_bundled_files)} CRX files with bundled file data matching the criteria.")
Step 2
import pandas as pd
# List to store all bundled file attributes
all_bundled_files_data = []
# Iterate through each CRX file with bundled file data
for crx_file in crx_files_with_bundled_files:
if 'relationships' in crx_file and 'bundled_files' in crx_file['relationships']:
for bundled_file in crx_file['relationships']['bundled_files']['data']:
all_bundled_files_data.append(bundled_file.get('attributes', {}))
# Create a DataFrame from the extracted bundled file attributes
df_bundled_files = pd.json_normalize(all_bundled_files_data)
# Filter for bundled files with 5 or more malicious detections
df_malicious_bundled_files = df_bundled_files[df_bundled_files['last_analysis_stats.malicious'] >= 5]
print(f"Total bundled files extracted: {len(df_bundled_files)}")
print(f"Bundled files with 5 or more malicious detections: {len(df_malicious_bundled_files)}")
df_malicious_bundled_files.head()
Step 3
import pandas as pd
import plotly.express as px
# Check if the 'magika' column exists and is not empty in the filtered DataFrame
if 'magika' in df_malicious_bundled_files.columns and not df_malicious_bundled_files['magika'].empty:
# Count the occurrences of each magika type
magika_counts = df_malicious_bundled_files['magika'].value_counts().reset_index()
magika_counts.columns = ['Magika Type', 'Count']
# Get the top 5 most frequent magika types
top_5_magika = magika_counts.head(5)
print("Top 5 Magika Types in malicious bundled files:")
print(top_5_magika)
# Visualize the top 5 magika types
fig = px.bar(top_5_magika,
x='Count',
y='Magika Type',
orientation='h',
title='Top 5 Magika Types in Malicious Bundled Files',
labels={'Count': 'Number of Occurrences', 'Magika Type': 'Magika Type'})
fig.update_layout(yaxis={'categoryorder':'total ascending'}) # Order bars by count
fig.show()
else:
print("The 'magika' column was not found or is empty in the filtered malicious bundled files DataFrame. Cannot identify top magika types.")
- Retrieve threat actors:
Retrieve threat actors targeting the United Kingdom with an espionage motivation. Sort the results in descending order of relevance. Display the total number of threat actors and their names.
- Investigate campaigns:
Retrieve information about threat actors and malware involved in campaigns targeting Pakistan. For each threat actor, retrieve its country of origin, motivations, and targeted industries. For each malware, retrieve its name.
What’s next
This workshop, co-authored with Aleksandar from Sentinel LABS, will be presented at future conferences to show the community how to get the most out of the VirusTotal API. We'll be updating the content of our meta colab regularly and will share more information soon about how to get the Google Colab.
In the meantime, if you have any feedback or ideas to contribute, we are open to suggestions.
Advanced Threat Hunting: Automating Large-Scale Operations with LLMs
Last week, we were fortunate enough to attend the fantastic LABScon conference, organized by the SentinelOne Labs team. While there, we presented a workshop titled 'Advanced Threat Hunting: Automating Large-Scale Operations with LLMs.' The main goal of this workshop was to show attendees how they could automate their research using the VirusTotal API and Gemini. Specifically, we demonstrated how to integrate the power of Google Colab to quickly and efficiently generate Jupyter notebooks using natural language.
It goes without saying that the use of LLMs is a must for every analyst today. For this reason, we also want to make life easier for everyone who uses the VirusTotal API for research.
The Power of the VirusTotal API and vt-py
The VirusTotal API is the programmatic gateway to our massive repository of threat intelligence data. While the VirusTotal GUI is great for agile querying, the API unlocks the ability to conduct large-scale, automated investigations and access raw data with more pivoting opportunities.
To make interacting with the API even easier, we recommend using the vt-py library. It simplifies much of the complexity of HTTP requests, JSON parsing, and rate limit management, making it the go-to choice for Python users.
From Natural Language to Actionable Intelligence with Gemini
To bridge the gap between human questions and API queries, we can leverage the integrated Gemini in Google Colab. We have created a "meta Colab" notebook that is pre-populated with working real code snippets for interacting with the VirusTotal API to retrieve different information such as campaigns, threat actors, malware, samples, URLs among others (which we will share soon). This provides Gemini with the necessary context to understand your natural language requests and generate accurate Python code to query the VirusTotal API. Gemini doesn't call the API directly; it creates the code snippet for you to execute.
For Gemini to generate accurate and relevant code, it needs context. Our meta Colab notebook is filled with examples that act as a guide. For complex questions, it will be nice to provide the exact field names that you want to work with. This context generally falls into two categories:
- Reference Documentation: We include detailed documentation directly in the Colab. For example, we provide a comprehensive list of all available file search modifiers for the VirusTotal Intelligence search endpoint. This gives Gemini the "vocabulary" it needs to construct precise queries.
- Working Code Examples: The notebook is pre-populated with dozens of working vt-py code snippets for common tasks like retrieving file information, performing an intelligence search, or getting relationships. This gives Gemini the "grammar" and correct patterns for interacting with our API.
Example of code snippet context that we have included in our meta colab:
query_results_with_behaviors = []
query = "have:sigma have:yara have:ids have:malware_config fs:1d+ have:bundled_file tag:overlay"
RELATIONS = "behaviours"
async for itemobj in cli.iterator('/intelligence/search',params={'query': query, 'relationships': RELATIONS, 'relationship_attributes[%s]'%(RELATIONS): '\*'},limit=0):
query_results_with_behaviors.append(itemobj.to_dict())
res_with_behaviors = list(query_results_with_behaviors)
Practical Example: Hunting for Malicious LNK Files
One of the exercises in our workshop focused on analyzing malicious LNK files submitted to VirusTotal in a specific period of time to limit results. The goal was to discover common execution techniques and interesting command-line patterns.
Here’s how you could approach this with natural language queries in our meta Colab:
1. Retrieve all LNK file objects matching the criteria.
You can start by asking Gemini:
Create a code snippet to retrieve all LNK files submitted from January 1st, 2025, until April 1st, 2025, with 5 or more positives.
Code generated:
query_results = []
# Query for LNK files submitted between January 1st, 2025, and April 1st, 2025, with 5 or more positives.
query = "fs:2025-01-01+ fs:2025-04-01- p:5+ type:lnk"
async for itemobj in cli.iterator('/intelligence/search',params={'query': "%s"%(query)},limit=0):
query_results.append(itemobj.to_dict())
lnk_files = list(query_results)
print(f"Found {len(lnk_files)} LNK files matching the criteria.")
2. Flatten the data and visualize it.
Next, you can ask Gemini to process the retrieved data and create visualizations:
Flatten the data in a dataframe and create a bar chart depicting the most common TargetFileDOSName values.
Code generated:
import pandas as pd
import plotly.express as px
# Extract the 'attributes' dictionary from each object in our results
attributes_list = [result.get('attributes', {}) for result in lnk_files]
# Use json_normalize to create a flat DataFrame
df_flat = pd.json_normalize(attributes_list)
# Check if 'exiftool.TargetFileDOSName' column exists and is not empty
if 'exiftool.TargetFileDOSName' in df_flat.columns and not df_flat['exiftool.TargetFileDOSName'].empty:
# Count the occurrences of each TargetFileDOSName
target_file_counts = df_flat['exiftool.TargetFileDOSName'].value_counts().reset_index()
target_file_counts.columns = ['TargetFileDOSName', 'Count']
# Get the top 10 most common TargetFileDOSName values for visualization
top_target_files = target_file_counts.head(10)
# Create an interactive bar chart
fig = px.bar(top_target_files,
x='Count',
y='TargetFileDOSName',
orientation='h',
title='Top 10 Most Common TargetFileDOSName in LNK files',
labels={'Count': 'Number of Occurrences', 'TargetFileDOSName': 'Target File DOS Name'})
fig.show()
else:
print("The 'exiftool.TargetFileDOSName' column was not found or is empty in the flattened data.")
print("DataFrame shape:", df_flat.shape)
df_flat.head()
This will generate a code snippet using pandas and Plotly to give you a clear view of the most frequent target files in the malicious LNKs.
3. Visualize relationships with a Sankey diagram.
To dig deeper and understand the relationships between the infected machines and the malware, you can ask for a more advanced visualization:
Create a Sankey diagram connecting MachineID (source) to TargetFileDOSName (target), with link widths representing the number of occurrences. Include only MachineID values with 10 or more occurrences, grouping the rest as "Other", and make the visualization interactive with hover details.
Code generated:
import plotly.graph_objects as go
import pandas as pd
# Ensure df_flat is available from previous steps
# If not, you might need to re-run the cell that creates df_flat.
# Check if necessary columns exist in df_flat
if 'lnk_info.machine_id' in df_flat.columns and 'exiftool.TargetFileDOSName' in df_flat.columns:
# Calculate the frequency of each machine ID and target file name pair
pair_frequencies = df_flat.groupby(['lnk_info.machine_id', 'exiftool.TargetFileDOSName']).size().reset_index(name='frequency')
# Identify MachineIDs with less than 10 occurrences
machine_id_counts = pair_frequencies.groupby('lnk_info.machine_id')['frequency'].sum().reset_index(name='count')
other_machine_ids = machine_id_counts[machine_id_counts['count'] < 10]['lnk_info.machine_id'].tolist()
# Replace infrequent MachineIDs with 'Other'
pair_frequencies['source_node'] = pair_frequencies['lnk_info.machine_id'].apply(lambda x: 'Other' if x in other_machine_ids else x)
# Aggregate frequencies for the 'Other' node and other valid nodes
sankey_data = pair_frequencies.groupby(['source_node', 'exiftool.TargetFileDOSName'])['frequency'].sum().reset_index()
# Create unique labels for the Sankey nodes
all_nodes = pd.concat([sankey_data['source_node'], sankey_data['exiftool.TargetFileDOSName']]).unique()
label_to_index = {label: i for i, label in enumerate(all_nodes)}
# Create the Sankey diagram
fig = go.Figure(data=[go.Sankey(
node=dict(
pad=15,
thickness=20,
line=dict(color="black", width=0.5),
label=all_nodes,
color="blue"
),
link=dict(
source=sankey_data['source_node'].map(label_to_index),
target=sankey_data['exiftool.TargetFileDOSName'].map(label_to_index),
value=sankey_data['frequency'],
label=sankey_data.apply(lambda row: f"{row['source_node']} -> {row['exiftool.TargetFileDOSName']}: {row['frequency']}", axis=1)
)
)])
fig.update_layout(title_text="MachineID to TargetFileDOSName Sankey Diagram (Infrequent MachineIDs grouped as 'Other')", font_size=10)
fig.show()
else:
print("Required columns 'lnk_info.machine_id' or 'exiftool.TargetFileDOSName' not found in the DataFrame.")
This will create a powerful visualization that shows how different machines are being targeted with specific files, allowing us identify patterns for each machineID metadata.
4. Analyze the behavior of the LNK files.
Finally, you can investigate the behavior of the LNK files themselves, for example, by looking at how they are executed:
Create a pie chart depicting the distribution of window behaviors for LNK target processes (hidden, minimized,...).
Code generated:
import plotly.express as px
import pandas as pd
# Check if the relevant column exists
if 'lnk_info.header.show_window_str' in df_flat.columns and not df_flat['lnk_info.header.show_window_str'].empty:
# Count the occurrences of each window behavior string
window_behavior_counts = df_flat['lnk_info.header.show_window_str'].value_counts().reset_index()
window_behavior_counts.columns = ['Window Behavior', 'Count']
# Create an interactive pie chart
fig = px.pie(window_behavior_counts,
names='Window Behavior',
values='Count',
title='Distribution of LNK Target Process Window Behaviors',
hover_data={'Count': True})
fig.show()
else:
print("The 'lnk_info.header.show_window_str' column was not found or is empty in the flattened data. Cannot create the pie chart.")
This allows you to quickly identify if the LNK files are trying to hide their execution, a common use case for malware.
5. Geographic Threat Mapping of Submissions
Understanding the geographical distribution of malware submissions can provide crucial insights into whether a campaign is widespread or targeting a specific region. By analyzing the submission metadata for our set of LNK files, an analyst can quickly identify potential hotspots, which could indicate a geographically focused attack. This is achieved by fetching the submissions relationship for each file, extracting the country of origin for each submission, and plotting this data on a world map (please note that submissions doesn’t mean targeted countries, but in some cases could be related).
Using the LNK files identified previously, create a code snippet that retrieves the submissions relationship for each file. Then, count the number of submissions from each country and display the results on a choropleth world map to visualize the submission hotspots.
In this case, Gemini uses the step-by-step execution, which basically is a set of consecutive executions. The code generated was:
Step 1
query_results_with_submissions = []
# Define the search query string for LNK files
query = "fs:2025-01-01+ fs:2025-04-01- p:5+ type:lnk"
# Define the relationship to request
RELATIONS = "submissions"
# Define the relationship attributes to retrieve (all attributes)
ATTRIBUTES_RELATIONS = "*"
# Use cli.iterator to perform an intelligence search with the specified query, relationships, and relationship attributes.
async for itemobj in cli.iterator('/intelligence/search', params={'query': query, 'relationships': RELATIONS, 'relationship_attributes[%s]'%(RELATIONS): ATTRIBUTES_RELATIONS}, limit=0):
query_results_with_submissions.append(itemobj.to_dict())
# Convert query_results_with_submissions to a list called lnk_files_with_submissions
lnk_files_with_submissions = list(query_results_with_submissions)
# Print the number of LNK files found with submission data.
print(f"Found {len(lnk_files_with_submissions)} LNK files with submission data matching the criteria.")
Step 2
import pandas as pd
# Initialize an empty list to store submission countries
submission_countries = []
# Iterate through each item (LNK file) in the lnk_files_with_submissions list
for item in lnk_files_with_submissions:
# Check if 'relationships' and 'submissions' exist and are not empty
if 'relationships' in item and 'submissions' in item['relationships'] and item['relationships']['submissions']['data']:
# Iterate through each submission object
for submission in item['relationships']['submissions']['data']:
# Extract the 'country' from 'submission['attributes']' and append it to the list
if 'country' in submission['attributes']:
submission_countries.append(submission['attributes']['country'])
# Convert the submission_countries list into a pandas Series
submission_countries_series = pd.Series(submission_countries)
# Count the occurrences of each country and store in a DataFrame
country_counts = submission_countries_series.value_counts().reset_index()
country_counts.columns = ['country', 'count']
print(f"Total unique countries found: {len(country_counts)}")
print("Top 5 submission countries:")
print(country_counts.head())
Step 3
!pip install pycountry
import plotly.express as px
import pandas as pd
import pycountry
# Function to get ISO-3 code from country name
def get_iso3(country_name):
try:
return pycountry.countries.search_fuzzy(country_name)[0].alpha_3
except:
return None
# Apply the function to get ISO-3 codes
country_counts['iso_alpha'] = country_counts['country'].apply(get_iso3)
# Create a world heatmap using Plotly Express with ISO-3 codes
fig = px.choropleth(country_counts.dropna(subset=['iso_alpha']), # Drop rows where ISO code couldn't be found
locations="iso_alpha", # Use ISO-3 codes for locations
locationmode='ISO-3', # Specify ISO-3 location mode
color="count",
hover_name="country",
color_continuous_scale="Viridis",
title="Submission Country Heatmap")
# Update layout to increase figure size
fig.update_layout(
autosize=False,
width=1000, # Adjust width as needed
height=600 # Adjust height as needed
)
fig.show()
More Examples
Here are some other examples of how you can use natural language to query the VirusTotal API:
This specific question is actually a really good example of how Google Colab will run the step-by-step process. As you can see, we asked different questions in the same prompt, for that reason Gemini will do it in different steps.
Retrieve all the samples from the /intelligence/search endpoint with the following query: fs:2025-08-01+ fs:2025-08-14- p:5+ tag:crx have:codeinsight. Get the bundled_files and create a new dataframe with them too, but store only those having 5 or more malicious value. Then identify the top 5 magika of those bundled files obtained
Code generated:
Step 1
query_results_with_bundled_files = []
# Define the search query string
query = "fs:2025-08-01+ fs:2025-08-14- p:5+ tag:crx have:codeinsight"
# Define the relationship to request
RELATIONS = "bundled_files"
# Define the relationship attributes to retrieve (all attributes)
ATTRIBUTES_RELATIONS = "*"
# Use cli.iterator to perform an intelligence search with the specified query, relationships, and relationship attributes.
async for itemobj in cli.iterator('/intelligence/search', params={'query': query, 'relationships': RELATIONS, 'relationship_attributes[%s]'%(RELATIONS): ATTRIBUTES_RELATIONS}, limit=0):
query_results_with_bundled_files.append(itemobj.to_dict())
# Convert query_results_with_bundled_files to a list called crx_files_with_bundled_files
crx_files_with_bundled_files = list(query_results_with_bundled_files)
# Print the number of found CRX files with bundled file data.
print(f"Found {len(crx_files_with_bundled_files)} CRX files with bundled file data matching the criteria.")
Step 2
import pandas as pd
# List to store all bundled file attributes
all_bundled_files_data = []
# Iterate through each CRX file with bundled file data
for crx_file in crx_files_with_bundled_files:
if 'relationships' in crx_file and 'bundled_files' in crx_file['relationships']:
for bundled_file in crx_file['relationships']['bundled_files']['data']:
all_bundled_files_data.append(bundled_file.get('attributes', {}))
# Create a DataFrame from the extracted bundled file attributes
df_bundled_files = pd.json_normalize(all_bundled_files_data)
# Filter for bundled files with 5 or more malicious detections
df_malicious_bundled_files = df_bundled_files[df_bundled_files['last_analysis_stats.malicious'] >= 5]
print(f"Total bundled files extracted: {len(df_bundled_files)}")
print(f"Bundled files with 5 or more malicious detections: {len(df_malicious_bundled_files)}")
df_malicious_bundled_files.head()
Step 3
import pandas as pd
import plotly.express as px
# Check if the 'magika' column exists and is not empty in the filtered DataFrame
if 'magika' in df_malicious_bundled_files.columns and not df_malicious_bundled_files['magika'].empty:
# Count the occurrences of each magika type
magika_counts = df_malicious_bundled_files['magika'].value_counts().reset_index()
magika_counts.columns = ['Magika Type', 'Count']
# Get the top 5 most frequent magika types
top_5_magika = magika_counts.head(5)
print("Top 5 Magika Types in malicious bundled files:")
print(top_5_magika)
# Visualize the top 5 magika types
fig = px.bar(top_5_magika,
x='Count',
y='Magika Type',
orientation='h',
title='Top 5 Magika Types in Malicious Bundled Files',
labels={'Count': 'Number of Occurrences', 'Magika Type': 'Magika Type'})
fig.update_layout(yaxis={'categoryorder':'total ascending'}) # Order bars by count
fig.show()
else:
print("The 'magika' column was not found or is empty in the filtered malicious bundled files DataFrame. Cannot identify top magika types.")
- Retrieve threat actors:
Retrieve threat actors targeting the United Kingdom with an espionage motivation. Sort the results in descending order of relevance. Display the total number of threat actors and their names.
- Investigate campaigns:
Retrieve information about threat actors and malware involved in campaigns targeting Pakistan. For each threat actor, retrieve its country of origin, motivations, and targeted industries. For each malware, retrieve its name.
What’s next
This workshop, co-authored with Aleksandar from Sentinel LABS, will be presented at future conferences to show the community how to get the most out of the VirusTotal API. We'll be updating the content of our meta colab regularly and will share more information soon about how to get the Google Colab.
In the meantime, if you have any feedback or ideas to contribute, we are open to suggestions.
Sentinels League: Live Rankings for the Threat Hunting World Championship
The Sentinels League is the official, week-by-week standings for the Threat Hunting World Championship – the first-of-its-kind tournament where the world’s top defenders go head-to-head across four surfaces: AI, Cloud, SIEM, and Endpoint. Thousands of blue teamers from more than 100 countries are tackling real-world attack scenarios to earn points, climb the tables, and secure their path to Las Vegas.
Bookmark this blog post to check your position, track the movement each week, and jump into the next qualifier if you’re not on the board yet.
More Than a Game | How the Sentinels League Work
Qualifiers run throughout the month of September across the four league tracks with players who finish in the top 50 in each league advancing to the Regional Finals on October 22 for the Americas, Europe, and Asia Pacific & Japan. From there, regional champions progress to the Grand Final at OneCon in Las Vegas from November 4 to 6, where the World Champion is crowned.
This is more than a game. It’s a global showdown that blends entertainment, education, and elite competition. Defenders everywhere will up-level their skills and battle for:
- $100,000 in prizes
- A championship trophy
- The prestige of being crowned World Champion
- Charitable donations made in partnership with the S Foundation on behalf of each finalist
Only one player will take home the title, but everyone gains the experience of battling in real-world scenarios that sharpen the skills cyber defenders use daily.
A Global Leaderboard in Action | Follow the League Tables Live
These games are grounded in real incidents and operational trade-offs. Players earn points for flags captured and accuracy under time limits. This means pace and precision both matter. The tables below display each player’s alias, alongside points, and the prize they would receive should they finish in that same position.
Qualifying Stages
Compete online from anywhere, or in-person at select events this month. Earn Threat Hunting Hero badges, prizes, and points that advance you up the league tables. Throughout September, players may enter once per qualifier and compete across all four tracks.
- AI Qualifier Games: Take on scenarios featuring AI attackers and AI-powered threat hunting tools.
- Cloud Qualifier Games: Track and neutralize threats across cloud-based attack surfaces.
- SIEM Qualifier Games: Assert your dominance in real-time SIEM hunting and remediation challenges.
- Endpoint Qualifier Games: Hunt down and remediate endpoint vulnerabilities in scenarios pulled straight from real-world incidents.
Regional Finals | October 22
The top 200 players from each region (Americas, Europe, Asia Pacific & Japan) will face off live in an action-packed online event. Only three regional champions will advance.
Grand Final | November 4–6 | OneCon, Las Vegas
Three finalists will earn an all-expenses-paid trip to OneCon 2025 in Las Vegas to compete live on stage for the World Championship title, the trophy, and the $100K prize pool.
Leagues Menu Quick Jump
AI Leagues
Live table for the AI League Qualifiers are as follows. Top 50 on October 2 qualify for the Regional Finals.
AI APJ League
| Rank | Alias | Score | Prize |
|---|---|---|---|
| 1 | Sean | 4800 | $2,000 + Entry |
| 2 | Gon | 4800 | $1,200 + Entry |
| 3 | Hyena | 4800 | $800 + Entry |
| 4 | 0xDariusNG | 4800 | $500 + Entry |
| 5 | PHEAKRO | 4780 | Entry |
| 6 | 0xKowloon | 4780 | Entry |
| 7 | Mingi | 4780 | $500 + Entry |
| 8 | injun | 4760 | $500 + Entry |
| 9 | cameronpaddyTL | 4740 | $500 + Entry |
| 10 | donghyeok | 4740 | $500 + Entry |
| 11 | Gowda | 4730 | Entry |
| 12 | kerostic | 4700 | Entry |
| 13 | Absol | 4700 | Entry |
| 14 | NotFound | 4700 | Entry |
| 15 | Jay | 4700 | Entry |
| 16 | Anonghost | 4700 | Entry |
| 17 | Siwoo | 4680 | Entry |
| 18 | qutypie | 4680 | Entry |
| 19 | AAA | 4680 | Entry |
| 20 | avynilite | 4680 | Entry |
| 21 | Shawn_Kwak | 4660 | Entry |
| 22 | ouoaaa | 4660 | Entry |
| 23 | N-dawg | 4660 | Entry |
| 24 | Johncena | 4660 | Entry |
| 25 | haon | 4660 | Entry |
| 26 | matrix | 4660 | Entry |
| 27 | meowfoobar | 4640 | Entry |
| 28 | bheda | 4640 | Entry |
| 29 | host | 4600 | Entry |
| 30 | weeknd | 4550 | Entry |
| 31 | davkjp | 4500 | Entry |
| 32 | ThreatAnalystX | 4500 | Entry |
| 33 | clerkofcourse | 4500 | Entry |
| 34 | Sujin | 4500 | Entry |
| 35 | heogi | 4400 | Entry |
| 36 | gwthm01 | 4400 | Entry |
| 37 | elesh27 | 4240 | Entry |
| 38 | 1-1063 | 4160 | Entry |
| 39 | mohan | 4150 | Entry |
| 40 | haysia-aml | 3980 | Entry |
| 41 | SmolAME | 3960 | Entry |
| 42 | riz_wan | 3920 | Entry |
| 43 | Ninja | 3860 | Entry |
| 44 | Paul-NZ | 3760 | Entry |
| 45 | dinnershow | 3700 | Entry |
| 46 | aaditya_khandke | 3680 | Entry |
| 47 | sanalk | 3660 | Entry |
| 48 | Gibbo | 3600 | Entry |
| 49 | Nisanak | 3520 | Entry |
| 50 | weeknd | 3460 | Entry |
AI EMEA League
| Rank | Alias | Score | Prize |
|---|---|---|---|
| 1 | ELL | 4800 | $2,000 + Entry |
| 2 | Andy | 4800 | $1,200 + Entry |
| 3 | Krzysztof | 4800 | Entry |
| 4 | christopher | 4800 | $500 + Entry |
| 5 | HermessNRJ | 4800 | $500 + Entry |
| 6 | jodie | 4800 | $500 + Entry |
| 7 | Arnau | 4800 | $500 + Entry |
| 8 | Fenio2 | 4800 | $500 + Entry |
| 9 | imouse | 4800 | Entry |
| 10 | TristanA | 4800 | $500 + Entry |
| 11 | SSman | 4800 | Entry |
| 12 | nicpooon | 4800 | Entry |
| 13 | goksara01 | 4800 | Entry |
| 14 | TomEdwards | 4800 | Entry |
| 15 | msnaydenov | 4800 | Entry |
| 16 | mrdiSec | 4800 | Entry |
| 17 | Kurty | 4800 | Entry |
| 18 | HackNSeek | 4780 | Entry |
| 19 | SEnev | 4780 | Entry |
| 20 | Plissken | 4780 | Entry |
| 21 | mka | 4780 | Entry |
| 22 | Ptikek | 4780 | Entry |
| 23 | Chris | 4780 | Entry |
| 24 | stahl | 4780 | Entry |
| 25 | D1vy | 4780 | Entry |
| 26 | alexcohen | 4780 | Entry |
| 27 | Krxsx | 4780 | Entry |
| 28 | hemalsoni22 | 4780 | Entry |
| 29 | bytesize | 4780 | Entry |
| 30 | manthan1501 | 4780 | Entry |
| 31 | buttercup6789 | 4780 | Entry |
| 32 | CBVirus | 4780 | Entry |
| 33 | Kamil7cd | 4760 | Entry |
| 34 | Pikachu | 4760 | Entry |
| 35 | krysix | 4760 | Entry |
| 36 | gandalf | 4760 | Entry |
| 37 | Parshwa | 4760 | Entry |
| 38 | P1ckl3 | 4760 | Entry |
| 39 | DenRubai | 4740 | Entry |
| 40 | A380 | 4740 | Entry |
| 41 | alwayshungry | 4740 | Entry |
| 42 | xdoubtful | 4720 | Entry |
| 43 | Sunny59 | 4720 | Entry |
| 44 | AJ56 | 4700 | Entry |
| 45 | nobody27 | 4680 | Entry |
| 46 | bluephish | 4680 | Entry |
| 47 | Kalilee | 4660 | Entry |
| 50 | ft44k | 4380 | Entry |
AI AMERICAS League
| Rank | Alias | Score | Prize |
|---|---|---|---|
| 1 | eforsha | 4800 | $2,000 + Entry |
| 2 | Thomas | 4800 | $1,200 + Entry |
| 3 | 1-2-3-4 | 4800 | $800 + Entry |
| 4 | AU1 | 4800 | $500 + Entry |
| 5 | Survivor4Ever | 4800 | $500 + Entry |
| 6 | NightHammer | 4800 | $500 + Entry |
| 7 | ZachsAlt | 4800 | $500 + Entry |
| 8 | Romulus | 4800 | $500 + Entry |
| 9 | pmchale | 4800 | $500 + Entry |
| 10 | ByKroo | 4800 | Entry |
| 11 | kquirosf102 | 4800 | Entry |
| 12 | JConatus | 4800 | Entry |
| 13 | bwillhelm | 4800 | Entry |
| 14 | jasonmull | 4800 | Entry |
| 15 | ThreatSlayer | 4800 | Entry |
| 16 | james | 4800 | Entry |
| 17 | JayHole | 4800 | Entry |
| 18 | capnjack | 4800 | Entry |
| 19 | mainasara | 4800 | Entry |
| 20 | Sil3nt_gh0st | 4800 | Entry |
| 21 | RakeshN | 4800 | Entry |
| 22 | ninjacat | 4800 | Entry |
| 23 | jswiegele | 4800 | Entry |
| 24 | Max | 4780 | Entry |
| 25 | nkoester | 4780 | Entry |
| 26 | benthehen100 | 4780 | Entry |
| 27 | nok0 | 4780 | Entry |
| 28 | max | 4780 | Entry |
| 29 | Dani | 4780 | Entry |
| 30 | testuser | 4780 | Entry |
| 31 | mprof | 4780 | Entry |
| 32 | caputdraconis | 4780 | Entry |
| 33 | colsaBoys | 4780 | Entry |
| 34 | Endlaze | 4780 | Entry |
| 35 | littymac | 4780 | Entry |
| 36 | jlytle | 4780 | Entry |
| 37 | ana7z | 4780 | Entry |
| 38 | mkilp | 4780 | Entry |
| 39 | ComradePanda | 4780 | Entry |
| 40 | SHWON | 4760 | Entry |
| 41 | s-swift | 4760 | Entry |
| 42 | sickstick | 4760 | Entry |
| 43 | David_S | 4760 | Entry |
| 44 | EchoNight | 4760 | Entry |
| 45 | gg88gg99 | 4760 | Entry |
| 46 | rtovell | 4760 | Entry |
| 47 | saberwolf617 | 4745 | Entry |
| 48 | alevine | 4740 | Entry |
| 49 | enleak | 4740 | Entry |
| 50 | ahmad | 4740 | Entry |
Back to the Menu Quick Jump
Cloud Leagues
Live table for the Cloud League Qualifiers are as follows. Top 50 on October 2 qualify for the Regional Finals.
Cloud APJ League
| Rank | Alias | Score | Prize |
|---|---|---|---|
| 1 | NotFound | 3900 | $2,000 + Entry |
| 2 | Sean | 3900 | $1,200 + Entry |
| 3 | Shawn_Kwak | 3900 | $800 + Entry |
| 4 | Absol | 3900 | $500 + Entry |
| 5 | Salmon-Mia | 3900 | $500 + Entry |
| 6 | injun | 3900 | $500 + Entry |
| 7 | Gon | 3900 | $500 + Entry |
| 8 | Hyena | 3900 | $500 + Entry |
| 9 | donghyeok | 3900 | $500 + Entry |
| 10 | Minyoung | 3900 | $500 + Entry |
| 11 | 1stTimer | 3900 | Entry |
| 12 | HoumanD | 3900 | Entry |
| 13 | mastoto | 3900 | Entry |
| 14 | Jim | 3900 | Entry |
| 15 | gwthm01 | 3900 | Entry |
| 16 | cyrusmehra | 3900 | Entry |
| 17 | kerostic | 3880 | Entry |
| 18 | 0xDariusNG | 3880 | Entry |
| 19 | Jay | 3880 | Entry |
| 20 | ouoaaa | 3880 | Entry |
| 21 | pgpt | 3880 | Entry |
| 22 | HNVN | 3880 | Entry |
| 23 | TI-MG | 3880 | Entry |
| 24 | weeknd | 3880 | Entry |
| 25 | Bolito687 | 3880 | Entry |
| 26 | Sujin | 3880 | Entry |
| 27 | Siwoo | 3860 | Entry |
| 28 | Johncena | 3860 | Entry |
| 29 | Nisanak | 3860 | Entry |
| 30 | 1-1063 | 3860 | Entry |
| 31 | Ketsui | 3860 | Entry |
| 32 | clerkofcourse | 3850 | Entry |
| 33 | wliu | 3840 | Entry |
| 34 | heogi | 3820 | Entry |
| 35 | usrbin | 3820 | Entry |
| 36 | SmolAME | 3810 | Entry |
| 37 | qutypie | 3800 | Entry |
| 38 | quifl | 3800 | Entry |
| 39 | avynilite | 3770 | Entry |
| 40 | sanketsalve | 3760 | Entry |
| 41 | r00t | 3750 | Entry |
| 42 | ctrlmurray | 3740 | Entry |
| 43 | Dia | 3680 | Entry |
| 44 | Gowda | 3460 | Entry |
| 45 | skkcyb3r | 3390 | Entry |
| 46 | ezhunt | 3080 | Entry |
| 47 | jeba | 2740 | Entry |
| 48 | josep | 2720 | Entry |
| 49 | pincode | 2700 | Entry |
| 50 | Shiva | 2660 | Entry |
Cloud EMEA League
| Rank | Alias | Score | Prize |
|---|---|---|---|
| 1 | ELL | 3900 | $2,000 + Entry |
| 2 | french_taco | 3900 | $1,200 + Entry |
| 3 | jodie | 3900 | $800 + Entry |
| 4 | Revil | 3900 | $500 + Entry |
| 5 | EthicalPetal | 3900 | $500 + Entry |
| 6 | hemalsoni22 | 3900 | $500 + Entry |
| 7 | Krish | 3900 | $500 + Entry |
| 8 | Parshwa | 3900 | $500 + Entry |
| 9 | D1vy | 3900 | $500 + Entry |
| 10 | HermessNRJ | 3900 | $500 + Entry |
| 11 | mka | 3900 | Entry |
| 12 | ah01 | 3900 | Entry |
| 13 | tomkerswill | 3900 | Entry |
| 14 | demisto | 3900 | Entry |
| 15 | P3ngu1nB3er | 3900 | Entry |
| 16 | Arnau | 3880 | Entry |
| 17 | A380 | 3880 | Entry |
| 18 | Lennard | 3880 | Entry |
| 19 | Fenio | 3880 | Entry |
| 20 | manthan1501 | 3880 | Entry |
| 21 | imouse | 3880 | Entry |
| 22 | rado-van | 3880 | Entry |
| 23 | MrHokage | 3880 | Entry |
| 24 | guin | 3880 | Entry |
| 25 | Duall | 3880 | Entry |
| 26 | jamesthor | 3880 | Entry |
| 27 | Dhara23 | 3870 | Entry |
| 28 | christopher | 3860 | Entry |
| 29 | moon77 | 3860 | Entry |
| 30 | eniz | 3860 | Entry |
| 31 | Oscar_G | 3860 | Entry |
| 32 | dcpl | 3860 | Entry |
| 33 | htue | 3860 | Entry |
| 34 | sug4r-wr41th | 3840 | Entry |
| 35 | modeus | 3840 | Entry |
| 36 | blackhat | 3840 | Entry |
| 37 | xdoubtful | 3840 | Entry |
| 38 | CBVirus | 3840 | Entry |
| 39 | Plissken | 3840 | Entry |
| 40 | Igor | 3840 | Entry |
| 41 | StijnG | 3820 | Entry |
| 42 | RDx | 3820 | Entry |
| 43 | JohnMatrix | 3820 | Entry |
| 44 | Ptikek | 3820 | Entry |
| 45 | Kalilee | 3800 | Entry |
| 46 | canigetabeepbeep | 3780 | Entry |
| 47 | SilentPursuit | 3780 | Entry |
| 48 | nobody27 | 3780 | Entry |
| 49 | Drako | 3770 | Entry |
| 50 | desidosa | 3760 | Entry |
Cloud AMERICAS League
| Rank | Alias | Score | Prize |
|---|---|---|---|
| 1 | Stephen | 3900 | $2,000 + Entry |
| 2 | Honu | 3900 | Entry |
| 3 | AU1 | 3900 | $800 + Entry |
| 4 | Red-Beard | 3900 | $500 + Entry |
| 5 | Thomas | 3900 | $500 + Entry |
| 6 | 1-2-3-4 | 3900 | $500 + Entry |
| 7 | nmkoester | 3900 | $500 + Entry |
| 8 | bwillhelm | 3900 | $500 + Entry |
| 9 | WilliamMailhot | 3900 | $500 + Entry |
| 10 | alevine | 3900 | $500 + Entry |
| 11 | eforsha | 3900 | Entry |
| 12 | GenericAll | 3900 | Entry |
| 13 | threathunting123 | 3900 | Entry |
| 14 | benthehen100 | 3900 | Entry |
| 15 | Cwallis | 3900 | Entry |
| 16 | Joshua_Knight | 3900 | Entry |
| 17 | JacobL | 3900 | Entry |
| 18 | josh_24v_15 | 3900 | Entry |
| 19 | james | 3900 | Entry |
| 20 | maverick | 3900 | Entry |
| 21 | Hunter53 | 3900 | Entry |
| 22 | tessah_k | 3900 | Entry |
| 23 | Wisdom1k | 3900 | Entry |
| 24 | riskybusiness | 3900 | Entry |
| 25 | rpatrick | 3900 | Entry |
| 26 | wizard113 | 3900 | Entry |
| 27 | Dr_Ew | 3900 | Entry |
| 28 | Survivor4Ever | 3900 | Entry |
| 29 | BGrad | 3900 | Entry |
| 30 | 0x626d | 3900 | Entry |
| 31 | _operator | 3900 | Entry |
| 32 | oj_cup | 3900 | Entry |
| 33 | ThreatSlayer | 3900 | Entry |
| 34 | Seasalt | 3900 | Entry |
| 35 | daswon | 3880 | Entry |
| 36 | dwest | 3880 | Entry |
| 37 | mprof | 3880 | Entry |
| 38 | Dani | 3880 | Entry |
| 39 | hue | 3880 | Entry |
| 40 | ZachsAlt | 3880 | Entry |
| 41 | flipyaforreal | 3880 | Entry |
| 42 | jswisher | 3880 | Entry |
| 43 | gary | 3880 | Entry |
| 44 | ana7z | 3880 | Entry |
| 45 | DefenderA | 3880 | Entry |
| 46 | Avlyssna | 3880 | Entry |
| 47 | JayHole | 3880 | Entry |
| 48 | Max | 3880 | Entry |
| 49 | TheExemplar | 3880 | Entry |
| 50 | eDak | 3880 | Entry |
Back to the Menu Quick Jump
SIEM Leagues
Live table for the SIEM League Qualifiers are as follows. Top 50 on October 2 qualify for the Regional Finals.
SIEM APJ League
| Rank | Alias | Score | Prize |
|---|---|---|---|
| 1 | Jay | 4100 | $2,000 + Entry |
| 2 | Sean | 4100 | $1,200 + Entry |
| 3 | ouoaaa | 4100 | $800 + Entry |
| 4 | injun | 4100 | $500 + Entry |
| 5 | Hyena | 4100 | $500 + Entry |
| 6 | 0xKowloon | 4100 | Entry |
| 7 | Gon | 4080 | $500 + Entry |
| 8 | NotFound | 4080 | $500 + Entry |
| 9 | drake | 3980 | $500 + Entry |
| 10 | Johncena | 3820 | $500 + Entry |
| 11 | Absol | 3800 | Entry |
| 12 | Shawn_Kwak | 3800 | Entry |
| 13 | Bolito687 | 3800 | Entry |
| 14 | heogi | 3780 | Entry |
| 15 | kerostic | 3760 | Entry |
| 16 | Mingi | 3720 | Entry |
| 17 | 1stTimer | 3680 | Entry |
| 18 | ctrlmurray | 3680 | Entry |
| 19 | avynilite | 3660 | Entry |
| 20 | Tape_Dispenser | 3600 | Entry |
| 21 | AgentMrX | 3600 | Entry |
| 22 | Duckduck | 3580 | Entry |
| 23 | SteveM | 3540 | Entry |
| 24 | Minyoung | 3500 | Entry |
| 25 | GCTDLover | 3500 | Entry |
| 26 | Salmon-Mia | 3480 | Entry |
| 27 | HYEOK | 3480 | Entry |
| 28 | Siwoo | 3440 | Entry |
| 29 | gwthm01 | 3440 | Entry |
| 30 | Sujin | 3440 | Entry |
| 31 | SmolAME | 3400 | Entry |
| 32 | 1-1063 | 3320 | Entry |
| 33 | host | 3200 | Entry |
| 34 | Ketsui | 3200 | Entry |
| 35 | 0xDariusNG | 3200 | Entry |
| 36 | tianred | 3180 | Entry |
| 37 | 1stTimer | 3180 | Entry |
| 38 | Anusthika | 3020 | Entry |
| 39 | null_faruq | 2780 | Entry |
| 40 | ace | 2760 | Entry |
| 41 | usrbin | 2680 | Entry |
| 42 | haysia-aml | 2660 | Entry |
| 43 | MooH | 2660 | Entry |
| 44 | CTF_threathunt9 | 2660 | Entry |
| 45 | clerkofcourse | 2640 | Entry |
| 46 | quifl | 2640 | Entry |
| 47 | shreyas | 2620 | Entry |
| 48 | hardikjain | 2580 | Entry |
| 49 | Genie | 2580 | Entry |
| 50 | Dastr0 | 2560 | Entry |
SIEM AMERICAS League
| Rank | Alias | Score | Prize |
|---|---|---|---|
| 1 | Red-Beard | 4100 | $2,000 + Entry |
| 2 | Romulus | 4100 | $1,200 + Entry |
| 3 | Survivor4Ever | 4100 | $800 + Entry |
| 4 | nok0 | 4000 | $500 + Entry |
| 5 | staas | 4000 | $500 + Entry |
| 6 | Sneha | 3960 | $500 + Entry |
| 7 | ninjascout_ii | 3900 | $500 + Entry |
| 8 | CmdnControl | 3900 | $500 + Entry |
| 9 | 1-2-3-4 | 3880 | $500 + Entry |
| 10 | post | 3880 | $500 + Entry |
| 11 | ZachsAlt | 3880 | Entry |
| 12 | jqueso | 3860 | Entry |
| 13 | rzv | 3840 | Entry |
| 14 | zero_cool | 3780 | Entry |
| 15 | SHWON | 3740 | Entry |
| 16 | m4lwhere | 3700 | Entry |
| 17 | rutvij2811 | 3700 | Entry |
| 18 | spelosi | 3680 | Entry |
| 19 | mp-549228 | 3640 | Entry |
| 20 | TheExemplar | 3620 | Entry |
| 21 | Max | 3600 | Entry |
| 22 | AU1 | 3580 | Entry |
| 23 | Sil3nt_gh0st | 3580 | Entry |
| 24 | Kizzmit | 3580 | Entry |
| 25 | mprof | 3500 | Entry |
| 26 | jasonmull | 3500 | Entry |
| 27 | riskybusiness | 3480 | Entry |
| 28 | Tester123 | 3480 | Entry |
| 29 | oj_cup | 3480 | Entry |
| 30 | noobpro | 3460 | Entry |
| 31 | eforsha | 3440 | Entry |
| 32 | french_taco | 3400 | Entry |
| 33 | Hacker | 3400 | Entry |
| 34 | Linus | 3400 | Entry |
| 35 | heringfish | 3400 | Entry |
| 36 | malik | 3400 | Entry |
| 37 | cyberpanda | 3400 | Entry |
| 38 | Dani | 3380 | Entry |
| 39 | LindzerBeamz | 3340 | Entry |
| 40 | Diasum | 3300 | Entry |
| 41 | NotTotallyHere | 3300 | Entry |
| 42 | dwest | 3300 | Entry |
| 43 | alevine | 3300 | Entry |
| 44 | james | 3300 | Entry |
| 45 | pgruntkowski | 3300 | Entry |
| 46 | ninjacat | 3280 | Entry |
| 47 | 4thelulz1 | 3280 | Entry |
| 48 | eDak | 3280 | Entry |
| 49 | OptimalNaptime | 3200 | Entry |
| 50 | Tony_Willey27 | 3200 | Entry |
SIEM EMEA League
| Rank | Alias | Score | Prize |
|---|---|---|---|
| 1 | Arnau | 3980 | $2,000 + Entry |
| 2 | acassano | 3900 | $1,200 + Entry |
| 3 | tocj | 3900 | $800 + Entry |
| 4 | JoeS | 3900 | $500 + Entry |
| 5 | carlosgomez | 3880 | $500 + Entry |
| 6 | demisto | 3880 | $500 + Entry |
| 7 | RDx | 3880 | Entry |
| 8 | jodie | 3860 | $500 + Entry |
| 9 | Pinax | 3860 | $500 + Entry |
| 10 | Chris | 3860 | $500 + Entry |
| 11 | Fenio | 3860 | Entry |
| 12 | desidosa | 3840 | Entry |
| 13 | mka | 3800 | Entry |
| 14 | Nirmit | 3800 | Entry |
| 15 | SSman | 3780 | Entry |
| 16 | karasek | 3780 | Entry |
| 17 | blackhat | 3760 | Entry |
| 18 | Kamil7cd | 3740 | Entry |
| 19 | rado-van | 3700 | Entry |
| 20 | Pst | 3700 | Entry |
| 21 | tomkerswill | 3700 | Entry |
| 22 | Mzk00 | 3680 | Entry |
| 23 | ALDX | 3620 | Entry |
| 24 | mtekbicak | 3580 | Entry |
| 25 | modeus | 3560 | Entry |
| 26 | andresitoo | 3540 | Entry |
| 27 | eniz | 3540 | Entry |
| 28 | DenRubai | 3540 | Entry |
| 29 | StijnG | 3500 | Entry |
| 30 | HackNSeek | 3500 | Entry |
| 31 | Plissken | 3480 | Entry |
| 32 | m3m3kritis | 3460 | Entry |
| 33 | trashclutch | 3460 | Entry |
| 34 | Dante | 3440 | Entry |
| 35 | DFJ | 3420 | Entry |
| 36 | __zCK | 3340 | Entry |
| 37 | alwayshungry | 3320 | Entry |
| 38 | seclingua | 3260 | Entry |
| 39 | ronald_mcdonald | 3260 | Entry |
| 40 | mara-deva | 3180 | Entry |
| 41 | ABogdan | 3160 | Entry |
| 42 | icheptrosu | 3160 | Entry |
| 43 | MrMurkl | 3160 | Entry |
| 44 | TristanA | 3040 | Entry |
| 45 | h4ckm4estro | 2920 | Entry |
| 46 | gen_kai | 2880 | Entry |
| 47 | Dani | 2860 | Entry |
| 48 | Graf | 2800 | Entry |
| 49 | hipparcos | 2760 | Entry |
| 50 | Bilal | 2740 | Entry |
Back to the Menu Quick Jump
Endpoint Leagues
Live table for the Endpoint League Qualifiers are as follows. Top 50 on October 2 qualify for the Regional Finals.
Endpoint APJ League
| Rank | Alias | Score | Prize |
|---|---|---|---|
| 1 | Salmon-Mia | 6100 | $2,000 + Entry |
| 2 | Jay | 6100 | $1,200 + Entry |
| 3 | ouoaaa | 6100 | $800 + Entry |
| 4 | Sean | 6100 | $500 + Entry |
| 5 | INTfinityBeyond | 6100 | $500 + Entry |
| 6 | tanjiro | 6100 | $500 + Entry |
| 7 | Tape_Dispenser | 6100 | $500 + Entry |
| 8 | Duckduck | 6100 | $500 + Entry |
| 9 | GCTDLover | 6100 | $500 + Entry |
| 10 | PrincessLeia | 6100 | $500 + Entry |
| 11 | injigi | 6100 | Entry |
| 12 | Hyena | 6100 | Entry |
| 13 | heogi | 6100 | Entry |
| 14 | HYEOK | 6100 | Entry |
| 15 | NotFound | 6100 | Entry |
| 16 | ctrlmurray | 6100 | Entry |
| 17 | 0xDariusNG | 6100 | Entry |
| 18 | Minyoung | 6100 | Entry |
| 19 | v_chips | 6100 | Entry |
| 20 | Muhammed | 6100 | Entry |
| 21 | avynilite | 6080 | Entry |
| 22 | ana | 6080 | Entry |
| 23 | nilnocnil | 6080 | Entry |
| 24 | jstanINTern | 6060 | Entry |
| 25 | Johncena | 6060 | Entry |
| 26 | matrix | 6060 | Entry |
| 27 | Siwoo | 6060 | Entry |
| 28 | DemetrianTitus | 6050 | Entry |
| 29 | kerostic | 6050 | Entry |
| 30 | BobCrusader | 6040 | Entry |
| 31 | pgpt | 6000 | Entry |
| 32 | SmolAME | 6000 | Entry |
| 33 | haszayan | 5990 | Entry |
| 34 | jsil | 5990 | Entry |
| 35 | JasonPhang98 | 5930 | Entry |
| 36 | MPrin | 5920 | Entry |
| 37 | null_faruq | 5900 | Entry |
| 38 | MooH | 5870 | Entry |
| 39 | clerkofcourse | 5850 | Entry |
| 40 | Anusthika | 5810 | Entry |
| 41 | JimmyJames007 | 5780 | Entry |
| 42 | drake | 5780 | Entry |
| 43 | l3Iadk | 5670 | Entry |
| 44 | tigerkali | 5650 | Entry |
| 45 | gwthm01 | 5580 | Entry |
| 46 | Anonghost | 5560 | Entry |
| 47 | ZKAD00SH | 5550 | Entry |
| 48 | Sujin | 5540 | Entry |
| 49 | Gowda | 5510 | Entry |
| 50 | qutypie | 5360 | Entry |
Endpoint AMERICAS League
| Rank | Alias | Score | Prize |
|---|---|---|---|
| 1 | AU1 | 6100 | $2,000 + Entry |
| 2 | eforsha | 6100 | $1,200 + Entry |
| 3 | Red-Beard | 6100 | $800 + Entry |
| 4 | Thomas | 6100 | $500 + Entry |
| 5 | Survivor4Ever | 6100 | $500 + Entry |
| 6 | Stephen | 6100 | $500 + Entry |
| 7 | wgadzinski | 6100 | $500 + Entry |
| 8 | jdennen | 6100 | $500 + Entry |
| 9 | french_taco | 6100 | $500 + Entry |
| 10 | Romulus | 6100 | $500 + Entry |
| 11 | 1-2-3-4 | 6100 | Entry |
| 12 | packetnerd | 6100 | Entry |
| 13 | josh_24v_15 | 6100 | Entry |
| 14 | jswisher | 6100 | Entry |
| 15 | nok0 | 6100 | Entry |
| 16 | bwillhelm | 6100 | Entry |
| 17 | bcs3c | 6100 | Entry |
| 18 | ThreatSlayer | 6100 | Entry |
| 19 | testuser | 6100 | Entry |
| 20 | benthehen100 | 6100 | Entry |
| 21 | mar | 6100 | Entry |
| 22 | alphacentauri | 6080 | Entry |
| 23 | jqueso | 6080 | Entry |
| 24 | IamMalayali | 6080 | Entry |
| 25 | tresscross | 6080 | Entry |
| 26 | OptimalNaptime | 6080 | Entry |
| 27 | ZachsAlt | 6080 | Entry |
| 28 | riskybusiness | 6080 | Entry |
| 29 | Cwallis | 6080 | Entry |
| 30 | Tester123 | 6080 | Entry |
| 31 | Dani | 6080 | Entry |
| 32 | mp-549228 | 6070 | Entry |
| 33 | dwest | 6060 | Entry |
| 34 | bat | 6060 | Entry |
| 35 | cgrussell | 6060 | Entry |
| 36 | David_S | 6060 | Entry |
| 37 | ninjascout_ii | 6060 | Entry |
| 38 | staas | 6050 | Entry |
| 39 | bsimm | 6050 | Entry |
| 40 | garfield | 6050 | Entry |
| 41 | Diasum | 6040 | Entry |
| 42 | SHWON | 6040 | Entry |
| 43 | CyberSaiyan84 | 6040 | Entry |
| 44 | Kratos | 6030 | Entry |
| 45 | LowImpulsControl | 6030 | Entry |
| 46 | relogonm | 6030 | Entry |
| 47 | WilliamMailhot | 6020 | Entry |
| 48 | purpleai | 6020 | Entry |
| 49 | silly_monkey | 6020 | Entry |
| 50 | Gagneet | 6020 | Entry |
Endpoint EMEA League
| Rank | Alias | Score | Prize |
|---|---|---|---|
| 1 | Tarak | 6100 | $2,000 + Entry |
| 2 | eceh | 6100 | Entry |
| 3 | Andy | 6100 | $800 + Entry |
| 4 | Naruto | 6100 | Entry |
| 5 | ELL | 6100 | $500 + Entry |
| 6 | jodie | 6100 | $500 + Entry |
| 7 | kochl0 | 6100 | Entry |
| 8 | mka | 6100 | Entry |
| 9 | RDx | 6090 | Entry |
| 10 | jasmineadams | 6080 | $500 + Entry |
| 11 | Sid_XDR | 6070 | Entry |
| 12 | carlosgomez | 6070 | Entry |
| 13 | gen_kai | 6060 | Entry |
| 14 | SSman | 6060 | Entry |
| 15 | Arnau | 6050 | Entry |
| 16 | Chris | 6050 | Entry |
| 17 | IamKD | 6040 | Entry |
| 18 | tdlwv | 6040 | Entry |
| 19 | acassano | 6020 | Entry |
| 20 | Linus | 6000 | Entry |
| 21 | Vaishnavi | 6000 | Entry |
| 22 | Meghana | 6000 | Entry |
| 23 | StijnG | 6000 | Entry |
| 24 | Revil | 6000 | Entry |
| 25 | D1vy | 6000 | Entry |
| 26 | Pinax | 6000 | Entry |
| 27 | Shreya | 6000 | Entry |
| 28 | moon77 | 5980 | Entry |
| 29 | Obfuscated_LOL | 5980 | Entry |
| 30 | Nico | 5980 | Entry |
| 31 | Devarsh | 5980 | Entry |
| 32 | hemalsoni22 | 5980 | Entry |
| 33 | manthan1501 | 5980 | Entry |
| 34 | jenilsadrani | 5980 | Entry |
| 35 | Krish | 5980 | Entry |
| 36 | jaygohel | 5960 | Entry |
| 37 | monster77 | 5960 | Entry |
| 38 | EthicalPetal | 5960 | Entry |
| 39 | Parshwa | 5950 | Entry |
| 40 | Skandora | 5940 | Entry |
| 41 | rado-van | 5940 | Entry |
| 42 | surennp | 5930 | Entry |
| 43 | palgari9718 | 5910 | Entry |
| 44 | karasek | 5890 | Entry |
| 45 | VRO | 5860 | Entry |
| 46 | sstoyanov | 5800 | Entry |
| 47 | Mzk00 | 5800 | Entry |
| 48 | Xavier | 5780 | Entry |
| 49 | eniz | 5680 | Entry |
| 50 | MelonPan | 5660 | Entry |
Back to the Menu Quick Jump
A Global Competition, Built for the Community
This championship is proud to unite thousands of cybersecurity defenders in a showcase of skill, innovation, and strategy. We invite you to share this blog for live updates, engage with us on social media, help grow the buzz across our community, and watch as the stage for threat hunting glory gets bigger. Also, it’s not too late to make a run at Regionals. Enter the next qualifier and save your spot in the Sentinels League today!
Participation is open worldwide. Prize eligibility is subject to Terms & Conditions and some countries are not eligible to receive monetary rewards. See the full rules for details.

Supercharging Your Threat Hunts: Join VirusTotal at Labscon for a Workshop on Automation and LLMs
Supercharging Your Threat Hunts: Join VirusTotal at Labscon for a Workshop on Automation and LLMs
Research that builds detections
Introduction
Tell me what role you have and I'll tell you how you use VirusTotal
Our approach
p:5+ have:behavior fs:30d+ not have:sigma
p:5+ (sandbox_name:"CAPE Sandbox" or sandbox_name:"Zenbox") fs:30d+ not have:sigma
p:5+ have:behavior fs:30d+ sigma_critical:0 sigma_high:0 sigma_medium:0 sigma_low:2-
Our detections for the community
Detect The Execution Of More.com And Vbc.exe Related to Lummac Stealer
title: Detect The Execution Of More.com And Vbc.exe Related to Lummac Stealer
id: 19b3806e-46f2-4b4c-9337-e3d8653245ea
status: experimental
description: Detects the execution of more.com and vbc.exe in the process tree. This behaviors was observed by a set of samples related to Lummac Stealer. The Lummac payload is injected into the vbc.exe process.
references:
- https://www.virustotal.com/gui/file/14d886517fff2cc8955844b252c985ab59f2f95b2849002778f03a8f07eb8aef
- https://strontic.github.io/xcyclopedia/library/more.com-EDB3046610020EE614B5B81B0439895E.html
- https://strontic.github.io/xcyclopedia/library/vbc.exe-A731372E6F6978CE25617AE01B143351.html
author: Joseliyo Sanchez, @Joseliyo_Jstnk
date: 2024-11-14
tags:
- attack.defense-evasion
- attack.t1055
logsource:
category: process_creation
product: windows
detection:
# VT Query: behaviour_processes:"C:\\Windows\\SysWOW64\\more.com" behaviour_processes:"C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\vbc.exe"
selection_parent:
ParentImage|endswith: '\more.com'
selection_child:
- Image|endswith: '\vbc.exe'
- OriginalFileName: 'vbc.exe'
condition: all of selection_*
falsepositives:
- Unknown
level: high
Sysmon event for: Detect The Execution Of More.com And Vbc.exe Related to Lummac Stealer
{
"System": {
"Provider": {
"Guid": "{5770385F-C22A-43E0-BF4C-06F5698FFBD9}",
"Name": "Microsoft-Windows-Sysmon"
},
"EventID": 1,
"Version": 5,
"Level": 4,
"Task": 1,
"Opcode": 0,
"Keywords": "0x8000000000000000",
"TimeCreated": {
"SystemTime": "2024-11-26T16:23:05.132539500Z"
},
"EventRecordID": 692861,
"Correlation": {},
"Execution": {
"ProcessID": 2396,
"ThreadID": 3116
},
"Channel": "Microsoft-Windows-Sysmon/Operational",
"Computer": "DESKTOP-B0T93D6",
"Security": {
"UserID": "S-1-5-18"
}
},
"EventData": {
"RuleName": "-",
"UtcTime": "2024-11-26 16:23:05.064",
"ProcessGuid": "{C784477D-F5E9-6745-6006-000000003F00}",
"ProcessId": 4184,
"Image": "C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\vbc.exe",
"FileVersion": "14.8.3761.0",
"Description": "Visual Basic Command Line Compiler",
"Product": "Microsoft® .NET Framework",
"Company": "Microsoft Corporation",
"OriginalFileName": "vbc.exe",
"CommandLine": "C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\vbc.exe",
"CurrentDirectory": "C:\\Users\\george\\AppData\\Roaming\\comlocal\\RUYCLAXYVMFJ\\",
"User": "DESKTOP-B0T93D6\\george",
"LogonGuid": "{C784477D-9D9B-66FF-6E87-050000000000}",
"LogonId": "0x5876e",
"TerminalSessionId": 1,
"IntegrityLevel": "High",
"Hashes": {
"SHA1": "61F4D9A9EE38DBC72E840B3624520CF31A3A8653",
"MD5": "FCCB961AE76D9E600A558D2D0225ED43",
"SHA256": "466876F453563A272ADB5D568670ECA98D805E7ECAA5A2E18C92B6D3C947DF93",
"IMPHASH": "1460E2E6D7F8ECA4240B7C78FA619D15"
},
"ParentProcessGuid": "{C784477D-F5D4-6745-5E06-000000003F00}",
"ParentProcessId": 6572,
"ParentImage": "C:\\Windows\\SysWOW64\\more.com",
"ParentCommandLine": "C:\\Windows\\SysWOW64\\more.com",
"ParentUser": "DESKTOP-B0T93D6\\george"
}
}
File Creation Related To RAT Clients
title: File Creation Related To RAT Clients
id: 2f3039c8-e8fe-43a9-b5cf-dcd424a2522d
status: experimental
description: File .conf created related to VenomRAT, AsyncRAT and Lummac samples observed in the wild.
references:
- https://www.virustotal.com/gui/file/c9f9f193409217f73cc976ad078c6f8bf65d3aabcf5fad3e5a47536d47aa6761
- https://www.virustotal.com/gui/file/e96a0c1bc5f720d7f0a53f72e5bb424163c943c24a437b1065957a79f5872675
author: Joseliyo Sanchez, @Joseliyo_Jstnk
date: 2024-11-15
tags:
- attack.execution
logsource:
category: file_event
product: windows
detection:
# VT Query: behaviour_files:"\\AppData\\Roaming\\DataLogs\\DataLogs.conf"
# VT Query: behaviour_files:"DataLogs.conf" or behaviour_files:"hvnc.conf" or behaviour_files:"dcrat.conf"
selection_required:
TargetFilename|contains: '\AppData\Roaming\'
selection_variants:
TargetFilename|endswith:
- '\datalogs.conf'
- '\hvnc.conf'
- '\dcrat.conf'
TargetFilename|contains:
- '\mydata\'
- '\datalogs\'
- '\hvnc\'
- '\dcrat\'
condition: all of selection_*
falsepositives:
- Legitimate software creating a file with the same name
level: high
Sysmon event for: File Creation Related To RAT Clients
{
"System": {
"Provider": {
"Guid": "{5770385F-C22A-43E0-BF4C-06F5698FFBD9}",
"Name": "Microsoft-Windows-Sysmon"
},
"EventID": 11,
"Version": 2,
"Level": 4,
"Task": 11,
"Opcode": 0,
"Keywords": "0x8000000000000000",
"TimeCreated": {
"SystemTime": "2024-12-02T00:52:23.072811600Z"
},
"EventRecordID": 1555690,
"Correlation": {},
"Execution": {
"ProcessID": 2624,
"ThreadID": 3112
},
"Channel": "Microsoft-Windows-Sysmon/Operational",
"Computer": "DESKTOP-B0T93D6",
"Security": {
"UserID": "S-1-5-18"
}
},
"EventData": {
"RuleName": "-",
"UtcTime": "2024-12-02 00:52:23.059",
"ProcessGuid": "{C784477D-04C6-674D-5C06-000000004B00}",
"ProcessId": 7592,
"Image": "C:\\Users\\george\\Desktop\\ezzz.exe",
"TargetFilename": "C:\\Users\\george\\AppData\\Roaming\\MyData\\DataLogs.conf",
"CreationUtcTime": "2024-12-02 00:52:23.059",
"User": "DESKTOP-B0T93D6\\george"
}
Wrapping up
sigma_rule:a1021d4086a92fd3782417a54fa5c5141d1e75c8afc9e73dc6e71ef9e1ae2e9c
sigma_rule:8f179585d5c1249ab1ef8cec45a16d112a53f91d143aa2b0b6713602b1d19252
Tracking Adversaries: Ghostwriter APT Infrastructure
Introduction to Infrastructure Pivoting
Pivoting on infrastructure is a handy skill for cyber threat
intelligence (CTI) analysts to learn. It can help to reveal the bigger picture
when it comes to malware, phishing, or network exploitation campaigns. Infrastructure
pivoting essentially is the act of looking for more systems an adversary has
created. The main benefit of this pursuit is the identification of additional
targets or victims, more tools or malware samples, and ultimately new insights
about the adversary’s capabilities.
If done correctly, being able to pivot on adversary
infrastructure will be very useful during incident response (IR) engagements. For
example, it may lead to being able to attribute the intrusion to a known
adversary. This will help others during an IR engagement understand the level
of threat posed to the victim organisation.
Receiving Threat Data
To be able to pivot on adversary infrastructure, threat data
is needed such as the intelligence shared by threat reports put out
by various researchers from public and private sector organisations. This
scenario, however, involves relying on the analysis skills of other researchers to explain
what the infrastructure is and when they observed it in use.
This blog will examine threat data provided by public sector
organisations such as the Computer Emergency Response Team of Ukraine (CERT-UA)
as well as cybersecurity vendors such as Deep Instinct, Cyble, and Fortinet.
These organisations have shared indicators of compromise (IOCs) uncovered
following analysis of adversary intrusion activities or upload to online
malware sandboxes, such as VirusTotal, among others.
Introduction to the Ghostwriter Campaign
On 3 June 2024, Fortinet shared a report
on malicious XLS macro documents leading to Cobalt Strike Beacons. Analysis of
the XLS documents showed that they appeared to be targeting the Ukrainian
military and linked to a known Belarusian state-sponsored APT group tracked as Ghostwriter
(aka UNC1151, UAC-0057, TA445). On 4 June 2024, Cyble also shared a report
on a similar campaign.
In both reports, if the XLS was opened and the macros were executed
by the target, a malicious DLL file was downloaded from an adversary-created domain.
In Fortinet’s report, two similar “.shop” domains were mentioned. In Cyble’s
report another “.shop” domain was also called out.
Overlapping IOCs
The first pivot on Ghostwriter APT infrastructure that will be
demonstrated involves finding indicators of compromise (IOCs) such as domains
and IP addresses that appear in multiple threat reports.
The fastest way to realize these overlaps is through
continuous collection of reported IOCs into a Threat Intelligence Platform
(TIP). This will reveal IOCs that appear in multiple threat reports through
tagging and sources of where IOCs come from. Eventually, one domain or IP
address will get reported by multiple entities and the connection will make
itself apparent.
In Figure 1 (see below) the domain “goudieelectric[.]shop”
appeared in both Cyble’s blog and Fortinet’s blog. Analysis of all three
domains found that they use the same generic top-level domain (gTLD),
registrar, and name servers, as well as have a robots.txt directory configured.
These common infrastructure characteristics indicate that all three domains
were created by the same adversary.
Domain Registration & Hosting Overlaps
When more IOCs are reported in other threat reports it is
possible to link them to other known domains, this is due to adversaries
reusing the same registrars, name servers, and gTLDs.
In Figure 2 (see below), Deep Instinct reported
two more domains that could also be linked to the previous three domains through
the mutual use of the PublicDomainsRegistry registrar, Cloudflare name servers,
and the robots.txt file.
Further, CERT-UA reported three more domains (see
Figure 3 below) that could be linked to the infrastructure cluster through this
same method as well. This pattern of behaviour is a strong indicator that these
domains were created by the same adversary.
Finding Unreported Domains
Since the domains from the above threat reports were
collected and linked together through overlapping attributes, it is now
possible to use these attributes to find more domains that had gone unreported.
Using a VirusTotal domain attribute query, additional domains
can be found by using the following registration pattern:
- Name Servers: CLOUDFLARE
- Registrar: PublicDomainRegistry
- TLD: *.shop
This revealed up to 24 domains that matched this pattern
that were likely created by Ghostwriter, a state-sponsored APT group:
- backstagemerch[.]shop
- bryndonovan[.]shop
- chaptercheats[.]shop
- clairedeco[.]shop
- connecticutchildrens[.]shop
- disneyfoodblog[.]shop
- eartheclipse[.]shop
- empoweringparents[.]shop
- foampartyhats[.]shop
- goudieelectric[.]shop
- ikitas[.]shop
- jackbenimblekids[.]shop
- kingarthurbaking[.]shop
- lansdownecentre[.]shop
- lauramcinerney[.]shop
- medicalnewstoday[.]shop
- moonlightmixes[.]shop
- penandthepad[.]shop
- physio-pedia[.]shop
- semanticscholar[.]shop
- simonandschuster[.]shop
- thevegan8[.]shop
- twisterplussize[.]shop
- utahsadventurefamily[.]shop
Note: VirusTotal domain searches are only available
to VirusTotal Enterprise users. There are other providers which allow you to search
for domain registration patterns such as DomainTools, Validin, and Zetalytics. There
also some free OSINT sites such as nslookup.io
and viewdns.info that can be useful in
certain scenarios.
Finding Related Malware Samples
Using the list of similar domains that were uncovered
through the registration pattern search, it is then possible to find additional
malware samples communicating with them.
This can be achieved by looking at domains in VirusTotal and checking the Relations tab can show communicating files as shown in Figure 4 below.
Using a VirusTotal graph can help to reveal every
communicating file with every domain discovered through the registration pattern
search, as shown in Figure 5 below.
URL to the VirusTotal Graph: https://www.virustotal.com/graph/embed/gd2c04407d9ba4b75b2ce73d6155d166d3ef75eaf29894ff5ac287c90400072bc?theme=dark
URL to the VirusTotal Collection: https://www.virustotal.com/gui/collection/2aa6b36a717be8bc49f7925434ca40f3ecb9f628414b491da3e985677508ca08/iocs
Lessons Learned
In conclusion, it is important for CTI analysts to closer
inspect the attributes of the IOCs they come across. It is not uncommon for
state-sponsored APT groups to make such mistakes when creating their
infrastructure to launch attacks from. By exploiting this fact, CTI analysts
can learn much more about the adversary’s targets, capabilities, and the behaviours
of the humans themselves behind such campaigns.
The importance of this type of work was demonstrated in
December 2023 when the US Treasury
sanctioned members of the Russian APT group known as Callisto
(aka Star Blizzard, BlueCharlie, COLDRIVER, GOSSAMER BEAR). The real world
identity of Andrey Korinets was revealed after he was sanctioned for fraudulently
creating and registering malicious domain infrastructure for Russian federal
security service (FSB) spear phishing campaigns.
Research that builds detections
Introduction
Tell me what role you have and I'll tell you how you use VirusTotal
Our approach
p:5+ have:behavior fs:30d+ not have:sigma
p:5+ (sandbox_name:"CAPE Sandbox" or sandbox_name:"Zenbox") fs:30d+ not have:sigma
p:5+ have:behavior fs:30d+ sigma_critical:0 sigma_high:0 sigma_medium:0 sigma_low:2-
Our detections for the community
Detect The Execution Of More.com And Vbc.exe Related to Lummac Stealer
title: Detect The Execution Of More.com And Vbc.exe Related to Lummac Stealer
id: 19b3806e-46f2-4b4c-9337-e3d8653245ea
status: experimental
description: Detects the execution of more.com and vbc.exe in the process tree. This behaviors was observed by a set of samples related to Lummac Stealer. The Lummac payload is injected into the vbc.exe process.
references:
- https://www.virustotal.com/gui/file/14d886517fff2cc8955844b252c985ab59f2f95b2849002778f03a8f07eb8aef
- https://strontic.github.io/xcyclopedia/library/more.com-EDB3046610020EE614B5B81B0439895E.html
- https://strontic.github.io/xcyclopedia/library/vbc.exe-A731372E6F6978CE25617AE01B143351.html
author: Joseliyo Sanchez, @Joseliyo_Jstnk
date: 2024-11-14
tags:
- attack.defense-evasion
- attack.t1055
logsource:
category: process_creation
product: windows
detection:
# VT Query: behaviour_processes:"C:\\Windows\\SysWOW64\\more.com" behaviour_processes:"C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\vbc.exe"
selection_parent:
ParentImage|endswith: '\more.com'
selection_child:
- Image|endswith: '\vbc.exe'
- OriginalFileName: 'vbc.exe'
condition: all of selection_*
falsepositives:
- Unknown
level: high
Sysmon event for: Detect The Execution Of More.com And Vbc.exe Related to Lummac Stealer
{
"System": {
"Provider": {
"Guid": "{5770385F-C22A-43E0-BF4C-06F5698FFBD9}",
"Name": "Microsoft-Windows-Sysmon"
},
"EventID": 1,
"Version": 5,
"Level": 4,
"Task": 1,
"Opcode": 0,
"Keywords": "0x8000000000000000",
"TimeCreated": {
"SystemTime": "2024-11-26T16:23:05.132539500Z"
},
"EventRecordID": 692861,
"Correlation": {},
"Execution": {
"ProcessID": 2396,
"ThreadID": 3116
},
"Channel": "Microsoft-Windows-Sysmon/Operational",
"Computer": "DESKTOP-B0T93D6",
"Security": {
"UserID": "S-1-5-18"
}
},
"EventData": {
"RuleName": "-",
"UtcTime": "2024-11-26 16:23:05.064",
"ProcessGuid": "{C784477D-F5E9-6745-6006-000000003F00}",
"ProcessId": 4184,
"Image": "C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\vbc.exe",
"FileVersion": "14.8.3761.0",
"Description": "Visual Basic Command Line Compiler",
"Product": "Microsoft® .NET Framework",
"Company": "Microsoft Corporation",
"OriginalFileName": "vbc.exe",
"CommandLine": "C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\vbc.exe",
"CurrentDirectory": "C:\\Users\\george\\AppData\\Roaming\\comlocal\\RUYCLAXYVMFJ\\",
"User": "DESKTOP-B0T93D6\\george",
"LogonGuid": "{C784477D-9D9B-66FF-6E87-050000000000}",
"LogonId": "0x5876e",
"TerminalSessionId": 1,
"IntegrityLevel": "High",
"Hashes": {
"SHA1": "61F4D9A9EE38DBC72E840B3624520CF31A3A8653",
"MD5": "FCCB961AE76D9E600A558D2D0225ED43",
"SHA256": "466876F453563A272ADB5D568670ECA98D805E7ECAA5A2E18C92B6D3C947DF93",
"IMPHASH": "1460E2E6D7F8ECA4240B7C78FA619D15"
},
"ParentProcessGuid": "{C784477D-F5D4-6745-5E06-000000003F00}",
"ParentProcessId": 6572,
"ParentImage": "C:\\Windows\\SysWOW64\\more.com",
"ParentCommandLine": "C:\\Windows\\SysWOW64\\more.com",
"ParentUser": "DESKTOP-B0T93D6\\george"
}
}
File Creation Related To RAT Clients
title: File Creation Related To RAT Clients
id: 2f3039c8-e8fe-43a9-b5cf-dcd424a2522d
status: experimental
description: File .conf created related to VenomRAT, AsyncRAT and Lummac samples observed in the wild.
references:
- https://www.virustotal.com/gui/file/c9f9f193409217f73cc976ad078c6f8bf65d3aabcf5fad3e5a47536d47aa6761
- https://www.virustotal.com/gui/file/e96a0c1bc5f720d7f0a53f72e5bb424163c943c24a437b1065957a79f5872675
author: Joseliyo Sanchez, @Joseliyo_Jstnk
date: 2024-11-15
tags:
- attack.execution
logsource:
category: file_event
product: windows
detection:
# VT Query: behaviour_files:"\\AppData\\Roaming\\DataLogs\\DataLogs.conf"
# VT Query: behaviour_files:"DataLogs.conf" or behaviour_files:"hvnc.conf" or behaviour_files:"dcrat.conf"
selection_required:
TargetFilename|contains: '\AppData\Roaming\'
selection_variants:
TargetFilename|endswith:
- '\datalogs.conf'
- '\hvnc.conf'
- '\dcrat.conf'
TargetFilename|contains:
- '\mydata\'
- '\datalogs\'
- '\hvnc\'
- '\dcrat\'
condition: all of selection_*
falsepositives:
- Legitimate software creating a file with the same name
level: high
Sysmon event for: File Creation Related To RAT Clients
{
"System": {
"Provider": {
"Guid": "{5770385F-C22A-43E0-BF4C-06F5698FFBD9}",
"Name": "Microsoft-Windows-Sysmon"
},
"EventID": 11,
"Version": 2,
"Level": 4,
"Task": 11,
"Opcode": 0,
"Keywords": "0x8000000000000000",
"TimeCreated": {
"SystemTime": "2024-12-02T00:52:23.072811600Z"
},
"EventRecordID": 1555690,
"Correlation": {},
"Execution": {
"ProcessID": 2624,
"ThreadID": 3112
},
"Channel": "Microsoft-Windows-Sysmon/Operational",
"Computer": "DESKTOP-B0T93D6",
"Security": {
"UserID": "S-1-5-18"
}
},
"EventData": {
"RuleName": "-",
"UtcTime": "2024-12-02 00:52:23.059",
"ProcessGuid": "{C784477D-04C6-674D-5C06-000000004B00}",
"ProcessId": 7592,
"Image": "C:\\Users\\george\\Desktop\\ezzz.exe",
"TargetFilename": "C:\\Users\\george\\AppData\\Roaming\\MyData\\DataLogs.conf",
"CreationUtcTime": "2024-12-02 00:52:23.059",
"User": "DESKTOP-B0T93D6\\george"
}
Wrapping up
sigma_rule:a1021d4086a92fd3782417a54fa5c5141d1e75c8afc9e73dc6e71ef9e1ae2e9c
sigma_rule:8f179585d5c1249ab1ef8cec45a16d112a53f91d143aa2b0b6713602b1d19252
