Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
epsylon
GitHub Repository: epsylon/ufonet
Path: blob/master/core/herd.py
1205 views
1
#!/usr/bin/env python3
2
# -*- coding: utf-8 -*-"
3
"""
4
This file is part of the UFONet project, https://ufonet.03c8.net
5
6
Copyright (c) 2013/2020 | psy <[email protected]>
7
8
You should have received a copy of the GNU General Public License along
9
with UFONet; if not, write to the Free Software Foundation, Inc., 51
10
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
11
"""
12
import socket, threading, logging, datetime, sys, os, re, time
13
from . import zombie
14
15
# zombie tracking class
16
class Herd(object):
17
# basic constructor
18
def __init__(self,ufonet):
19
super(Herd, self).__init__()
20
self.ufonet=ufonet
21
self.reset()
22
self.total_connections=0
23
self.total_hits=0
24
self.total_fails=0
25
self.total_time=0
26
self.total_size=0
27
self.total_connection_fails=0
28
self.living=threading.active_count()
29
self.stats={}
30
self.zombies_ready = []
31
32
# property setup
33
def reset(self):
34
self.active = []
35
self.done = []
36
self.lock = threading.Lock()
37
self.result={}
38
self.connection={}
39
40
# clean temporary statistic files
41
def cleanup(self):
42
try:
43
if os.path.exists("/tmp/ufonet.html"):
44
os.remove("/tmp/ufonet.html")
45
if os.path.exists("/tmp/ufonet.html.tmp"):
46
os.remove("/tmp/ufonet.html.tmp")
47
except:
48
pass
49
50
# got a new one!
51
def new_zombie(self, zombie):
52
self.total_connections+=1
53
if zombie not in self.stats:
54
self.stats[zombie]=[]
55
with self.lock:
56
self.active.append(zombie)
57
58
# give me your report & byebye
59
def kill_zombie(self, zombie, result, connection_failed):
60
with self.lock:
61
try:
62
self.result[zombie]=str(result)
63
self.connection[zombie]=connection_failed
64
self.done.append(zombie)
65
if result[0]==200 :
66
self.total_hits+=1
67
else:
68
self.total_fails+=1
69
if connection_failed:
70
self.total_connection_fails+=1
71
self.active.remove(zombie)
72
self.total_time+=result[1]
73
self.total_size+=result[2]
74
if zombie in self.stats:
75
self.stats[zombie].append(result)
76
else:
77
pass
78
except:
79
pass
80
81
# head count (+/- headless zombies)
82
# active thread count = 1 principal + 1/zombie
83
def no_more_zombies(self):
84
ac=threading.active_count()-1
85
options = self.ufonet.options
86
if options.verbose == True:
87
if ac>self.living:
88
if ac-self.living not in self.ufonet.ac_control:
89
print("[Info] [AI] [Control] Number of Active [ARMY] returning from battle front: "+ str(ac-self.living))
90
self.ufonet.ac_control.append(ac-self.living)
91
with self.lock:
92
return ac==self.living
93
94
# retrieve result by zombie name
95
def get_result(self,zombie):
96
return self.result[zombie] or False
97
98
# retrieve connection status by zombie name
99
def connection_failed(self,zombie):
100
return self.connection[zombie] or False
101
102
# retrieve size on correct format
103
def sizeof_fmt(self, size, suffix='B'):
104
for unit in ['','Ki','Mi','Gi','Ti','Pi','Ei','Zi']:
105
if abs(size) < 1024.0:
106
return "%3.1f%s%s" % (size, unit, suffix)
107
size /= 1024.0
108
return "%.1f%s%s" % (size, 'Yi', suffix)
109
110
# generate html statistics
111
def dump_html(self,final=False):
112
buf=""
113
out=self.get_stat()
114
if os.path.exists("/tmp/ufonet.html.tmp"):
115
try:
116
self.cleanup()
117
except:
118
print('[Info] Previous tmp file found... html content will not be updated.')
119
pass
120
buf += "<div>" + os.linesep
121
if out['err'] is not None:
122
buf += "<div>Errors : <br/>"+str(out['err'])+'</div>'+os.linesep
123
buf += "<h2>Conn: " +str(self.total_connections)+" - Zombies: "+str(len(self.stats))+" -> Hits: "+str(self.total_hits)+" - Fails: "+str(self.total_fails)+"</h2>"+os.linesep
124
buf += "<div id='zombie_list'><h3>Zombies: </h3>"
125
if len(out['data'])==0:
126
buf += "waiting..."
127
for l in out["data"] :
128
hits='<font color="green">'+str(out['data'][l]['hits'])+'</font>'
129
fails=str(out['data'][l]['fails'])
130
buf += "<button href='#' title='"+l+"' onclick=\"zombie_detail("+str(out['data'][l])+")\">"+fails+"/"+hits+"</button>"+os.linesep
131
buf += "</div>"
132
if out['max_hits'] > 0:
133
buf += "<hr/>"+os.linesep
134
buf += "<div>Zombie 0day: "+str( out['max_hitz'])+ " with "+str( out['max_hits'])+ " hits</div>"+os.linesep
135
if out['max_fails'] > 0:
136
buf += "<hr/>"+os.linesep
137
buf += "<div>Worst zombie: "+str(out['max_failz'])+ " with "+str(out['max_fails'])+" fails</div>"+os.linesep
138
buf += "<hr/>"+os.linesep
139
try:
140
buf += "<div>Total time:" +str(out['total_time'])+ " | Avg time:"+str(out['avg_time'])+"</div>"+os.linesep
141
buf += "<div>Total size:"+str(out['total_size'])+" | Avg size:"+str(out['avg_size'])+"</div>"+os.linesep
142
buf += "<hr/>"+os.linesep
143
except:
144
pass
145
buf += "<div><h3>Troops: </h3></div>"+os.linesep
146
buf += "<div>Aliens: " + str(self.ufonet.total_aliens) + " | Hits: " + str(self.ufonet.aliens_hit) + " | Fails: " + str(self.ufonet.aliens_fail)+"</div>" + os.linesep
147
buf += "<div>Droids: " + str(self.ufonet.total_droids) + " | Hits: " + str(self.ufonet.droids_hit) + " | Fails: " + str(self.ufonet.droids_fail)+"</div>" + os.linesep
148
buf += "<div>X-RPCs: " + str(self.ufonet.total_rpcs) + " | Hits: " + str(self.ufonet.rpcs_hit) + " | Fails: " + str(self.ufonet.rpcs_fail)+"</div>" + os.linesep
149
buf += "<div>UCAVs: " + str(self.ufonet.total_ucavs) + " | Hits: " + str(self.ufonet.ucavs_hit) + " | Fails: " + str(self.ufonet.ucavs_fail)+"</div>" + os.linesep
150
f = open("/tmp/ufonet.html.tmp", "w")
151
f.write(buf)
152
if(final):
153
f.write("<script>hdone=true</script>")
154
f.close()
155
try:
156
os.rename("/tmp/ufonet.html.tmp","/tmp/ufonet.html")
157
except:
158
pass
159
160
# generate statistics for stdout
161
def format(self, out):
162
if len(out['data'])==0:
163
print("[Info] Not any feedback data to show. Exiting...")
164
return
165
print('='*42)
166
print("Herd statistics")
167
print("="*42)
168
for zo in out['data']:
169
z=out['data'][zo]
170
print('Zombie :', z['name'], " | ", z['hits'], " hits ", z['fails'] ," fails ", z['retries'], " retries ")
171
print(" Times:", z['time'], " total ", z['min_time'], " min ", z['avg_time'] ," avg ", z['max_time'], " max ")
172
print(" Sizes:", z['size'], " total ", z['min_size'], " min ", z['size'] ," avg ", z['max_size'], " max ")
173
print("-"*21)
174
if out['max_hits'] > 0:
175
print("="*80)
176
print("Zombie 0day: ", out['max_hitz'], " with ", out['max_hits'], " hits")
177
if out['max_fails'] > 0:
178
print("="*80)
179
print("Worst zombie: ", out['max_failz'], " with ", out['max_fails'], " fails")
180
print("="*80)
181
print("Total invocations:", self.total_connections,"| Zombies:", str(self.ufonet.total_zombie),"| Hits:", self.total_hits,"| Fails:", self.total_fails)
182
print("Total time:", out['total_time'], "| Avg time:", out['avg_time'])
183
print("Total size:", out['total_size'],"| Avg size:", out['avg_size'])
184
print("-"*21)
185
print("="*42)
186
print("Troops statistics")
187
print("="*42)
188
print("Aliens: " + str(self.ufonet.total_aliens) + " | Hits: " + str(self.ufonet.aliens_hit) + " | Fails: " + str(self.ufonet.aliens_fail))
189
print("Droids: " + str(self.ufonet.total_droids) + " | Hits: " + str(self.ufonet.droids_hit) + " | Fails: " + str(self.ufonet.droids_fail))
190
print("X-RPCs: " + str(self.ufonet.total_rpcs) + " | Hits: " + str(self.ufonet.rpcs_hit) + " | Fails: " + str(self.ufonet.rpcs_fail))
191
print("UCAVs : " + str(self.ufonet.total_ucavs) + " | Hits: " + str(self.ufonet.ucavs_hit) + " | Fails: " + str(self.ufonet.ucavs_fail))
192
print("-"*21)
193
print("\n") # gui related
194
print('='*21)
195
196
# show what we have
197
def get_stat(self):
198
data={}
199
out={'err':None,"header":"","data":{},"total":{},"footer":"",'max_fails':0,'max_failz':"",'max_hits':0,'max_hitz':""}
200
if os.path.exists("html.tmp"):
201
out['err']= "\n[Info] Previous tmp file found... html content will not be updated."
202
return out
203
if self.total_connections==0:
204
out['err']= "\n[Error] No herd without zombies..."
205
return out
206
if len(self.stats)==0:
207
out['err']= "\n[Error] No statistics available..."
208
return out
209
self.zero_fails = 0
210
for zombie_stat in self.stats:
211
zs=self.stats[zombie_stat]
212
try:
213
entry={'name':zombie_stat,"hits":0,"fails":0,"retries":0,"time":0,"max_time":0,"min_time":zs[0][1],"avg_time":0,"size":0,"max_size":0,"min_size":zs[0][2],"avg_size":0}
214
except:
215
out['err']= "\n[Error] No statistics available...\n"
216
return out
217
if len(zs)==0:
218
continue
219
for line in zs:
220
if line[0]==200:
221
entry['hits']+=1
222
else:
223
entry['fails']+=1
224
try:
225
if self.connection[zombie_stat]:
226
entry['retries']+=1
227
except:
228
entry['retries']=entry['retries'] # black magic!
229
entry['time']+=line[1]
230
if line[1]>entry['max_time']:
231
entry['max_time']=line[1]
232
if line[1]<entry['min_time']:
233
entry['min_time']=line[1]
234
entry['size']+=line[2]
235
if line[2]>entry['max_size']:
236
entry['max_size']=line[2]
237
if line[2]<entry['min_size']:
238
entry['min_size']=line[2]
239
if entry['fails'] == 0:
240
self.zero_fails +=1
241
entry['min_time'] = str(datetime.timedelta(seconds=entry['min_time']))
242
entry['avg_time'] = str(datetime.timedelta(seconds=entry['time']/len(zs)))
243
entry['max_time'] = str(datetime.timedelta(seconds=entry['max_time']))
244
entry['time'] = str(datetime.timedelta(seconds=entry['time']))
245
entry['min_size'] = self.sizeof_fmt(int(entry['min_size']))
246
entry['avg_size'] = self.sizeof_fmt(int(entry['size']/len(zs)))
247
entry['max_size'] = self.sizeof_fmt(int(entry['max_size']))
248
entry['size']=self.sizeof_fmt(int(entry['size']))
249
if entry['fails'] > out['max_fails']:
250
out['max_fails'] = entry['fails']
251
out['max_failz'] = zombie_stat
252
if entry['hits'] > out['max_hits']:
253
out['max_hits'] = entry['hits']
254
out['max_hitz'] = zombie_stat
255
if entry['fails'] == 0:
256
if zombie_stat not in self.zombies_ready: # parse for repetitions
257
self.zombies_ready.append(zombie_stat)
258
data[entry['name']] = entry
259
out['total_time'] = str(datetime.timedelta(seconds=self.total_time))
260
out['avg_time_calc'] = self.total_time/self.total_connections
261
out['avg_time'] = str(datetime.timedelta(seconds=out['avg_time_calc']))
262
out['total_size'] = self.sizeof_fmt(int(self.total_size))
263
out['avg_size'] = self.sizeof_fmt(int(self.total_size/self.total_connections))
264
out['data']=data
265
return out
266
267
# wrapper
268
def dump(self):
269
out=self.get_stat()
270
self.format(out)
271
272
def list_fails(self):
273
options = self.ufonet.options
274
if self.total_connections==0:
275
return
276
if self.zombies_ready == None: # if not herd return
277
return
278
if not options.forceyes:
279
print('-'*25)
280
update_reply = input("Do you want to update your army (Y/n)")
281
print('-'*25)
282
else:
283
update_reply = "Y"
284
if update_reply == "n" or update_reply == "N":
285
print("\nBye!\n")
286
return
287
else:
288
self.ufonet.update_zombies(self.zombies_ready)
289
print("\n[Info] - Botnet updated! ;-)\n")
290
if os.path.exists('mothership') == True:
291
os.remove('mothership') # remove mothership stream
292
if os.path.exists('alien') == True:
293
os.remove('alien') # remove random alien worker
294
295