Added pipe implementation

changed some functionality with tail/head/cat to work with pipe implementation
Got rid of ;'s of some places where it should not be :)
This commit is contained in:
davegermiquet
2016-05-28 16:54:36 -04:00
parent 91866e3868
commit 96dc66bad0
6 changed files with 338 additions and 118 deletions

View File

@@ -174,8 +174,8 @@ pages for more information and options.
self.exit()
def do_locked(self):
self.write('E: Could not open lock file /var/lib/apt/lists/lock - open (13: Permission denied)\n')
self.write('E: Unable to lock the list directory\n')
self.error('E: Could not open lock file /var/lib/apt/lists/lock - open (13: Permission denied)\n')
self.error('E: Unable to lock the list directory\n')
self.exit()
commands['/usr/bin/apt-get'] = command_aptget

View File

@@ -1,10 +1,9 @@
# Copyright (c) 2010 Upi Tamminen <desaster@gmail.com>
# See the COPYRIGHT file for more information
import os
import getopt
import copy
from os import path
from cowrie.core.honeypot import HoneyPotCommand
from cowrie.core.fs import *
@@ -16,18 +15,20 @@ class command_cat(HoneyPotCommand):
def start(self):
if not self.args or self.args[0] == '>':
pass
if self.input_data:
self.write("\n")
self.write(self.input_data)
else:
for arg in self.args:
path = self.fs.resolve_path(arg, self.protocol.cwd)
if self.fs.isdir(path):
self.write('cat: %s: Is a directory\n' % (arg,))
self.error('cat: %s: Is a directory\n' % (arg,))
continue
try:
self.write(self.fs.file_contents(path))
except:
self.write('cat: %s: No such file or directory\n' % (arg,))
self.exit()
self.error('cat: %s: No such file or directory\n' % (arg,))
self.exit()
def lineReceived(self, line):
log.msg(eventid='cowrie.session.file_download',
@@ -35,49 +36,65 @@ class command_cat(HoneyPotCommand):
input=line,
format='INPUT (%(realm)s): %(input)s')
def handle_CTRL_D(self):
self.exit()
commands['/bin/cat'] = command_cat
class command_tail(HoneyPotCommand):
class command_grep(HoneyPotCommand):
"""
"""
def grep_get_contents(self, filename, match):
try:
contents = self.fs.file_contents(filename)
self.grep_application(contents, match)
except:
self.error("grep: %s: No such file or directory\n" % (filename,))
def grep_application(self, contents, match):
match = path.basename(match)
match = match.replace("\"","")
contentsplit = contents.split('\n')
matches = re.compile(".*" + match + ".*")
for line in contentsplit:
if matches.match(line):
self.write(line + '\n')
def help(self):
self.error( '\nusage: grep [-abcDEFGHhIiJLlmnOoPqRSsUVvwxZ] [-A num] [-B num] [-C[num]]\n ')
self.error ('[-e pattern] [-f file] [--binary-files=value] [--color=when]\n ')
self.error ('[--context[=num]] [--directories=action] [--label] [--line-buffered]\n')
self.error ('[--null] [pattern] [file ...]\n')
def start(self):
self.n = 10
if not self.args or self.args[0] == '>':
pass
else:
try:
optlist, args = getopt.getopt(self.args, 'n:')
optlist, args = getopt.getopt(self.args, 'abcDEFGHhIiJLlmnOoPqRSsUVvwxZA:B:C:e:f:')
except getopt.GetoptError as err:
self.write("tail: invalid option -- '%s'\n" % (arg,))
self.error("grep: invalid option -- %s" % (err.opt))
self.help()
self.exit()
return
for opt in optlist:
if opt[0] == '-n':
self.n = int(opt[1])
if opt == '-h':
self.help()
for arg in args:
path = self.fs.resolve_path(arg, self.protocol.cwd)
if self.fs.isdir(path):
self.write("tail: error reading `%s': Is a directory\n" % (arg,))
continue
try:
file = self.fs.file_contents(path).split('\n')
lines = int(len(file))
if lines < self.n:
self.n = lines - 1
i = 0
for j in range((lines - self.n - 1), lines):
if i < self.n:
self.write(file[j]+'\n')
i += 1
except:
self.write("tail: cannot open `%s' for reading: No such file or directory\n" % (arg,))
self.exit()
if not self.input_data:
files = self.check_arguments("grep", args[1:])
for path in files:
self.grep_get_contents(path,args[0])
else:
self.write("\n")
self.grep_application(self.input_data,args[0])
self.exit()
def lineReceived(self, line):
log.msg(eventid='cowrie.session.file_download',
@@ -85,17 +102,36 @@ class command_tail(HoneyPotCommand):
input=line,
format='INPUT (%(realm)s): %(input)s')
def handle_CTRL_D(self):
self.exit()
commands['/bin/tail'] = command_tail
commands['/usr/bin/tail'] = command_tail
commands['/bin/grep'] = command_grep
commands['/usr/bin/grep'] = command_grep
class command_head(HoneyPotCommand):
class command_tail(HoneyPotCommand):
"""
"""
def tail_get_contents(self, filename):
try:
contents = self.fs.file_contents(filename)
self.tail_application(contents)
except:
self.error("tail: cannot open `%s' for reading: No such file or directory\n" % (filename,))
def tail_application(self, contents):
contentsplit = contents.split('\n')
lines = int(len(contentsplit))
if lines < self.n:
self.n = lines - 1
i = 0
for j in range((lines - self.n - 1), lines):
if i < self.n:
self.write(contentsplit[j] + '\n')
i += 1
def start(self):
self.n = 10
if not self.args or self.args[0] == '>':
@@ -104,46 +140,103 @@ class command_head(HoneyPotCommand):
try:
optlist, args = getopt.getopt(self.args, 'n:')
except getopt.GetoptError as err:
self.write("head: invalid option -- '%s'\n" % (arg,))
self.error("tail: invalid option -- '%s'\n" % (err.opt))
self.exit()
return
for opt in optlist:
if opt[0] == '-n':
self.n = int(opt[1])
for arg in args:
path = self.fs.resolve_path(arg, self.protocol.cwd)
if self.fs.isdir(path):
self.write("head: error reading `%s': Is a directory\n" % (arg,))
continue
try:
file = self.fs.file_contents(path).split('\n')
i = 0
for line in file:
if i < self.n:
self.write(line+'\n')
i += 1
except:
self.write("head: cannot open `%s' for reading: No such file or directory\n" % (arg,))
self.exit()
if not opt[1].isdigit():
self.error("tail: illegal offset -- %s\n" % opt[1])
else:
self.n = int(opt[1])
if not self.input_data:
files = self.check_arguments("tail", args)
for path in files:
self.tail_get_contents(path)
else:
self.write("\n")
self.tail_application(self.input_data)
self.exit()
def lineReceived(self, line):
log.msg(eventid='cowrie.session.file_download', realm='head', input=line,
format='INPUT (%(realm)s): %(input)s' )
log.msg(eventid='cowrie.session.file_download',
realm='tail',
input=line,
format='INPUT (%(realm)s): %(input)s')
def handle_CTRL_D(self):
self.exit()
commands['/bin/tail'] = command_tail
commands['/usr/bin/tail'] = command_tail
class command_head(HoneyPotCommand):
"""
"""
def head_application(self, contents):
i = 0
contentsplit = str(contents).split("\n")
for line in contentsplit:
if i < self.n:
self.write(line + '\n')
i += 1
def head_get_file_contents(self, filename):
try:
contents = self.fs.file_contents(filename)
self.head_application(contents)
except:
self.write("head: cannot open `%s' for reading: No such file or directory\n" % (filename,))
def start(self):
self.n = 10
if not self.args or self.args[0] == '>':
pass
else:
try:
optlist, args = getopt.getopt(self.args, 'n:')
except getopt.GetoptError as err:
self.error("head: invalid option -- '%s'\n" % (err.opt,))
self.exit()
return
for opt in optlist:
if opt[0] == '-n':
if not opt[1].isdigit():
self.error("head: illegal offset -- %s\n" % opt[1])
else:
self.n = int(opt[1])
if not self.input_data:
files = self.check_arguments("head", args)
for path in files:
self.head_get_file_contents(path)
else:
self.write("\n")
self.head_application(self.input_data)
self.exit()
def lineReceived(self, line):
log.msg(eventid='cowrie.session.file_download', realm='head', input=line,
format='INPUT (%(realm)s): %(input)s')
def handle_CTRL_D(self):
self.exit()
commands['/bin/head'] = command_head
commands['/usr/bin/head'] = command_head
class command_cd(HoneyPotCommand):
"""
"""
def call(self):
if not self.args or self.args[0] == "~":
path = self.protocol.user.avatar.home
@@ -158,15 +251,16 @@ class command_cd(HoneyPotCommand):
self.write('bash: cd: OLDPWD not set\n')
return
if inode is None or inode is False:
self.write('bash: cd: %s: No such file or directory\n' % (path,))
self.error('bash: cd: %s: No such file or directory\n' % (path,))
return
if inode[A_TYPE] != T_DIR:
self.write('bash: cd: %s: Not a directory\n' % (path,))
self.error('bash: cd: %s: Not a directory\n' % (path,))
return
self.protocol.cwd = newpath
commands['cd'] = command_cd
commands['cd'] = command_cd
class command_rm(HoneyPotCommand):
def call(self):
@@ -192,13 +286,15 @@ class command_rm(HoneyPotCommand):
i[A_NAME])
else:
dir.remove(i)
commands['/bin/rm'] = command_rm
commands['/bin/rm'] = command_rm
class command_cp(HoneyPotCommand):
"""
"""
def call(self):
if not len(self.args):
self.write("cp: missing file operand\n")
@@ -206,7 +302,7 @@ class command_cp(HoneyPotCommand):
return
try:
optlist, args = getopt.gnu_getopt(self.args,
'-abdfiHlLPpRrsStTuvx')
'-abdfiHlLPpRrsStTuvx')
except getopt.GetoptError as err:
self.write('Unrecognized option\n')
return
@@ -220,7 +316,7 @@ class command_cp(HoneyPotCommand):
if len(args) < 2:
self.write("cp: missing destination file operand after `%s'\n" % \
(self.args[0],))
(self.args[0],))
self.write("Try `cp --help' for more information.\n")
return
sources, dest = args[:-1], args[-1]
@@ -242,7 +338,7 @@ class command_cp(HoneyPotCommand):
parent = os.path.dirname(resolv(dest))
if not self.fs.exists(parent):
self.write("cp: cannot create regular file " + \
"`%s': No such file or directory\n" % (dest,))
"`%s': No such file or directory\n" % (dest,))
return
for src in sources:
@@ -264,9 +360,10 @@ class command_cp(HoneyPotCommand):
dir.remove([x for x in dir if x[A_NAME] == outfile][0])
s[A_NAME] = outfile
dir.append(s)
commands['/bin/cp'] = command_cp
commands['/bin/cp'] = command_cp
class command_mv(HoneyPotCommand):
"""
@@ -288,7 +385,7 @@ class command_mv(HoneyPotCommand):
if len(args) < 2:
self.write("mv: missing destination file operand after `%s'\n" % \
(self.args[0],))
(self.args[0],))
self.write("Try `mv --help' for more information.\n")
return
sources, dest = args[:-1], args[-1]
@@ -297,7 +394,7 @@ class command_mv(HoneyPotCommand):
return
if dest[-1] == '/' and not self.fs.exists(resolv(dest)) and \
len(sources) != 1:
len(sources) != 1:
self.write(
"mv: cannot create regular file `%s': Is a directory\n" % \
(dest,))
@@ -310,8 +407,8 @@ class command_mv(HoneyPotCommand):
parent = os.path.dirname(resolv(dest))
if not self.fs.exists(parent):
self.write("mv: cannot create regular file " + \
"`%s': No such file or directory\n" % \
(dest,))
"`%s': No such file or directory\n" % \
(dest,))
return
for src in sources:
@@ -334,9 +431,10 @@ class command_mv(HoneyPotCommand):
sdir.remove(s)
else:
s[A_NAME] = outfile
commands['/bin/mv'] = command_mv
commands['/bin/mv'] = command_mv
class command_mkdir(HoneyPotCommand):
"""
@@ -355,13 +453,15 @@ class command_mkdir(HoneyPotCommand):
'mkdir: cannot create directory `%s\': ' % f + \
'No such file or directory\n')
return
commands['/bin/mkdir'] = command_mkdir
commands['/bin/mkdir'] = command_mkdir
class command_rmdir(HoneyPotCommand):
"""
"""
def call(self):
for f in self.args:
path = self.fs.resolve_path(f, self.protocol.cwd)
@@ -386,17 +486,20 @@ class command_rmdir(HoneyPotCommand):
return
dir.remove(i)
break
commands['/bin/rmdir'] = command_rmdir
commands['/bin/rmdir'] = command_rmdir
class command_pwd(HoneyPotCommand):
"""
"""
def call(self):
self.write(self.protocol.cwd+'\n')
commands['/bin/pwd'] = command_pwd
def call(self):
self.write(self.protocol.cwd + '\n')
commands['/bin/pwd'] = command_pwd
class command_touch(HoneyPotCommand):
@@ -418,6 +521,8 @@ class command_touch(HoneyPotCommand):
# FIXME: modify the timestamp here
continue
self.fs.mkfile(path, 0, 0, 0, 33188)
commands['/usr/bin/touch'] = command_touch
commands['/bin/touch'] = command_touch

