From 45c0d32df4679ba04bedb64e61d6b080b3870c85 Mon Sep 17 00:00:00 2001 From: Michel Oosterhof Date: Tue, 3 Feb 2015 16:36:46 +0000 Subject: [PATCH] first --- kippo/commands/__init__.py | 3 +- kippo/commands/iptables.py | 390 +++++++++++++++++++++++++++++++++++++ 2 files changed, 392 insertions(+), 1 deletion(-) create mode 100644 kippo/commands/iptables.py diff --git a/kippo/commands/__init__.py b/kippo/commands/__init__.py index 756d957..f95d4ad 100644 --- a/kippo/commands/__init__.py +++ b/kippo/commands/__init__.py @@ -18,5 +18,6 @@ __all__ = [ 'malware', 'netstat', 'which', - 'gcc' + 'gcc', + 'iptables' ] diff --git a/kippo/commands/iptables.py b/kippo/commands/iptables.py new file mode 100644 index 0000000..6a78c18 --- /dev/null +++ b/kippo/commands/iptables.py @@ -0,0 +1,390 @@ +# Copyright (c) 2013 Bas Stottelaar + +from twisted.internet import reactor +from kippo.core.honeypot import HoneyPotCommand, ModifiedOptionParser, OptionParsingError, OptionParsingExit + +commands = {} + +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 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] ] [-h | --help] + * iptables [[-t | --table] ] [-v | --version] + * iptables [[-t | --table] ] [-F | --flush] + * iptables [[-t | --table] ] [-L | --list] + * iptables [[-t | --table] ] [-S | --list-rules] + * 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_and_exit([ + '%s: can\'t initialize iptables table \'%s\': Table does not exist (do you need to insmod?)' % (command_iptables.APP_NAME, table), + 'Perhaps iptables or your kernel needs to be upgraded.' + ]) + 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_and_exit("%s: No chain/target/match by that name." % command_iptables.APP_NAME) + return False + + # Exists + return True + + def show_version(self): + """ Show version and exit """ + self.writeln_and_exit('%s %s' % (command_iptables.APP_NAME, command_iptables.APP_VERSION)) + + def show_help(self): + """ Show help and exit """ + + self.writeln_and_exit([ + '%s %s' % (command_iptables.APP_NAME, command_iptables.APP_VERSION), + '', + '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= try to insert modules using this command', + ' --set-counters PKTS BYTES set the counter during insert/append', + '[!] --version -V print package version.', + ]) + + 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_and_exit(output) + 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_and_exit("\n\n".join(output)) + 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_and_exit([ + '%s %s: can\'t initialize iptables table \'filter\': Permission denied (you must be root)' % (command_iptables.APP_NAME, command_iptables.APP_VERSION), + 'Perhaps iptables or your kernel needs to be upgraded.' + ]) + + def no_command(self): + """ Print no command message and exit """ + + self.writeln_and_exit([ + '%s %s: no command specified' % (command_iptables.APP_NAME, command_iptables.APP_VERSION), + 'Try `iptables -h\' or \'iptables --help\' for more information.', + ]) + + def unknown_option(self, option): + """ Print unknown option message and exit """ + + self.writeln_and_exit([ + '%s %s: unknown option \'%s\'' % (command_iptables.APP_NAME, command_iptables.APP_VERSION, option), + 'Try `iptables -h\' or \'iptables --help\' for more information.', + ]) + + def bad_argument(self, argument): + """ Print bad argument and exit """ + + self.writeln_and_exit([ + 'Bad argument \'%s\'' % argument, + 'Try `iptables -h\' or \'iptables --help\' for more information.', + ]) + +# Definition +commands['/sbin/iptables'] = command_iptables