def get_coords_from_ipstack(ip): logger.info('Geolocating via Ipstack API.') url = 'http://api.ipstack.com/{0}?access_key={1}'.format( ip, app.config['IPSTACK_API_KEY']) response = requests.get(url) logger.info('Ipstack API response:\n{}'.format(response.content)) jsondata = None try: jsondata = response.json() except ValueError as e: logger.error('{}.'.format(e)) data = {'lat': None, 'lng': None} # Avoid the KeyError. For some reason, a successful API call to Ipstack doesn't include # the 'success' key in the json result, but a failed call does, and the value is False if 'success' in jsondata and not jsondata['success']: logger.info('Ipstack API call failed: {}'.format( jsondata['error']['type'])) # Return with empty data so the caller knows to default to the fallback API return data if jsondata: data['lat'] = jsondata['latitude'] data['lng'] = jsondata['longitude'] return data
def process_json(data, jsondata): logger.info('Processing JSON data.') logger.info('Data received:\n{}'.format(jsondata)) # process Google device data if jsondata.get('scan_results'): aps = parse_google(jsondata['scan_results']) if aps: logger.info('Parsed access points: {}'.format(aps)) coords = get_coords_from_google(aps) if all([x for x in coords.values()]): add_beacon( target_guid=data['target'], agent=data['agent'], ip=data['ip'], port=data['port'], useragent=data['useragent'], comment=data['comment'], lat=coords['lat'], lng=coords['lng'], acc=coords['acc'], ) return True else: logger.error('Invalid coordinates data.') else: # handle empty data logger.info('No AP data received.') else: # handle unrecognized data logger.info('Unrecognized data received from the agent.')
def process_ip(data): logger.info('Processing IP address.') coords = get_coords_from_ipstack(data['ip']) if not all([x for x in coords.values()]): # No data. try again with the fallback. logger.info('Using fallback API.') coords = get_coords_from_ipinfo(data['ip']) if all([x for x in coords.values()]): add_beacon(target_guid=data['target'], agent=data['agent'], ip=data['ip'], port=data['port'], useragent=data['useragent'], comment=data['comment'], lat=coords['lat'], lng=coords['lng'], acc='Unknown', country=coords['country_name'], region=coords['region_name'], city=coords['city']) return True else: logger.error('Invalid coordinates data.') return False
def api_beacon(target, agent): logger.info('{}'.format('=' * 50)) data = {'target': target, 'agent': agent} logger.info('Target: {}'.format(target)) logger.info('Agent: {}'.format(agent)) # check if target is valid if target not in [x.guid for x in Target.query.all()]: logger.error('Invalid target GUID.') abort(404) # extract universal parameters comment = b64d(request.values.get('comment', '')).decode() or None ip = request.environ['REMOTE_ADDR'] if (request.headers.get('X-Real-IP')): ip = request.headers.get('X-Real-IP') port = request.environ['REMOTE_PORT'] useragent = request.environ['HTTP_USER_AGENT'] logger.info('Connection from {} @ {}:{} via {}'.format( target, ip, port, agent)) logger.info('Parameters: {}'.format(request.values.to_dict())) logger.info('User-Agent: {}'.format(useragent)) logger.info('Comment: {}'.format(comment)) data.update(request.values.to_dict()) data.update({ 'comment': comment, 'ip': ip, 'port': port, 'useragent': useragent }) # process json payloads if request.json: if process_json(data, request.json): abort(404) # process known coordinates if all(k in data for k in ('lat', 'lng', 'acc')): if process_known_coords(data): abort(404) # process wireless survey elif all(k in data for k in ('os', 'data')): if process_wlan_survey(data): abort(404) # process ip geolocation (includes fallback) process_ip(data) if re.search('\.(png|jpg|gif)$', data['agent'].lower()): if os.path.exists( os.path.join(os.getcwd(), 'server/honeybadger/static/' + data['agent'])): return send_from_directory(directory='static', filename=data['agent'], as_attachment=False, cache_timeout=0) else: abort(404) abort(404)
def get_coords_from_uniapple(ip): logger.info('Geolocating via Uniapple API.') url = 'http://uniapple.net/geoip/?ip={}'.format(ip) content = urllib2.urlopen(url).read() logger.info('Uniapple API response:\n{}'.format(content)) jsondata = None try: jsondata = json.loads(content) except ValueError as e: logger.error('{}.'.format(e)) data = {'lat': None, 'lng': None} if jsondata: data['lat'] = jsondata['latitude'] data['lng'] = jsondata['longitude'] return data
def get_coords_from_ipinfo(ip): # New fallback, ipinfo doesn't require an API key for a certain number of API calls logger.info('Geolocating via Ipinfo.io API.') url = 'https://ipinfo.io/{}'.format(ip) response = requests.get(url) logger.info('Ipinfo.io API response:\n{}'.format(response.content)) jsondata = None try: jsondata = response.json() except ValueError as e: logger.error('{}.'.format(e)) data = {'lat': None, 'lng': None} if jsondata and 'loc' in jsondata: data['lat'] = jsondata['loc'].split(',')[0] data['lng'] = jsondata['loc'].split(',')[1] if 'bogon' in jsondata and jsondata['bogon']: logger.info('Ipinfo.io cannot geolocate IP {}'.format(ip)) return data
def process_ip(data): logger.info('Processing IP address.') coords = get_coords_from_uniapple(data['ip']) if all([x for x in coords.values()]): add_beacon( target_guid=data['target'], agent=data['agent'], ip=data['ip'], port=data['port'], useragent=data['useragent'], comment=data['comment'], lat=coords['lat'], lng=coords['lng'], acc='Unknown', ) return True else: logger.error('Invalid coordinates data.') return False
def api_beacon(target, agent): logger.info('{}'.format('=' * 50)) data = {'target': target, 'agent': agent} logger.info('Target: {}'.format(target)) logger.info('Agent: {}'.format(agent)) # check if target is valid if target not in [x.guid for x in Target.query.all()]: logger.error('Invalid target GUID.') abort(404) # extract universal parameters comment = request.values.get('comment', '').decode('base64') or None ip = request.environ['REMOTE_ADDR'] port = request.environ['REMOTE_PORT'] useragent = request.environ['HTTP_USER_AGENT'] data.update({ 'comment': comment, 'ip': ip, 'port': port, 'useragent': useragent }) logger.info('Connection from {} @ {}:{} via {}'.format( target, ip, port, agent)) logger.info('Parameters: {}'.format(request.values.to_dict())) logger.info('User-Agent: {}'.format(useragent)) logger.info('Comment: {}'.format(comment)) data.update(request.values.to_dict()) # process json payloads if request.json: if process_json(data, request.json): abort(404) # process known coordinates if all(k in data for k in ('lat', 'lng', 'acc')): if process_known_coords(data): abort(404) # process wireless survey elif all(k in data for k in ('os', 'data')): if process_wlan_survey(data): abort(404) # process ip geolocation (fallback) process_ip(data) abort(404)
def process_wlan_survey(data): logger.info('Processing wireless survey data.') os = data['os'] _data = data['data'] content = b64d(_data).decode() logger.info('Data received:\n{}'.format(_data)) logger.info('Decoded Data:\n{}'.format(content)) if _data: aps = [] if re.search('^mac os x', os.lower()): aps = parse_airport(content) elif re.search('^windows', os.lower()): aps = parse_netsh(content) elif re.search('^linux', os.lower()): aps = parse_iwlist(content) # handle recognized data if aps: logger.info('Parsed access points: {}'.format(aps)) coords = get_coords_from_google(aps) if all([x for x in coords.values()]): add_beacon( target_guid=data['target'], agent=data['agent'], ip=data['ip'], port=data['port'], useragent=data['useragent'], comment=data['comment'], lat=coords['lat'], lng=coords['lng'], acc=coords['acc'], ) return True else: logger.error('Invalid coordinates data.') else: # handle unrecognized data logger.info('No parsable WLAN data received.') else: # handle blank data logger.info('No data received from the agent.') return False
def get_coords_from_google(aps): logger.info('Geolocating via Google Geolocation API.') url = 'https://www.googleapis.com/geolocation/v1/geolocate?key={}'.format( app.config['GOOGLE_API_KEY']) data = {"wifiAccessPoints": []} for ap in aps: data['wifiAccessPoints'].append(ap.serialized_for_google) data_json = json.dumps(data) headers = {'Content-Type': 'application/json'} response = requests.post(url=url, data=data_json, headers=headers) logger.info("Google API response: {}".format(response.content)) jsondata = None try: jsondata = response.json() except ValueError as e: logger.error('{}.'.format(e)) data = {'lat': None, 'lng': None, 'acc': None} if jsondata: data['acc'] = jsondata['accuracy'] data['lat'] = jsondata['location']['lat'] data['lng'] = jsondata['location']['lng'] return data