Search

Zeek for Hunting Splunk app

Zeek 로그를 기반으로 가능한 위협헌팅 대시보드를 다수 만든 앱
상당히 참고할만한 쿼리문이 많았다. 이에 쿼리문 상당수를 추출해 정리했다.

DNS Insights

Anomalies :: DGA Pattern in Requests

description : insights on the dns query requests with potential DGA (domain generating algorithm) pattern Domain Name Entropy [ Based on Shannon Algorithm ]
index=zeek sourcetype=*dns* NOT query IN (*arpa,*.local) | eval queryLength=len(query) | stats count by queryLength, query | where queryLength>20 | eval list="mozilla" | `ut_parse_extended(query, list)` | `ut_shannon(query)` | eval ut_shannon1 = ut_shannon | eval subdomain1= mvindex(split(query,"."),0) | `ut_shannon(subdomain1)` | eval ut_shannon2 = ut_shannon | table ut_domain query subdomain1 queryLength ut_shannon1 ut_shannon2 | eval ut_shannon1= round(ut_shannon1,2) | eval ut_shannon2= round(ut_shannon2,2) | sort - ut_shannon1 ut_shannon2 | rename query as "DNS Query" ut_shannon1 as "Entropy Score for full query" subdomain1 as "DNS Subdomain" ut_shannon2 as "Entropy Score for Sub-Domain" | fields - ut_domain queryLength
SQL
복사
index=zeek sourcetype=*dns* NOT query IN (*arpa,*.local) query="*.*" NOT [|inputlookup whitelist.csv |fields src_ip] NOT [|inputlookup random_dns.csv |fields query] NOT [|inputlookup query_fam_used.csv |fields query] | eval queryLength=len(query) | stats count by queryLength, query | eval list="mozilla" | `ut_parse_extended(query, list)` | `ut_shannon(query)` | eval ut_shannon1 = ut_shannon | eval subdomain1= mvindex(split(query,"."),0) | `ut_shannon(subdomain1)` | eval ut_shannon2 = ut_shannon | table ut_domain query subdomain1 queryLength ut_shannon1 ut_shannon2 | eval ut_shannon1= round(ut_shannon1,2) | eval ut_shannon2= round(ut_shannon2,2) | sort - ut_shannon1 ut_shannon2 | rename query as "DNS Query" ut_shannon1 as "Entropy Score for full query" subdomain1 as "DNS Subdomain" ut_shannon2 as "Entropy Score for Sub-Domain" | fields - ut_domain queryLength
SQL
복사
Lengthy Domain name requests
index=zeek sourcetype=bro:dns:json NOT query IN (*arpa,*.local) | eval queryLength=len(query) | stats count by queryLength, query | where queryLength>20 | eval list="mozilla" | `ut_parse_extended(query, list)` | search NOT [| inputlookup subdomainlookup.csv | rename domain as ut_domain ] | sort -queryLength, count | table query queryLength count | rename query as "DNS Query" queryLength as Length count as Occurrence
SQL
복사

Anomalies :: Excessive Sub-Domains

description : insights on high number of sub-domain related to main domain name [Potential data exfiltration ]
Excessive Subdomains in the DNS requests [Potential Data Exfil]
eventtype=zeek_idx_sourcetype NOT query IN (*.arpa) | eval list="mozilla" | `ut_parse_extended(query, list)` | stats dc(ut_subdomain) as qty BY ut_domain | where qty > 2 | search NOT [| inputlookup subdomainlookup.csv | rename domain as ut_domain ] | head 25 | rename ut_domain as "Domain Name" | sort - qty
SQL
복사

Anomalies :: Newly Seen Domain Names

description : insights on the newly requested domain names within the past 24hrs against historical DNS requests (7 days/14 days/ 30 days /etc.)
Newly seen Domain Name in requests
index=zeek sourcetype=*dns NOT query IN (*arpa,*.local) earliest=$tokenbaseline$ latest=now() | eval list="mozilla" | `ut_parse_extended(query, list)` | stats earliest(_time) AS "firstseen" latest(_time) AS "lastseen" BY ut_domain | eval newlyseen=if(firstseen >= relative_time(now(), "-1d@d"), 1, 0) | table ut_domain lastseen firstseen newlyseen | convert ctime(firstseen) ctime(lastseen) | rename ut_domain as "Domain Name" firstseen as "First Seen" lastseen as "Last Seen" | where newlyseen=1 | fields - newlyseen "First Seen" "Last Seen"
SQL
복사

