herd.py 12 KB

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