Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ElasticSearch support to Kippo #135

Open
wants to merge 28 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
a6071e1
Add proper .gitignore for Python project
ikoniaris Jul 26, 2014
5c04489
Add ElasticSearch section to config file
ikoniaris Jul 26, 2014
05d3207
Add ElasticSearch logger
ikoniaris Jul 26, 2014
b62a527
Implement handleClientVersion to get the version from args
ikoniaris Jul 26, 2014
c714820
Convert id from long to string in ES mapping
ikoniaris Jul 26, 2014
aa25f14
Fix timestamp format for ElasticSearch logging
ikoniaris Jul 26, 2014
ac4c908
Add local GeoIP.dat file
ikoniaris Jul 26, 2014
5f4a8bd
Add private and public key files to gitignore
ikoniaris Jul 26, 2014
c1a4561
Remove redundant lines from kippo config file
ikoniaris Jul 26, 2014
544b895
Fix variable name
ikoniaris Jul 26, 2014
025dea6
Remove commented out code and move GeoIP to run()
ikoniaris Jul 26, 2014
e985616
Remove id from ES mapping and json indexing
ikoniaris Jul 26, 2014
b10f06f
Comment out the ES config section
ikoniaris Jul 26, 2014
7f4692e
Use os.path to open the GeoIP.dat file
ikoniaris Jul 26, 2014
9b43565
Update from upstream
ikoniaris Jul 19, 2015
d02bf97
Merge branch 'desaster-master'
ikoniaris Jul 19, 2015
6d14761
Get uname -a from host system
ikoniaris Jul 22, 2015
222cdd5
Change ES type to "kippo" in config
ikoniaris Jul 23, 2015
d5c32df
Add Elasticsearch output for commands
ikoniaris Jul 23, 2015
16deba4
Add Elasticsearch output for downloads
ikoniaris Jul 23, 2015
8621ce3
Add outfile and url to mapping, use ip type for ip and boolean for su…
ikoniaris Jul 23, 2015
89e977a
Make ES' ip field a multi field
ikoniaris Jul 23, 2015
1ad1a5e
Use new way of defining multi_fields
ikoniaris Jul 23, 2015
4d846e2
Switch ip to string, add new ip.ipv4 field to it
ikoniaris Jul 23, 2015
c768af6
Add generic send_to_elasticsearch method
ikoniaris Jul 23, 2015
db31eb3
Fix index() function
ikoniaris Jul 23, 2015
269348e
Remove ES exception handling, fix variable name
ikoniaris Jul 23, 2015
5ea4b9b
Convert timestampts to UTC
ikoniaris Jul 28, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions kippo.cfg.dist
Original file line number Diff line number Diff line change
Expand Up @@ -199,3 +199,17 @@ interact_port = 5123

#[database_textlog]
#logfile = kippo-textlog.log

# ElasticSearch logging module
#
# Log login attempts to an ElasticSearch instance/cluster in realtime.
# To transfer existing Kippo data to ES see: http://bruteforce.gr/kippo2elasticsearch
#
# To enable this module, remove the comments below, including the
# [database_elasticsearch] line.

#[database_elasticsearch]
#host = 127.0.0.1
#port = 9200
#index = kippo
#type = kippo
7 changes: 4 additions & 3 deletions kippo/commands/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,10 @@ def call(self):
class command_uname(HoneyPotCommand):
def call(self):
if len(self.args) and self.args[0].strip() in ('-a', '--all'):
self.writeln(
'Linux %s 2.6.26-2-686 #1 SMP Wed Nov 4 20:45:37 UTC 2009 i686 GNU/Linux' % \
self.honeypot.hostname)
uname_list = list(os.uname())
uname_list[1] = self.honeypot.hostname
uname_str = " ".join(uname_list)
self.writeln(uname_str)
else:
self.writeln('Linux')
commands['/bin/uname'] = command_uname
Expand Down
154 changes: 154 additions & 0 deletions kippo/dblog/elasticsearch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import collections
import GeoIP
import time
import json
import uuid
import os
import datetime

import pyes
import pyes.exceptions
from twisted.python import log

from kippo.core import dblog