Anomalies :: NXDomain in Respones

description : insights on the 'NXDOMAIN' flagged dns response
Filter based on TLD
Response distribution (Note : Filter Applied Here)
index=zeek sourcetype=*dns* NOT query IN (*.arpa,*-guest,local) query IN (*.*) query =* | stats count by rcode_name
SQL
복사
NXDOMAIN : Source IP
eventtype=zeek_idx_sourcetype sourcetype="*dns*" NOT query IN (ISATAP,WORKGROUP) query=*$tokenTLD$ rcode_name IN (NXDOMAIN) | bin span=1d _time | table _time id.orig_h id.orig_p id.resp_h id.resp_p query query_type rcode_name | timechart span=1d count by id.orig_h
SQL
복사
Top Level Domain : NXDOMIN
index=zeek sourcetype=bro:dns:json NOT query IN (*.arpa,*-guest,local) query IN (*.*) NOT rcode_name = NOERROR | eval list="mozilla" | `ut_parse_extended(query, list)` | stats dc(ut_subdomain) as qty BY ut_domain | rename ut_domain as "Domain Name" | sort - qty
SQL
복사
Sub Domain : NXDOMAIN (Note : Filter Applied Here)
index=zeek sourcetype=bro:dns:json NOT query IN (*.arpa,*-guest,local) query IN (*.*) NOT rcode_name = NOERROR query =*$tokenTLD$ | stats count by query | sort -count
SQL
복사

Analytics :: LFO/MFO Frequency

description : insights on frequency based analysis / stack counting for the domain names
Top Requested Domains (known domain excluded)
index=zeek sourcetype="*dns*" | eventstats count AS total | eval list="mozilla" | lookup ut_parse_extended_lookup url as query list as list | spath input=ut_subdomain_parts | fields - ut_subdomain_parts | stats count by ut_domain total | eval percentage=round((count/total)*100,4) | sort percentage | search NOT ut_domain IN (none,*.arpa,google.com,kaspersky.com,microsoft.com) | head 20 | table ut_domain percentage
SQL
복사
Least Requested Domains (LFO : Least Frequency of Occurrence)
index=zeek sourcetype="*dns*" | eventstats count AS total | eval list="mozilla" | lookup ut_parse_extended_lookup url as query list as list | spath input=ut_subdomain_parts | fields - ut_subdomain_parts | stats count by ut_domain total | eval percentage=round((count/total)*100,1) | sort +percentage | search NOT ut_domain IN (none,*.arpa) | head 20 | table ut_domain percentage | rename ut_domain as "Domain Name" percentage as "%" | sort "%"
SQL
복사
Top Requested Domains (MFO: : Most Frequency of Occurrence) (known domain excluded)
index=zeek sourcetype="*dns*" | eventstats count AS total | eval list="mozilla" | lookup ut_parse_extended_lookup url as query list as list | spath input=ut_subdomain_parts | fields - ut_subdomain_parts | stats count by ut_domain total | eval percentage=round((count/total)*100,1) | sort - percentage | search NOT ut_domain IN (none,*.arpa) | head 20 | table ut_domain percentage | rename ut_domain as "Domain Name" percentage as "%"
SQL
복사

File Insights

Anomalies :: Watering-Hole Pattern

description : insights on watering-hole pattern in file download
File Downloads [ only Executable]
index=zeek sourcetype="*http*" resp_mime_types IN ("application/x-dosexec","application/x-debian-package","application/x-gzip","application/msword","application/zip") | eval a=host | eval web_url=mvindex(a,-1) | eval uri_filename=mvindex(split(url,"/"),-1) | search uri_filename=*.* | table uid user_agent uri method web_url resp_mime_types uri_filename id.orig_h | stats values(id.orig_h) as source dc(id.orig_h) as count by uri_filename | rename uri_filename as "File Name" | where count>1
SQL
복사

DHCP Insights

Anomalies :: MAC Spoofing Attempts

description : insights on MAC address spoofing
Potential MAC Address Spoofing Attempts
same machine with more than one mac address is may be due to running VM inside the endpoint
index=zeek sourcetype=*dhcp* | table _time host_name client_addr mac msg_types* | stats values(mac) as "MAC Address" dc(mac) as dc_count by host_name | where dc_count>1 | rename host_name as Hostname | fields "MAC Address" Hostname
SQL
복사

Anomalies :: Suspicious Hostnames

description : insights on the suspicious endpoints requesting for dhcp IP address
Weird Hostnames observed in DHCP requests
Computer Hostname pattern other than normal naming convention used in an Enterprise
index=zeek sourcetype=*dhcp* | dedup host_name | search NOT host_name IN (*-*,".") | eval host_name=upper(host_name) | rename mac as "MAC Address" host_name as Hostname | table Hostname "MAC Address"
SQL
복사

SSH Insights

External :: SSH Outgoing to Internet

description : insights on the SSH outgoing connection from internal to external
Connection to External [ with data transfer]
index=zeek sourcetype IN (*conn*) id.resp_p =22 NOT id.resp_h IN (10.0.0.0/8,172.16.0.0/12,192.168.0.0/16) | table _time uid id.orig_h id.orig_p id.resp_h id.resp_p sourcetype orig_bytes resp_bytes conn_state | join uid type=left [ search eventtype=zeek_idx_sourcetype sourcetype IN (*ssh*) NOT id.resp_h IN (10.0.0.0/8,172.16.0.0/12,192.168.0.0/16) | table _time uid id.orig_h id.resp_h id.resp_p auth_success cipher_alg client server direction host_key_alg host_key kex_alg mac_alg version auth_attempts | table _time uid auth_success auth_attempts direction client server] | eval total_bytes=orig_bytes+ resp_bytes | search total_bytes > 0 | fields - sourcetype id.orig_p id.resp_p total_bytes server | fillnull value="-"
SQL
복사
Connection to External [ without data transfer]
index=zeek IN (*conn*) id.resp_p =22 NOT id.resp_h IN (10.0.0.0/8,172.16.0.0/12,192.168.0.0/16) | table _time uid id.orig_h id.orig_p id.resp_h id.resp_p sourcetype orig_bytes resp_bytes conn_state | join uid type=left [ search eventtype=zeek_idx_sourcetype sourcetype IN (bro:ssh:json) NOT id.resp_h IN (10.0.0.0/8,172.16.0.0/12,192.168.0.0/16) | table _time uid id.orig_h id.resp_h id.resp_p auth_success cipher_alg client server direction host_key_alg host_key kex_alg mac_alg version auth_attempts | table _time uid auth_success auth_attempts direction client server] | eval total_bytes=orig_bytes+ resp_bytes | fillnull value=0 total_bytes | search total_bytes=0 | fields - sourcetype id.orig_p id.resp_p total_bytes server | fillnull value="-"
SQL
복사

External :: SSH Incoming from Internet

