Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
Name: cloudflare-domain-rank-top.storm
Author: bartosz.roszewski@smartcontract.com
Author: exc3l_one@protonmail.com
Modified By: [email protected], [email protected]
Last Modified: 2023-11-15

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
Name: cloudflare-domain-rank.storm
Author: bartosz.roszewski@smartcontract.com
Author: exc3l_one@protonmail.com
Modified By: [email protected], [email protected]
Last Modified: 2023-11-15

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
Name: crowdstrike-falcon-actors.storm
Author: bartosz.roszewski@smartcontract.com
Author: exc3l_one@protonmail.com
Modified By: [email protected], [email protected]
Last Modified: 2023-11-15

Expand Down
70 changes: 70 additions & 0 deletions snippets/loobins-project-ingest/loobins-project-ingest.storm
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
Name: loobins-project-ingest.storm
Author: [email protected]
Modified By: TODO
Last Modified: 2024-06-28

Description: Ingest the JSON representation of the LOOBins project to model the ou:techniques associated with built-in macOS binaries

References:
-- LOOBins Project Website: https://loobins.io/
*/

// Generate the LOOBins project ou:org node
init {
{[ou:org=$lib.gen.orgByFqdn("loobins.io")
:name="loobins"
:url="https://www.loobins.io/"
:desc="Living Off the Orchard: macOS Binaries (LOOBins) is designed to provide detailed information on various built-in macOS binaries and how they can be used by threat actors for malicious purposes."
]}
Comment on lines +15 to +19
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This subquery won't actually execute since there are no nodes in the pipeline here

}

// Get the LOOBins project data in JSON
$url = "https://www.loobins.io/loobins.json"
$resp = $lib.inet.http.get($url)