View File

@@ -0,0 +1,43 @@
__author__ = 'davegermiquet'
class StdOutStdErrEmulationProtocol(object):
def __init__(self,protocol,cmd,cmdargs,input_data,next_protocol):
self.cmd=cmd
self.cmdargs=cmdargs
self.input_data=input_data
self.next_protocol=next_protocol
self.data = ""
self.err_data = ""
self.protocol = protocol
def connectionMade(self):
self.input_data = None
def outReceived(self, data):
self.data = self.data + data
if not self.next_protocol:
self.protocol.terminal.write(data)
def errReceived(self, data):
self.protocol.terminal.write(data )
self.err_data = self.err_data + data
def inConnectionLost(self):
pass
def outConnectionLost(self):
if self.next_protocol:
self.next_protocol.input_data = self.data
npcmd=self.next_protocol.cmd
npcmdargs=self.next_protocol.cmdargs
self.protocol.call_command(self.next_protocol,npcmd,*npcmdargs)
def errConnectionLost(self):
pass
def processExited(self, reason):
print "processExited for %s, status %d" % (self.cmd,reason.value.exitCode,)
def processEnded(self, reason):
print "processEnded for %s, status %d" % (self.cmd,reason.value.exitCode,)

View File

@@ -12,28 +12,27 @@ import copy
import time
from twisted.python import log, failure
from cowrie.core import StdOutStdErrEmulationProtocol as pipe
from twisted.internet import error
from cowrie.core import fs
from cowrie.core import shlex
from twisted.conch.ssh import session
class HoneyPotCommand(object):
"""
"""
def __init__(self, protocol, *args):
self.protocol = protocol
self.args = list(args)
self.environ = self.protocol.cmdstack[0].environ
self.fs = self.protocol.fs
self.data = None
self.input_data = None
# MS-DOS style redirect handling, inside the command
# TODO: handle >>, 2>, etc
if '>' in self.args:
self.writtenBytes = 0
self.write = self.writeToFile
self.write = self.write_to_file
index = self.args.index(">")
self.outfile = self.fs.resolve_path(str(self.args[(index + 1)]), self.protocol.cwd)
del self.args[index:]
@@ -48,10 +47,23 @@ class HoneyPotCommand(object):
with open(self.safeoutfile, 'a'):
self.fs.update_realfile(self.fs.getfile(self.outfile), self.safeoutfile)
else:
self.write = self.protocol.terminal.write
self.write = self.protocol.pp.outReceived
self.error = self.protocol.pp.errReceived
def check_arguments(self,application,args):
files = []
for arg in args:
path = self.fs.resolve_path(arg, self.protocol.cwd)
if self.fs.isdir(path):
self.error("%s: error reading `%s': Is a directory\n" % (application,arg,))
continue
files.append(path)
return files
def writeToFile(self, data):
def set_input_data(self,data):
self.input_data = data
def write_to_file(self, data):
"""
"""
with open(self.safeoutfile, 'a') as f:
@@ -59,7 +71,6 @@ class HoneyPotCommand(object):
self.writtenBytes += len(data)
self.fs.update_size(self.outfile, self.writtenBytes)
def start(self):
"""
"""
@@ -92,7 +103,6 @@ class HoneyPotCommand(object):
self.write('^C\n')
self.exit()
def lineReceived(self, line):
"""
"""
@@ -137,7 +147,7 @@ class HoneyPotShell(object):
"""
"""
log.msg('CMD: %s' % (line,))
self.lexer = shlex.shlex(instream=line, punctuation_chars=True);
self.lexer = shlex.shlex(instream=line, punctuation_chars=True)
tokens = []
while True:
try:
@@ -196,6 +206,7 @@ class HoneyPotShell(object):
def runCommand(self):
"""
"""
pp = None
def runOrPrompt():
if len(self.cmdpending):
self.runCommand()
@@ -205,6 +216,25 @@ class HoneyPotShell(object):
ret = failure.Failure(error.ProcessDone(status=""))
self.protocol.terminal.transport.processEnded(ret)
def parsed_arguments(arguments):
parsed_arguments = []
for arg in arguments:
parsed_arguments.append(arg)
return parsed_arguments
def parse_file_arguments(arguments):
parsed_arguments = []
for arg in arguments:
matches = self.protocol.fs.resolve_path_wc(arg, self.protocol.cwd)
if matches:
parsed_arguments.extend(matches)
else:
parsed_arguments.append(arg)
return parsed_arguments
if not len(self.cmdpending):
if self.interactive:
self.showPrompt()
@@ -218,38 +248,62 @@ class HoneyPotShell(object):
# Probably no reason to be this comprehensive for just PATH...
environ = copy.copy(self.environ)
cmd = None
cmd_array = [ ]
cmd = {}
while len(cmdAndArgs):
piece = cmdAndArgs.pop(0)
if piece.count('='):
key, value = piece.split('=', 1)
environ[key] = value
continue
cmd = piece
cmd['command'] = piece
cmd['rargs'] = [];
break
args = cmdAndArgs
if not cmd:
if not cmd['command']:
runOrPrompt()
return
rargs = []
for arg in args:
matches = self.protocol.fs.resolve_path_wc(arg, self.protocol.cwd)
if matches:
rargs.extend(matches)
else:
rargs.append(arg)
cmdclass = self.protocol.getCommand(cmd, environ['PATH'].split(':'))
if cmdclass:
log.msg(eventid='cowrie.command.success', input=' '.join(cmd2), format='Command found: %(input)s')
self.protocol.call_command(cmdclass, *rargs)
else:
log.msg(eventid='cowrie.command.failed',
input=' '.join(cmd2), format='Command not found: %(input)s')
self.protocol.terminal.write('bash: %s: command not found\n' % (cmd,))
runOrPrompt()
pipe_indices = [i for i, x in enumerate(cmdAndArgs) if x == "|"]
multipleCmdArgs = []
pipe_indices.append(len(cmdAndArgs))
start = 0;
# Gather all arguments with pipes
for index,pipe_indice in enumerate(pipe_indices):
multipleCmdArgs.append(cmdAndArgs[start:pipe_indice])
start = pipe_indice+1
cmd['rargs'] = parse_file_arguments(multipleCmdArgs.pop(0))
cmd_array.append(cmd)
cmd = {}
for index,value in enumerate(multipleCmdArgs):
cmd['command'] = value.pop(0)
cmd['rargs'] = parsed_arguments(value)
cmd_array.append(cmd)
cmd = {}
lastpp = None
for index,cmd in reversed(list(enumerate(cmd_array))):
cmdclass = self.protocol.getCommand(cmd['command'], environ['PATH'] .split(':'))
if cmdclass:
log.msg(eventid='cowrie.command.success', input=' '.join(cmd2), format='Command found: %(input)s')
if index == len(cmd_array)-1:
lastpp = pipe.StdOutStdErrEmulationProtocol(self.protocol,cmdclass,cmd['rargs'],None,None)
pp = lastpp
else:
pp = pipe.StdOutStdErrEmulationProtocol(self.protocol,cmdclass,cmd['rargs'],None,lastpp)
lastpp = pp
else:
log.msg(eventid='cowrie.command.failed',
input=' '.join(cmd2), format='Command not found: %(input)s')
self.protocol.terminal.write('bash: %s: command not found\n' % (cmd['command'],))
runOrPrompt()
if pp:
self.protocol.call_command(pp,cmdclass,*cmd['rargs'])
def resume(self):
"""
@@ -292,6 +346,14 @@ class HoneyPotShell(object):
self.protocol.terminal.write(prompt % attrs)
self.protocol.ps = (prompt % attrs , '> ')
def eofReceived(self):
"""
this should probably not go through ctrl-d, but use processprotocol to close stdin
"""
log.msg("received eof, sending ctrl-d to command")
if len(self.cmdstack):
self.cmdstack[-1].handle_CTRL_D()
def handle_CTRL_C(self):
"""
@@ -306,7 +368,8 @@ class HoneyPotShell(object):
"""
"""
log.msg('Received CTRL-D, exiting..')
self.protocol.call_command(self.protocol.commands['exit'])
self.pp.outConnectionLost()
self.protocol.call_command(None,self.protocol.commands['exit'])
def handle_TAB(self):