description : insights on the SSH outgoing connection from internet to internal
Connections with Data Transfer [ From Internet ]
index=zeek sourcetype IN (*conn*) id.resp_p =22 id.resp_h IN (10.0.0.0/8,172.16.0.0/12,192.168.0.0/16) NOT id.orig_h IN (10.0.0.0/8,172.16.0.0/12,192.168.0.0/16) | table _time uid id.orig_h id.orig_p id.resp_h id.resp_p sourcetype orig_bytes resp_bytes conn_state | join uid type=left [ search eventtype=zeek_idx_sourcetype sourcetype IN (*ssh*) id.resp_h IN (10.0.0.0/8,172.16.0.0/12,192.168.0.0/16) NOT id.orig_h IN (10.0.0.0/8,172.16.0.0/12,192.168.0.0/16) | table _time uid id.orig_h id.resp_h id.resp_p auth_success cipher_alg client server direction host_key_alg host_key kex_alg mac_alg version auth_attempts | table _time uid auth_success auth_attempts direction client server] | eval total_bytes=orig_bytes+ resp_bytes | search total_bytes > 0 | fields - sourcetype id.orig_p id.resp_p total_bytes | fillnull value="-"
SQL
복사
Connections without Data Transfer [ From Internet ]
index=zeek sourcetype IN (*conn*) id.resp_p =22 id.resp_h IN (10.0.0.0/8,172.16.0.0/12,192.168.0.0/16) NOT id.orig_h IN (10.0.0.0/8,172.16.0.0/12,192.168.0.0/16) | table _time uid id.orig_h id.orig_p id.resp_h id.resp_p sourcetype orig_bytes resp_bytes conn_state | join uid type=left [ search eventtype=zeek_idx_sourcetype sourcetype IN (*ssh*) id.resp_h IN (10.0.0.0/8,172.16.0.0/12,192.168.0.0/16) NOT id.orig_h IN (10.0.0.0/8,172.16.0.0/12,192.168.0.0/16) | table _time uid id.orig_h id.resp_h id.resp_p auth_success cipher_alg client server direction host_key_alg host_key kex_alg mac_alg version auth_attempts | table _time uid auth_success auth_attempts direction client server] | eval total_bytes=orig_bytes+ resp_bytes | fillnull value=0 total_bytes | search total_bytes=0 | fields - sourcetype id.orig_p id.resp_p total_bytes | fillnull value="-"
SQL
복사
SSH Access [ From Internet ]
index=zeek sourcetype IN (*conn*) id.resp_p =22 id.resp_h IN (10.0.0.0/8,172.16.0.0/12,192.168.0.0/16) NOT id.orig_h IN (10.0.0.0/8,172.16.0.0/12,192.168.0.0/16) | table _time uid id.orig_h id.orig_p id.resp_h id.resp_p sourcetype orig_bytes resp_bytes conn_state | join uid type=left [ search eventtype=zeek_idx_sourcetype sourcetype IN (*ssh*) id.resp_h IN (10.0.0.0/8,172.16.0.0/12,192.168.0.0/16) NOT id.orig_h IN (10.0.0.0/8,172.16.0.0/12,192.168.0.0/16) | table _time uid id.orig_h id.resp_h id.resp_p auth_success cipher_alg client server direction host_key_alg host_key kex_alg mac_alg version auth_attempts | table _time uid auth_success auth_attempts direction client server] | eval total_bytes=orig_bytes+ resp_bytes | search total_bytes > 0 | fields - sourcetype id.orig_p id.resp_p total_bytes | fillnull value="-"
SQL
복사

Internal :: SSH communications

description : SSH traffic within the organization [Private |P Segments only]
Internal SSH Connections
index=zeek sourcetype IN (*conn*) id.resp_p =22 id.resp_h IN (10.0.0.0/8,172.16.0.0/12,192.168.0.0/16) id.orig_h IN (10.0.0.0/8,172.16.0.0/12,192.168.0.0/16) id.orig_h=* id.resp_h=* | table _time uid id.orig_h id.orig_p id.resp_h id.resp_p sourcetype orig_bytes resp_bytes conn_state | join uid type=left [ search eventtype=zeek_idx_sourcetype sourcetype IN (*ssh*) id.resp_h IN (10.0.0.0/8,172.16.0.0/12,192.168.0.0/16) id.orig_h IN (10.0.0.0/8,172.16.0.0/12,192.168.0.0/16) id.orig_h=* id.resp_h=* | table _time uid id.orig_h id.resp_h id.resp_p auth_success cipher_alg client server direction host_key_alg host_key kex_alg mac_alg version auth_attempts | table _time uid auth_success auth_attempts direction client server] | eval total_bytes=orig_bytes+ resp_bytes | search total_bytes > 0 | fields - sourcetype id.orig_p id.resp_p total_bytes | fillnull value="-"
SQL
복사

TLS/SSL Insights

Anomalies :: Conn using Expired Certs

description : insights on outgoing connection with expired TLS certificates
Expired Cert used in the TLS outgoing connections
index=zeek sourcetype="*ssl*" validation_status IN ("certificate has expired") NOT dest_ip IN ("8.43.72.97","8.43.72.98") | rex "O=(?<Org>[^,]+)" | eval Org= mvindex(split(mvindex(split(Org,","),0),"\\"),0) | fillnull value=unknown | dedup server_name | iplocation dest_ip | search NOT server_name IN (*apple*) | table server_name dest_ip validation_status issuer Country | fields - issuer | rename server_name as "Domain Name" dest_ip as "IP Address"
SQL
복사

Anomalies :: Conn using Self Signed Certs

