1
1
#!/usr/bin/python
2
2
3
+ """
4
+ Domain analysis script. Performs a DNS query and examines the response to determine if domain is Fast-Flux.
5
+ Also performs a check using multiple methods to determine whether domain name was algorithmically generated (DGA).
6
+ Author: Etienne Stalmans
7
+ Version: 1.0 (2013)
8
+ """
9
+
3
10
import sys ,string
4
11
import getopt
5
12
import dns .resolver
@@ -15,7 +22,7 @@ def main(self,domain,verbose):
15
22
'server_rotate' : 0 ,'server' :[] }
16
23
self .verbose = verbose
17
24
self .domain = domain
18
- self .gl = Geolocate .Geolocate ('GeoIPCity .dat' )
25
+ self .gl = Geolocate .Geolocate ('GeoLiteCity .dat' )
19
26
self .geoIP = pygeoip .GeoIP ('GeoIPASNum.dat' )
20
27
self .urla = URLAnalysis .urlanalyse ()
21
28
self .urla .main ('output_b.dgt' ,'output_m.dgt' )
@@ -69,7 +76,10 @@ def get_asn(self,ip):
69
76
return [asnrec .split (' ' )[0 ],country ]
70
77
71
78
def get_dns (self ,qname ):
72
-
79
+ """
80
+ Perform the DNS query and send the result to the analyzer.
81
+ @param qname the domain to query
82
+ """
73
83
rdtype = dns .rdatatype .A
74
84
rdclass = dns .rdataclass .IN
75
85
request = dns .message .make_query (qname , rdtype , rdclass )
@@ -92,7 +102,13 @@ def get_dns(self,qname):
92
102
self .analyse (response )
93
103
94
104
def analyse (self ,response ):
95
-
105
+ """
106
+ Perform analysis on a DNS query response.
107
+ Checks for Fast-Flux using modified Holz classifier, Jaroslaw/Patrycja classifier.
108
+ Checks for Fast-Flux using Geolocation
109
+ Checks for DGA using multiple statistical classifiers
110
+ @param response from the DNS query
111
+ """
96
112
qname = str (response .question ).split ()[1 ] #Query Name
97
113
98
114
network_ranges = [] #A record responses
@@ -132,14 +148,14 @@ def analyse(self,response):
132
148
if '.' in str (a ): #weak check that it is an IP address returned
133
149
answers .append (str (a ))
134
150
135
- a_ttl = response .answer [0 ].ttl
136
- a_count = len (answers )
151
+ a_ttl = response .answer [0 ].ttl #get the TTL
152
+ a_count = len (answers ) #the number of IP addresses returned
137
153
138
154
if a_count > 0 :
139
155
for ip in answers :
140
156
ip = str (ip )
141
157
st = ip [:ip .rfind ('.' )]
142
- asnd = self .get_asn (st )
158
+ asnd = self .get_asn (ip )
143
159
if asnd :
144
160
asn ,country = asnd
145
161
if country not in countries :
@@ -151,8 +167,8 @@ def analyse(self,response):
151
167
152
168
153
169
diff_count = len (network_ranges ) #number of IP ranges we have
154
- country_count = len (countries )
155
- asn_count = len (asns )
170
+ country_count = len (countries ) #number of countries that host A records
171
+ asn_count = len (asns ) #number of netblocks
156
172
if a_ttl <= 300 :
157
173
ttl_score = 1
158
174
@@ -172,12 +188,11 @@ def analyse(self,response):
172
188
print "Modified Jaroslaw/Patrycja: Score (%i) Classified (%s)" % (jp_score ,"\033 [91mFast-Flux\033 [0m" if jp_score >= 18 else "\033 [92mClean\033 [0m" )
173
189
print "Rule Based: %s" % ("\033 [91mFast-Flux\033 [0m" if diff_count != 0 and a_count >= 2 or ns_count > 1 and ((diff_count >= 1 and asn_count > 1 )or ttl_score == 1 ) else "\033 [92mClean\033 [0m" )
174
190
print "\n ---- Geolocation ----"
175
- self .gl .calcValues (answers )
191
+ self .gl .calcValues (answers ) #do geolocation check
176
192
print "\n ---- URL Analysis ----"
177
- self .urla .checkDomain (qname )
193
+ self .urla .checkDomain (qname ) #do check for DGA
178
194
179
195
def setOpts (argv ):
180
- #defaults
181
196
parser = argparse .ArgumentParser ()
182
197
parser .add_argument ('-d' , '--domain' ,dest = 'domain' ,action = 'store' ,required = True ,
183
198
help = "A Domain to analyse" )
0 commit comments