mirror of
https://github.com/aljazceru/cowrie.git
synced 2026-01-27 18:14:32 +01:00
Merge branch 'master' of https://github.com/micheloosterhof/kippo into logging-2
This commit is contained in:
@@ -24,7 +24,6 @@
|
||||
* fixes for behavior with non-existent files (cd /test, cat /test/nonexistent, etc)
|
||||
* fix for ability to ping/ssh non-existent IP address
|
||||
* always send ssh exit-status 0 on exec and shell
|
||||
* add 1st version of 'netstat'
|
||||
* ls output is now alphabetically sorted
|
||||
* banner_file is deprecated. honeyfs/etc/issue.net is default
|
||||
* add 'dir' alias for 'ls'
|
||||
@@ -32,4 +31,5 @@
|
||||
* add 'users' aliased to 'whoami'
|
||||
* add 'killall' and 'killall5' aliased to nop
|
||||
* add 'poweroff' 'halt' and 'reboot' aliases for shutdown
|
||||
* add 'which' and environment passing to commands
|
||||
* add environment passing to commands
|
||||
* added 'which', 'netstat' and 'gcc' from kippo-extra
|
||||
|
||||
10
README.md
10
README.md
@@ -4,15 +4,6 @@ Kippo is a medium interaction SSH honeypot designed to log brute force attacks a
|
||||
|
||||
Kippo is inspired, but not based on [Kojoney](http://kojoney.sourceforge.net/).
|
||||
|
||||
## Demo
|
||||
|
||||
Some interesting logs from a live Kippo installation below (viewable within a web browser with the help of Ajaxterm). Note that some commands may have been improved since these logs were recorded.
|
||||
|
||||
* [2009-11-22](http://kippo.rpg.fi/playlog/?l=20091122-075013-5055.log)
|
||||
* [2009-11-23](http://kippo.rpg.fi/playlog/?l=20091123-003854-3359.log)
|
||||
* [2009-11-23](http://kippo.rpg.fi/playlog/?l=20091123-012814-626.log)
|
||||
* [2010-03-16](http://kippo.rpg.fi/playlog/?l=20100316-233121-1847.log)
|
||||
|
||||
## Features
|
||||
|
||||
Some interesting features:
|
||||
@@ -20,7 +11,6 @@ Some interesting features:
|
||||
* Possibility of adding fake file contents so the attacker can 'cat' files such as /etc/passwd. Only minimal file contents are included
|
||||
* Session logs stored in an [UML Compatible](http://user-mode-linux.sourceforge.net/) format for easy replay with original timings
|
||||
* Just like Kojoney, Kippo saves files downloaded with wget for later inspection
|
||||
* Trickery; ssh pretends to connect somewhere, exit doesn't really exit, etc
|
||||
|
||||
## Requirements
|
||||
|
||||
|
||||
@@ -1 +1,8 @@
|
||||
root:0:123456
|
||||
# This file contains user authorizations.
|
||||
# To allow all passwords, use '*'
|
||||
# To deny specific passwords, user '!password'
|
||||
# The file is processed linearly, denials need to happen before allows.
|
||||
# Default config allows all root passwords except 'root' and '123456'
|
||||
root:0:!root
|
||||
root:0:!123456
|
||||
root:0:*
|
||||
|
||||
@@ -18,4 +18,6 @@ __all__ = [
|
||||
'malware',
|
||||
'netstat',
|
||||
'which',
|
||||
'gcc',
|
||||
'iptables'
|
||||
]
|
||||
|
||||
274
kippo/commands/gcc.py
Normal file
274
kippo/commands/gcc.py
Normal file
@@ -0,0 +1,274 @@
|
||||
# Copyright (c) 2013 Bas Stottelaar <basstottelaar [AT] gmail [DOT] com>
|
||||
|
||||
import time
|
||||
import re
|
||||
import getopt
|
||||
import random
|
||||
|
||||
from twisted.internet import reactor
|
||||
from kippo.core.honeypot import HoneyPotCommand
|
||||
|
||||
commands = {}
|
||||
|
||||
class command_gcc(HoneyPotCommand):
|
||||
# Name of program. Under OSX, you might consider i686-apple-darwin11-llvm-gcc-X.X
|
||||
APP_NAME = "gcc"
|
||||
|
||||
# GCC verson, used in help, version and the commandline name gcc-X.X
|
||||
APP_VERSION = (4, 7, 2)
|
||||
|
||||
# Random binary data, which looks awesome. You could change this to whatever you want, but this
|
||||
# data will be put in the actual file and thus exposed to our hacker when he\she cats the file.
|
||||
RANDOM_DATA = "\x6a\x00\x48\x89\xe5\x48\x83\xe4\xf0\x48\x8b\x7d\x08\x48\x8d\x75\x10\x89\xfa" \
|
||||
"\x83\xc2\x01\xc1\xe2\x03\x48\x01\xf2\x48\x89\xd1\xeb\x04\x48\x83\xc1\x08\x48" \
|
||||
"\x83\x39\x00\x75\xf6\x48\x83\xc1\x08\xe8\x0c\x00\x00\x00\x89\xc7\xe8\xb9\x00" \
|
||||
"\x00\x00\xf4\x90\x90\x90\x90\x55\x48\x89\xe5\x48\x83\xec\x40\x89\x7d\xfc\x48" \
|
||||
"\x89\x75\xf0\x48\x8b\x45\xf0\x48\x8b\x00\x48\x83\xf8\x00\x75\x0c\xb8\x00\x00" \
|
||||
"\x00\x00\x89\xc7\xe8\x8c\x00\x00\x00\x48\x8b\x45\xf0\x48\x8b\x40\x08\x30\xc9" \
|
||||
"\x48\x89\xc7\x88\xc8\xe8\x7e\x00\x00\x00\x89\xc1\x89\x4d\xdc\x48\x8d\x0d\xd8" \
|
||||
"\x01\x00\x00\x48\x89\xcf\x48\x89\x4d\xd0\xe8\x72\x00\x00\x00\x8b\x4d\xdc\x30" \
|
||||
"\xd2\x48\x8d\x3d\xa4\x00\x00\x00\x89\xce\x88\x55\xcf\x48\x89\xc2\x8a\x45\xcf" \
|
||||
"\xe8\x53\x00\x00\x00\x8b\x45\xdc\x88\x05\xc3\x01\x00\x00\x8b\x45\xdc\xc1\xe8" \
|
||||
"\x08\x88\x05\xb8\x01\x00\x00\x8b\x45\xdc\xc1\xe8\x10\x88\x05\xad\x01\x00\x00" \
|
||||
"\x8b\x45\xdc\xc1\xe8\x18\x88\x05\xa2\x01\x00\x00\x48\x8b\x45\xd0\x48\x89\x45" \
|
||||
"\xe0\x48\x8b\x45\xe0\xff\xd0\x8b\x45\xec\x48\x83\xc4\x40\x5d\xc3\xff\x25\x3e" \
|
||||
"\x01\x00\x00\xff\x25\x40\x01\x00\x00\xff\x25\x42\x01\x00\x00\xff\x25\x44\x01" \
|
||||
"\x00\x00\x4c\x8d\x1d\x1d\x01\x00\x00\x41\x53\xff\x25\x0d\x01\x00\x00\x90\x68" \
|
||||
"\x00\x00\x00\x00\xe9\xe6\xff\xff\xff\x68\x0c\x00\x00\x00\xe9\xdc\xff\xff\xff" \
|
||||
"\x68\x1d\x00\x00\x00\xe9\xd2\xff\xff\xff\x68\x2b\x00\x00\x00\xe9\xc8\xff\xff" \
|
||||
"\xff\x01\x00\x00\x00\x1c\x00\x00\x00\x00\x00\x00\x00\x1c\x00\x00\x00\x00\x00" \
|
||||
"\x00\x00\x1c\x00\x00\x00\x02\x00\x00\x00\x00\x0e\x00\x00\x34\x00\x00\x00\x34" \
|
||||
"\x00\x00\x00\xf5\x0e\x00\x00\x00\x00\x00\x00\x34\x00\x00\x00\x03\x00\x00\x00" \
|
||||
"\x0c\x00\x02\x00\x14\x00\x02\x00\x00\x00\x00\x01\x40\x00\x00\x00\x00\x00\x00" \
|
||||
"\x01\x00\x00\x00"
|
||||
|
||||
def start(self):
|
||||
"""
|
||||
Parse as much as possible from a GCC syntax and generate the output
|
||||
that is requested. The file that is generated can be read (and will)
|
||||
output garbage from an actual file, but when executed, it will generate
|
||||
a segmentation fault.
|
||||
|
||||
The input files are expected to exists, but can be empty.
|
||||
|
||||
Verified syntaxes, including non-existing files:
|
||||
* gcc test.c
|
||||
* gcc test.c -o program
|
||||
* gcc test1.c test2.c
|
||||
* gcc test1.c test2.c -o program
|
||||
* gcc test.c -o program -lm
|
||||
* gcc -g test.c -o program -lm
|
||||
* gcc test.c -DF_CPU=16000000 -I../etc -o program
|
||||
* gcc test.c -O2 -o optimized_program
|
||||
* gcc test.c -Wstrict-overflow=n -o overflowable_program
|
||||
|
||||
Others:
|
||||
* gcc
|
||||
* gcc -h
|
||||
* gcc -v
|
||||
* gcc --help
|
||||
* gcc --version
|
||||
"""
|
||||
|
||||
output_file = None
|
||||
input_files = 0
|
||||
complete = True
|
||||
|
||||
# Parse options or display no files
|
||||
try:
|
||||
opts, args = getopt.gnu_getopt(self.args, 'ESchvgo:x:l:I:W:D:X:O:', ['help', 'version', 'param'])
|
||||
except getopt.GetoptError as err:
|
||||
self.no_files()
|
||||
return
|
||||
|
||||
# Parse options
|
||||
for o, a in opts:
|
||||
if o in ("-v"):
|
||||
self.version(short=False)
|
||||
return
|
||||
elif o in ("--version"):
|
||||
self.version(short=True)
|
||||
return
|
||||
elif o in ("-h"):
|
||||
self.arg_missing("-h")
|
||||
return
|
||||
elif o in ("--help"):
|
||||
self.help()
|
||||
return
|
||||
elif o in ("-o"):
|
||||
if len(a) == 0:
|
||||
self.arg_missing("-o")
|
||||
else:
|
||||
output_file = a
|
||||
|
||||
# Check for *.c or *.cpp files
|
||||
for value in args:
|
||||
if '.c' in value.lower():
|
||||
sourcefile = self.fs.resolve_path(value, self.honeypot.cwd)
|
||||
|
||||
if self.fs.exists(sourcefile):
|
||||
input_files = input_files + 1
|
||||
else:
|
||||
self.writeln("%s: %s: No such file or directory" % (command_gcc.APP_NAME, value))
|
||||
complete = False
|
||||
|
||||
# To generate, or not
|
||||
if input_files > 0 and complete:
|
||||
func = lambda: self.generate_file(output_file if output_file else 'a.out')
|
||||
timeout = 0.1 + random.random()
|
||||
|
||||
# Schedule call to make it more time consuming and real
|
||||
self.scheduled = reactor.callLater(timeout, func)
|
||||
else:
|
||||
self.no_files()
|
||||
|
||||
def ctrl_c(self):
|
||||
""" Make sure the scheduled call will be canceled """
|
||||
|
||||
if getattr(self, 'scheduled', False):
|
||||
self.scheduled.cancel()
|
||||
|
||||
def no_files(self):
|
||||
""" Notify user there are no input files, and exit """
|
||||
self.writeln( """gcc: fatal error: no input files
|
||||
compilation terminated.""" )
|
||||
self.exit()
|
||||
|
||||
|
||||
def version(self, short):
|
||||
""" Print long or short version, and exit """
|
||||
|
||||
# Generate version number
|
||||
version = '.'.join([ str(v) for v in command_gcc.APP_VERSION[:3] ])
|
||||
version_short = '.'.join([ str(v) for v in command_gcc.APP_VERSION[:2] ])
|
||||
|
||||
if short:
|
||||
data = ( """%s (Debian %s-8) %s
|
||||
Copyright (C) 2010 Free Software Foundation, Inc.
|
||||
This is free software; see the source for copying conditions. There is NO
|
||||
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.""", (command_gcc.APP_NAME, version, version) )
|
||||
else:
|
||||
data = ( """Using built-in specs.
|
||||
COLLECT_GCC=gcc
|
||||
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.7/lto-wrapper
|
||||
Target: x86_64-linux-gnu
|
||||
Configured with: ../src/configure -v --with-pkgversion=\'Debian %s-5\' --with-bugurl=file:///usr/share/doc/gcc-%s/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-%s --enable-shared --enable-multiarch --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/%s --libdir=/usr/lib --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-objc-gc --with-arch-32=i586 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
|
||||
Thread model: posix
|
||||
gcc version %s (Debian %s-5)""" % (version, version_short, version_short, version_short, version, version))
|
||||
|
||||
# Write
|
||||
self.writeln(data)
|
||||
self.exit()
|
||||
|
||||
def generate_file(self, outfile):
|
||||
data = ""
|
||||
# TODO: make sure it is written to temp file, not downloads
|
||||
safeoutfile = '%s/%s_%s' % \
|
||||
(self.honeypot.env.cfg.get('honeypot', 'download_path'),
|
||||
time.strftime('%Y%m%d%H%M%S'),
|
||||
re.sub('[^A-Za-z0-9]', '_', outfile))
|
||||
|
||||
# Data contains random garbage from an actual file, so when
|
||||
# catting the file, you'll see some 'real' compiled data
|
||||
for i in range(random.randint(3, 15)):
|
||||
if random.randint(1, 3) == 1:
|
||||
data = data + command_gcc.RANDOM_DATA[::-1]
|
||||
else:
|
||||
data = data + command_gcc.RANDOM_DATA
|
||||
|
||||
# Write random data
|
||||
with open(safeoutfile, 'wb') as f: f.write(data)
|
||||
|
||||
# Output file
|
||||
outfile = self.fs.resolve_path(outfile, self.honeypot.cwd)
|
||||
|
||||
# Create file for the honeypot
|
||||
self.fs.mkfile(outfile, 0, 0, len(data), 33188)
|
||||
self.fs.update_realfile(self.fs.getfile(outfile), safeoutfile)
|
||||
|
||||
# Segfault command
|
||||
class segfault_command(HoneyPotCommand):
|
||||
def call(self):
|
||||
self.write("Segmentation fault\n")
|
||||
|
||||
# Trick the 'new compiled file' as an segfault
|
||||
self.honeypot.commands[outfile] = segfault_command
|
||||
|
||||
# Done
|
||||
self.exit()
|
||||
|
||||
def arg_missing(self, arg):
|
||||
""" Print missing argument message, and exit """
|
||||
self.writeln("%s: argument to '%s' is missing" % (command_gcc.APP_NAME, arg))
|
||||
self.exit()
|
||||
|
||||
def help(self):
|
||||
""" Print help info, and exit """
|
||||
|
||||
version = '.'.join([ str(v) for v in command_gcc.APP_VERSION[:2] ])
|
||||
|
||||
self.writeln( """Usage: gcc [options] file...
|
||||
Options:
|
||||
-pass-exit-codes Exit with highest error code from a phase
|
||||
--help Display this information
|
||||
--target-help Display target specific command line options
|
||||
--help={common|optimizers|params|target|warnings|[^]{joined|separate|undocumented}}[,...]
|
||||
Display specific types of command line options
|
||||
(Use '-v --help' to display command line options of sub-processes)
|
||||
--version Display compiler version information
|
||||
-dumpspecs Display all of the built in spec strings
|
||||
-dumpversion Display the version of the compiler
|
||||
-dumpmachine Display the compiler's target processor
|
||||
-print-search-dirs Display the directories in the compiler's search path
|
||||
-print-libgcc-file-name Display the name of the compiler's companion library
|
||||
-print-file-name=<lib> Display the full path to library <lib>
|
||||
-print-prog-name=<prog> Display the full path to compiler component <prog>
|
||||
-print-multiarch Display the target's normalized GNU triplet, used as
|
||||
a component in the library path
|
||||
-print-multi-directory Display the root directory for versions of libgcc
|
||||
-print-multi-lib Display the mapping between command line options and
|
||||
multiple library search directories
|
||||
-print-multi-os-directory Display the relative path to OS libraries
|
||||
-print-sysroot Display the target libraries directory
|
||||
-print-sysroot-headers-suffix Display the sysroot suffix used to find headers
|
||||
-Wa,<options> Pass comma-separated <options> on to the assembler
|
||||
-Wp,<options> Pass comma-separated <options> on to the preprocessor
|
||||
-Wl,<options> Pass comma-separated <options> on to the linker
|
||||
-Xassembler <arg> Pass <arg> on to the assembler
|
||||
-Xpreprocessor <arg> Pass <arg> on to the preprocessor
|
||||
-Xlinker <arg> Pass <arg> on to the linker
|
||||
-save-temps Do not delete intermediate files
|
||||
-save-temps=<arg> Do not delete intermediate files
|
||||
-no-canonical-prefixes Do not canonicalize paths when building relative
|
||||
prefixes to other gcc components
|
||||
-pipe Use pipes rather than intermediate files
|
||||
-time Time the execution of each subprocess
|
||||
-specs=<file> Override built-in specs with the contents of <file>
|
||||
-std=<standard> Assume that the input sources are for <standard>
|
||||
--sysroot=<directory> Use <directory> as the root directory for headers
|
||||
and libraries
|
||||
-B <directory> Add <directory> to the compiler's search paths
|
||||
-v Display the programs invoked by the compiler
|
||||
-### Like -v but options quoted and commands not executed
|
||||
-E Preprocess only; do not compile, assemble or link
|
||||
-S Compile only; do not assemble or link
|
||||
-c Compile and assemble, but do not link
|
||||
-o <file> Place the output into <file>
|
||||
-pie Create a position independent executable
|
||||
-shared Create a shared library
|
||||
-x <language> Specify the language of the following input files
|
||||
Permissible languages include: c c++ assembler none
|
||||
'none' means revert to the default behavior of
|
||||
guessing the language based on the file's extension
|
||||
|
||||
Options starting with -g, -f, -m, -O, -W, or --param are automatically
|
||||
passed on to the various sub-processes invoked by gcc. In order to pass
|
||||
other options on to these processes the -W<letter> options must be used.
|
||||
|
||||
For bug reporting instructions, please see:
|
||||
<file:///usr/share/doc/gcc-4.7/README.Bugs>.""")
|
||||
self.exit()
|
||||
|
||||
# Definitions
|
||||
commands['/usr/bin/gcc'] = command_gcc
|
||||
commands['/usr/bin/gcc-%s' % ('.'.join([ str(v) for v in command_gcc.APP_VERSION[:2] ]))] = command_gcc
|
||||
414
kippo/commands/iptables.py
Normal file
414
kippo/commands/iptables.py
Normal file
@@ -0,0 +1,414 @@
|
||||
# Copyright (c) 2013 Bas Stottelaar <basstottelaar [AT] gmail [DOT] com>
|
||||
|
||||
import optparse
|
||||
|
||||
from twisted.internet import reactor
|
||||
from kippo.core.honeypot import HoneyPotCommand
|
||||
|
||||
commands = {}
|
||||
|
||||
class OptionParsingError(RuntimeError):
|
||||
def __init__(self, msg):
|
||||
self.msg = msg
|
||||
|
||||
class OptionParsingExit(Exception):
|
||||
def __init__(self, status, msg):
|
||||
self.msg = msg
|
||||
self.status = status
|
||||
|
||||
class ModifiedOptionParser(optparse.OptionParser):
|
||||
def error(self, msg):
|
||||
raise OptionParsingError(msg)
|
||||
|
||||
def exit(self, status=0, msg=None):
|
||||
raise OptionParsingExit(status, msg)
|
||||
|
||||
|
||||
class command_iptables(HoneyPotCommand):
|
||||
# Do not resolve args
|
||||
resolve_args = False
|
||||
|
||||
# iptables app name
|
||||
APP_NAME = "iptables"
|
||||
|
||||
# iptables app version, used in help messages etc.
|
||||
APP_VERSION = "v1.4.14"
|
||||
|
||||
# Default iptable table
|
||||
DEFAULT_TABLE = "filter"
|
||||
|
||||
def user_is_root(self):
|
||||
return self.honeypot.user.username == 'root'
|
||||
|
||||
def start(self):
|
||||
"""
|
||||
Emulate iptables commands, including permission checking.
|
||||
|
||||
Verified examples:
|
||||
* iptables -A OUTPUT -o eth0 -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT
|
||||
* iptables -A INPUT -i eth0 -p tcp -s "127.0.0.1" -j DROP
|
||||
|
||||
Others:
|
||||
* iptables
|
||||
* iptables [[-t | --table] <name>] [-h | --help]
|
||||
* iptables [[-t | --table] <name>] [-v | --version]
|
||||
* iptables [[-t | --table] <name>] [-F | --flush] <chain>
|
||||
* iptables [[-t | --table] <name>] [-L | --list] <chain>
|
||||
* iptables [[-t | --table] <name>] [-S | --list-rules] <chain>
|
||||
* iptables --this-is-invalid
|
||||
"""
|
||||
|
||||
# In case of no arguments
|
||||
if len(self.args) == 0:
|
||||
self.no_command()
|
||||
return
|
||||
|
||||
# Utils
|
||||
def optional_arg(arg_default):
|
||||
def func(option,opt_str,value,parser):
|
||||
if parser.rargs and not parser.rargs[0].startswith('-'):
|
||||
val=parser.rargs[0]
|
||||
parser.rargs.pop(0)
|
||||
else:
|
||||
val=arg_default
|
||||
setattr(parser.values,option.dest,val)
|
||||
return func
|
||||
|
||||
# Initialize options
|
||||
parser = ModifiedOptionParser(add_help_option=False)
|
||||
parser.add_option("-h", "--help", dest="help", action="store_true")
|
||||
parser.add_option("-V", "--version", dest="version", action="store_true")
|
||||
parser.add_option("-v", "--verbose", dest="verbose", action="store_true")
|
||||
parser.add_option("-x", "--exact", dest="exact", action="store_true")
|
||||
parser.add_option("--line-numbers", dest="line_numbers", action="store_true")
|
||||
parser.add_option("--modprobe", dest="modprobe", action="store")
|
||||
|
||||
parser.add_option("-t", "--table", dest="table", action="store", default=command_iptables.DEFAULT_TABLE)
|
||||
parser.add_option("-F", "--flush", dest="flush", action="callback", callback=optional_arg(True))
|
||||
parser.add_option("-Z", "--zero", dest="zero", action="callback", callback=optional_arg(True))
|
||||
parser.add_option("-S", "--list-rules", dest="list_rules", action="callback", callback=optional_arg(True))
|
||||
parser.add_option("-L", "--list", dest="list", action="callback", callback=optional_arg(True))
|
||||
parser.add_option("-A", "--append", dest="append", action="store")
|
||||
parser.add_option("-D", "--delete", dest="delete", action="store")
|
||||
parser.add_option("-I", "--insert", dest="insert", action="store")
|
||||
parser.add_option("-R", "--replace", dest="replace", action="store")
|
||||
parser.add_option("-N", "--new-chain", dest="new_chain", action="store")
|
||||
parser.add_option("-X", "--delete-chain", dest="delete_chain", action="store")
|
||||
parser.add_option("-P", "--policy", dest="policy", action="store")
|
||||
parser.add_option("-E", "--rename-chain", dest="rename_chain", action="store")
|
||||
|
||||
parser.add_option("-p", "--protocol", dest="protocol", action="store")
|
||||
parser.add_option("-s", "--source", dest="source", action="store")
|
||||
parser.add_option("-d", "--destination", dest="destination", action="store")
|
||||
parser.add_option("-j", "--jump", dest="jump", action="store")
|
||||
parser.add_option("-g", "--goto", dest="goto", action="store")
|
||||
parser.add_option("-i", "--in-interface", dest="in_interface", action="store")
|
||||
parser.add_option("-o", "--out-interface", dest="out_interface", action="store")
|
||||
parser.add_option("-f", "--fragment", dest="fragment", action="store_true")
|
||||
parser.add_option("-c", "--set-counters", dest="set_counters", action="store")
|
||||
parser.add_option("-m", "--match", dest="match", action="store")
|
||||
|
||||
parser.add_option("--sport", "--source-ports", dest="source_ports", action="store")
|
||||
parser.add_option("--dport", "--destination-ports", dest="dest_ports", action="store")
|
||||
parser.add_option("--ports", dest="ports", action="store")
|
||||
parser.add_option("--state", dest="state", action="store")
|
||||
|
||||
|
||||
# Parse options or display no files
|
||||
try:
|
||||
(opts, args) = parser.parse_args(list(self.args))
|
||||
except OptionParsingError, e:
|
||||
self.bad_argument(self.args[0])
|
||||
return
|
||||
except OptionParsingExit, e:
|
||||
self.unknown_option(e)
|
||||
return
|
||||
|
||||
# Initialize table
|
||||
if not self.setup_table(opts.table):
|
||||
return
|
||||
|
||||
# Parse options
|
||||
if opts.help:
|
||||
self.show_help()
|
||||
return
|
||||
elif opts.version:
|
||||
self.show_version()
|
||||
return
|
||||
elif opts.flush:
|
||||
self.flush("" if opts.flush == True else opts.flush)
|
||||
return
|
||||
elif opts.list:
|
||||
self.list("" if opts.list == True else opts.list)
|
||||
return
|
||||
elif opts.list_rules:
|
||||
self.list_rules("" if opts.list_rules == True else opts.list_rules)
|
||||
return
|
||||
|
||||
# Done
|
||||
self.exit()
|
||||
|
||||
def setup_table(self, table):
|
||||
"""
|
||||
Called during startup to make sure the current environment has some
|
||||
fake rules in memory.
|
||||
"""
|
||||
|
||||
# Create fresh tables on start
|
||||
if not hasattr(self.honeypot.env, 'iptables'):
|
||||
setattr(self.honeypot.env, 'iptables', {
|
||||
"raw": {
|
||||
"PREROUTING": [],
|
||||
"OUTPUT": []
|
||||
},
|
||||
"filter": {
|
||||
"INPUT": [
|
||||
('ACCEPT', 'tcp', '--', 'anywhere', 'anywhere', 'tcp', 'dpt:ssh'),
|
||||
('DROP', 'all', '--', 'anywhere', 'anywhere', '', '')
|
||||
],
|
||||
"FORWARD": [],
|
||||
"OUTPUT": []
|
||||
},
|
||||
"mangle": {
|
||||
"PREROUTING": [],
|
||||
"INPUT": [],
|
||||
"FORWARD": [],
|
||||
"OUTPUT": [],
|
||||
"POSTROUTING": []
|
||||
},
|
||||
"nat": {
|
||||
"PREROUTING": [],
|
||||
"OUTPUT": []
|
||||
}
|
||||
})
|
||||
|
||||
# Get the tables
|
||||
self.tables = getattr(self.honeypot.env, 'iptables')
|
||||
|
||||
# Verify selected table
|
||||
if not self.is_valid_table(table):
|
||||
return False
|
||||
|
||||
# Set table
|
||||
self.current_table = self.tables[table]
|
||||
|
||||
# Done
|
||||
return True
|
||||
|
||||
def is_valid_table(self, table):
|
||||
if self.user_is_root():
|
||||
# Verify table existence
|
||||
if not table in self.tables.iterkeys():
|
||||
self.writeln( """%s: can\'t initialize iptables table \'%s\': Table does not exist (do you need to insmod?)
|
||||
Perhaps iptables or your kernel needs to be upgraded.""" % (command_iptables.APP_NAME, table) )
|
||||
self.exit()
|
||||
else:
|
||||
# Exists
|
||||
return True
|
||||
else:
|
||||
self.no_permission()
|
||||
|
||||
# Failed
|
||||
return False
|
||||
|
||||
def is_valid_chain(self, chain):
|
||||
# Verify chain existence. Requires valid table first
|
||||
if not chain in self.current_table.iterkeys():
|
||||
self.writeln("%s: No chain/target/match by that name." % command_iptables.APP_NAME)
|
||||
self.exit()
|
||||
return False
|
||||
|
||||
# Exists
|
||||
return True
|
||||
|
||||
def show_version(self):
|
||||
""" Show version and exit """
|
||||
self.writeln('%s %s' % (command_iptables.APP_NAME, command_iptables.APP_VERSION))
|
||||
self.exit()
|
||||
|
||||
def show_help(self):
|
||||
""" Show help and exit """
|
||||
|
||||
self.writeln( """%s %s'
|
||||
|
||||
Usage: iptables -[AD] chain rule-specification [options]
|
||||
iptables -I chain [rulenum] rule-specification [options]
|
||||
iptables -R chain rulenum rule-specification [options]
|
||||
iptables -D chain rulenum [options]
|
||||
iptables -[LS] [chain [rulenum]] [options]
|
||||
iptables -[FZ] [chain] [options]
|
||||
iptables -[NX] chain
|
||||
iptables -E old-chain-name new-chain-name
|
||||
iptables -P chain target [options]
|
||||
iptables -h (print this help information)
|
||||
|
||||
Commands:
|
||||
Either long or short options are allowed.
|
||||
--append -A chain Append to chain
|
||||
--delete -D chain Delete matching rule from chain
|
||||
--delete -D chain rulenum
|
||||
Delete rule rulenum (1 = first) from chain
|
||||
--insert -I chain [rulenum]
|
||||
Insert in chain as rulenum (default 1=first)
|
||||
--replace -R chain rulenum
|
||||
Replace rule rulenum (1 = first) in chain
|
||||
--list -L [chain [rulenum]]
|
||||
List the rules in a chain or all chains
|
||||
--list-rules -S [chain [rulenum]]
|
||||
Print the rules in a chain or all chains
|
||||
--flush -F [chain] Delete all rules in chain or all chains
|
||||
--zero -Z [chain [rulenum]]
|
||||
Zero counters in chain or all chains
|
||||
--new -N chain Create a new user-defined chain
|
||||
--delete-chain
|
||||
-X [chain] Delete a user-defined chain
|
||||
--policy -P chain target
|
||||
Change policy on chain to target
|
||||
--rename-chain
|
||||
-E old-chain new-chain
|
||||
Change chain name, (moving any references)
|
||||
Options:
|
||||
[!] --proto -p proto protocol: by number or name, eg. \'tcp\'
|
||||
[!] --source -s address[/mask][...]
|
||||
source specification
|
||||
[!] --destination -d address[/mask][...]
|
||||
destination specification
|
||||
[!] --in-interface -i input name[+]
|
||||
network interface name ([+] for wildcard)
|
||||
--jump -j target
|
||||
target for rule (may load target extension)
|
||||
--goto -g chain
|
||||
jump to chain with no return
|
||||
--match -m match
|
||||
extended match (may load extension)
|
||||
--numeric -n numeric output of addresses and ports
|
||||
[!] --out-interface -o output name[+]
|
||||
network interface name ([+] for wildcard)
|
||||
--table -t table table to manipulate (default: \'filter\')
|
||||
--verbose -v verbose mode
|
||||
--line-numbers print line numbers when listing
|
||||
--exact -x expand numbers (display exact values)
|
||||
[!] --fragment -f match second or further fragments only
|
||||
--modprobe=<command> try to insert modules using this command
|
||||
--set-counters PKTS BYTES set the counter during insert/append
|
||||
[!] --version -V print package version.""" % (command_iptables.APP_NAME, command_iptables.APP_VERSION))
|
||||
self.exit()
|
||||
|
||||
def list_rules(self, chain):
|
||||
""" List current rules as commands"""
|
||||
|
||||
if self.user_is_root():
|
||||
if len(chain) > 0:
|
||||
print chain
|
||||
# Check chain
|
||||
if not self.is_valid_chain(chain):
|
||||
return
|
||||
|
||||
chains = [chain]
|
||||
else:
|
||||
chains = self.current_table.iterkeys()
|
||||
|
||||
# Output buffer
|
||||
output = []
|
||||
|
||||
for chain in chains:
|
||||
output.append("-P %s ACCEPT" % chain)
|
||||
|
||||
# Done
|
||||
self.writeln(output)
|
||||
self.exit()
|
||||
else:
|
||||
self.no_permission()
|
||||
|
||||
def list(self, chain):
|
||||
""" List current rules """
|
||||
|
||||
if self.user_is_root():
|
||||
if len(chain) > 0:
|
||||
print chain
|
||||
# Check chain
|
||||
if not self.is_valid_chain(chain):
|
||||
return
|
||||
|
||||
chains = [chain]
|
||||
else:
|
||||
chains = self.current_table.iterkeys()
|
||||
|
||||
# Output buffer
|
||||
output = []
|
||||
|
||||
for chain in chains:
|
||||
# Chain table header
|
||||
chain_output = [
|
||||
"Chain %s (policy ACCEPT)" % chain,
|
||||
"target prot opt source destination",
|
||||
]
|
||||
|
||||
# Format the rules
|
||||
for rule in self.current_table[chain]:
|
||||
chain_output.append(
|
||||
"%-10s %-4s %-3s %-20s %-20s %s %s" % rule,
|
||||
)
|
||||
|
||||
# Create one string
|
||||
output.append("\n".join(chain_output))
|
||||
|
||||
# Done
|
||||
self.writeln("\n\n".join(output))
|
||||
self.exit()
|
||||
else:
|
||||
self.no_permission()
|
||||
|
||||
def flush(self, chain):
|
||||
""" Mark rules as flushed """
|
||||
|
||||
if self.user_is_root():
|
||||
if len(chain) > 0:
|
||||
# Check chain
|
||||
if not self.is_valid_chain(chain):
|
||||
return
|
||||
|
||||
chains = [chain]
|
||||
else:
|
||||
chains = self.current_table.iterkeys()
|
||||
|
||||
# Flush
|
||||
for chain in chains:
|
||||
self.current_table[chain] = []
|
||||
|
||||
self.exit()
|
||||
else:
|
||||
self.no_permission()
|
||||
|
||||
def no_permission(self):
|
||||
self.writeln( """%s %s: can\'t initialize iptables table \'filter\': Permission denied (you must be root)
|
||||
Perhaps iptables or your kernel needs to be upgraded."""
|
||||
% (command_iptables.APP_NAME, command_iptables.APP_VERSION) )
|
||||
self.exit()
|
||||
|
||||
def no_command(self):
|
||||
""" Print no command message and exit """
|
||||
|
||||
self.writeln( """%s %s: no command specified'
|
||||
Try `iptables -h\' or \'iptables --help\' for more information."""
|
||||
% (command_iptables.APP_NAME, command_iptables.APP_VERSION) )
|
||||
self.exit()
|
||||
|
||||
def unknown_option(self, option):
|
||||
""" Print unknown option message and exit """
|
||||
|
||||
self.writeln( """%s %s: unknown option \'%s\''
|
||||
Try `iptables -h\' or \'iptables --help\' for more information."""
|
||||
% (command_iptables.APP_NAME, command_iptables.APP_VERSION, option) )
|
||||
self.exit()
|
||||
|
||||
def bad_argument(self, argument):
|
||||
""" Print bad argument and exit """
|
||||
|
||||
self.writeln( """Bad argument \'%s\'' % argument,
|
||||
Try `iptables -h\' or \'iptables --help\' for more information."""
|
||||
% argument )
|
||||
self.exit()
|
||||
|
||||
# Definition
|
||||
commands['/sbin/iptables'] = command_iptables
|
||||
@@ -41,6 +41,9 @@ class UserDB(object):
|
||||
if not line:
|
||||
continue
|
||||
|
||||
if line.startswith( '#' ):
|
||||
continue
|
||||
|
||||
(login, uid_str, passwd) = line.split(':', 2)
|
||||
|
||||
uid = 0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* From https://github.com/threatstream/kippo/blob/master/kippo/dblog/hpfeeds.py */
|
||||
## From https://github.com/threatstream/kippo/blob/master/kippo/dblog/hpfeeds.py
|
||||
|
||||
from kippo.core import dblog
|
||||
from twisted.python import log
|
||||
|
||||
67
utils/elk/README.md
Normal file
67
utils/elk/README.md
Normal file
@@ -0,0 +1,67 @@
|
||||
# How to process Kippo output in an ELK stack
|
||||
|
||||
(Note: work in progress, instructions are not verified)
|
||||
|
||||
|
||||
## Prerequisites
|
||||
|
||||
* Working Kippo installation
|
||||
* Kippo JSON log file (enable database json in kippo.cfg)
|
||||
|
||||
## Installation
|
||||
|
||||
* Install logstash, elasticsearch and kibana
|
||||
|
||||
```
|
||||
apt-get install logstash
|
||||
apt-get install elasticsearch
|
||||
````
|
||||
|
||||
* Install Kibana
|
||||
|
||||
This may be different depending on your operating system. Kibana will need additional components such as a web server
|
||||
|
||||
|
||||
## ElasticSearch Configuration
|
||||
|
||||
TBD
|
||||
|
||||
## Logstash Configuration
|
||||
|
||||
* Download GeoIP data
|
||||
|
||||
```
|
||||
wget http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz
|
||||
wget http://download.maxmind.com/download/geoip/database/asnum/GeoIPASNum.dat.gz
|
||||
```
|
||||
|
||||
* Place these somewhere in your filesystem.
|
||||
|
||||
* Configure logstash
|
||||
|
||||
```
|
||||
cp logstash-kippo.conf /etc/logstash/conf.d
|
||||
```
|
||||
|
||||
* Make sure the configuration file is correct. Check the input section (path), filter (geoip databases) and output (elasticsearch hostname)
|
||||
|
||||
```
|
||||
service logstash restart
|
||||
```
|
||||
|
||||
* By default the logstash is creating debug logs in /tmp.
|
||||
|
||||
* To test whether logstash is working correctly, check the file in /tmp
|
||||
|
||||
```
|
||||
tail /tmp/kippo-logstash.log
|
||||
```
|
||||
|
||||
* To test whether data is loaded into ElasticSearch, run the following query:
|
||||
|
||||
```
|
||||
http://<hostname>:9200/_search?q=kippo&size=5
|
||||
```
|
||||
|
||||
* If this gives output, your data is correctly loaded into ElasticSearch
|
||||
|
||||
@@ -18,8 +18,31 @@
|
||||
]
|
||||
},
|
||||
"filter": {
|
||||
"list": {},
|
||||
"ids": []
|
||||
"list": {
|
||||
"0": {
|
||||
"type": "terms",
|
||||
"field": "_type",
|
||||
"value": "kippo",
|
||||
"mandate": "must",
|
||||
"active": true,
|
||||
"alias": "",
|
||||
"id": 0
|
||||
},
|
||||
"1": {
|
||||
"type": "time",
|
||||
"field": "@timestamp",
|
||||
"from": "now-30d",
|
||||
"to": "now",
|
||||
"mandate": "must",
|
||||
"active": true,
|
||||
"alias": "",
|
||||
"id": 1
|
||||
}
|
||||
},
|
||||
"ids": [
|
||||
0,
|
||||
1
|
||||
]
|
||||
}
|
||||
},
|
||||
"rows": [
|
||||
@@ -30,6 +53,39 @@
|
||||
"collapse": false,
|
||||
"collapsable": true,
|
||||
"panels": [
|
||||
{
|
||||
"error": false,
|
||||
"span": 3,
|
||||
"editable": true,
|
||||
"type": "terms",
|
||||
"loadingEditor": false,
|
||||
"field": "sensor",
|
||||
"exclude": [],
|
||||
"missing": false,
|
||||
"other": false,
|
||||
"size": 5,
|
||||
"order": "count",
|
||||
"style": {
|
||||
"font-size": "10pt"
|
||||
},
|
||||
"donut": false,
|
||||
"tilt": false,
|
||||
"labels": true,
|
||||
"arrangement": "horizontal",
|
||||
"chart": "table",
|
||||
"counter_pos": "above",
|
||||
"spyable": true,
|
||||
"queries": {
|
||||
"mode": "all",
|
||||
"ids": [
|
||||
0
|
||||
]
|
||||
},
|
||||
"tmode": "terms",
|
||||
"tstat": "total",
|
||||
"valuefield": "",
|
||||
"title": "Sensors"
|
||||
},
|
||||
{
|
||||
"error": false,
|
||||
"span": 3,
|
||||
@@ -97,39 +153,6 @@
|
||||
"tstat": "total",
|
||||
"valuefield": "",
|
||||
"title": "Successes"
|
||||
},
|
||||
{
|
||||
"error": false,
|
||||
"span": 3,
|
||||
"editable": true,
|
||||
"type": "terms",
|
||||
"loadingEditor": false,
|
||||
"field": "sensor",
|
||||
"exclude": [],
|
||||
"missing": false,
|
||||
"other": false,
|
||||
"size": 5,
|
||||
"order": "count",
|
||||
"style": {
|
||||
"font-size": "10pt"
|
||||
},
|
||||
"donut": false,
|
||||
"tilt": false,
|
||||
"labels": true,
|
||||
"arrangement": "horizontal",
|
||||
"chart": "table",
|
||||
"counter_pos": "above",
|
||||
"spyable": true,
|
||||
"queries": {
|
||||
"mode": "all",
|
||||
"ids": [
|
||||
0
|
||||
]
|
||||
},
|
||||
"tmode": "terms",
|
||||
"tstat": "total",
|
||||
"valuefield": "",
|
||||
"title": "Sensors"
|
||||
}
|
||||
],
|
||||
"notice": false
|
||||
@@ -231,7 +254,7 @@
|
||||
"editable": true,
|
||||
"type": "terms",
|
||||
"loadingEditor": false,
|
||||
"field": "username",
|
||||
"field": "username.raw",
|
||||
"exclude": [],
|
||||
"missing": false,
|
||||
"other": false,
|
||||
@@ -264,7 +287,7 @@
|
||||
"editable": true,
|
||||
"type": "terms",
|
||||
"loadingEditor": false,
|
||||
"field": "username",
|
||||
"field": "username.raw",
|
||||
"exclude": [],
|
||||
"missing": false,
|
||||
"other": false,
|
||||
@@ -307,7 +330,7 @@
|
||||
"editable": true,
|
||||
"type": "terms",
|
||||
"loadingEditor": false,
|
||||
"field": "password",
|
||||
"field": "password.raw",
|
||||
"exclude": [],
|
||||
"missing": false,
|
||||
"other": false,
|
||||
@@ -340,7 +363,7 @@
|
||||
"editable": true,
|
||||
"type": "terms",
|
||||
"loadingEditor": false,
|
||||
"field": "password",
|
||||
"field": "password.raw",
|
||||
"exclude": [],
|
||||
"missing": false,
|
||||
"other": false,
|
||||
@@ -383,7 +406,7 @@
|
||||
"editable": true,
|
||||
"type": "terms",
|
||||
"loadingEditor": false,
|
||||
"field": "client",
|
||||
"field": "client.raw",
|
||||
"exclude": [],
|
||||
"missing": false,
|
||||
"other": false,
|
||||
@@ -416,7 +439,7 @@
|
||||
"editable": true,
|
||||
"type": "terms",
|
||||
"loadingEditor": false,
|
||||
"field": "client",
|
||||
"field": "client.raw",
|
||||
"exclude": [],
|
||||
"missing": false,
|
||||
"other": false,
|
||||
@@ -480,15 +503,23 @@
|
||||
"error": false,
|
||||
"span": 4,
|
||||
"editable": true,
|
||||
"type": "map",
|
||||
"type": "terms",
|
||||
"loadingEditor": false,
|
||||
"map": "europe",
|
||||
"colors": [
|
||||
"#A0E2E2",
|
||||
"#265656"
|
||||
],
|
||||
"size": 100,
|
||||
"field": "geoip.country_name.raw",
|
||||
"exclude": [],
|
||||
"missing": false,
|
||||
"other": true,
|
||||
"size": 20,
|
||||
"order": "count",
|
||||
"style": {
|
||||
"font-size": "10pt"
|
||||
},
|
||||
"donut": false,
|
||||
"tilt": false,
|
||||
"labels": true,
|
||||
"arrangement": "horizontal",
|
||||
"chart": "table",
|
||||
"counter_pos": "above",
|
||||
"spyable": true,
|
||||
"queries": {
|
||||
"mode": "all",
|
||||
@@ -496,8 +527,10 @@
|
||||
0
|
||||
]
|
||||
},
|
||||
"title": "Attack map (Europe)",
|
||||
"field": "country_code2"
|
||||
"tmode": "terms",
|
||||
"tstat": "count",
|
||||
"valuefield": "",
|
||||
"title": "Countries"
|
||||
}
|
||||
],
|
||||
"notice": false
|
||||
@@ -506,7 +539,7 @@
|
||||
"title": "Events",
|
||||
"height": "650px",
|
||||
"editable": true,
|
||||
"collapse": false,
|
||||
"collapse": true,
|
||||
"collapsable": true,
|
||||
"panels": [
|
||||
{
|
||||
@@ -551,11 +584,54 @@
|
||||
}
|
||||
],
|
||||
"notice": false
|
||||
},
|
||||
{
|
||||
"title": "ASN",
|
||||
"height": "150px",
|
||||
"editable": true,
|
||||
"collapse": false,
|
||||
"collapsable": true,
|
||||
"panels": [
|
||||
{
|
||||
"error": false,
|
||||
"span": 4,
|
||||
"editable": true,
|
||||
"type": "terms",
|
||||
"loadingEditor": false,
|
||||
"field": "geoip.asn.raw",
|
||||
"exclude": [],
|
||||
"missing": true,
|
||||
"other": true,
|
||||
"size": 20,
|
||||
"order": "count",
|
||||
"style": {
|
||||
"font-size": "10pt"
|
||||
},
|
||||
"donut": false,
|
||||
"tilt": false,
|
||||
"labels": true,
|
||||
"arrangement": "horizontal",
|
||||
"chart": "table",
|
||||
"counter_pos": "above",
|
||||
"spyable": true,
|
||||
"queries": {
|
||||
"mode": "all",
|
||||
"ids": [
|
||||
0
|
||||
]
|
||||
},
|
||||
"tmode": "terms",
|
||||
"tstat": "total",
|
||||
"valuefield": "",
|
||||
"title": "ASN"
|
||||
}
|
||||
],
|
||||
"notice": false
|
||||
}
|
||||
],
|
||||
"editable": true,
|
||||
"index": {
|
||||
"interval": "none",
|
||||
"interval": "day",
|
||||
"pattern": "[logstash-]YYYY.MM.DD",
|
||||
"default": "_all",
|
||||
"warm_fields": false
|
||||
@@ -624,8 +700,10 @@
|
||||
"2h",
|
||||
"1d"
|
||||
],
|
||||
"timefield": "timestamp",
|
||||
"enable": true
|
||||
"timefield": "@timestamp",
|
||||
"enable": true,
|
||||
"now": true,
|
||||
"filter_id": 1
|
||||
}
|
||||
],
|
||||
"refresh": false
|
||||
@@ -1,9 +1,13 @@
|
||||
|
||||
input {
|
||||
# this is the actual live log file to monitor
|
||||
file {
|
||||
path => ["/home/michel/src/kippo-git/log/kippo.json", "/home/kippo/kippo-git/log/kippo.json"]
|
||||
# path => ["/home/michel/src/kippo-git/log/kippo.json"]
|
||||
codec => json
|
||||
path => ["/home/kippo/kippo-git/log/kippo.json"]
|
||||
codec => json_lines
|
||||
type => "kippo"
|
||||
}
|
||||
# this is to send old logs to for reprocessing
|
||||
tcp {
|
||||
port => 3333
|
||||
type => "kippo"
|
||||
}
|
||||
}
|
||||
@@ -11,11 +15,13 @@ input {
|
||||
filter {
|
||||
if [type] == "kippo" {
|
||||
|
||||
date {
|
||||
match => [ "timestamp", "ISO8601" ]
|
||||
locale => "en"
|
||||
json {
|
||||
source => message
|
||||
}
|
||||
|
||||
date {
|
||||
match => [ "timestamp", "ISO8601" ]
|
||||
}
|
||||
|
||||
if [src_ip] {
|
||||
|
||||
@@ -46,7 +52,10 @@ filter {
|
||||
|
||||
output {
|
||||
if [type] == "kippo" {
|
||||
elasticsearch { host => helium }
|
||||
elasticsearch {
|
||||
host => helium
|
||||
protocol => http
|
||||
}
|
||||
file {
|
||||
path => "/tmp/kippo-logstash.log"
|
||||
codec => json
|
||||
Reference in New Issue
Block a user