description : TLS self signed certificate usage on outgoing traffic
Outgoing Connections [ Non Web Ports ]
index=zeek sourcetype="*ssl*" validation_status IN ("self signed certificate in certificate chain","self signed certificate") id.resp_p!=443 NOT id.resp_h IN (10.0.0.0/8,172.16.0.0/12,192.168.0.0/16) | rex "O=(?<Org>[^,]+)" | eval Org= mvindex(split(mvindex(split(Org,","),0),"\\"),0) | fillnull value=unknown | dedup server_name | iplocation dest_ip | search NOT server_name IN (*apple*) | table server_name dest_ip validation_status issuer Country id.resp_p | fields - issuer | rename server_name as "Domain Name" dest_ip as "IP Address" validation_status as cert_details | sort cert_details
SQL
복사
Internal Connections [ Non Web Ports ]
index=zeek sourcetype="*ssl*" validation_status IN ("self signed certificate in certificate chain","self signed certificate") id.resp_p!=443 id.resp_h IN (10.0.0.0/8,172.16.0.0/12,192.168.0.0/16) | rex "O=(?<Org>[^,]+)" | eval Org= mvindex(split(mvindex(split(Org,","),0),"\\"),0) | fillnull value=unknown | dedup server_name | iplocation dest_ip | search NOT server_name IN (*apple*) | table server_name dest_ip validation_status issuer id.resp_p | fields - issuer | rename server_name as "Domain Name" dest_ip as "IP Address" validation_status as cert_details| sort cert_details
SQL
복사
Outgoing Connections [Web Ports ] - Geolocations (Pie Charts)
index=zeek sourcetype="*ssl*" validation_status IN ("self signed certificate in certificate chain","self signed certificate") id.resp_p=443 | rex "O=(?<Org>[^,]+)" | eval Org= mvindex(split(mvindex(split(Org,","),0),"\\"),0) | fillnull value=unknown | dedup server_name | iplocation dest_ip | search NOT server_name IN (*apple*) | table server_name dest_ip validation_status issuer Country Org id.resp_p | fields - issuer | rename server_name as "Domain Name" dest_ip as "IP Address" | stats count by Country
SQL
복사
Outgoing Connections [Web Ports ]
index=zeek sourcetype="*ssl*" validation_status IN ("self signed certificate in certificate chain","self signed certificate") id.resp_p=443 | rex "O=(?<Org>[^,]+)" | eval Org= mvindex(split(mvindex(split(Org,","),0),"\\"),0) | fillnull value=unknown | dedup server_name | iplocation dest_ip | search NOT server_name IN (*apple*) | table server_name dest_ip validation_status issuer Country id.resp_p | fields - issuer | rename server_name as "Domain Name" dest_ip as "IP Address" validation_status as cert_details | sort cert_details
SQL
복사

Anomalies :: Now Born Cert in traffic

description : insights on the TLS certificate aging (no of days) and the provider details
New Born TLS certificates [age < 30 days]
Let's Encrypt Certs
index=zeek sourcetype="*x509*" | dedup certificate.serial | rex "O=(?<Org>[^,]+)" | eval Org= mvindex(split(mvindex(split(Org,","),0),"\\"),0) | rename certificate.not_valid_after as expiry certificate.not_valid_before as issue | eval c_time = now() | eval days = round((c_time - issue)/86400,0) | search days<30 Org="Let's Encrypt" | convert ctime(issue) | table _time c_time issue expiry days certificate.serial certificate.issuer Org | fields - expiry _time c_time certificate.issuer issue | sort days
SQL
복사
New Born TLS certificates [age < 30 days] Other Certs
index=zeek sourcetype="*x509*" | dedup certificate.serial | rex "O=(?<Org>[^,]+)" | eval Org= mvindex(split(mvindex(split(Org,","),0),"\\"),0) | rename certificate.not_valid_after as expiry certificate.not_valid_before as issue | eval c_time = now() | eval days = round((c_time - issue)/86400,0) | search days<30 Org="Let's Encrypt" | convert ctime(issue) | table _time c_time issue expiry days certificate.serial certificate.issuer Org | fields - expiry _time c_time certificate.issuer issue | sort days
SQL
복사

✓ 다른 [정리] 포스트

베트남 환전 왜 한국에서 하면 안될까? (한국 vs 공항 vs 금은방)
Travel
베트남 환전 왜 한국에서 하면 안될까? (한국 vs 공항 vs 금은방)
Travel
Load more
︎ 더 많은 게시물을 보려면
︎ 작성자가 궁금하면?
 2023. Absolroot all rights reserved.