# This is the ES mapping, we mostly need it to mark specific fields as "not_analyzed"
kippo_mapping = {
"client": {
"type": "string",
"index": "not_analyzed"
},
"country": {
"type": "string"
},
"input": {
"type": "string",
"index": "not_analyzed"
},
"ip": {
"type": "string",
"index": "not_analyzed",
"fields": {
"ipv4": {
"type": "ip",
}
}
},
"log_type": {
"type": "string"
},
"outfile": {
"type": "string",
"index": "not_analyzed"
},
"password": {
"type": "string",
"index": "not_analyzed"
},
"sensor": {
"type": "string",
"index": "not_analyzed"
},
"session": {
"type": "string",
"index": "not_analyzed"
},
"success": {
"type": "boolean"
},
"timestamp": {
"type": "date",
"format": "dateOptionalTime"
},
"url": {
"type": "string",
"index": "not_analyzed"
},
"username": {
"type": "string",
"index": "not_analyzed"
}
}


class DBLogger(dblog.DBLogger):
def start(self, cfg):
self.es_host = cfg.get('database_elasticsearch', 'host')
self.es_port = cfg.get('database_elasticsearch', 'port')
self.es_index = cfg.get('database_elasticsearch', 'index')
self.es_type = cfg.get('database_elasticsearch', 'type')
self.es_conn = pyes.ES('{0}:{1}'.format(self.es_host, self.es_port))
self.run(cfg)

def run(self, cfg):
self.geoip = GeoIP.open(os.path.join(os.path.dirname(__file__), "geoip/GeoIP.dat"), GeoIP.GEOIP_STANDARD)
self.es_conn.indices.create_index_if_missing(self.es_index)
self.es_conn.indices.put_mapping(self.es_type, {'properties': kippo_mapping}, [self.es_index])

def createSession(self, peerIP, peerPort, hostIP, hostPort):
self.remote_ip = peerIP
self.sensor_ip = self.getSensor() or hostIP
sid = uuid.uuid1().hex
return sid

def handleClientVersion(self, session, args):
self.client_version = args['version']

def send_to_elasticsearch(self, json_doc):
self.es_conn.index(json_doc, self.es_index, self.es_type)

def handleLoginAttempt(self, session, args, success):
login_dict = collections.OrderedDict()
login_dict['log_type'] = "login_attempt"
login_dict['session'] = session
login_dict['success'] = success
login_dict['username'] = args['username']
login_dict['password'] = args['password']
login_dict['timestamp'] = datetime.datetime.utcnow().isoformat() + 'Z'
login_dict['country'] = self.geoip.country_code_by_addr(self.remote_ip)
login_dict['ip'] = self.remote_ip
login_dict['client'] = self.client_version
login_dict['sensor'] = self.sensor_ip
login_json = json.dumps(login_dict)
self.send_to_elasticsearch(login_json)

def handleLoginFailed(self, session, args):
self.handleLoginAttempt(session, args, 0)

def handleLoginSucceeded(self, session, args):
self.handleLoginAttempt(session, args, 1)

def handleCommandAttempt(self, session, args, success):
command_dict = collections.OrderedDict()
command_dict['log_type'] = "command"
command_dict['session'] = session
command_dict['success'] = success
command_dict['input'] = args['input']
command_dict['timestamp'] = datetime.datetime.utcnow().isoformat() + 'Z'
command_dict['country'] = self.geoip.country_code_by_addr(self.remote_ip)
command_dict['ip'] = self.remote_ip
command_dict['client'] = self.client_version
command_dict['sensor'] = self.sensor_ip
command_json = json.dumps(command_dict)
self.send_to_elasticsearch(command_json)

def handleCommand(self, session, args):
self.handleCommandAttempt(session, args, 1)

def handleUnknownCommand(self, session, args):
self.handleCommandAttempt(session, args, 0)

def handleFileDownload(self, session, args):
download_dict = collections.OrderedDict()
download_dict['log_type'] = "download"
download_dict['session'] = session
download_dict['url'] = args['url']
download_dict['outfile'] = args['outfile']
download_dict['timestamp'] = datetime.datetime.utcnow().isoformat() + 'Z'
download_dict['country'] = self.geoip.country_code_by_addr(self.remote_ip)
download_dict['ip'] = self.remote_ip
download_dict['client'] = self.client_version
download_dict['sensor'] = self.sensor_ip
download_json = json.dumps(download_dict)
self.send_to_elasticsearch(download_json)
Binary file added kippo/dblog/geoip/GeoIP.dat
Binary file not shown.