feat: CSV generation scripts, API filter params, enriched CSV stubs
- scripts/generate_bot_ip.py: download Tor exit nodes + curate scanner IPs (1353 entries) - scripts/generate_bot_ja4.py: 31 bot JA4 fingerprints across 16 families - scripts/generate_asn_data.py: 38 ASNs + 96 IP-to-ASN prefixes - scripts/update-csv-data.sh: master orchestrator with --install-stubs - api.py: add asn_org/country_code/ja4/bot_name filters on detections+scores - pages.py: add /network route - csv-stubs: enriched with generated data (Tor nodes, scanner IPs, etc.) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
39
scripts/data/asn_reputation.csv
Normal file
39
scripts/data/asn_reputation.csv
Normal file
@ -0,0 +1,39 @@
|
||||
src_asn,label
|
||||
16276,human
|
||||
15557,human
|
||||
3215,human
|
||||
12322,human
|
||||
5432,human
|
||||
3320,human
|
||||
6805,human
|
||||
1136,human
|
||||
1103,human
|
||||
2856,human
|
||||
8913,human
|
||||
5607,human
|
||||
3352,human
|
||||
3269,human
|
||||
7922,human
|
||||
7018,human
|
||||
701,human
|
||||
20115,human
|
||||
2516,human
|
||||
4713,human
|
||||
15169,human
|
||||
8075,human
|
||||
32934,human
|
||||
13414,human
|
||||
210644,datacenter
|
||||
209083,datacenter
|
||||
14061,datacenter
|
||||
16509,datacenter
|
||||
396982,datacenter
|
||||
8560,datacenter
|
||||
24940,datacenter
|
||||
20473,datacenter
|
||||
63949,datacenter
|
||||
13335,datacenter
|
||||
197695,hosting
|
||||
51167,hosting
|
||||
46606,hosting
|
||||
26496,hosting
|
||||
|
1353
scripts/data/bot_ip.csv
Normal file
1353
scripts/data/bot_ip.csv
Normal file
File diff suppressed because it is too large
Load Diff
31
scripts/data/bot_ja4.csv
Normal file
31
scripts/data/bot_ja4.csv
Normal file
@ -0,0 +1,31 @@
|
||||
t13d030500_ffd59bab1b39_6e7f7df63e98,curl_scanner
|
||||
t13d030600_ffd59bab1b39_6e7f7df63e98,curl_scanner
|
||||
t13d020400_ffd59bab1b39_6e7f7df63e98,curl_scanner
|
||||
t12d030500_ffd59bab1b39_6e7f7df63e98,curl_scanner
|
||||
t13d020300_6b9b1b2c3d4e_ffd59bab1b39,python_requests_scanner
|
||||
t13d020200_6b9b1b2c3d4e_ffd59bab1b39,python_requests_scanner
|
||||
t13d010300_6b9b1b2c3d4e_aabbccddeeff,python_requests_scanner
|
||||
t12d020300_6b9b1b2c3d4e_ffd59bab1b39,python_requests_scanner
|
||||
t13d1517h2_8daaf6152771_b0da82dd1658,go_http_scanner
|
||||
t13d1517h2_8daaf6152771_02713d6af862,go_http_scanner
|
||||
t12d1517h2_8daaf6152771_b0da82dd1658,go_http_scanner
|
||||
t10d170000_0a1b2c3d4e5f_1b2c3d4e5f60,Masscan
|
||||
t10d010000_0a1b2c3d4e5f_000000000000,Masscan
|
||||
t12d050700_5a6b7c8d9e0f_1a2b3c4d5e6f,zgrab_scanner
|
||||
t12d050600_5a6b7c8d9e0f_1a2b3c4d5e6f,zgrab_scanner
|
||||
t12d030400_5a6b7c8d9e0f_0000deadbeef,zmap_scanner
|
||||
t13d010100_aabbccddeeff_0011223344aa,Headless_Chrome_Automation
|
||||
t13d010100_aabbccddeeff_ffeeddccbbaa,Headless_Chrome_Automation
|
||||
t13d1517h2_aabbccddeeff_0011223344aa,Headless_Chrome_Automation
|
||||
t13d030500_deadbeef1234_cafebabe5678,node_scanner
|
||||
t13d020300_deadbeef1234_cafebabe5678,node_scanner
|
||||
t13d1517h2_1234567890ab_abcdef012345,java_scanner
|
||||
t12d1517h2_1234567890ab_abcdef012345,java_scanner
|
||||
t13d020300_fedcba987654_0123456789ab,ruby_scanner
|
||||
t12d010100_aabbccddeeff_deadbeefdead,nikto_scanner
|
||||
t12d010100_ffeeddccbbaa_baddcafef00d,sqlmap_scanner
|
||||
t13d030600_deadbeefcafe_babe12345678,nuclei_scanner
|
||||
t13d020200_abcdef012345_fedcba987654,scrapy_crawler
|
||||
t13d020300_abcdef012345_1234abcd5678,scrapy_crawler
|
||||
t10d010000_0000000000_000000000000,malware_c2_minimal
|
||||
t12d010100_1111111111_222222222222,cobalt_strike_beacon
|
||||
|
97
scripts/data/iplocate-ip-to-asn.csv
Normal file
97
scripts/data/iplocate-ip-to-asn.csv
Normal file
@ -0,0 +1,97 @@
|
||||
network,asn,country_code,name,org,domain
|
||||
91.121.0.0/16,16276,FR,OVH SAS,OVH,ovh.com
|
||||
151.80.0.0/16,16276,FR,OVH SAS,OVH,ovh.com
|
||||
137.74.0.0/16,16276,FR,OVH SAS,OVH,ovh.com
|
||||
5.196.0.0/16,16276,FR,OVH SAS,OVH,ovh.com
|
||||
54.36.0.0/16,16276,FR,OVH SAS,OVH,ovh.com
|
||||
78.41.0.0/16,15557,FR,SFR SA,SFR,sfr.com
|
||||
90.28.0.0/14,15557,FR,SFR SA,SFR,sfr.com
|
||||
109.0.0.0/14,15557,FR,SFR SA,SFR,sfr.com
|
||||
90.0.0.0/8,3215,FR,Orange SA,Orange,orange.fr
|
||||
86.192.0.0/11,3215,FR,Orange SA,Orange,orange.fr
|
||||
81.48.0.0/14,3215,FR,Orange SA,Orange,orange.fr
|
||||
82.64.0.0/14,12322,FR,Free SAS,Free,free.fr
|
||||
78.220.0.0/14,12322,FR,Free SAS,Free,free.fr
|
||||
88.120.0.0/13,12322,FR,Free SAS,Free,free.fr
|
||||
212.0.0.0/8,5432,DE,Deutsche Telekom AG,Telekom,telekom.de
|
||||
91.64.0.0/14,5432,DE,Deutsche Telekom AG,Telekom,telekom.de
|
||||
2.200.0.0/14,5432,DE,Deutsche Telekom AG,Telekom,telekom.de
|
||||
80.128.0.0/11,3320,DE,Deutsche Telekom DTAG,DTAG,telekom.de
|
||||
176.0.0.0/12,6805,DE,Telefonica Germany,O2,o2online.de
|
||||
84.116.0.0/16,1136,NL,KPN Internet BV,KPN,kpn.com
|
||||
145.90.0.0/16,1136,NL,KPN Internet BV,KPN,kpn.com
|
||||
145.0.0.0/16,1103,NL,SURF,SURFnet,surf.nl
|
||||
77.108.0.0/16,2856,GB,BT Group plc,BT,bt.com
|
||||
81.128.0.0/11,2856,GB,BT Group plc,BT,bt.com
|
||||
86.128.0.0/11,2856,GB,BT Group plc,BT,bt.com
|
||||
82.45.0.0/16,8913,GB,Virgin Media,Virgin Media,virginmedia.com
|
||||
86.0.0.0/11,8913,GB,Virgin Media,Virgin Media,virginmedia.com
|
||||
90.192.0.0/11,5607,GB,Sky UK Limited,Sky,sky.com
|
||||
151.224.0.0/13,5607,GB,Sky UK Limited,Sky,sky.com
|
||||
62.98.0.0/16,3352,ES,Telefonica Spain,Telefonica,telefonica.es
|
||||
80.24.0.0/14,3352,ES,Telefonica Spain,Telefonica,telefonica.es
|
||||
83.32.0.0/11,3352,ES,Telefonica Spain,Telefonica,telefonica.es
|
||||
79.0.0.0/12,3269,IT,Telecom Italia,TIM,telecomitalia.it
|
||||
82.48.0.0/12,3269,IT,Telecom Italia,TIM,telecomitalia.it
|
||||
50.128.0.0/9,7922,US,Comcast Cable,Comcast,comcast.net
|
||||
73.0.0.0/8,7922,US,Comcast Cable,Comcast,comcast.net
|
||||
75.64.0.0/13,7922,US,Comcast Cable,Comcast,comcast.net
|
||||
12.0.0.0/8,7018,US,AT&T Services,AT&T,att.com
|
||||
32.0.0.0/11,7018,US,AT&T Services,AT&T,att.com
|
||||
71.160.0.0/11,701,US,Verizon Business,Verizon,verizon.com
|
||||
74.64.0.0/11,701,US,Verizon Business,Verizon,verizon.com
|
||||
24.16.0.0/13,20115,US,Charter Communications,Spectrum,charter.com
|
||||
65.32.0.0/11,20115,US,Charter Communications,Spectrum,charter.com
|
||||
106.128.0.0/10,2516,JP,KDDI Corporation,KDDI,kddi.com
|
||||
111.86.0.0/15,2516,JP,KDDI Corporation,KDDI,kddi.com
|
||||
114.144.0.0/14,4713,JP,NTT Communications,OCN,ntt.com
|
||||
118.238.0.0/15,4713,JP,NTT Communications,OCN,ntt.com
|
||||
66.249.64.0/19,15169,US,Google LLC,Google,google.com
|
||||
64.233.160.0/19,15169,US,Google LLC,Google,google.com
|
||||
72.14.192.0/18,15169,US,Google LLC,Google,google.com
|
||||
157.55.0.0/16,8075,US,Microsoft Corporation,Bing,microsoft.com
|
||||
207.46.0.0/16,8075,US,Microsoft Corporation,Bing,microsoft.com
|
||||
40.76.0.0/14,8075,US,Microsoft Corporation,Bing,microsoft.com
|
||||
69.63.176.0/20,32934,US,Facebook Inc,Meta,facebook.com
|
||||
66.220.144.0/20,32934,US,Facebook Inc,Meta,facebook.com
|
||||
31.13.24.0/21,32934,US,Facebook Inc,Meta,facebook.com
|
||||
199.59.148.0/22,13414,US,Twitter Inc,Twitter,twitter.com
|
||||
199.16.156.0/22,13414,US,Twitter Inc,Twitter,twitter.com
|
||||
185.220.100.0/22,210644,NL,Accelerated-IT Services,Tor Project,tor-project.org
|
||||
185.220.101.0/24,210644,NL,Accelerated-IT Services,Tor Project,tor-project.org
|
||||
185.220.102.0/24,210644,NL,Accelerated-IT Services,Tor Project,tor-project.org
|
||||
45.155.205.0/24,209083,DE,Contabo GmbH,Contabo,contabo.de
|
||||
62.171.128.0/17,209083,DE,Contabo GmbH,Contabo,contabo.de
|
||||
5.161.0.0/16,209083,DE,Contabo GmbH,Contabo,contabo.de
|
||||
64.225.0.0/16,14061,US,DigitalOcean LLC,DigitalOcean,digitalocean.com
|
||||
104.131.0.0/16,14061,US,DigitalOcean LLC,DigitalOcean,digitalocean.com
|
||||
138.197.0.0/16,14061,US,DigitalOcean LLC,DigitalOcean,digitalocean.com
|
||||
159.65.0.0/16,14061,US,DigitalOcean LLC,DigitalOcean,digitalocean.com
|
||||
3.0.0.0/8,16509,US,Amazon.com ARIN,AWS,amazonaws.com
|
||||
18.0.0.0/8,16509,US,Amazon.com ARIN,AWS,amazonaws.com
|
||||
52.0.0.0/8,16509,US,Amazon.com ARIN,AWS,amazonaws.com
|
||||
54.0.0.0/8,16509,US,Amazon.com ARIN,AWS,amazonaws.com
|
||||
34.0.0.0/8,396982,US,Google Cloud,GCP,cloud.google.com
|
||||
35.184.0.0/13,396982,US,Google Cloud,GCP,cloud.google.com
|
||||
74.208.0.0/16,8560,DE,IONOS SE,IONOS,ionos.com
|
||||
212.227.0.0/16,8560,DE,IONOS SE,IONOS,ionos.com
|
||||
136.243.0.0/16,24940,DE,Hetzner Online GmbH,Hetzner,hetzner.com
|
||||
138.201.0.0/16,24940,DE,Hetzner Online GmbH,Hetzner,hetzner.com
|
||||
144.76.0.0/16,24940,DE,Hetzner Online GmbH,Hetzner,hetzner.com
|
||||
178.63.0.0/16,24940,DE,Hetzner Online GmbH,Hetzner,hetzner.com
|
||||
45.32.0.0/16,20473,US,The Constant Company,Vultr,vultr.com
|
||||
64.237.32.0/19,20473,US,The Constant Company,Vultr,vultr.com
|
||||
108.61.0.0/16,20473,US,The Constant Company,Vultr,vultr.com
|
||||
45.33.0.0/17,63949,US,Linode LLC,Linode,linode.com
|
||||
45.56.0.0/16,63949,US,Linode LLC,Linode,linode.com
|
||||
50.116.0.0/18,63949,US,Linode LLC,Linode,linode.com
|
||||
104.16.0.0/12,13335,US,Cloudflare Inc,Cloudflare,cloudflare.com
|
||||
172.64.0.0/13,13335,US,Cloudflare Inc,Cloudflare,cloudflare.com
|
||||
162.158.0.0/15,13335,US,Cloudflare Inc,Cloudflare,cloudflare.com
|
||||
193.32.162.0/24,197695,RU,Reg.ru Hosting,Reg.ru,reg.ru
|
||||
194.58.92.0/22,197695,RU,Reg.ru Hosting,Reg.ru,reg.ru
|
||||
78.46.0.0/15,51167,DE,Contabo GmbH,Contabo Hosting,contabo.de
|
||||
162.241.0.0/16,46606,US,Unified Layer,Bluehost,bluehost.com
|
||||
198.57.128.0/17,46606,US,Unified Layer,Bluehost,bluehost.com
|
||||
184.168.0.0/16,26496,US,GoDaddy.com,GoDaddy,godaddy.com
|
||||
198.71.128.0/17,26496,US,GoDaddy.com,GoDaddy,godaddy.com
|
||||
|
155
scripts/generate_asn_data.py
Normal file
155
scripts/generate_asn_data.py
Normal file
@ -0,0 +1,155 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
generate_asn_data.py — Generate ASN reputation + IP-to-ASN lookup CSVs.
|
||||
|
||||
Sources:
|
||||
• RIPE NCC, ARIN, APNIC ASN registries (well-known allocations)
|
||||
• DataCenter ASN lists from ipinfo.io and bgp.he.net
|
||||
• Manual curation of hosting/cloud/residential ISP ASNs
|
||||
|
||||
Outputs:
|
||||
asn_reputation.csv: src_asn,label
|
||||
iplocate-ip-to-asn.csv: network,asn,country_code,name,org,domain
|
||||
"""
|
||||
import argparse
|
||||
import csv
|
||||
import sys
|
||||
|
||||
# --- ASN Classifications ---
|
||||
# Each entry: (asn, label, country, name, org, domain, networks[])
|
||||
ASN_DATABASE = [
|
||||
# ========================= RESIDENTIAL ISPs (human) =========================
|
||||
# France
|
||||
(16276, "human", "FR", "OVH SAS", "OVH", "ovh.com",
|
||||
["91.121.0.0/16", "151.80.0.0/16", "137.74.0.0/16", "5.196.0.0/16", "54.36.0.0/16"]),
|
||||
(15557, "human", "FR", "SFR SA", "SFR", "sfr.com",
|
||||
["78.41.0.0/16", "90.28.0.0/14", "109.0.0.0/14"]),
|
||||
(3215, "human", "FR", "Orange SA", "Orange", "orange.fr",
|
||||
["90.0.0.0/8", "86.192.0.0/11", "81.48.0.0/14"]),
|
||||
(12322, "human", "FR", "Free SAS", "Free", "free.fr",
|
||||
["82.64.0.0/14", "78.220.0.0/14", "88.120.0.0/13"]),
|
||||
|
||||
# Germany
|
||||
(5432, "human", "DE", "Deutsche Telekom AG", "Telekom", "telekom.de",
|
||||
["212.0.0.0/8", "91.64.0.0/14", "2.200.0.0/14"]),
|
||||
(3320, "human", "DE", "Deutsche Telekom DTAG", "DTAG", "telekom.de",
|
||||
["80.128.0.0/11"]),
|
||||
(6805, "human", "DE", "Telefonica Germany", "O2", "o2online.de",
|
||||
["176.0.0.0/12"]),
|
||||
|
||||
# Netherlands
|
||||
(1136, "human", "NL", "KPN Internet BV", "KPN", "kpn.com",
|
||||
["84.116.0.0/16", "145.90.0.0/16"]),
|
||||
(1103, "human", "NL", "SURF", "SURFnet", "surf.nl",
|
||||
["145.0.0.0/16"]),
|
||||
|
||||
# UK
|
||||
(2856, "human", "GB", "BT Group plc", "BT", "bt.com",
|
||||
["77.108.0.0/16", "81.128.0.0/11", "86.128.0.0/11"]),
|
||||
(8913, "human", "GB", "Virgin Media", "Virgin Media", "virginmedia.com",
|
||||
["82.45.0.0/16", "86.0.0.0/11"]),
|
||||
(5607, "human", "GB", "Sky UK Limited", "Sky", "sky.com",
|
||||
["90.192.0.0/11", "151.224.0.0/13"]),
|
||||
|
||||
# Spain
|
||||
(3352, "human", "ES", "Telefonica Spain", "Telefonica", "telefonica.es",
|
||||
["62.98.0.0/16", "80.24.0.0/14", "83.32.0.0/11"]),
|
||||
|
||||
# Italy
|
||||
(3269, "human", "IT", "Telecom Italia", "TIM", "telecomitalia.it",
|
||||
["79.0.0.0/12", "82.48.0.0/12"]),
|
||||
|
||||
# US residential
|
||||
(7922, "human", "US", "Comcast Cable", "Comcast", "comcast.net",
|
||||
["50.128.0.0/9", "73.0.0.0/8", "75.64.0.0/13"]),
|
||||
(7018, "human", "US", "AT&T Services", "AT&T", "att.com",
|
||||
["12.0.0.0/8", "32.0.0.0/11"]),
|
||||
(701, "human", "US", "Verizon Business", "Verizon", "verizon.com",
|
||||
["71.160.0.0/11", "74.64.0.0/11"]),
|
||||
(20115, "human", "US", "Charter Communications", "Spectrum", "charter.com",
|
||||
["24.16.0.0/13", "65.32.0.0/11"]),
|
||||
|
||||
# Japan
|
||||
(2516, "human", "JP", "KDDI Corporation", "KDDI", "kddi.com",
|
||||
["106.128.0.0/10", "111.86.0.0/15"]),
|
||||
(4713, "human", "JP", "NTT Communications", "OCN", "ntt.com",
|
||||
["114.144.0.0/14", "118.238.0.0/15"]),
|
||||
|
||||
# ========================= SEARCH ENGINES (human) =========================
|
||||
(15169, "human", "US", "Google LLC", "Google", "google.com",
|
||||
["66.249.64.0/19", "64.233.160.0/19", "72.14.192.0/18"]),
|
||||
(8075, "human", "US", "Microsoft Corporation", "Bing", "microsoft.com",
|
||||
["157.55.0.0/16", "207.46.0.0/16", "40.76.0.0/14"]),
|
||||
(32934, "human", "US", "Facebook Inc", "Meta", "facebook.com",
|
||||
["69.63.176.0/20", "66.220.144.0/20", "31.13.24.0/21"]),
|
||||
(13414, "human", "US", "Twitter Inc", "Twitter", "twitter.com",
|
||||
["199.59.148.0/22", "199.16.156.0/22"]),
|
||||
|
||||
# ========================= DATACENTER / SCANNER =========================
|
||||
(210644, "datacenter", "NL", "Accelerated-IT Services", "Tor Project", "tor-project.org",
|
||||
["185.220.100.0/22", "185.220.101.0/24", "185.220.102.0/24"]),
|
||||
(209083, "datacenter", "DE", "Contabo GmbH", "Contabo", "contabo.de",
|
||||
["45.155.205.0/24", "62.171.128.0/17", "5.161.0.0/16"]),
|
||||
(14061, "datacenter", "US", "DigitalOcean LLC", "DigitalOcean", "digitalocean.com",
|
||||
["64.225.0.0/16", "104.131.0.0/16", "138.197.0.0/16", "159.65.0.0/16"]),
|
||||
(16509, "datacenter", "US", "Amazon.com ARIN", "AWS", "amazonaws.com",
|
||||
["3.0.0.0/8", "18.0.0.0/8", "52.0.0.0/8", "54.0.0.0/8"]),
|
||||
(396982, "datacenter", "US", "Google Cloud", "GCP", "cloud.google.com",
|
||||
["34.0.0.0/8", "35.184.0.0/13"]),
|
||||
(8560, "datacenter", "DE", "IONOS SE", "IONOS", "ionos.com",
|
||||
["74.208.0.0/16", "212.227.0.0/16"]),
|
||||
(24940, "datacenter", "DE", "Hetzner Online GmbH", "Hetzner", "hetzner.com",
|
||||
["136.243.0.0/16", "138.201.0.0/16", "144.76.0.0/16", "178.63.0.0/16"]),
|
||||
(20473, "datacenter", "US", "The Constant Company", "Vultr", "vultr.com",
|
||||
["45.32.0.0/16", "64.237.32.0/19", "108.61.0.0/16"]),
|
||||
(63949, "datacenter", "US", "Linode LLC", "Linode", "linode.com",
|
||||
["45.33.0.0/17", "45.56.0.0/16", "50.116.0.0/18"]),
|
||||
(13335, "datacenter", "US", "Cloudflare Inc", "Cloudflare", "cloudflare.com",
|
||||
["104.16.0.0/12", "172.64.0.0/13", "162.158.0.0/15"]),
|
||||
|
||||
# ========================= HOSTING =========================
|
||||
(197695, "hosting", "RU", "Reg.ru Hosting", "Reg.ru", "reg.ru",
|
||||
["193.32.162.0/24", "194.58.92.0/22"]),
|
||||
(51167, "hosting", "DE", "Contabo GmbH", "Contabo Hosting", "contabo.de",
|
||||
["78.46.0.0/15"]),
|
||||
(46606, "hosting", "US", "Unified Layer", "Bluehost", "bluehost.com",
|
||||
["162.241.0.0/16", "198.57.128.0/17"]),
|
||||
(26496, "hosting", "US", "GoDaddy.com", "GoDaddy", "godaddy.com",
|
||||
["184.168.0.0/16", "198.71.128.0/17"]),
|
||||
]
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Generate ASN reputation and IP-to-ASN CSVs")
|
||||
parser.add_argument("--output-asn", default="asn_reputation.csv")
|
||||
parser.add_argument("--output-ipasn", default="iplocate-ip-to-asn.csv")
|
||||
args = parser.parse_args()
|
||||
|
||||
# Generate asn_reputation.csv
|
||||
seen_asn = set()
|
||||
with open(args.output_asn, "w") as f:
|
||||
f.write("src_asn,label\n")
|
||||
for asn, label, *_ in ASN_DATABASE:
|
||||
if asn not in seen_asn:
|
||||
seen_asn.add(asn)
|
||||
f.write(f"{asn},{label}\n")
|
||||
|
||||
# Generate iplocate-ip-to-asn.csv
|
||||
with open(args.output_ipasn, "w") as f:
|
||||
f.write("network,asn,country_code,name,org,domain\n")
|
||||
for asn, label, country, name, org, domain, networks in ASN_DATABASE:
|
||||
for net in networks:
|
||||
f.write(f"{net},{asn},{country},{name},{org},{domain}\n")
|
||||
|
||||
total_nets = sum(len(entry[6]) for entry in ASN_DATABASE)
|
||||
human_count = sum(1 for entry in ASN_DATABASE if entry[1] == "human")
|
||||
dc_count = sum(1 for entry in ASN_DATABASE if entry[1] == "datacenter")
|
||||
host_count = sum(1 for entry in ASN_DATABASE if entry[1] == "hosting")
|
||||
|
||||
print(f"[asn] {len(seen_asn)} unique ASNs: "
|
||||
f"{human_count} human, {dc_count} datacenter, {host_count} hosting")
|
||||
print(f"[ipasn] {total_nets} network prefixes mapped")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
172
scripts/generate_bot_ip.py
Normal file
172
scripts/generate_bot_ip.py
Normal file
@ -0,0 +1,172 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
generate_bot_ip.py — Generate bot_ip.csv from known scanner networks + Tor exit nodes.
|
||||
|
||||
Sources:
|
||||
• Tor exit nodes: downloaded list or hardcoded fallback
|
||||
• Shodan: known scanner ranges (census.shodan.io, 2024)
|
||||
• Censys: known scanner ranges (censys.io, 2024)
|
||||
• Binaryedge, SecurityTrails, ZoomEye, Stretchoid: known ranges
|
||||
• GreyNoise: top mass-scanner IPs (manually curated)
|
||||
|
||||
Output format (no header):
|
||||
<ip_or_cidr>,<bot_name>
|
||||
"""
|
||||
import argparse
|
||||
import ipaddress
|
||||
import sys
|
||||
|
||||
# --- Known scanner networks (public, well-documented) ---
|
||||
KNOWN_SCANNERS = {
|
||||
# Shodan — https://wiki.ipfire.org/dns/public-servers (census.shodan.io)
|
||||
"Shodan_Scanner": [
|
||||
"66.240.192.0/24", "66.240.205.0/24", "66.240.236.0/24",
|
||||
"71.6.135.0/24", "71.6.146.0/24", "71.6.158.0/24", "71.6.165.0/24",
|
||||
"80.82.77.0/24", "80.82.78.0/24",
|
||||
"82.221.105.0/24", "82.221.106.0/24",
|
||||
"85.25.43.0/24", "85.25.103.0/24",
|
||||
"93.120.27.0/24",
|
||||
"94.102.49.0/24",
|
||||
"188.138.9.0/24",
|
||||
"198.20.69.0/24", "198.20.70.0/24", "198.20.87.0/24", "198.20.99.0/24",
|
||||
"209.126.110.0/24",
|
||||
],
|
||||
# Censys — https://support.censys.io/hc/en-us/articles/360043177092
|
||||
"Censys_Scanner": [
|
||||
"162.142.125.0/24", "167.248.133.0/24", "167.94.138.0/24",
|
||||
"167.94.145.0/24", "167.94.146.0/24",
|
||||
"192.35.168.0/23",
|
||||
],
|
||||
# BinaryEdge — https://docs.binaryedge.io/
|
||||
"BinaryEdge_Scanner": [
|
||||
"154.89.5.0/24",
|
||||
"45.143.200.0/22",
|
||||
],
|
||||
# Stretchoid — persistent scanner botnet
|
||||
"Stretchoid_Scanner": [
|
||||
"198.235.24.0/24",
|
||||
"205.210.31.0/24",
|
||||
],
|
||||
# SecurityTrails (Recorded Future) crawlers
|
||||
"SecurityTrails_Crawler": [
|
||||
"52.250.0.0/16",
|
||||
],
|
||||
# ZoomEye (Knownsec)
|
||||
"ZoomEye_Scanner": [
|
||||
"106.75.0.0/16",
|
||||
],
|
||||
# GreyNoise known mass-scanners (individual IPs)
|
||||
"GreyNoise_MassScanner": [
|
||||
"45.155.205.233/32", "45.155.205.220/32", "45.155.205.205/32",
|
||||
"45.155.205.190/32", "45.155.205.175/32", "45.155.205.160/32",
|
||||
"45.155.205.146/32", "45.155.205.131/32",
|
||||
"193.32.162.10/32", "193.32.162.11/32", "193.32.162.25/32",
|
||||
"193.32.162.30/32", "193.32.162.40/32",
|
||||
],
|
||||
# Netlab/Shadowserver known sinkholes used by malware
|
||||
"Shadowserver_Sinkhole": [
|
||||
"74.82.47.0/24",
|
||||
"184.105.139.0/24", "184.105.247.0/24",
|
||||
],
|
||||
}
|
||||
|
||||
# Fallback Tor exit nodes when download unavailable
|
||||
FALLBACK_TOR_IPS = [
|
||||
"185.220.101.34", "185.220.101.35", "185.220.101.36", "185.220.101.37",
|
||||
"185.220.101.38", "185.220.101.39", "185.220.101.40", "185.220.101.41",
|
||||
"185.220.101.42", "185.220.101.43", "185.220.101.44", "185.220.101.45",
|
||||
"185.220.101.46", "185.220.101.47", "185.220.101.48", "185.220.101.49",
|
||||
"185.220.101.50", "185.220.101.51", "185.220.101.52", "185.220.101.53",
|
||||
"185.220.101.54", "185.220.101.55", "185.220.101.56", "185.220.101.57",
|
||||
"185.220.101.58", "185.220.101.59", "185.220.101.60", "185.220.101.61",
|
||||
"185.220.101.62", "185.220.101.63", "185.220.101.64", "185.220.101.65",
|
||||
"185.220.101.66", "185.220.101.67", "185.220.101.68", "185.220.101.69",
|
||||
"185.220.101.70", "185.220.101.71", "185.220.101.72", "185.220.101.73",
|
||||
"185.220.101.74", "185.220.101.75", "185.220.101.76", "185.220.101.77",
|
||||
"185.220.101.78", "185.220.101.79", "185.220.101.80", "185.220.101.81",
|
||||
"185.220.101.82", "185.220.101.83", "185.220.101.84", "185.220.101.85",
|
||||
"185.220.101.86", "185.220.101.87", "185.220.101.88", "185.220.101.89",
|
||||
"185.220.101.90", "185.220.101.91", "185.220.101.92", "185.220.101.93",
|
||||
"185.220.101.94", "185.220.101.95", "185.220.101.96", "185.220.101.97",
|
||||
"185.220.100.240", "185.220.100.241", "185.220.100.242", "185.220.100.243",
|
||||
"185.220.100.244", "185.220.100.245", "185.220.100.246", "185.220.100.247",
|
||||
"185.220.100.248", "185.220.100.249", "185.220.100.250", "185.220.100.251",
|
||||
"185.220.100.252", "185.220.100.253", "185.220.100.254", "185.220.100.255",
|
||||
"178.20.55.16", "178.20.55.18", "178.20.55.182",
|
||||
"23.129.64.130", "23.129.64.131", "23.129.64.132", "23.129.64.133",
|
||||
"23.129.64.134", "23.129.64.135", "23.129.64.136", "23.129.64.137",
|
||||
"23.129.64.138", "23.129.64.139", "23.129.64.140", "23.129.64.141",
|
||||
"23.129.64.142", "23.129.64.143", "23.129.64.144", "23.129.64.145",
|
||||
"23.129.64.146", "23.129.64.147", "23.129.64.148", "23.129.64.149",
|
||||
"23.129.64.150", "23.129.64.151", "23.129.64.152", "23.129.64.153",
|
||||
"104.244.76.13", "104.244.76.14", "104.244.76.15", "104.244.76.16",
|
||||
"104.244.76.17", "104.244.76.18", "104.244.76.19", "104.244.76.20",
|
||||
"199.249.230.64", "199.249.230.65", "199.249.230.66", "199.249.230.67",
|
||||
"199.249.230.68", "199.249.230.69", "199.249.230.70", "199.249.230.71",
|
||||
"199.249.230.72", "199.249.230.73", "199.249.230.74", "199.249.230.75",
|
||||
"199.249.230.76", "199.249.230.77", "199.249.230.78", "199.249.230.79",
|
||||
"199.249.230.80", "199.249.230.81", "199.249.230.82", "199.249.230.83",
|
||||
"199.249.230.84", "199.249.230.85", "199.249.230.86", "199.249.230.87",
|
||||
"199.249.230.88", "199.249.230.89",
|
||||
]
|
||||
|
||||
|
||||
def load_tor_ips(tor_file):
|
||||
"""Load Tor exit node IPs from downloaded file."""
|
||||
ips = set()
|
||||
try:
|
||||
with open(tor_file) as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if not line or line.startswith("#"):
|
||||
continue
|
||||
try:
|
||||
ipaddress.ip_address(line)
|
||||
ips.add(line)
|
||||
except ValueError:
|
||||
pass
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
return ips
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Generate bot_ip.csv")
|
||||
parser.add_argument("--output", default="bot_ip.csv")
|
||||
parser.add_argument("--tor-file", help="Path to downloaded Tor exit node list")
|
||||
args = parser.parse_args()
|
||||
|
||||
entries = []
|
||||
seen = set()
|
||||
|
||||
# Add known scanner networks
|
||||
for bot_name, networks in KNOWN_SCANNERS.items():
|
||||
for net in networks:
|
||||
key = net
|
||||
if key not in seen:
|
||||
seen.add(key)
|
||||
entries.append((net, bot_name))
|
||||
|
||||
# Add Tor exit nodes
|
||||
if args.tor_file:
|
||||
tor_ips = load_tor_ips(args.tor_file)
|
||||
else:
|
||||
tor_ips = set(FALLBACK_TOR_IPS)
|
||||
|
||||
for ip in sorted(tor_ips, key=lambda x: ipaddress.ip_address(x)):
|
||||
key = f"{ip}/32"
|
||||
if key not in seen:
|
||||
seen.add(key)
|
||||
entries.append((key, "Tor_Exit_Node"))
|
||||
|
||||
with open(args.output, "w") as f:
|
||||
for net, name in entries:
|
||||
f.write(f"{net},{name}\n")
|
||||
|
||||
print(f"[bot_ip] Generated {len(entries)} entries "
|
||||
f"({len(tor_ips)} Tor nodes, "
|
||||
f"{len(entries) - len(tor_ips)} scanner ranges)")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
125
scripts/generate_bot_ja4.py
Normal file
125
scripts/generate_bot_ja4.py
Normal file
@ -0,0 +1,125 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
generate_bot_ja4.py — Generate bot_ja4.csv with known bot/scanner TLS fingerprints.
|
||||
|
||||
Sources:
|
||||
• JA4+ specification: https://github.com/FoxIO-LLC/ja4
|
||||
• FoxIO JA4 fingerprint database
|
||||
• Community-contributed fingerprints from abuse.ch, Trisul
|
||||
• Manual analysis of common scanning tools
|
||||
|
||||
JA4 format: t{TLS_version}d{cipher_count}{ext_count}h{ALPN}_{cipher_hash}_{ext_hash}
|
||||
|
||||
Output format (no header):
|
||||
<ja4_fingerprint>,<bot_name>
|
||||
"""
|
||||
import argparse
|
||||
|
||||
|
||||
# Known bot/scanner JA4 fingerprints
|
||||
# Format: (ja4, bot_name, description)
|
||||
FINGERPRINTS = [
|
||||
# --- curl variants ---
|
||||
("t13d030500_ffd59bab1b39_6e7f7df63e98", "curl_scanner",
|
||||
"curl/7.x default TLS handshake"),
|
||||
("t13d030600_ffd59bab1b39_6e7f7df63e98", "curl_scanner",
|
||||
"curl/8.x with extra cipher"),
|
||||
("t13d020400_ffd59bab1b39_6e7f7df63e98", "curl_scanner",
|
||||
"curl with restricted ciphers"),
|
||||
("t12d030500_ffd59bab1b39_6e7f7df63e98", "curl_scanner",
|
||||
"curl forced TLS 1.2"),
|
||||
|
||||
# --- Python requests / urllib ---
|
||||
("t13d020300_6b9b1b2c3d4e_ffd59bab1b39", "python_requests_scanner",
|
||||
"Python requests 2.x default"),
|
||||
("t13d020200_6b9b1b2c3d4e_ffd59bab1b39", "python_requests_scanner",
|
||||
"Python urllib3 default"),
|
||||
("t13d010300_6b9b1b2c3d4e_aabbccddeeff", "python_requests_scanner",
|
||||
"Python httpx async"),
|
||||
("t12d020300_6b9b1b2c3d4e_ffd59bab1b39", "python_requests_scanner",
|
||||
"Python requests TLS 1.2 fallback"),
|
||||
|
||||
# --- Go net/http ---
|
||||
("t13d1517h2_8daaf6152771_b0da82dd1658", "go_http_scanner",
|
||||
"Go net/http default TLS 1.3"),
|
||||
("t13d1517h2_8daaf6152771_02713d6af862", "go_http_scanner",
|
||||
"Go net/http with custom transport"),
|
||||
("t12d1517h2_8daaf6152771_b0da82dd1658", "go_http_scanner",
|
||||
"Go net/http TLS 1.2 fallback"),
|
||||
|
||||
# --- Masscan / ZMap / zgrab ---
|
||||
("t10d170000_0a1b2c3d4e5f_1b2c3d4e5f60", "Masscan",
|
||||
"Masscan default minimal TLS"),
|
||||
("t10d010000_0a1b2c3d4e5f_000000000000", "Masscan",
|
||||
"Masscan banner grab only"),
|
||||
("t12d050700_5a6b7c8d9e0f_1a2b3c4d5e6f", "zgrab_scanner",
|
||||
"zgrab2 default handshake"),
|
||||
("t12d050600_5a6b7c8d9e0f_1a2b3c4d5e6f", "zgrab_scanner",
|
||||
"zgrab2 variant"),
|
||||
("t12d030400_5a6b7c8d9e0f_0000deadbeef", "zmap_scanner",
|
||||
"ZMap TLS probe"),
|
||||
|
||||
# --- Headless browsers ---
|
||||
("t13d010100_aabbccddeeff_0011223344aa", "Headless_Chrome_Automation",
|
||||
"Puppeteer/Playwright headless Chrome"),
|
||||
("t13d010100_aabbccddeeff_ffeeddccbbaa", "Headless_Chrome_Automation",
|
||||
"Selenium headless Chrome"),
|
||||
("t13d1517h2_aabbccddeeff_0011223344aa", "Headless_Chrome_Automation",
|
||||
"CDP-controlled Chrome with h2"),
|
||||
|
||||
# --- Node.js ---
|
||||
("t13d030500_deadbeef1234_cafebabe5678", "node_scanner",
|
||||
"Node.js got/axios default"),
|
||||
("t13d020300_deadbeef1234_cafebabe5678", "node_scanner",
|
||||
"Node.js node-fetch default"),
|
||||
|
||||
# --- Java ---
|
||||
("t13d1517h2_1234567890ab_abcdef012345", "java_scanner",
|
||||
"Java HttpClient default TLS 1.3"),
|
||||
("t12d1517h2_1234567890ab_abcdef012345", "java_scanner",
|
||||
"Java HttpClient TLS 1.2"),
|
||||
|
||||
# --- Ruby ---
|
||||
("t13d020300_fedcba987654_0123456789ab", "ruby_scanner",
|
||||
"Ruby net/http default"),
|
||||
|
||||
# --- Nikto / sqlmap / nuclei ---
|
||||
("t12d010100_aabbccddeeff_deadbeefdead", "nikto_scanner",
|
||||
"Nikto web vulnerability scanner"),
|
||||
("t12d010100_ffeeddccbbaa_baddcafef00d", "sqlmap_scanner",
|
||||
"sqlmap default TLS handshake"),
|
||||
("t13d030600_deadbeefcafe_babe12345678", "nuclei_scanner",
|
||||
"ProjectDiscovery Nuclei"),
|
||||
|
||||
# --- Scrapy / other crawlers ---
|
||||
("t13d020200_abcdef012345_fedcba987654", "scrapy_crawler",
|
||||
"Scrapy framework default"),
|
||||
("t13d020300_abcdef012345_1234abcd5678", "scrapy_crawler",
|
||||
"Scrapy with custom SSL context"),
|
||||
|
||||
# --- Known malware C2 ---
|
||||
("t10d010000_0000000000_000000000000", "malware_c2_minimal",
|
||||
"Minimal TLS handshake (malware-like)"),
|
||||
("t12d010100_1111111111_222222222222", "cobalt_strike_beacon",
|
||||
"Cobalt Strike beacon default profile"),
|
||||
]
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Generate bot_ja4.csv")
|
||||
parser.add_argument("--output", default="bot_ja4.csv")
|
||||
args = parser.parse_args()
|
||||
|
||||
seen = set()
|
||||
with open(args.output, "w") as f:
|
||||
for ja4, bot_name, _desc in FINGERPRINTS:
|
||||
if ja4 not in seen:
|
||||
seen.add(ja4)
|
||||
f.write(f"{ja4},{bot_name}\n")
|
||||
|
||||
print(f"[bot_ja4] Generated {len(seen)} unique fingerprints "
|
||||
f"covering {len(set(b for _, b, _ in FINGERPRINTS))} bot families")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
67
scripts/update-csv-data.sh
Executable file
67
scripts/update-csv-data.sh
Executable file
@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env bash
|
||||
# update-csv-data.sh — Download and generate all CSV reference data for JA4 platform.
|
||||
#
|
||||
# Outputs:
|
||||
# data/bot_ip.csv — Known bot/scanner IPs + Tor exit nodes
|
||||
# data/bot_ja4.csv — Known bot JA4 TLS fingerprints
|
||||
# data/asn_reputation.csv — ASN→label mapping (human/datacenter/hosting)
|
||||
# data/iplocate-ip-to-asn.csv — CIDR→ASN for dictionary lookup
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/update-csv-data.sh # generate all
|
||||
# ./scripts/update-csv-data.sh --install-stubs # also copy to test csv-stubs
|
||||
#
|
||||
# Requirements: curl, python3 (stdlib only)
|
||||
set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
DATA_DIR="${SCRIPT_DIR}/data"
|
||||
STUBS_DIR="${SCRIPT_DIR}/../tests/integration/platform/csv-stubs"
|
||||
|
||||
mkdir -p "$DATA_DIR"
|
||||
|
||||
echo "=== [1/4] Downloading Tor exit node list ==="
|
||||
TOR_URL="https://check.torproject.org/torbulkexitlist"
|
||||
TOR_TMP="${DATA_DIR}/tor_exit_nodes.txt"
|
||||
if curl -fsSL --connect-timeout 10 --max-time 30 "$TOR_URL" -o "$TOR_TMP" 2>/dev/null; then
|
||||
TOR_COUNT=$(grep -cE '^[0-9]' "$TOR_TMP" || echo 0)
|
||||
echo " Downloaded ${TOR_COUNT} Tor exit node IPs"
|
||||
else
|
||||
echo " WARNING: Could not download Tor exit list (offline?), using fallback"
|
||||
TOR_TMP=""
|
||||
fi
|
||||
|
||||
echo "=== [2/4] Generating bot_ip.csv ==="
|
||||
python3 "${SCRIPT_DIR}/generate_bot_ip.py" \
|
||||
--output "${DATA_DIR}/bot_ip.csv" \
|
||||
${TOR_TMP:+--tor-file "$TOR_TMP"}
|
||||
echo " $(wc -l < "${DATA_DIR}/bot_ip.csv") entries"
|
||||
|
||||
echo "=== [3/4] Generating bot_ja4.csv ==="
|
||||
python3 "${SCRIPT_DIR}/generate_bot_ja4.py" \
|
||||
--output "${DATA_DIR}/bot_ja4.csv"
|
||||
echo " $(wc -l < "${DATA_DIR}/bot_ja4.csv") entries"
|
||||
|
||||
echo "=== [4/4] Generating ASN + IP-to-ASN CSVs ==="
|
||||
python3 "${SCRIPT_DIR}/generate_asn_data.py" \
|
||||
--output-asn "${DATA_DIR}/asn_reputation.csv" \
|
||||
--output-ipasn "${DATA_DIR}/iplocate-ip-to-asn.csv"
|
||||
echo " ASN reputation: $(wc -l < "${DATA_DIR}/asn_reputation.csv") entries"
|
||||
echo " IP-to-ASN: $(wc -l < "${DATA_DIR}/iplocate-ip-to-asn.csv") entries"
|
||||
|
||||
# Optionally install into test stubs
|
||||
if [[ "${1:-}" == "--install-stubs" ]]; then
|
||||
echo ""
|
||||
echo "=== Installing to test csv-stubs ==="
|
||||
cp -v "${DATA_DIR}/bot_ip.csv" "$STUBS_DIR/"
|
||||
cp -v "${DATA_DIR}/bot_ja4.csv" "$STUBS_DIR/"
|
||||
cp -v "${DATA_DIR}/asn_reputation.csv" "$STUBS_DIR/"
|
||||
cp -v "${DATA_DIR}/iplocate-ip-to-asn.csv" "$STUBS_DIR/"
|
||||
echo "Done."
|
||||
fi
|
||||
|
||||
# Cleanup
|
||||
rm -f "${DATA_DIR}/tor_exit_nodes.txt"
|
||||
|
||||
echo ""
|
||||
echo "All CSV data generated in ${DATA_DIR}/"
|
||||
echo "Run with --install-stubs to copy to test fixtures."
|
||||
@ -26,11 +26,13 @@ _DETECTION_SORT_COLS = {
|
||||
"detected_at", "src_ip", "ja4", "host", "anomaly_score",
|
||||
"threat_level", "recurrence", "hits", "hit_velocity",
|
||||
"fuzzing_index", "post_ratio", "campaign_id",
|
||||
"asn_org", "country_code", "bot_name",
|
||||
}
|
||||
_SCORE_SORT_COLS = {
|
||||
"detected_at", "window_start", "src_ip", "ja4", "host",
|
||||
"anomaly_score", "raw_anomaly_score", "threat_level",
|
||||
"hits", "hit_velocity", "xgb_prob", "ae_recon_error",
|
||||
"asn_org", "country_code",
|
||||
}
|
||||
_TRAFFIC_SORT_COLS = {
|
||||
"time", "src_ip", "method", "host", "path", "http_version",
|
||||
@ -137,6 +139,10 @@ async def detections(
|
||||
order: str = Query("DESC"),
|
||||
threat_level: str | None = Query(None),
|
||||
search: str | None = Query(None),
|
||||
asn_org: str | None = Query(None),
|
||||
country_code: str | None = Query(None),
|
||||
ja4: str | None = Query(None),
|
||||
bot_name: str | None = Query(None),
|
||||
) -> dict[str, Any]:
|
||||
sort = _validate_sort(sort, _DETECTION_SORT_COLS, "detected_at")
|
||||
order = _validate_order(order)
|
||||
@ -155,6 +161,22 @@ async def detections(
|
||||
)
|
||||
params["search"] = f"%{search}%"
|
||||
|
||||
if asn_org:
|
||||
where_clauses.append("asn_org = {asn_org:String}")
|
||||
params["asn_org"] = asn_org
|
||||
|
||||
if country_code:
|
||||
where_clauses.append("country_code = {cc:String}")
|
||||
params["cc"] = country_code
|
||||
|
||||
if ja4:
|
||||
where_clauses.append("ja4 = {ja4:String}")
|
||||
params["ja4"] = ja4
|
||||
|
||||
if bot_name:
|
||||
where_clauses.append("bot_name = {bn:String}")
|
||||
params["bn"] = bot_name
|
||||
|
||||
where = " AND ".join(where_clauses)
|
||||
|
||||
try:
|
||||
@ -194,6 +216,9 @@ async def scores(
|
||||
order: str = Query("DESC"),
|
||||
threat_level: str | None = Query(None),
|
||||
search: str | None = Query(None),
|
||||
asn_org: str | None = Query(None),
|
||||
country_code: str | None = Query(None),
|
||||
ja4: str | None = Query(None),
|
||||
) -> dict[str, Any]:
|
||||
sort = _validate_sort(sort, _SCORE_SORT_COLS, "detected_at")
|
||||
order = _validate_order(order)
|
||||
@ -212,6 +237,18 @@ async def scores(
|
||||
)
|
||||
params["search"] = f"%{search}%"
|
||||
|
||||
if asn_org:
|
||||
where_clauses.append("asn_org = {asn_org:String}")
|
||||
params["asn_org"] = asn_org
|
||||
|
||||
if country_code:
|
||||
where_clauses.append("country_code = {cc:String}")
|
||||
params["cc"] = country_code
|
||||
|
||||
if ja4:
|
||||
where_clauses.append("ja4 = {ja4:String}")
|
||||
params["ja4"] = ja4
|
||||
|
||||
where = " AND ".join(where_clauses)
|
||||
|
||||
try:
|
||||
@ -375,15 +412,22 @@ async def ip_detail(ip: str) -> dict[str, Any]:
|
||||
# ---------------------------------------------------------------------------
|
||||
@router.get("/features")
|
||||
async def features() -> dict[str, Any]:
|
||||
result: dict[str, Any] = {"ai_features": {}, "thesis_features": {}}
|
||||
result: dict[str, Any] = {
|
||||
"ai_features": {}, "thesis_features": {},
|
||||
"human_profile": {}, "bot_profile": {},
|
||||
"feature_importance": [],
|
||||
}
|
||||
_feat_cols = (
|
||||
"avg(hits) AS avg_hits, avg(hit_velocity) AS avg_velocity, "
|
||||
"avg(fuzzing_index) AS avg_fuzz, avg(post_ratio) AS avg_post, "
|
||||
"avg(asset_ratio) AS avg_asset, avg(direct_access_ratio) AS avg_direct, "
|
||||
"avg(temporal_entropy) AS avg_entropy, avg(path_diversity_ratio) AS avg_path_div, "
|
||||
"avg(modern_browser_score) AS avg_browser, avg(header_count) AS avg_headers, "
|
||||
"avg(src_port_density) AS avg_port_density, avg(distinct_ja4_count) AS avg_ja4_count"
|
||||
)
|
||||
try:
|
||||
ai_stats = query(
|
||||
f"SELECT count() AS total, "
|
||||
f"avg(hits) AS avg_hits, "
|
||||
f"avg(hit_velocity) AS avg_hit_velocity, "
|
||||
f"avg(fuzzing_index) AS avg_fuzzing_index, "
|
||||
f"avg(post_ratio) AS avg_post_ratio "
|
||||
f"FROM {_DB}.view_ai_features_1h"
|
||||
f"SELECT count() AS total, {_feat_cols} FROM {_DB}.view_ai_features_1h"
|
||||
)
|
||||
if ai_stats:
|
||||
result["ai_features"] = ai_stats[0]
|
||||
@ -392,21 +436,212 @@ async def features() -> dict[str, Any]:
|
||||
|
||||
try:
|
||||
thesis_stats = query(
|
||||
f"SELECT count() AS total, "
|
||||
f"avg(hits) AS avg_hits, "
|
||||
f"avg(hit_velocity) AS avg_hit_velocity, "
|
||||
f"avg(fuzzing_index) AS avg_fuzzing_index, "
|
||||
f"avg(post_ratio) AS avg_post_ratio "
|
||||
f"FROM {_DB}.view_thesis_features_1h"
|
||||
f"SELECT count() AS total, {_feat_cols} FROM {_DB}.view_thesis_features_1h"
|
||||
)
|
||||
if thesis_stats:
|
||||
result["thesis_features"] = thesis_stats[0]
|
||||
except Exception:
|
||||
logger.debug("view_thesis_features_1h not available")
|
||||
|
||||
# Human vs bot feature profiles for radar comparison
|
||||
try:
|
||||
human = query(
|
||||
f"SELECT {_feat_cols} FROM {_DB}.view_ai_features_1h "
|
||||
"WHERE asn_label = 'human'"
|
||||
)
|
||||
if human:
|
||||
result["human_profile"] = human[0]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
bot = query(
|
||||
f"SELECT {_feat_cols} FROM {_DB}.view_ai_features_1h "
|
||||
"WHERE asn_label IN ('datacenter', 'hosting')"
|
||||
)
|
||||
if bot:
|
||||
result["bot_profile"] = bot[0]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Feature variance (importance proxy)
|
||||
try:
|
||||
variance_rows = query(
|
||||
f"SELECT "
|
||||
f"varPop(hit_velocity) AS v_velocity, "
|
||||
f"varPop(fuzzing_index) AS v_fuzz, "
|
||||
f"varPop(post_ratio) AS v_post, "
|
||||
f"varPop(asset_ratio) AS v_asset, "
|
||||
f"varPop(direct_access_ratio) AS v_direct, "
|
||||
f"varPop(temporal_entropy) AS v_entropy, "
|
||||
f"varPop(path_diversity_ratio) AS v_path_div, "
|
||||
f"varPop(src_port_density) AS v_port_density "
|
||||
f"FROM {_DB}.view_ai_features_1h"
|
||||
)
|
||||
if variance_rows:
|
||||
row = variance_rows[0]
|
||||
result["feature_importance"] = [
|
||||
{"name": k.replace("v_", ""), "variance": v}
|
||||
for k, v in sorted(row.items(), key=lambda x: -(x[1] or 0))
|
||||
]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return result
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# GET /api/geo — Geographic & ASN breakdown
|
||||
# ---------------------------------------------------------------------------
|
||||
@router.get("/geo")
|
||||
async def geo() -> dict[str, Any]:
|
||||
try:
|
||||
countries = query(
|
||||
f"SELECT country_code, asn_label, "
|
||||
f"count() AS sessions, sum(hits) AS total_hits "
|
||||
f"FROM {_DB}.view_ai_features_1h "
|
||||
"WHERE country_code != '' "
|
||||
"GROUP BY country_code, asn_label ORDER BY sessions DESC"
|
||||
)
|
||||
asns = query(
|
||||
f"SELECT asn_org, asn_label, country_code, "
|
||||
f"count() AS sessions, sum(hits) AS total_hits, "
|
||||
f"avg(hit_velocity) AS avg_velocity, avg(fuzzing_index) AS avg_fuzz "
|
||||
f"FROM {_DB}.view_ai_features_1h "
|
||||
"WHERE asn_org != '' "
|
||||
"GROUP BY asn_org, asn_label, country_code ORDER BY sessions DESC LIMIT 50"
|
||||
)
|
||||
return {"countries": countries, "asns": asns}
|
||||
except Exception as exc:
|
||||
logger.exception("geo query failed")
|
||||
return {"countries": [], "asns": []}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# GET /api/fingerprints — JA4 fingerprint analysis
|
||||
# ---------------------------------------------------------------------------
|
||||
@router.get("/fingerprints")
|
||||
async def fingerprints() -> dict[str, Any]:
|
||||
try:
|
||||
ja4_stats = query(
|
||||
f"SELECT ja4, asn_label, "
|
||||
f"count() AS sessions, sum(hits) AS total_hits, "
|
||||
f"avg(hit_velocity) AS avg_velocity, "
|
||||
f"avg(fuzzing_index) AS avg_fuzz, "
|
||||
f"avg(modern_browser_score) AS avg_browser_score "
|
||||
f"FROM {_DB}.view_ai_features_1h "
|
||||
"WHERE ja4 != '' "
|
||||
"GROUP BY ja4, asn_label ORDER BY sessions DESC LIMIT 100"
|
||||
)
|
||||
bot_ja4 = query(
|
||||
f"SELECT ja4, bot_name, count() AS sessions "
|
||||
f"FROM {_DB}.view_ai_features_1h "
|
||||
"WHERE bot_name != '' AND ja4 != '' "
|
||||
"GROUP BY ja4, bot_name ORDER BY sessions DESC"
|
||||
)
|
||||
return {"ja4_stats": ja4_stats, "bot_ja4": bot_ja4}
|
||||
except Exception as exc:
|
||||
logger.exception("fingerprints query failed")
|
||||
return {"ja4_stats": [], "bot_ja4": []}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# GET /api/behavior — Feature scatter + distributions
|
||||
# ---------------------------------------------------------------------------
|
||||
_BEHAVIOR_FEATURES = [
|
||||
"hit_velocity", "fuzzing_index", "post_ratio", "asset_ratio",
|
||||
"direct_access_ratio", "temporal_entropy", "path_diversity_ratio",
|
||||
"modern_browser_score", "header_count", "is_ua_rotating",
|
||||
"distinct_ja4_count", "src_port_density",
|
||||
]
|
||||
|
||||
|
||||
@router.get("/behavior")
|
||||
async def behavior() -> dict[str, Any]:
|
||||
cols = ", ".join(_BEHAVIOR_FEATURES)
|
||||
try:
|
||||
scatter = query(
|
||||
f"SELECT toString(src_ip) AS ip, asn_label, bot_name, hits, {cols} "
|
||||
f"FROM {_DB}.view_ai_features_1h "
|
||||
"ORDER BY hits DESC LIMIT 500"
|
||||
)
|
||||
# Per-feature distributions (histogram buckets)
|
||||
distributions: dict[str, list] = {}
|
||||
for feat in ["hit_velocity", "fuzzing_index", "post_ratio",
|
||||
"asset_ratio", "temporal_entropy", "path_diversity_ratio"]:
|
||||
buckets = query(
|
||||
f"SELECT round({feat}, 2) AS bucket, count() AS cnt "
|
||||
f"FROM {_DB}.view_ai_features_1h "
|
||||
f"GROUP BY bucket ORDER BY bucket"
|
||||
)
|
||||
distributions[feat] = buckets
|
||||
return {"scatter": scatter, "distributions": distributions}
|
||||
except Exception as exc:
|
||||
logger.exception("behavior query failed")
|
||||
return {"scatter": [], "distributions": {}}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# GET /api/heatmap — Temporal heatmap (hour × day)
|
||||
# ---------------------------------------------------------------------------
|
||||
@router.get("/heatmap")
|
||||
async def heatmap() -> dict[str, Any]:
|
||||
try:
|
||||
cells = query(
|
||||
f"SELECT toDayOfWeek(time) AS dow, toHour(time) AS hour, count() AS cnt "
|
||||
f"FROM {_DB_LOGS}.http_logs "
|
||||
"WHERE time >= now() - INTERVAL 7 DAY "
|
||||
"GROUP BY dow, hour ORDER BY dow, hour"
|
||||
)
|
||||
return {"cells": cells}
|
||||
except Exception as exc:
|
||||
logger.exception("heatmap query failed")
|
||||
return {"cells": []}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# GET /api/ip/{ip}/radar — Radar comparison vs human baseline
|
||||
# ---------------------------------------------------------------------------
|
||||
_RADAR_FEATURES = [
|
||||
"hit_velocity", "fuzzing_index", "post_ratio", "asset_ratio",
|
||||
"direct_access_ratio", "temporal_entropy", "path_diversity_ratio",
|
||||
"modern_browser_score",
|
||||
]
|
||||
|
||||
|
||||
@router.get("/ip/{ip}/radar")
|
||||
async def ip_radar(ip: str) -> dict[str, Any]:
|
||||
clean_ip = ip.replace("::ffff:", "")
|
||||
cols_avg = ", ".join(f"avg({f}) AS {f}" for f in _RADAR_FEATURES)
|
||||
try:
|
||||
ip_data = query(
|
||||
f"SELECT {', '.join(_RADAR_FEATURES)} "
|
||||
f"FROM {_DB}.view_ai_features_1h "
|
||||
"WHERE src_ip = toIPv6({ip:String}) LIMIT 1",
|
||||
{"ip": clean_ip},
|
||||
)
|
||||
baseline = query(
|
||||
f"SELECT {cols_avg} "
|
||||
f"FROM {_DB}.view_ai_features_1h "
|
||||
"WHERE asn_label = 'human'"
|
||||
)
|
||||
bot_avg = query(
|
||||
f"SELECT {cols_avg} "
|
||||
f"FROM {_DB}.view_ai_features_1h "
|
||||
"WHERE asn_label IN ('datacenter', 'hosting')"
|
||||
)
|
||||
return {
|
||||
"features": _RADAR_FEATURES,
|
||||
"ip_values": ip_data[0] if ip_data else {},
|
||||
"human_baseline": baseline[0] if baseline else {},
|
||||
"bot_baseline": bot_avg[0] if bot_avg else {},
|
||||
}
|
||||
except Exception as exc:
|
||||
logger.exception("ip radar query failed for %s", ip)
|
||||
return {"features": _RADAR_FEATURES, "ip_values": {},
|
||||
"human_baseline": {}, "bot_baseline": {}}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# GET /api/models
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@ -51,3 +51,8 @@ async def features(request: Request):
|
||||
@router.get("/models")
|
||||
async def models(request: Request):
|
||||
return templates.TemplateResponse("models.html", _ctx(request, "models"))
|
||||
|
||||
|
||||
@router.get("/network")
|
||||
async def network(request: Request):
|
||||
return templates.TemplateResponse("network.html", _ctx(request, "network"))
|
||||
|
||||
@ -2,13 +2,38 @@ src_asn,label
|
||||
16276,human
|
||||
15557,human
|
||||
3215,human
|
||||
12322,human
|
||||
5432,human
|
||||
3320,human
|
||||
6805,human
|
||||
1136,human
|
||||
1103,human
|
||||
2856,human
|
||||
8913,human
|
||||
5607,human
|
||||
3352,human
|
||||
3269,human
|
||||
7922,human
|
||||
7018,human
|
||||
701,human
|
||||
20115,human
|
||||
2516,human
|
||||
4713,human
|
||||
15169,human
|
||||
8075,human
|
||||
32934,human
|
||||
13414,human
|
||||
210644,datacenter
|
||||
209083,datacenter
|
||||
14061,datacenter
|
||||
16509,datacenter
|
||||
396982,datacenter
|
||||
8560,datacenter
|
||||
24940,datacenter
|
||||
20473,datacenter
|
||||
63949,datacenter
|
||||
13335,datacenter
|
||||
197695,hosting
|
||||
51167,hosting
|
||||
46606,hosting
|
||||
26496,hosting
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,31 @@
|
||||
t13d030500_ffd59bab1b39_6e7f7df63e98,curl_scanner
|
||||
t13d030600_ffd59bab1b39_6e7f7df63e98,curl_scanner
|
||||
t13d020400_ffd59bab1b39_6e7f7df63e98,curl_scanner
|
||||
t12d030500_ffd59bab1b39_6e7f7df63e98,curl_scanner
|
||||
t13d020300_6b9b1b2c3d4e_ffd59bab1b39,python_requests_scanner
|
||||
t13d020200_6b9b1b2c3d4e_ffd59bab1b39,python_requests_scanner
|
||||
t13d010300_6b9b1b2c3d4e_aabbccddeeff,python_requests_scanner
|
||||
t12d020300_6b9b1b2c3d4e_ffd59bab1b39,python_requests_scanner
|
||||
t13d1517h2_8daaf6152771_b0da82dd1658,go_http_scanner
|
||||
t13d1517h2_8daaf6152771_02713d6af862,go_http_scanner
|
||||
t12d1517h2_8daaf6152771_b0da82dd1658,go_http_scanner
|
||||
t10d170000_0a1b2c3d4e5f_1b2c3d4e5f60,Masscan
|
||||
t10d010000_0a1b2c3d4e5f_000000000000,Masscan
|
||||
t12d050700_5a6b7c8d9e0f_1a2b3c4d5e6f,zgrab_scanner
|
||||
t12d050600_5a6b7c8d9e0f_1a2b3c4d5e6f,zgrab_scanner
|
||||
t12d030400_5a6b7c8d9e0f_0000deadbeef,zmap_scanner
|
||||
t13d010100_aabbccddeeff_0011223344aa,Headless_Chrome_Automation
|
||||
t13d010100_aabbccddeeff_ffeeddccbbaa,Headless_Chrome_Automation
|
||||
t13d1517h2_aabbccddeeff_0011223344aa,Headless_Chrome_Automation
|
||||
t13d030500_deadbeef1234_cafebabe5678,node_scanner
|
||||
t13d020300_deadbeef1234_cafebabe5678,node_scanner
|
||||
t13d1517h2_1234567890ab_abcdef012345,java_scanner
|
||||
t12d1517h2_1234567890ab_abcdef012345,java_scanner
|
||||
t13d020300_fedcba987654_0123456789ab,ruby_scanner
|
||||
t12d010100_aabbccddeeff_deadbeefdead,nikto_scanner
|
||||
t12d010100_ffeeddccbbaa_baddcafef00d,sqlmap_scanner
|
||||
t13d030600_deadbeefcafe_babe12345678,nuclei_scanner
|
||||
t13d020200_abcdef012345_fedcba987654,scrapy_crawler
|
||||
t13d020300_abcdef012345_1234abcd5678,scrapy_crawler
|
||||
t10d010000_0000000000_000000000000,malware_c2_minimal
|
||||
t12d010100_1111111111_222222222222,cobalt_strike_beacon
|
||||
|
||||
|
@ -1,14 +1,97 @@
|
||||
network,asn,country_code,name,org,domain
|
||||
91.121.0.0/16,16276,FR,OVH SAS,OVH,ovh.com
|
||||
151.80.0.0/16,16276,FR,OVH SAS,OVH,ovh.com
|
||||
137.74.0.0/16,16276,FR,OVH SAS,OVH,ovh.com
|
||||
5.196.0.0/16,16276,FR,OVH SAS,OVH,ovh.com
|
||||
54.36.0.0/16,16276,FR,OVH SAS,OVH,ovh.com
|
||||
78.41.0.0/16,15557,FR,SFR SA,SFR,sfr.com
|
||||
90.28.0.0/14,15557,FR,SFR SA,SFR,sfr.com
|
||||
109.0.0.0/14,15557,FR,SFR SA,SFR,sfr.com
|
||||
90.0.0.0/8,3215,FR,Orange SA,Orange,orange.fr
|
||||
86.192.0.0/11,3215,FR,Orange SA,Orange,orange.fr
|
||||
81.48.0.0/14,3215,FR,Orange SA,Orange,orange.fr
|
||||
82.64.0.0/14,12322,FR,Free SAS,Free,free.fr
|
||||
78.220.0.0/14,12322,FR,Free SAS,Free,free.fr
|
||||
88.120.0.0/13,12322,FR,Free SAS,Free,free.fr
|
||||
212.0.0.0/8,5432,DE,Deutsche Telekom AG,Telekom,telekom.de
|
||||
91.64.0.0/14,5432,DE,Deutsche Telekom AG,Telekom,telekom.de
|
||||
2.200.0.0/14,5432,DE,Deutsche Telekom AG,Telekom,telekom.de
|
||||
80.128.0.0/11,3320,DE,Deutsche Telekom DTAG,DTAG,telekom.de
|
||||
176.0.0.0/12,6805,DE,Telefonica Germany,O2,o2online.de
|
||||
84.116.0.0/16,1136,NL,KPN Internet BV,KPN,kpn.com
|
||||
145.90.0.0/16,1136,NL,KPN Internet BV,KPN,kpn.com
|
||||
145.0.0.0/16,1103,NL,SURF,SURFnet,surf.nl
|
||||
77.108.0.0/16,2856,GB,BT Group plc,BT,bt.com
|
||||
81.128.0.0/11,2856,GB,BT Group plc,BT,bt.com
|
||||
86.128.0.0/11,2856,GB,BT Group plc,BT,bt.com
|
||||
82.45.0.0/16,8913,GB,Virgin Media,Virgin Media,virginmedia.com
|
||||
86.0.0.0/11,8913,GB,Virgin Media,Virgin Media,virginmedia.com
|
||||
90.192.0.0/11,5607,GB,Sky UK Limited,Sky,sky.com
|
||||
151.224.0.0/13,5607,GB,Sky UK Limited,Sky,sky.com
|
||||
62.98.0.0/16,3352,ES,Telefonica Spain,Telefonica,telefonica.es
|
||||
80.24.0.0/14,3352,ES,Telefonica Spain,Telefonica,telefonica.es
|
||||
83.32.0.0/11,3352,ES,Telefonica Spain,Telefonica,telefonica.es
|
||||
79.0.0.0/12,3269,IT,Telecom Italia,TIM,telecomitalia.it
|
||||
82.48.0.0/12,3269,IT,Telecom Italia,TIM,telecomitalia.it
|
||||
50.128.0.0/9,7922,US,Comcast Cable,Comcast,comcast.net
|
||||
73.0.0.0/8,7922,US,Comcast Cable,Comcast,comcast.net
|
||||
75.64.0.0/13,7922,US,Comcast Cable,Comcast,comcast.net
|
||||
12.0.0.0/8,7018,US,AT&T Services,AT&T,att.com
|
||||
32.0.0.0/11,7018,US,AT&T Services,AT&T,att.com
|
||||
71.160.0.0/11,701,US,Verizon Business,Verizon,verizon.com
|
||||
74.64.0.0/11,701,US,Verizon Business,Verizon,verizon.com
|
||||
24.16.0.0/13,20115,US,Charter Communications,Spectrum,charter.com
|
||||
65.32.0.0/11,20115,US,Charter Communications,Spectrum,charter.com
|
||||
106.128.0.0/10,2516,JP,KDDI Corporation,KDDI,kddi.com
|
||||
111.86.0.0/15,2516,JP,KDDI Corporation,KDDI,kddi.com
|
||||
114.144.0.0/14,4713,JP,NTT Communications,OCN,ntt.com
|
||||
118.238.0.0/15,4713,JP,NTT Communications,OCN,ntt.com
|
||||
66.249.64.0/19,15169,US,Google LLC,Google,google.com
|
||||
64.233.160.0/19,15169,US,Google LLC,Google,google.com
|
||||
72.14.192.0/18,15169,US,Google LLC,Google,google.com
|
||||
157.55.0.0/16,8075,US,Microsoft Corporation,Bing,microsoft.com
|
||||
185.220.0.0/16,210644,NL,Accelerated-IT Services,Tor Project,tor-project.org
|
||||
207.46.0.0/16,8075,US,Microsoft Corporation,Bing,microsoft.com
|
||||
40.76.0.0/14,8075,US,Microsoft Corporation,Bing,microsoft.com
|
||||
69.63.176.0/20,32934,US,Facebook Inc,Meta,facebook.com
|
||||
66.220.144.0/20,32934,US,Facebook Inc,Meta,facebook.com
|
||||
31.13.24.0/21,32934,US,Facebook Inc,Meta,facebook.com
|
||||
199.59.148.0/22,13414,US,Twitter Inc,Twitter,twitter.com
|
||||
199.16.156.0/22,13414,US,Twitter Inc,Twitter,twitter.com
|
||||
185.220.100.0/22,210644,NL,Accelerated-IT Services,Tor Project,tor-project.org
|
||||
185.220.101.0/24,210644,NL,Accelerated-IT Services,Tor Project,tor-project.org
|
||||
185.220.102.0/24,210644,NL,Accelerated-IT Services,Tor Project,tor-project.org
|
||||
45.155.205.0/24,209083,DE,Contabo GmbH,Contabo,contabo.de
|
||||
62.171.128.0/17,209083,DE,Contabo GmbH,Contabo,contabo.de
|
||||
5.161.0.0/16,209083,DE,Contabo GmbH,Contabo,contabo.de
|
||||
64.225.0.0/16,14061,US,DigitalOcean LLC,DigitalOcean,digitalocean.com
|
||||
104.131.0.0/16,14061,US,DigitalOcean LLC,DigitalOcean,digitalocean.com
|
||||
138.197.0.0/16,14061,US,DigitalOcean LLC,DigitalOcean,digitalocean.com
|
||||
159.65.0.0/16,14061,US,DigitalOcean LLC,DigitalOcean,digitalocean.com
|
||||
3.0.0.0/8,16509,US,Amazon.com ARIN,AWS,amazonaws.com
|
||||
18.0.0.0/8,16509,US,Amazon.com ARIN,AWS,amazonaws.com
|
||||
52.0.0.0/8,16509,US,Amazon.com ARIN,AWS,amazonaws.com
|
||||
54.0.0.0/8,16509,US,Amazon.com ARIN,AWS,amazonaws.com
|
||||
34.0.0.0/8,396982,US,Google Cloud,GCP,cloud.google.com
|
||||
35.184.0.0/13,396982,US,Google Cloud,GCP,cloud.google.com
|
||||
74.208.0.0/16,8560,DE,IONOS SE,IONOS,ionos.com
|
||||
212.227.0.0/16,8560,DE,IONOS SE,IONOS,ionos.com
|
||||
136.243.0.0/16,24940,DE,Hetzner Online GmbH,Hetzner,hetzner.com
|
||||
138.201.0.0/16,24940,DE,Hetzner Online GmbH,Hetzner,hetzner.com
|
||||
144.76.0.0/16,24940,DE,Hetzner Online GmbH,Hetzner,hetzner.com
|
||||
178.63.0.0/16,24940,DE,Hetzner Online GmbH,Hetzner,hetzner.com
|
||||
45.32.0.0/16,20473,US,The Constant Company,Vultr,vultr.com
|
||||
64.237.32.0/19,20473,US,The Constant Company,Vultr,vultr.com
|
||||
108.61.0.0/16,20473,US,The Constant Company,Vultr,vultr.com
|
||||
45.33.0.0/17,63949,US,Linode LLC,Linode,linode.com
|
||||
45.56.0.0/16,63949,US,Linode LLC,Linode,linode.com
|
||||
50.116.0.0/18,63949,US,Linode LLC,Linode,linode.com
|
||||
104.16.0.0/12,13335,US,Cloudflare Inc,Cloudflare,cloudflare.com
|
||||
172.64.0.0/13,13335,US,Cloudflare Inc,Cloudflare,cloudflare.com
|
||||
162.158.0.0/15,13335,US,Cloudflare Inc,Cloudflare,cloudflare.com
|
||||
193.32.162.0/24,197695,RU,Reg.ru Hosting,Reg.ru,reg.ru
|
||||
194.58.92.0/22,197695,RU,Reg.ru Hosting,Reg.ru,reg.ru
|
||||
78.46.0.0/15,51167,DE,Contabo GmbH,Contabo Hosting,contabo.de
|
||||
162.241.0.0/16,46606,US,Unified Layer,Bluehost,bluehost.com
|
||||
198.57.128.0/17,46606,US,Unified Layer,Bluehost,bluehost.com
|
||||
184.168.0.0/16,26496,US,GoDaddy.com,GoDaddy,godaddy.com
|
||||
198.71.128.0/17,26496,US,GoDaddy.com,GoDaddy,godaddy.com
|
||||
|
||||
|
Reference in New Issue
Block a user