def render_GET(self, request): try: token = request.args.get('token', None)[0] fmt = request.args.get('fmt', None)[0] canarydrop = Canarydrop(**get_canarydrop(canarytoken=token)) if not canarydrop: raise NoCanarytokenPresent() if fmt == 'zip': request.setHeader("Content-Type", "application/zip") request.setHeader("Content-Disposition", 'attachment; filename={token}.zip'\ .format(token=token)) return make_canary_zip(hostname= canarydrop.get_hostname(with_random=False)) elif fmt == 'msword': request.setHeader("Content-Type", "application/vnd.openxmlformats-officedocument"+\ ".wordprocessingml.document") request.setHeader("Content-Disposition", 'attachment; filename={token}.docx'\ .format(token=token)) return make_canary_msword(url=canarydrop.get_url()) elif fmt == 'pdf': request.setHeader("Content-Type", "application/pdf") request.setHeader("Content-Disposition", 'attachment; filename={token}.pdf'\ .format(token=token)) return make_canary_pdf(hostname=canarydrop.get_hostname(nxdomain=True, with_random=False)) except Exception as e: log.err('Unexpected error in download: {err}'.format(err=e)) return NoResource().render(request)
def render_GET(self, request): try: token = request.args.get('token', None)[0] auth = request.args.get('auth', None)[0] canarydrop = Canarydrop(**get_canarydrop(canarytoken=token)) if not canarydrop['auth'] or canarydrop['auth'] != auth: raise NoCanarytokenPresent() if canarydrop.get('triggered_list', None): for timestamp in canarydrop['triggered_list'].keys(): formatted_timestamp = datetime.datetime.fromtimestamp( float(timestamp)).strftime('%Y %b %d %H:%M:%S') canarydrop['triggered_list'][ formatted_timestamp] = canarydrop[ 'triggered_list'].pop(timestamp) if canarydrop.get('memo'): canarydrop['memo'] = unicode(canarydrop['memo'], "utf8") except (TypeError, NoCanarytokenPresent): return NoResource().render(request) g_api_key = get_canary_google_api_key() template = env.get_template('history.html') return template.render(canarydrop=canarydrop, API_KEY=g_api_key).encode('utf8')
def query(self, query, src_ip): """ Check if the query should be answered dynamically, otherwise dispatch to the fallback resolver. """ IS_NX_DOMAIN = True in [ query.name.name.lower().endswith(d) for d in settings.NXDOMAINS ] if (not True in [ query.name.name.lower().endswith(d) for d in self.canary_domains ] and not IS_NX_DOMAIN): return defer.fail(error.DNSQueryRefusedError()) if query.type == dns.NS: return defer.succeed(self._do_ns_response(name=query.name.name)) if query.type == dns.SOA: return defer.succeed(self._do_soa_response(name=query.name.name)) if query.type != dns.A: return defer.succeed(self._do_no_response(query=query)) try: token = Canarytoken(value=query.name.name) canarydrop = Canarydrop(**get_canarydrop( canarytoken=token.value())) src_data = self.look_for_source_data(token=token.value(), value=query.name.name) if canarydrop._drop['type'] == 'my_sql': d = deferLater(reactor, 10, self.dispatch, canarydrop=canarydrop, src_ip=src_ip, src_data=src_data) d.addErrback(self._handleMySqlErr) else: self.dispatch(canarydrop=canarydrop, src_ip=src_ip, src_data=src_data) except (NoCanarytokenPresent, NoCanarytokenFound): # If we dont find a canarytoken, lets just continue. No need to log. pass except Exception as e: log.error(e) if IS_NX_DOMAIN: return defer.fail(error.DomainError()) return defer.succeed(self._do_dynamic_response(name=query.name.name))
def render_POST(self, request): try: try: token = request.args.get('token', None)[0] auth = request.args.get('auth', None)[0] canarydrop = Canarydrop(**get_canarydrop(canarytoken=token)) if not canarydrop['auth'] or canarydrop['auth'] != auth: raise NoCanarytokenPresent() except (IndexError, TypeError, NoCanarytokenPresent): return NoResource().render(request) try: email_enable_status = request.args.get('email_enable', None)[0] == "on" except (TypeError, IndexError): email_enable_status = False try: webhook_enable_status = request.args.get('webhook_enable', None)[0] == "on" except (TypeError, IndexError): webhook_enable_status = False try: sms_enable_status = request.args.get('sms_enable', None)[0] == "on" except (TypeError, IndexError): sms_enable_status = False try: web_image_status = request.args.get('web_image_enable', None)[0] == "on" except (TypeError, IndexError): web_image_status = False try: token_fmt = request.args.get('fmt', None)[0] except (TypeError, IndexError): token_fmt = '' canarydrop['alert_email_enabled'] = email_enable_status canarydrop['alert_webhook_enabled'] = webhook_enable_status canarydrop['alert_sms_enabled'] = sms_enable_status canarydrop['web_image_enabled'] = web_image_status save_canarydrop(canarydrop=canarydrop) g_api_key = get_canary_google_api_key() template = env.get_template('manage.html') return template.render(canarydrop=canarydrop, saved=True, settings=settings, API_KEY=g_api_key).encode('utf8') except Exception as e: import traceback log.err('Exception in manage.html: {e}, {stack}'.format(e=e, stack=traceback.format_exc())) template = env.get_template('manage.html') return template.render(canarydrop=canarydrop, error=e, settings=settings).encode('utf8')
def poll(self, imgur_token=None): try: count = get_imgur_count(imgur_id=imgur_token["id"]) if count > imgur_token["count"]: canarydrop = Canarydrop(**get_canarydrop(canarytoken=imgur_token["canarytoken"])) self.dispatch(canarydrop=canarydrop, count=count, imgur_id=imgur_token["id"]) imgur_token["count"] = count save_imgur_token(imgur_token=imgur_token) except Exception as e: log.err("Imgur error: {error}".format(error=e))
def render_POST(self, request): request.responseHeaders.addRawHeader(b"content-type", b"application/json") response = {} try: token = request.args.get('token', None)[0] auth = request.args.get('auth', None)[0] setting = request.args.get('setting', None)[0] canarydrop = Canarydrop(**get_canarydrop(canarytoken=token)) if not canarydrop['auth'] or canarydrop['auth'] != auth: raise NoCanarytokenPresent() if setting not in [ 'clonedsite', 'email_enable', 'webhook_enable', 'sms_enable', 'browser_scanner_enable', 'web_image_enable' ]: raise NoCanarytokenPresent() except (IndexError, TypeError, NoCanarytokenPresent): return NoResource().render(request) if setting == 'clonedsite': try: clonedsite = request.args['clonedsite'][0] if not clonedsite: raise KeyError cloned_token = {'clonedsite': clonedsite, 'canarytoken': token} canarydrop.clonedsite_token = save_clonedsite_token( cloned_token) save_canarydrop(canarydrop) response[ 'clonedsite_js'] = canarydrop.get_cloned_site_javascript() response['clonedsite'] = clonedsite except (IndexError, KeyError): return NoResource().render(request) elif setting == "email_enable": canarydrop['alert_email_enabled'] = request.args['value'][ 0] == "on" elif setting == "webhook_enable": canarydrop['alert_webhook_enabled'] = request.args['value'][ 0] == "on" elif setting == "sms_enable": canarydrop['alert_sms_enabled'] = request.args['value'][0] == "on" elif setting == "browser_scanner_enable": canarydrop['browser_scanner_enabled'] = request.args['value'][ 0] == "on" elif setting == "web_image_enable": canarydrop['web_image_enabled'] = request.args['value'][0] == "on" save_canarydrop(canarydrop=canarydrop) response['result'] = 'success' return simplejson.dumps(response)
def poll(self, imgur_token=None): try: count = get_imgur_count(imgur_id=imgur_token['id']) if count > imgur_token['count']: canarydrop = Canarydrop(**get_canarydrop( canarytoken=imgur_token['canarytoken'])) self.dispatch(canarydrop=canarydrop, count=count, imgur_id=imgur_token['id']) imgur_token['count'] = count save_imgur_token(imgur_token=imgur_token) except Exception as e: log.warn('Imgur error: {error}'.format(error=e))
def validateTo(self, user): # Only messages directed to the "console" user are accepted. try: token = Canarytoken(value=user.dest.local) self.canarydrop = Canarydrop(**get_canarydrop(canarytoken=token.value())) return lambda: CanaryMessage(esmtp=self) except (NoCanarytokenPresent, NoCanarytokenFound): log.err("No token in recipient address: {address}".format(address=user.dest.local)) except Exception as e: log.err(e) raise smtp.SMTPBadRcpt(user)
def render_GET(self, request): # A GET request to a token URL can trigger one of a few responses: # 1. Check if link has been clicked on (rather than loaded from an # <img>) by looking at the Accept header, then: # 1a. If browser security if enabled, serve that page and stop. # 1b. If fortune in enabled, serve a fortune and stop. # 2. Otherwise we'll serve an image: # 2a. If a custom image is attached to the canarydrop, serve that and stop. # 2b. Serve our default 1x1 gif request.setHeader("Server", "Apache") try: token = Canarytoken(value=request.path) canarydrop = Canarydrop(**get_canarydrop(canarytoken=token.value())) if request.args.get("ts_key", [None])[0]: canarydrop._drop["hit_time"] = request.args.get("ts_key", [None])[0] else: canarydrop._drop["hit_time"] = datetime.datetime.utcnow().strftime("%s.%f") useragent = request.getHeader("User-Agent") src_ip = request.getHeader("x-forwarded-for") # location and refere are for cloned sites location = request.args.get("l", [None])[0] referer = request.args.get("r", [None])[0] self.dispatch(canarydrop=canarydrop, src_ip=src_ip, useragent=useragent, location=location, referer=referer) if "text/html" in request.getHeader("Accept"): if canarydrop["browser_scanner_enabled"]: template = env.get_template("browser_scanner.html") return template.render(key=canarydrop._drop["hit_time"], canarytoken=token.value()).encode("utf8") elif TOKEN_RETURN == "fortune": try: fortune = subprocess.check_output("/usr/games/fortune") template = env.get_template("fortune.html") return template.render(fortune=fortune).encode("utf8") except Exception as e: log.err("Could not get a fortune: {e}".format(e=e)) if canarydrop["web_image_enabled"] and os.path.exists(canarydrop["web_image_path"]): mimetype = "image/" + canarydrop["web_image_path"][-3:] with open(canarydrop["web_image_path"], "r") as f: contents = f.read() request.setHeader("Content-Type", mimetype) return contents except Exception as e: log.err("Error in render GET: {error}".format(error=e)) request.setHeader("Content-Type", "image/gif") return self.GIF
def render_GET(self, request): try: token = request.args.get('token', None)[0] auth = request.args.get('auth', None)[0] canarydrop = Canarydrop(**get_canarydrop(canarytoken=token)) if not canarydrop['auth'] or canarydrop['auth'] != auth: raise NoCanarytokenPresent() except (TypeError, NoCanarytokenPresent): return NoResource().render(request) template = env.get_template('manage.html') return template.render(canarydrop=canarydrop).encode('utf8')
def received_imgur_count(self, body, imgur_token): try: body = simplejson.loads(body) count = int(body['data'][imgur_token['id']]) log.info('Count for imgur token '+imgur_token['id']+ ' was '+str(count)) if count > imgur_token['count']: canarydrop = Canarydrop(**get_canarydrop( canarytoken=imgur_token['canarytoken'])) self.dispatch(canarydrop=canarydrop, count=count, imgur_id=imgur_token['id']) imgur_token['count'] = count save_imgur_token(imgur_token=imgur_token) except Exception as e: log.warn('Imgur error: {error}'.format(error=e))
def render_GET(self, request): try: token = Canarytoken(value=request.path) canarydrop = Canarydrop(**get_canarydrop(canarytoken=token.value())) useragent = request.getHeader('User-Agent') src_ip = request.getHeader('x-forwarded-for') self.dispatch(canarydrop=canarydrop, src_ip=src_ip, useragent=useragent) except: log.err('No canarytoken seen in: {path}'.format(path=request.path)) request.setHeader("Content-Type", "image/gif") request.setHeader("Server", "Apache") return self.GIF
def validateTo(self, user): # Only messages directed to the "console" user are accepted. try: token = Canarytoken(value=user.dest.local) self.canarydrop = Canarydrop(**get_canarydrop( canarytoken=token.value())) return lambda: CanaryMessage(esmtp=self) except (NoCanarytokenPresent, NoCanarytokenFound): log.warn('No token in recipient address: {address}'\ .format(address=user.dest.local)) except Exception as e: log.error(e) raise smtp.SMTPBadRcpt(user)
def render_POST(self, request): try: fields = cgi.FieldStorage( fp=request.content, headers=request.getAllHeaders(), environ={ 'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': request.getAllHeaders()['content-type'], }) #hacky way to parse out file contents and filenames token = request.args.get('token', None)[0] fmt = request.args.get('fmt', None)[0] if fmt not in ['authenticode']: raise Exception('Unsupported token type for POST.') canarydrop = Canarydrop(**get_canarydrop(canarytoken=token)) if not canarydrop: raise NoCanarytokenPresent() if fmt == 'authenticode': filename = fields['file_for_signing'].filename filebody = fields['file_for_signing'].value if len(filebody) > int(settings.MAX_UPLOAD_SIZE): response['Error'] = 4 response[ 'Message'] = 'File too large. File size must be < ' + str( int(settings.MAX_UPLOAD_SIZE) / (1024 * 1024)) + 'MB.' raise Exception('File too large') if not filename.lower().endswith(('exe', 'dll')): raise Exception( 'Uploaded authenticode file must be an exe or dll') signed_contents = make_canary_authenticode_binary( hostname=canarydrop.get_hostname(with_random=False, as_url=True), filebody=filebody) request.setHeader("Content-Type", "octet/stream") request.setHeader("Content-Disposition", 'attachment; filename={filename}.signed'\ .format(filename=filename)) return signed_contents except Exception as e: log.err('Unexpected error in POST download: {err}'.format(err=e)) template = env.get_template('error.html') return template.render(error=e.message).encode('utf8') return NoResource().render(request)
def render_GET(self, request): try: token = Canarytoken(value=request.path) canarydrop = Canarydrop(**get_canarydrop( canarytoken=token.value())) useragent = request.getHeader('User-Agent') src_ip = request.getHeader('x-forwarded-for') self.dispatch(canarydrop=canarydrop, src_ip=src_ip, useragent=useragent) except: log.err('No canarytoken seen in: {path}'.format(path=request.path)) request.setHeader("Content-Type", "image/gif") request.setHeader("Server", "Apache") return self.GIF
def poll(self, linkedin_account=None): try: current_count = get_linkedin_viewer_count( username=linkedin_account['username'], password=linkedin_account['password']) except LinkedInFailure as e: log.err('Could not retrieve linkedin view count: {error}'\ .format(error=e)) return if current_count > linkedin_account['count']: canarydrop = Canarydrop(**get_canarydrop( canarytoken=linkedin_account['canarytoken'])) self.dispatch(canarydrop=canarydrop, count=current_count, linkedin_username=linkedin_account['username']) linkedin_account['count'] = current_count save_linkedin_account(linkedin_account=linkedin_account)
def poll(self, bitcoin_account=None): try: current_balance = get_bitcoin_address_balance( address=bitcoin_account['address']) except BitcoinFailure as e: log.err('Could not retrieve bitcoin balance: {error}'\ .format(error=e)) return if current_balance> bitcoin_account['balance']: canarydrop = Canarydrop(**get_canarydrop( canarytoken=bitcoin_account['canarytoken'])) self.dispatch(canarydrop=canarydrop, new_balance=current_balance, old_balance=bitcoin_account['balance'], address=bitcoin_account['address']) bitcoin_account['balance'] = current_balance save_bitcoin_account(bitcoin_account=bitcoin_account)
def render_POST(self, request): try: fields = cgi.FieldStorage( fp = request.content, headers = request.getAllHeaders(), environ = {'REQUEST_METHOD':'POST', 'CONTENT_TYPE': request.getAllHeaders()['content-type'], } )#hacky way to parse out file contents and filenames token = request.args.get('token', None)[0] fmt = request.args.get('fmt', None)[0] if fmt not in ['authenticode']: raise Exception('Unsupported token type for POST.') canarydrop = Canarydrop(**get_canarydrop(canarytoken=token)) if not canarydrop: raise NoCanarytokenPresent() if fmt == 'authenticode': filename = fields['file_for_signing'].filename filebody = fields['file_for_signing'].value if len(filebody) > settings.MAX_UPLOAD_SIZE: raise Exception('File too large') if not filename.lower().endswith(('exe','dll')): raise Exception('Uploaded authenticode file must be an exe or dll') signed_contents = make_canary_authenticode_binary(hostname= canarydrop.get_hostname(with_random=False, as_url=True), filebody=filebody) request.setHeader("Content-Type", "octet/stream") request.setHeader("Content-Disposition", 'attachment; filename={filename}.signed'\ .format(filename=filename)) return signed_contents except Exception as e: log.err('Unexpected error in POST download: {err}'.format(err=e)) template = env.get_template('error.html') return template.render(error=e.message).encode('utf8') return NoResource().render(request)
def render_GET(self, request): try: token = request.args.get('token', None)[0] auth = request.args.get('auth', None)[0] canarydrop = Canarydrop(**get_canarydrop(canarytoken=token)) if not canarydrop['auth'] or canarydrop['auth'] != auth: raise NoCanarytokenPresent() if canarydrop.get('triggered_list', None): for timestamp in canarydrop['triggered_list'].keys(): formatted_timestamp = datetime.datetime.fromtimestamp( float(timestamp)).strftime('%Y %b %d %H:%M:%S') canarydrop['triggered_list'][formatted_timestamp] = canarydrop['triggered_list'].pop(timestamp) except (TypeError, NoCanarytokenPresent): return NoResource().render(request) g_api_key = get_canary_google_api_key() template = env.get_template('manage.html') return template.render(canarydrop=canarydrop, API_KEY=g_api_key).encode('utf8')
def chirp(self, trigger): try: token = Canarytoken(value=trigger['tf']) self.canarydrop = Canarydrop(**get_canarydrop(canarytoken=token.value())) if self.enricher: self.enricher(trigger, self.canarydrop, self.factory.dispatch) else: self.factory.dispatch( canarydrop=self.canarydrop, src_ip=trigger['ip'], useragent=trigger['useragent'], location=trigger['location'] ) except (NoCanarytokenPresent, NoCanarytokenFound): log.warn('No token for {tf} | Cert: {f}'.format(tf=trigger['tf'], f=trigger['f'])) except Exception as e: log.error("Exception in chirp: {}".format(e))
def render_GET(self, request): try: token = Canarytoken(value=request.path) canarydrop = Canarydrop(**get_canarydrop(canarytoken=token.value())) useragent = request.getHeader('User-Agent') src_ip = request.getHeader('x-forwarded-for') #location and refere are for cloned sites location = request.args.get('l', [None])[0] referer = request.args.get('r', [None])[0] self.dispatch(canarydrop=canarydrop, src_ip=src_ip, useragent=useragent, location=location, referer=referer) except: log.err('No canarytoken seen in: {path}'.format(path=request.path)) request.setHeader("Content-Type", "image/gif") request.setHeader("Server", "Apache") return self.GIF
def query(self, query, src_ip): """ Check if the query should be answered dynamically, otherwise dispatch to the fallback resolver. """ self.logfile.write('%r\n' % query) self.logfile.flush() if query.type == dns.NS: return defer.succeed(self._do_ns_response(name=query.name.name)) if query.type != dns.A: return defer.succeed(self._do_no_response(query=query)) #return defer.fail(error.DomainError()) try: token = Canarytoken(value=query.name.name) canarydrop = Canarydrop(**get_canarydrop( canarytoken=token.value())) src_data = self.look_for_source_data(token=token.value(), value=query.name.name) self.dispatch(canarydrop=canarydrop, src_ip=src_ip, src_data=src_data) # return defer.succeed( # self._do_dynamic_response(name=query.name.name, # response=response)) except NoCanarytokenPresent: log.err('No token seen in query: {query}'.format( query=query.name.name)) except Exception as e: log.err(e) if query.name.name in settings.NXDOMAINS: return defer.fail(error.DomainError()) return defer.succeed(self._do_dynamic_response(name=query.name.name))
def render_POST(self, request): try: try: token = request.args.get('token', None)[0] auth = request.args.get('auth', None)[0] canarydrop = Canarydrop(**get_canarydrop(canarytoken=token)) if not canarydrop['auth'] or canarydrop['auth'] != auth: raise NoCanarytokenPresent() except (IndexError, NoCanarytokenPresent): return NoResource().render(request) try: email_enable_status = request.args.get('email_enable', None)[0] == "on" except (TypeError, IndexError): email_enable_status = False try: sms_enable_status = request.args.get('sms_enable', None)[0] == "on" except (TypeError, IndexError): sms_enable_status = False canarydrop['alert_email_enabled'] = email_enable_status canarydrop['alert_sms_enabled'] = sms_enable_status save_canarydrop(canarydrop=canarydrop) template = env.get_template('manage.html') return template.render(canarydrop=canarydrop, saved=True, settings=settings).encode('utf8') except Exception as e: template = env.get_template('manage.html') return template.render(canarydrop=canarydrop, error=e, settings=settings).encode('utf8')
def query(self, query, src_ip): """ Check if the query should be answered dynamically, otherwise dispatch to the fallback resolver. """ self.logfile.write('%r\n' % query) self.logfile.flush() if not True in [query.name.name.lower().endswith(d) for d in self.canary_domains]: return defer.fail(error.DNSQueryRefusedError()) if query.type == dns.NS: return defer.succeed(self._do_ns_response(name=query.name.name)) if query.type == dns.SOA: return defer.succeed(self._do_soa_response(name=query.name.name)) if query.type != dns.A: return defer.succeed(self._do_no_response(query=query)) try: token = Canarytoken(value=query.name.name) canarydrop = Canarydrop(**get_canarydrop(canarytoken=token.value())) src_data = self.look_for_source_data(token=token.value(), value=query.name.name) self.dispatch(canarydrop=canarydrop, src_ip=src_ip, src_data=src_data) except (NoCanarytokenPresent, NoCanarytokenFound): # If we dont find a canarytoken, lets just continue. No need to log. pass except Exception as e: log.err(e) if query.name.name in settings.NXDOMAINS: return defer.fail(error.DomainError()) return defer.succeed(self._do_dynamic_response(name=query.name.name))
def render_GET(self, request): try: token = Canarytoken(value=request.path) canarydrop = Canarydrop(**get_canarydrop( canarytoken=token.value())) useragent = request.getHeader('User-Agent') src_ip = request.getHeader('x-forwarded-for') #location and refere are for cloned sites location = request.args.get('l', [None])[0] referer = request.args.get('r', [None])[0] self.dispatch(canarydrop=canarydrop, src_ip=src_ip, useragent=useragent, location=location, referer=referer) except: log.err('No canarytoken seen in: {path}'.format(path=request.path)) request.setHeader("Content-Type", "image/gif") request.setHeader("Server", "Apache") return self.GIF
def query(self, query, src_ip): """ Check if the query should be answered dynamically, otherwise dispatch to the fallback resolver. """ self.logfile.write('%r\n' % query) self.logfile.flush() if query.type == dns.NS: return defer.succeed(self._do_ns_response(name=query.name.name)) if query.type != dns.A: return defer.succeed(self._do_no_response(query=query)) #return defer.fail(error.DomainError()) try: token = Canarytoken(value=query.name.name) canarydrop = Canarydrop(**get_canarydrop(canarytoken=token.value())) src_data = self.look_for_source_data(token=token.value(), value=query.name.name) self.dispatch(canarydrop=canarydrop, src_ip=src_ip, src_data=src_data) # return defer.succeed( # self._do_dynamic_response(name=query.name.name, # response=response)) except NoCanarytokenPresent: log.err('No token seen in query: {query}'.format(query=query.name.name)) except Exception as e: log.err(e) if query.name.name in settings.NXDOMAINS: return defer.fail(error.DomainError()) return defer.succeed(self._do_dynamic_response(name=query.name.name))
def dispatch_alert(self, username, src_host, additional_info): token = Canarytoken(value=username) canarydrop = Canarydrop(**get_canarydrop(canarytoken=token.value())) self.dispatch(canarydrop=canarydrop, src_ip=src_host, additional_info=additional_info)
def render_GET(self, request): #A GET request to a token URL can trigger one of a few responses: # 1. Check if link has been clicked on (rather than loaded from an # <img>) by looking at the Accept header, then: # 1a. If browser security if enabled, serve that page and stop. # 1b. If fortune in enabled, serve a fortune and stop. # 2. Otherwise we'll serve an image: # 2a. If a custom image is attached to the canarydrop, serve that and stop. # 2b. Serve our default 1x1 gif request.setHeader("Server", "Apache") try: token = Canarytoken(value=request.path) canarydrop = Canarydrop(**get_canarydrop( canarytoken=token.value())) if request.args.get('ts_key', [None])[0]: canarydrop._drop['hit_time'] = request.args.get( 'ts_key', [None])[0] else: canarydrop._drop['hit_time'] = datetime.datetime.utcnow( ).strftime("%s.%f") useragent = request.getHeader('User-Agent') src_ip = request.getHeader('x-forwarded-for') #location and refere are for cloned sites location = request.args.get('l', [None])[0] referer = request.args.get('r', [None])[0] self.dispatch(canarydrop=canarydrop, src_ip=src_ip, useragent=useragent, location=location, referer=referer) if 'redirect_url' in canarydrop._drop and canarydrop._drop[ 'redirect_url']: # if fast redirect if canarydrop._drop['type'] == 'fast_redirect': return redirectTo(canarydrop._drop['redirect_url'], request) #template = env.get_template('browser_scanner.html') #return template.render(key=canarydrop._drop['hit_time'], # canarytoken=token.value()).encode('utf8') elif canarydrop._drop['type'] == 'slow_redirect': template = env.get_template('browser_scanner.html') return template.render( key=canarydrop._drop['hit_time'], canarytoken=token.value(), redirect_url=canarydrop._drop['redirect_url']).encode( 'utf8') if request.getHeader( 'Accept') and "text/html" in request.getHeader('Accept'): if canarydrop['browser_scanner_enabled']: template = env.get_template('browser_scanner.html') return template.render(key=canarydrop._drop['hit_time'], canarytoken=token.value(), redirect_url='').encode('utf8') elif TOKEN_RETURN == 'fortune': try: fortune = subprocess.check_output('/usr/games/fortune') template = env.get_template('fortune.html') return template.render(fortune=fortune).encode('utf8') except Exception as e: log.error('Could not get a fortune: {e}'.format(e=e)) if canarydrop['web_image_enabled'] and os.path.exists( canarydrop['web_image_path']): mimetype = "image/" + canarydrop['web_image_path'][-3:] with open(canarydrop['web_image_path'], "r") as f: contents = f.read() request.setHeader("Content-Type", mimetype) return contents except Exception as e: log.warn('Error in render GET: {error}'.format(error=e)) request.setHeader("Content-Type", "image/gif") return self.GIF
def render_POST(self, request): try: token = Canarytoken(value=request.path) canarydrop = Canarydrop(**get_canarydrop( canarytoken=token.value())) #if key and token args are present, we are either: # -posting browser info # -getting an aws trigger (key == aws_s3) # otherwise, slack api token data perhaps #store the info and don't re-render if canarydrop._drop['type'] == 'slack_api': canarydrop._drop['hit_time'] = datetime.datetime.utcnow( ).strftime("%s.%f") useragent = request.args.get('user_agent', [None])[0] src_ip = request.args.get('ip', [None])[0] additional_info = { 'Slack Log Data': { k: v for k, v in request.args.iteritems() if k not in ['user_agent', 'ip'] } } self.dispatch(canarydrop=canarydrop, src_ip=src_ip, useragent=useragent, additional_info=additional_info) return self.GIF if canarydrop._drop['type'] == 'aws_keys': canarydrop._drop['hit_time'] = datetime.datetime.utcnow( ).strftime("%s.%f") useragent = request.args.get('user_agent', [None])[0] src_ip = request.args.get('ip', [None])[0] safety_net = request.args.get('safety_net', [None])[0] last_used = request.args.get('last_used', [None])[0] additional_info = { 'AWS Key Log Data': { k: v for k, v in request.args.iteritems() if k not in ['user_agent', 'ip'] } } if safety_net: log.info('AWS Safety Net triggered for {}'.format( token.value())) self.dispatch(canarydrop=canarydrop, src_ip=src_ip, useragent=useragent, additional_info=additional_info) return self.GIF key = request.args['key'][0] if key and token: if key == 'aws_s3': try: canarydrop._drop[ 'hit_time'] = datetime.datetime.utcnow().strftime( "%s.%f") src_ip = request.args['RemoteIP'][0] additional_info = { 'AWS Log Data': { k: v for k, v in request.args.iteritems() if k not in ['key', 'src_ip'] } } self.dispatch(canarydrop=canarydrop, src_ip=src_ip, additional_info=additional_info) except Exception as e: log.error('Error in s3 post: {error}'.format(error=e)) elif 'secretkeeper_photo' in request.args: log.error('Saving secretkeeper_photo') try: fields = cgi.FieldStorage( fp=request.content, headers=request.getAllHeaders(), environ={ 'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': request.getAllHeaders()['content-type'], } ) #hacky way to parse out file contents and filenames filename = fields['secretkeeper_photo'].filename filebody = fields['secretkeeper_photo'].value if len(filebody) > MAX_UPLOAD_SIZE: raise Exception('File too large') r = hashlib.md5(os.urandom(32)).hexdigest() filepath = os.path.join(WEB_IMAGE_UPLOAD_PATH, r[:2], r[2:]) + '.png' if not os.path.exists(os.path.dirname(filepath)): try: os.makedirs(os.path.dirname(filepath)) except OSError as exc: # Guard against race condition if exc.errno != errno.EEXIST: raise with open(filepath, "w") as f: f.write(filebody) canarydrop.add_additional_info_to_hit( hit_time=key, additional_info={'secretkeeper_photo': filepath}) except Exception as e: log.error( 'Error in secretkeeper_photo post: {error}'.format( error=e)) else: additional_info = { k: v for k, v in request.args.iteritems() if k not in ['key', 'canarytoken', 'name'] } canarydrop.add_additional_info_to_hit( hit_time=key, additional_info={ request.args['name'][0]: additional_info }) return 'success' else: return self.render_GET(request) except Exception as e: return self.render_GET(request)
def render_POST(self, request): try: try: token = request.args.get('token', None)[0] auth = request.args.get('auth', None)[0] canarydrop = Canarydrop(**get_canarydrop(canarytoken=token)) if not canarydrop['auth'] or canarydrop['auth'] != auth: raise NoCanarytokenPresent() except (IndexError, NoCanarytokenPresent): return NoResource().render(request) try: email_enable_status = request.args.get('email_enable', None)[0] == "on" except (TypeError, IndexError): email_enable_status = False try: webhook_enable_status = request.args.get('webhook_enable', None)[0] == "on" except (TypeError, IndexError): webhook_enable_status = False try: sms_enable_status = request.args.get('sms_enable', None)[0] == "on" except (TypeError, IndexError): sms_enable_status = False try: web_image_status = request.args.get('web_image_enable', None)[0] == "on" except (TypeError, IndexError): web_image_status = False try: token_fmt = request.args.get('fmt', None)[0] except (TypeError, IndexError): token_fmt = '' canarydrop['alert_email_enabled'] = email_enable_status canarydrop['alert_webhook_enabled'] = webhook_enable_status canarydrop['alert_sms_enabled'] = sms_enable_status canarydrop['web_image_enabled'] = web_image_status if token_fmt == 'web_image': if not settings.WEB_IMAGE_UPLOAD_PATH: raise Exception("Image upload not supported, set CANARY_WEB_IMAGE_UPLOAD_PATH in frontend.env.") fields = cgi.FieldStorage( fp = request.content, headers = request.getAllHeaders(), environ = {'REQUEST_METHOD':'POST', 'CONTENT_TYPE': request.getAllHeaders()['content-type'], } ) filename = fields['web_image'].filename filebody = fields['web_image'].value if len(filebody) > settings.MAX_UPLOAD_SIZE: raise Exception('File too large') if not filename.lower().endswith(('.png','.gif','.jpg')): raise Exception('Uploaded image must be a PNG, GIF or JPG') ext = filename.lower()[-4:] #create a random local filename r = hashlib.md5(os.urandom(32)).hexdigest() filepath = os.path.join(settings.WEB_IMAGE_UPLOAD_PATH, r[:2], r[2:])+ext if not os.path.exists(os.path.dirname(filepath)): try: os.makedirs(os.path.dirname(filepath)) except OSError as exc: # Guard against race condition if exc.errno != errno.EEXIST: raise with open(filepath, "w") as f: f.write(filebody) canarydrop['web_image_enabled'] = True canarydrop['web_image_path'] = filepath save_canarydrop(canarydrop=canarydrop) g_api_key = get_canary_google_api_key() template = env.get_template('manage.html') return template.render(canarydrop=canarydrop, saved=True, settings=settings, API_KEY=g_api_key).encode('utf8') except Exception as e: import traceback log.err('Exception in manage.html: {e}, {stack}'.format(e=e, stack=traceback.format_exc())) template = env.get_template('manage.html') return template.render(canarydrop=canarydrop, error=e, settings=settings).encode('utf8')
def render_GET(self, request): try: token = request.args.get('token', None)[0] fmt = request.args.get('fmt', None)[0] auth = request.args.get('auth', None)[0] canarydrop = Canarydrop(**get_canarydrop(canarytoken=token)) if not canarydrop: raise NoCanarytokenPresent() if not canarydrop['auth'] or canarydrop['auth'] != auth: raise NoCanarytokenPresent() if fmt == 'zip': request.setHeader("Content-Type", "application/zip") request.setHeader("Content-Disposition", 'attachment; filename={token}.zip'\ .format(token=token)) return make_canary_zip(hostname=canarydrop.get_hostname( with_random=False)) elif fmt == 'msword': request.setHeader("Content-Type", "application/vnd.openxmlformats-officedocument"+\ ".wordprocessingml.document") request.setHeader("Content-Disposition", 'attachment; filename={token}.docx'\ .format(token=token)) return make_canary_msword(url=canarydrop.get_url()) elif fmt == 'pdf': request.setHeader("Content-Type", "application/pdf") request.setHeader("Content-Disposition", 'attachment; filename={token}.pdf'\ .format(token=token)) return make_canary_pdf(hostname=canarydrop.get_hostname( nxdomain=True, with_random=False)) elif fmt == 'awskeys': request.setHeader("Content-Type", "text/plain") request.setHeader("Content-Disposition", 'attachment; filename=credentials') text="[default]\naws_access_key={id}\naws_secret_access_key={k}\nregion={r}\noutput={o}"\ .format(id=canarydrop['aws_access_key_id'], k=canarydrop['aws_secret_access_key'], r=canarydrop['region'], o=canarydrop['output']) return text elif fmt == 'incidentlist_json': request.setHeader("Content-Type", "text/plain") request.setHeader("Content-Disposition", 'attachment; filename={token}_history.json'\ .format(token=token)) return simplejson.dumps(canarydrop['triggered_list'], indent=4) elif fmt == 'incidentlist_csv': request.setHeader("Content-Type", "text/plain") request.setHeader("Content-Disposition", 'attachment; filename={token}_history.csv'\ .format(token=token)) csvOutput = StringIO() incident_list = canarydrop['triggered_list'] writer = csv.writer(csvOutput) details = [] for key in incident_list: for element in incident_list[key].keys(): details.append(element) headers = ["Timestamp"] + details writer.writerow(headers) items = [] for item in details: for key in incident_list: items.append(incident_list[key][item]) for key in incident_list: data = [ datetime.datetime.fromtimestamp( float(key)).strftime('%Y-%m-%d %H:%M:%S.%s') ] + items writer.writerow(data) return csvOutput.getvalue() except Exception as e: log.err('Unexpected error in download: {err}'.format(err=e)) return NoResource().render(request)
def render_POST(self, request): try: key = request.args["key"][0] token = Canarytoken(value=request.path) canarydrop = Canarydrop(**get_canarydrop(canarytoken=token.value())) # if key and token args are present, we are either: # -posting browser info # -getting an aws trigger (key == aws_s3) # store the info and don't re-render if key and token: if key == "aws_s3": try: canarydrop._drop["hit_time"] = datetime.datetime.utcnow().strftime("%s.%f") src_ip = request.args["RemoteIP"][0] additional_info = { "AWS Log Data": {k: v for k, v in request.args.iteritems() if k not in ["key", "src_ip"]} } self.dispatch(canarydrop=canarydrop, src_ip=src_ip, additional_info=additional_info) except Exception as e: log.err("Error in s3 post: {error}".format(error=e)) elif "secretkeeper_photo" in request.args: log.err("Saving secretkeeper_photo") try: fields = cgi.FieldStorage( fp=request.content, headers=request.getAllHeaders(), environ={"REQUEST_METHOD": "POST", "CONTENT_TYPE": request.getAllHeaders()["content-type"]}, ) # hacky way to parse out file contents and filenames filename = fields["secretkeeper_photo"].filename filebody = fields["secretkeeper_photo"].value if len(filebody) > MAX_UPLOAD_SIZE: raise Exception("File too large") r = hashlib.md5(os.urandom(32)).hexdigest() filepath = os.path.join(WEB_IMAGE_UPLOAD_PATH, r[:2], r[2:]) + ".png" if not os.path.exists(os.path.dirname(filepath)): try: os.makedirs(os.path.dirname(filepath)) except OSError as exc: # Guard against race condition if exc.errno != errno.EEXIST: raise with open(filepath, "w") as f: f.write(filebody) canarydrop.add_additional_info_to_hit( hit_time=key, additional_info={"secretkeeper_photo": filepath} ) except Exception as e: log.err("Error in secretkeeper_photo post: {error}".format(error=e)) else: additional_info = { k: v for k, v in request.args.iteritems() if k not in ["key", "canarytoken", "name"] } canarydrop.add_additional_info_to_hit( hit_time=key, additional_info={request.args["name"][0]: additional_info} ) return "success" else: return self.render_GET(request) except Exception as e: return self.render_GET(request)
def dispatch(self, **kwargs): canarytoken = kwargs.pop('canarytoken') # TODO: If canarydrop no longer exists, delete key -> canarytoken mapping in WireGuard keymap kwargs['canarydrop'] = Canarydrop(**queries.get_canarydrop(canarytoken)) InputChannel.dispatch(self, **kwargs)