Threatview.io provides some excellent threat intelligence feeds that can be used with Azure Sentinel as external sources. The Threatview.io feeds are updated regularly – generated daily at 11PM UTC – so you can be sure that the most current indicators will be available.
The feeds are available from here: https://cda.ms/2mc
The feeds are provided as links to files in .txt format so you can use the externaldata operator for KQL to pull in the feeds in real-time for KQL queries.
The following feeds are available:
- OSINT Threat Feed – Malicious indicators of compromise gathered from OSINT Source – Twitter and Pastebin
- C2 Hunt Feed – Infrastructure hosting Command & Control Servers found during Proactive Hunt by Threatview.io
- IP Blocklist – Malicious IP Blocklist for known Bad IP addresses
- Domain Blocklist – Malicious Domains identified for phishing/ serving malware/ command and control
- MD5 Hash Blocklist – MD5 hashes of malicious files or associated with – malware, ransomware, hack tools, bots etc.
- URL Blocklist – Malicious URL’s serving malware, phishing, botnets and C2
- Bitcoin Address Intel – Bitcoin addresses identified to be linked with malicious activity
- SHA File Hash Blocklist – SHA hashes of files known or linked with malware exec
Let’s walk through what a couple of these KQL queries will look like.
The OSINT threat feed is a single column list of IP addresses. In the KQL query we’re creating the OSINT variable (let operator) and filling it with the contents of the external file from Threatview.io using the project operator. Notice we’ve identified the format (txt) and told the query to ignore the first row in the text file (its a header) and head straight to the IP addresses.
let OSINT = externaldata(IP: string,values:dynamic) [@"https://threatview.io/Downloads/Experimental-IOC-Tweets.txt"] with (format="txt",ignoreFirstRecord=true) | where IP !startswith "#" | project IP;
Now, let’s tie these IP addresses to our environment and search to see if any of our device assets have attempted to access any of the known-bad IPs. We’ll do this by joining our newly formed variable (list of IP addresses) with the DeviceNetworkEvents table (exposed through the Microsoft Defender for Endpoints connection) where there’s been a successful connection.
Here’s the full query:
let OSINT = externaldata(IP: string,values:dynamic) [@"https://threatview.io/Downloads/Experimental-IOC-Tweets.txt"] with (format="txt",ignoreFirstRecord=true) | where IP !startswith "#" | project IP; OSINT | join (DeviceNetworkEvents | where ActionType =="ConnectionSuccess" ) on $left.IP == $right.RemoteIP | project TimeGenerated, IP, RemoteIP, DeviceName, RemoteUrl, InitiatingProcessFileName
The IP Blocklist query will be similar. Use the same idea as above, except there’s one small exception to take note of. The IP Blocklist txt file doesn’t contain a header row, so as you can see in the following full query, there’s no ignoreFirstRecord=true to tell the query to skip the first line like before.
In the following query I used the same DeviceNetworkEvents table for the join, so essentially much of the solution is the same. Call me lazy if you want, but Defender for Endpoint tables offer so much value.
let IPBlock = externaldata(IP: string,values:dynamic) [@"https://threatview.io/Downloads/IP-High-Confidence-Feed.txt"] with (format="txt") | where IP !startswith "#" | project IP; IPBlock | join (DeviceNetworkEvents | where ActionType =="ConnectionSuccess" ) on $left.IP == $right.RemoteIP | project TimeGenerated, IP, RemoteIP, DeviceName, RemoteUrl, InitiatingProcessFileName
BTW: A couple of the feeds contain spaces in the filename so you will need to encode the URL for a space (%20). If you’re not comfortable with that, here’s an awesome online URL Encoder/Decoder: https://cda.ms/2mk
Give it whirl. These are some valuable feeds that will provide some additional value. If you create something cool, share it.
[Subscribe to the RSS feed for this blog]
[Subscribe to the Weekly Azure Sentinel Newsletter]