if ($resp.code = 200) {
$body = $lib.json.load($body)

for $bin in $body {
Comment on lines +27 to +29
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$body = $lib.json.load($body)
for $bin in $body {
for $bin in $resp.json() {

// Create a software node for each LOOBin
[it:prod:soft=$lib.gen.softByName($bin.name)
:desc:short=$bin.short_description
:desc=$bin.full_description
:type=macos.loobin
:islib=false
:isos=false
]

// Link the software node to the filepaths it uses
for $path in $bin.paths {
[+(uses)> {[file:path=$path]}]
}
Comment on lines +40 to +42
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This loop will cause the it:prod:soft node to multiply, you can move the loop inside the edge add subquery (also more efficient that way)

Suggested change
for $path in $bin.paths {
[+(uses)> {[file:path=$path]}]
}
for $path in $bin.paths {
[+(uses)> {
for $path in $bin.paths {[file:path=$path]}
}]


// Create technique nodes
for $ttp in $bin.example_use_cases {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what the intended output of the macro is, but should probably move this loop into a subquery to avoid duplicate it:prod:soft nodes being yielded.

[+(uses)> {[ou:technique=("loobins", $ttp.code)
:desc=$ttp.description
:name=$ttp.name
:reporter=$lib.gen.orgByFqdn("loobins.io")
:reporter:name="loobins.io"
:type=macos.loobins
]

// Link the technique to the command it uses by creating a it:proc:exec node to represent the command and the file:base
[+(refs)> {
[it:exec:proc=($ttp.code, "loobins")
:cmd=$ttp.code
:path:base=$bin.name
:name=$bin.name
]
}]}]

//Alternative method: linking the technique to the command via a light edge (choose either this one or the one above, not both at the same time)
//[+(refs)> {[it:cmd=$ttp.code]}]}]
}

}
} else {
$lib.warn("Returned HTTP code: {code}", code=$resp.code)
}
67 changes: 67 additions & 0 deletions snippets/osint-threat-feeds/c2-tracker-feed-ingest.storm
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
Name: c2-tracker-feed-ingest.storm
Author: [email protected]
Modified By: TODO
Last Modified: 2024-06-28

Description: Ingest the TXT feeds from the C2-Tracker Github project by montysecurity

References:
-- Project Repo: https://github.com/montysecurity/C2-Tracker
*/


$url = "https://github.com/montysecurity/C2-Tracker/tree/main/data"
$resp = $lib.inet.http.get($url)

if ($resp.code = 200 ) {
$data = $resp.body.decode()
// Extract the JSON representation of the Github repo tree from the HTML page
$data = $lib.regex.search('(<script type="application\/json" data-target="react-app\.embeddedData">)(.+)(<\/script>)', $data)
$data = $lib.json.load($data.1)

// Iterate over every family tracked by the C2-Tracker project
for $family_feed in $data.payload.tree.items {

if ($family_feed.name = "all.txt") { continue }

$lib.print(`Fetching data for family: {$family_feed.name}`)

// Fetch the raw file containing the IPs for the family
$c2_feed = $lib.inet.http.get(`https://raw.githubusercontent.com/montysecurity/C2-Tracker/main/{$family_feed.path}`)

if ($c2_feed.code = 200) {
// Split the feed by newline
$c2_feed = $c2_feed.body.decode().split("\n")

// Create new inet:ipv4 nodes for each IP in the feed
for $c2_ip in $c2_feed {
($ok, $ip_val) = $lib.trycast(inet:ipv4, $c2_ip)
if ($ok) {
[inet:ipv4=$c2_ip]

// Extract the family name + type from the filename and add a timestamp to the rep node
$tag = $family_feed.name.replace(" IPs.txt", "").replace(" C4 ", " C2 ").split(" ")

// The following if/elif section covers various lengths of the family naming structure, prone to breaking but more granular
if ($lib.len($tag) = 2) {
$tag = $lib.str.concat($tag.1, ".", $tag.0)
[+#rep.c2_tracker.$tag=($lib.time.now())]
} elif ($lib.len($tag) = 1) {
if ($tag ~= "RAT") {$tag = $lib.str.concat("rat", ".", $tag.0)}
if ($tag ~= "C2") {$tag = $lib.str.concat("c2", ".", $tag.0)}
[+#rep.c2_tracker.$tag=($lib.time.now())]
} elif ($lib.len($tag) = 3) {
$tag = $lib.str.concat($tag.2, ".", $tag.0, "_", $tag.1)
[+#rep.c2_tracker.$tag=($lib.time.now())]
} elif ($lib.len($tag) = 4) {
$tag = $lib.str.concat($tag.3, ".", $tag.0, "_", $tag.1, "_", $tag.2)
[+#rep.c2_tracker.$tag=($lib.time.now())]
}
}
}
}
}
}
// Do not yield any nodes because it's a lot of data
| spin
85 changes: 85 additions & 0 deletions snippets/osint-threat-feeds/drb-feed-ingest.storm
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
Name: drb-feed-ingest.storm
Author: [email protected]
Modified By: TODO
Last Modified: 2024-06-28

Description: Ingest the CSV feeds from the C2IntelFeeds Github project by drb-ra.

References:
-- Project Repo: https://github.com/drb-ra/C2IntelFeeds
*/

function ingestC2URLs() {
$url = "https://github.com/drb-ra/C2IntelFeeds/raw/master/feeds/domainC2swithURLwithIP-30day-filter-abused.csv"
$resp = $lib.inet.http.get($url)

if ($resp.code = 200) {
$body = $resp.body.decode()
$body = $body.split("\n")
for $line in $body {
// Remove comment and empty lines
if (not $line^="#" and $line != "") {
$line = $line.split(",")

// Create the domain and inet:dns:a nodes
[inet:fqdn=$line.0]
if ($line.3~="\d\.") {
[inet:dns:a=($line.0, $line.3)]
[inet:ipv4=$line.3]
}
//Create the inet:url nodes
$url = $lib.str.concat("https://", $line.0, $line.2)
[inet:url=$url]

//Since the tags are written in prose, we manually extract them from the CSV with keywords
//The feed persists for 30 days, so we add the timestamp to the tags to reflect when it appears/dissapears from the threat feed
//Remove the timestamp if this is not accurate enough for your use case
if ($line.1 ~= "Cobalt") {[+#rep.c2feeds.mal.cobaltstrike=($lib.time.now())]}
if ($line.1 ~= "PoshC2") {[+#rep.c2feeds.mal.poshc2=($lib.time.now())]}
if ($line.1 ~= "Empire") {[+#rep.c2feeds.mal.empire=($lib.time.now())]}
if ($line.1 ~= "Sliver") {[+#rep.c2feeds.mal.sliver=($lib.time.now())]}
if ($line.1 ~= "Deimos") {[+#rep.c2feeds.mal.deimos=($lib.time.now())]}
if ($line.1 ~= "Havoc") {[+#rep.c2feeds.mal.havoc=($lib.time.now())]}
if ($line.1 ~= "Covenant") {[+#rep.c2feeds.mal.covenant=($lib.time.now())]}
if ($line.1 ~= "Front") {[+#rep.c2feeds.ttp.fronting_domain=($lib.time.now())]}

}
}
} else {
$lib.warn("Returned HTTP code: {code}", code=$resp.code)
}
}

function ingestC2IPs() {
$url = "https://raw.githubusercontent.com/drb-ra/C2IntelFeeds/master/feeds/IPC2s.csv"
$resp = $lib.inet.http.get($url)

if ($resp.code = 200) {
$body = $resp.body.decode()
$body = $body.split("\n")
for $line in $body {
// Remove comment and empty lines
if (not $line^="#" and $line != "") {
$line = $line.split(",")
if ($line.0~="\d\.") {
[inet:ipv4=$line.0]
}

if ($line.1 ~= "Cobalt") {[+#rep.c2feeds.mal.cobaltstrike.susp=($lib.time.now())]}
if ($line.1 ~= "PoshC2") {[+#rep.c2feeds.mal.poshc2.susp=($lib.time.now())]}
if ($line.1 ~= "Empire") {[+#rep.c2feeds.mal.empire.susp=($lib.time.now())]}
if ($line.1 ~= "Sliver") {[+#rep.c2feeds.mal.sliver.susp=($lib.time.now())]}
if ($line.1 ~= "Deimos") {[+#rep.c2feeds.mal.deimos.susp=($lib.time.now())]}
if ($line.1 ~= "Havoc") {[+#rep.c2feeds.mal.havoc.susp=($lib.time.now())]}
if ($line.1 ~= "Covenant") {[+#rep.c2feeds.mal.covenant.susp=($lib.time.now())]}

}
}
} else {
$lib.warn("Returned HTTP code: {code}", code=$resp.code)
}
}

$ingestC2IPs()
$ingestC2URLs()
28 changes: 28 additions & 0 deletions snippets/osint-threat-feeds/phishunt-feed-ingest.storm
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
Name: phishunt-feed-ingest.storm
Author: [email protected]
Modified By: TODO
Last Modified: 2024-06-28

Description: Ingest the daily Phishing feed from Phishunt.io project. Recommend to ingest this as a daily crontab job.

References:
-- Phishunt Project Website: https://phishunt.io/
*/

$url = "https://phishunt.io/feed.txt"
$resp = $lib.inet.http.get($url)

if ($resp.code = 200) {
$body = $resp.body.decode()
$body = $body.split("\n")
for $url in $body {
// Create the URL and rep tag
[inet:url=$url] [+#rep.phishunt.phishing=($lib.time.now())]

//Uncomment the following line to also add a more genering cno.infra tag
//[+#cno.infra.phishing=($lib.time.now())]
}
} else {
$lib.warn("Returned HTTP code: {code}", code=$resp.code)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
Name: ransomwhere-addresses-ingest.storm
Author: [email protected]
Modified By: TODO
Last Modified: 2024-06-28

Description: Ingests cryptocurrency addresses related to ransomwhere payments from the RansomWhere project API

References:
-- RansomWhere Project Website: https://ransomwhe.re/#browse
*/

// Fetch the ransomwhe.re data
$resp = $lib.inet.http.get("https://api.ransomwhe.re/export")

if ($resp.code = 200) {
$data = $lib.json.load($resp.body)

// Iterate over the addresses and ingest them into the platform - supports
for $address in $data.result {

// At the moment ransomwhere only supports bitcoin addresses, so other chains are for future proofing only
switch $address.blockchain {
"bitcoin": {[crypto:currency:address=(btc, $address.address)]},
"ethereum": {[crypto:currency:address=(eth, $address.address)]},
"monero": {[crypto:currency:address=(xmr, $address.address)]},
"ripple": {[crypto:currency:address=(xrp, $address.address)]},
"zcash": {[crypto:currency:address=(zec, $address.address)]},
"dogecoin": {[crypto:currency:address=(doge, $address.address)]} //because why not
}

// Cleanup the tags to make them more Synapse friendly, apply to the crypto:currency:address node
$tag = $address.family.replace("(", "").replace(")", "").replace(" / ", "_").replace(" ", "_").replace("/", "_")
[+#rep.ransomwhe_re.$tag]

}
} else {
$lib.warn("Received non-200 HTTP code: {code}", code=$resp.code)
}
// Don't return the addresses since it's a lot of data
| spin
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
Name: wiz-cloud-threat-landscape.storm
Author: [email protected]
Modified By: TODO
Last Modified: 2024-06-28

Description: Ingest the STIX2.1 data feed from Wiz.io's Cloud Threat Landscape feed. This feed contains information about threat actors, campaigns, malware, and attack patterns affecting the cloud.

References:
-- STIX2.1 Feed: https://www.wiz.io/feed/cloud-threats-landscape/stix.json
*/

// Pull the STIX2.1 data
$url = "https://www.wiz.io/feed/cloud-threats-landscape/stix.json"
$resp = $lib.inet.http.get($url)

if ($resp.code = 200) {

$data = $lib.json.load($resp.body)

// Define the STIX2.1 object and relationship mappings
$config = ({
"objects": {
"tool": {
"storm": "($ok, $name) = $lib.trycast(it:prod:softname, $object.name) if $ok { it:prod:softname=$name -> it:prod:soft return($node) [ it:prod:soft=('wiz.io', $name) :name=$name ] return($node) }"
},
"threat-actor": {
"storm": "[ risk:threat=$lib.gen.riskThreat($object.name, 'wiz.io') :tag=`rep.wiz_io.{$object.name}` ] $node.data.set(stix:object, $object) return($node)"
},
"campaign": {
"storm": "[ ou:campaign=(stix, campaign, $object.id) :name?=$object.name :desc?=$object.description :goal={[ou:goal=('wiz.io', $object.objective) :name=$object.objective]} .seen?=$object.last_seen .seen?=$object.first_seen ] [<(refs)+ {[media:news=$lib.gen.newsByUrl($object.external_reference.url, true)]}] $node.data.set(stix:object, $object) return($node)"
},
"malware": {
"storm": "[ risk:tool:software=$lib.gen.riskToolSoftware($object.name, 'wiz.io')] return($node)"
},
"attack-pattern": {
"storm": "[ ou:technique=($object.name, 'wiz.io') :name=$object.name ] return($node)"
}
},
"relationships": [{
"type": ["campaign", "attributed-to", "threat-actor"],
// Link the ou:campaign to the risk:threat node and add the threat actor's tag to the ou:campaign node to support the Vertex-Triage Power-Up pivoting
"storm": "yield $n1node [<(refs)+ {yield $n2node}] | $tag = `rep.wiz_io.{$node.pack().1.props.'org:name'}` [+#$tag ]"
}, {
"type": ["threat-actor", "uses", "tool"],
"storm": "yield $n1node [ +(uses)> { yield $n2node } ]"
}, {
"type": ["threat-actor", "uses", "malware"],
"storm": "yield $n1node [ +(uses)> { yield $n2node } ]"
}, {
"type": ["campaign", "uses", "malware"],
"storm": "yield $n1node [ +(uses)> { yield $n2node } ]"
}, {
"type": ["campaign", "uses", "tool"],
"storm": "yield $n1node [ +(uses)> { yield $n2node } ]"
}, {
"type": ["campaign", "uses", "attack-pattern"],
"storm": "yield $n1node [ +(uses)> { yield $n2node } ]"
}]
})

// Ingest the STIX2.1 data
$lib.stix.import.ingest($data, $config)

} else {
$lib.warn("Received non-200 HTTP response: {code}", code = $resp.code)
}
Loading