View File

@@ -13,10 +13,9 @@ import hashlib
from twisted.python import failure, log
from twisted.internet import error
from twisted.protocols.policies import TimeoutMixin
from twisted.conch import recvline
from twisted.conch.ssh import session
from twisted.conch.insults import insults
from cowrie.core import honeypot
from cowrie.core import ttylog
from cowrie.core import utils
@@ -31,18 +30,19 @@ class HoneyPotBaseProtocol(insults.TerminalProtocol, TimeoutMixin):
self.cfg = self.user.cfg
self.hostname = avatar.server.hostname
self.fs = avatar.server.fs
self.pp = None
if self.fs.exists(avatar.avatar.home):
self.cwd = avatar.avatar.home
else:
self.cwd = '/'
self.input_data = None
self.data = None;
self.commands = {}
import cowrie.commands
for c in cowrie.commands.__all__:
module = __import__('cowrie.commands.%s' % (c,),
globals(), locals(), ['commands'])
self.commands.update(module.commands)
self.password_input = False
self.cmdstack = []
@@ -58,8 +58,6 @@ class HoneyPotBaseProtocol(insults.TerminalProtocol, TimeoutMixin):
def connectionMade(self):
"""
"""
transport = self.terminal.transport.session.conn.transport
self.realClientIP = transport.transport.getPeer().host
@@ -168,12 +166,17 @@ class HoneyPotBaseProtocol(insults.TerminalProtocol, TimeoutMixin):
self.cmdstack[-1].lineReceived(line)
def call_command(self, cmd, *args):
def call_command(self, pp,cmd, *args):
"""
"""
self.pp = pp
obj = cmd(self, *args)
obj.set_input_data(pp.input_data)
self.cmdstack.append(obj)
obj.start()
self.pp.outConnectionLost()
def uptime(self, reset=None):
@@ -208,6 +211,7 @@ class HoneyPotExecProtocol(HoneyPotBaseProtocol):
class HoneyPotInteractiveProtocol(HoneyPotBaseProtocol, recvline.HistoricRecvLine):
"""
"""
@@ -216,7 +220,6 @@ class HoneyPotInteractiveProtocol(HoneyPotBaseProtocol, recvline.HistoricRecvLin
recvline.HistoricRecvLine.__init__(self)
HoneyPotBaseProtocol.__init__(self, avatar)
def connectionMade(self):
"""
"""
@@ -251,6 +254,7 @@ class HoneyPotInteractiveProtocol(HoneyPotBaseProtocol, recvline.HistoricRecvLin
"""
try:
self.terminal.write(self.fs.file_contents('/etc/motd')+'\n')
except:
pass
@@ -289,11 +293,14 @@ class HoneyPotInteractiveProtocol(HoneyPotBaseProtocol, recvline.HistoricRecvLin
self.setInsertMode()
def call_command(self, cmd, *args):
def call_command(self, pp,cmd, *args):
"""
"""
self.pp = pp
self.setTypeoverMode()
HoneyPotBaseProtocol.call_command(self, cmd, *args)
HoneyPotBaseProtocol.call_command(self, pp,cmd, *args)
def characterReceived(self, ch, moreCharactersComing):

View File

@@ -75,6 +75,8 @@ class LoggingServerProtocol(insults.ServerProtocol):
for i in self.interactors:
i.sessionWrite(bytes)
self.data = bytes
if self.ttylog_open:
ttylog.ttylog_write(self.ttylogFile, len(bytes),
ttylog.TYPE_OUTPUT, time.time(), bytes)