Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import asyncio
- import json
- import logging
- import socket
- from geoip2.database import Reader
- import geoip2
- import aioredis
- from tanner.dorks_manager import DorksManager
- from tanner.config import TannerConfig
- class SessionAnalyzer:
- def __init__(self, loop=None):
- self._loop = loop if loop is not None else asyncio.get_event_loop()
- self.queue = asyncio.Queue(loop=self._loop)
- self.logger = logging.getLogger('tanner.session_analyzer.SessionAnalyzer')
- self.attacks = ['sqli', 'rfi', 'lfi', 'xss', 'php_code_injection', 'cmd_exec', 'crlf']
- # f = open('penis.txt','w')
- # f.write(str("DEBI:"))
- # f.close()
- async def analyze(self, session_key, redis_client):
- session = None
- await asyncio.sleep(1, loop=self._loop)
- try:
- session = await redis_client.get(session_key, encoding='utf-8')
- session = json.loads(session)
- except (aioredis.ProtocolError, TypeError, ValueError) as error:
- self.logger.exception('Can\'t get session for analyze: %s', error)
- else:
- result = await self.create_stats(session, redis_client)
- await self.queue.put(result)
- await self.save_session(redis_client)
- async def save_session(self, redis_client):
- while not self.queue.empty():
- session = await self.queue.get()
- s_key = session['snare_uuid']
- del_key = session['sess_uuid']
- try:
- await redis_client.zadd(s_key, session['start_time'], json.dumps(session))
- await redis_client.delete(*[del_key])
- except aioredis.ProtocolError as redis_error:
- self.logger.exception('Error with redis. Session will be returned to the queue: %s', redis_error)
- self.queue.put(session)
- def save_dict_to_file(dic):
- f = open('dict.txt','w')
- f.write(str(dic))
- f.close()
- async def create_stats(self, session, redis_client):
- sess_duration = session['end_time'] - session['start_time']
- referer = None
- if sess_duration != 0:
- rps = session['count'] / sess_duration
- else:
- rps = 0
- location_info = await self._loop.run_in_executor(
- None, self.find_location, session['peer']['ip']
- )
- tbr, errors, hidden_links, attack_types = await self.analyze_paths(session['paths'],
- redis_client)
- attack_count = self.set_attack_count(attack_types)
- stats = dict(
- sess_uuid=session['sess_uuid'],
- peer_ip=session['peer']['ip'],
- peer_port=session['peer']['port'],
- location=location_info,
- user_agent=session['user_agent'],
- snare_uuid=session['snare_uuid'],
- start_time=session['start_time'],
- end_time=session['end_time'],
- requests_in_second=rps,
- approx_time_between_requests=tbr,
- accepted_paths=session['count'],
- errors=errors,
- hidden_links=hidden_links,
- attack_types=attack_types,
- attack_count=attack_count,
- paths=session['paths'],
- cookies=session['cookies'],
- referer=session['referer']
- )
- owner = await self.choose_possible_owner(stats)
- stats.update(owner)
- save_dict_to_file(stats)
- return stats
- @staticmethod
- async def analyze_paths(paths, redis_client):
- tbr = []
- attack_types = []
- current_path = paths[0]
- dorks = await redis_client.smembers(DorksManager.dorks_key)
- for _, path in enumerate(paths, start=1):
- tbr.append(path['timestamp'] - current_path['timestamp'])
- current_path = path
- tbr_average = sum(tbr) / float(len(tbr))
- errors = 0
- hidden_links = 0
- for path in paths:
- if path['response_status'] != 200:
- errors += 1
- if path['path'] in dorks:
- hidden_links += 1
- if 'attack_type' in path:
- attack_types.append(path['attack_type'])
- return tbr_average, errors, hidden_links, attack_types
- def set_attack_count(self, attack_types):
- attacks = self.attacks.copy()
- attacks.append('index')
- attack_count = {k: 0 for k in attacks}
- for attack in attacks:
- attack_count[attack] = attack_types.count(attack)
- count = {k: v for k, v in attack_count.items() if v != 0}
- return count
- async def choose_possible_owner(self, stats):
- owner_names = ['user', 'tool', 'crawler', 'attacker', 'admin']
- possible_owners = {k: 0.0 for k in owner_names}
- if stats['peer_ip'] == '127.0.0.1' or stats['peer_ip'] == '::1':
- possible_owners['admin'] = 1.0
- with open(TannerConfig.get('DATA', 'crawler_stats')) as f:
- bots_owner = await self._loop.run_in_executor(None, f.read)
- crawler_hosts = ['googlebot.com', 'baiduspider', 'search.msn.com', 'spider.yandex.com', 'crawl.sogou.com']
- possible_owners['crawler'], possible_owners['tool'] = await self.detect_crawler(
- stats, bots_owner, crawler_hosts
- )
- possible_owners['attacker'] = await self.detect_attacker(
- stats, bots_owner, crawler_hosts
- )
- maxcf = max([possible_owners['crawler'], possible_owners['attacker'], possible_owners['tool']])
- possible_owners['user'] = round(1 - maxcf, 2)
- owners = {k: v for k, v in possible_owners.items() if v != 0}
- return {'possible_owners': owners}
- @staticmethod
- def find_location(ip):
- reader = Reader(TannerConfig.get('DATA', 'geo_db'))
- try:
- location = reader.city(ip)
- info = dict(
- country=location.country.name,
- country_code=location.country.iso_code,
- city=location.city.name,
- zip_code=location.postal.code,
- )
- except geoip2.errors.AddressNotFoundError:
- info = "NA" # When IP doesn't exist in the db, set info as "NA - Not Available"
- return info
- async def detect_crawler(self, stats, bots_owner, crawler_hosts):
- for path in stats['paths']:
- if path['path'] == '/robots.txt':
- return (1.0, 0.0)
- if stats['requests_in_second'] > 10:
- if stats['referer'] is not None:
- return (0.0, 0.5)
- if stats['user_agent'] is not None and stats['user_agent'] in bots_owner:
- return (0.85, 0.15)
- return (0.5, 0.85)
- if stats['user_agent'] is not None and stats['user_agent'] in bots_owner:
- hostname, _, _ = await self._loop.run_in_executor(
- None, socket.gethostbyaddr, stats['peer_ip']
- )
- if hostname is not None:
- for crawler_host in crawler_hosts:
- if crawler_host in hostname:
- return (0.75, 0.15)
- return (0.25, 0.15)
- return (0.0, 0.0)
- async def detect_attacker(self, stats, bots_owner, crawler_hosts):
- if set(stats['attack_types']).intersection(self.attacks):
- return 1.0
- if stats['requests_in_second'] > 10:
- return 0.0
- if stats['user_agent'] is not None and stats['user_agent'] in bots_owner:
- hostname, _, _ = await self._loop.run_in_executor(
- None, socket.gethostbyaddr, stats['peer_ip']
- )
- if hostname is not None:
- for crawler_host in crawler_hosts:
- if crawler_host in hostname:
- return 0.25
- return 0.75
- if stats['hidden_links'] > 0:
- return 0.5
- return 0.0
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement