mirror of
https://github.com/cowrie/cowrie.git
synced 2025-07-01 18:07:27 -04:00
@ -9,6 +9,7 @@ __all__ = [
|
||||
"awk",
|
||||
"base",
|
||||
"base64",
|
||||
"bash",
|
||||
"busybox",
|
||||
"cat",
|
||||
"chmod",
|
||||
|
||||
@ -17,7 +17,6 @@ from twisted.python import failure, log
|
||||
|
||||
from cowrie.core import utils
|
||||
from cowrie.shell.command import HoneyPotCommand
|
||||
from cowrie.shell.honeypot import HoneyPotShell
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@ -206,19 +205,6 @@ commands["/usr/bin/printf"] = Command_printf
|
||||
commands["printf"] = Command_printf
|
||||
|
||||
|
||||
class Command_exit(HoneyPotCommand):
|
||||
def call(self) -> None:
|
||||
stat = failure.Failure(error.ProcessDone(status=""))
|
||||
self.protocol.terminal.transport.processEnded(stat)
|
||||
|
||||
def exit(self) -> None:
|
||||
pass
|
||||
|
||||
|
||||
commands["exit"] = Command_exit
|
||||
commands["logout"] = Command_exit
|
||||
|
||||
|
||||
class Command_clear(HoneyPotCommand):
|
||||
def call(self) -> None:
|
||||
self.protocol.terminal.reset()
|
||||
@ -1003,42 +989,6 @@ commands["/usr/bin/yes"] = Command_yes
|
||||
commands["yes"] = Command_yes
|
||||
|
||||
|
||||
class Command_sh(HoneyPotCommand):
|
||||
def call(self) -> None:
|
||||
if self.args and self.args[0].strip() == "-c":
|
||||
line = " ".join(self.args[1:])
|
||||
|
||||
# it might be sh -c 'echo "sometext"', so don't use line.strip('\'\"')
|
||||
if (line[0] == "'" and line[-1] == "'") or (
|
||||
line[0] == '"' and line[-1] == '"'
|
||||
):
|
||||
line = line[1:-1]
|
||||
|
||||
self.execute_commands(line)
|
||||
|
||||
elif self.input_data:
|
||||
self.execute_commands(self.input_data.decode("utf8"))
|
||||
|
||||
# TODO: handle spawning multiple shells, support other sh flags
|
||||
|
||||
def execute_commands(self, cmds: str) -> None:
|
||||
# self.input_data holds commands passed via PIPE
|
||||
# create new HoneyPotShell for our a new 'sh' shell
|
||||
self.protocol.cmdstack.append(HoneyPotShell(self.protocol, interactive=False))
|
||||
|
||||
# call lineReceived method that indicates that we have some commands to parse
|
||||
self.protocol.cmdstack[-1].lineReceived(cmds)
|
||||
|
||||
# remove the shell
|
||||
self.protocol.cmdstack.pop()
|
||||
|
||||
|
||||
commands["/bin/bash"] = Command_sh
|
||||
commands["bash"] = Command_sh
|
||||
commands["/bin/sh"] = Command_sh
|
||||
commands["sh"] = Command_sh
|
||||
|
||||
|
||||
class Command_php(HoneyPotCommand):
|
||||
HELP = (
|
||||
"Usage: php [options] [-f] <file> [--] [args...]\n"
|
||||
|
||||
83
src/cowrie/commands/bash.py
Normal file
83
src/cowrie/commands/bash.py
Normal file
@ -0,0 +1,83 @@
|
||||
# Copyright (c) 2009 Upi Tamminen <desaster@gmail.com>
|
||||
# See the COPYRIGHT file for more information
|
||||
|
||||
# coding=utf-8
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from twisted.internet import error
|
||||
from twisted.python import failure
|
||||
|
||||
from cowrie.shell.command import HoneyPotCommand
|
||||
from cowrie.shell.honeypot import HoneyPotShell
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Callable
|
||||
|
||||
commands: dict[str, Callable] = {}
|
||||
|
||||
|
||||
class Command_sh(HoneyPotCommand):
|
||||
def start(self) -> None:
|
||||
if self.args and self.args[0].strip() == "-c":
|
||||
line = " ".join(self.args[1:])
|
||||
|
||||
# it might be sh -c 'echo "sometext"', so don't use line.strip('\'\"')
|
||||
if (line[0] == "'" and line[-1] == "'") or (
|
||||
line[0] == '"' and line[-1] == '"'
|
||||
):
|
||||
line = line[1:-1]
|
||||
|
||||
self.execute_commands(line)
|
||||
self.exit()
|
||||
|
||||
elif self.input_data:
|
||||
self.execute_commands(self.input_data.decode("utf8"))
|
||||
self.exit()
|
||||
|
||||
else:
|
||||
self.interactive_shell()
|
||||
|
||||
# TODO: handle spawning multiple shells, support other sh flags
|
||||
|
||||
def execute_commands(self, cmds: str) -> None:
|
||||
# self.input_data holds commands passed via PIPE
|
||||
# create new HoneyPotShell for our a new 'sh' shell
|
||||
self.protocol.cmdstack.append(HoneyPotShell(self.protocol, interactive=False))
|
||||
|
||||
# call lineReceived method that indicates that we have some commands to parse
|
||||
self.protocol.cmdstack[-1].lineReceived(cmds)
|
||||
|
||||
# remove the shell
|
||||
self.protocol.cmdstack.pop()
|
||||
|
||||
def interactive_shell(self) -> None:
|
||||
shell = HoneyPotShell(self.protocol, interactive=True)
|
||||
parentshell = self.protocol.cmdstack[-2]
|
||||
# TODO: copy more variables, but only exported variables
|
||||
try:
|
||||
shell.environ["SHLVL"] = str(int(parentshell.environ["SHLVL"]) + 1)
|
||||
except KeyError:
|
||||
shell.environ["SHLVL"] = "1"
|
||||
self.protocol.cmdstack.append(shell)
|
||||
self.protocol.cmdstack.remove(self)
|
||||
|
||||
|
||||
commands["/bin/bash"] = Command_sh
|
||||
commands["bash"] = Command_sh
|
||||
commands["/bin/sh"] = Command_sh
|
||||
commands["sh"] = Command_sh
|
||||
|
||||
|
||||
class Command_exit(HoneyPotCommand):
|
||||
def call(self) -> None:
|
||||
# this removes the second last command, which is the shell
|
||||
self.protocol.cmdstack.pop(-2)
|
||||
if len(self.protocol.cmdstack) < 2:
|
||||
stat = failure.Failure(error.ProcessDone(status=""))
|
||||
self.protocol.terminal.transport.processEnded(stat)
|
||||
|
||||
|
||||
commands["exit"] = Command_exit
|
||||
commands["logout"] = Command_exit
|
||||
@ -28,8 +28,8 @@ For complete documentation, run: info coreutils 'env invocation'
|
||||
class Command_env(HoneyPotCommand):
|
||||
def call(self) -> None:
|
||||
# This only show environ vars, not the shell vars. Need just to mimic real systems
|
||||
for i in list(self.protocol.environ.keys()):
|
||||
self.write(f"{i}={self.protocol.environ[i]}\n")
|
||||
for i in list(self.environ.keys()):
|
||||
self.write(f"{i}={self.environ[i]}\n")
|
||||
|
||||
|
||||
commands["/usr/bin/env"] = Command_env
|
||||
|
||||
@ -56,7 +56,7 @@ class Command_gcc(HoneyPotCommand):
|
||||
|
||||
scheduled: Deferred
|
||||
|
||||
def start(self) -> None:
|
||||
def call(self) -> None:
|
||||
"""
|
||||
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)
|
||||
@ -151,17 +151,16 @@ class Command_gcc(HoneyPotCommand):
|
||||
|
||||
def no_files(self) -> None:
|
||||
"""
|
||||
Notify user there are no input files, and exit
|
||||
Notify user there are no input files
|
||||
"""
|
||||
self.write(
|
||||
"""gcc: fatal error: no input files
|
||||
compilation terminated.\n"""
|
||||
)
|
||||
self.exit()
|
||||
|
||||
def version(self, short: bool) -> None:
|
||||
"""
|
||||
Print long or short version, and exit
|
||||
Print long or short version
|
||||
"""
|
||||
|
||||
# Generate version number
|
||||
@ -184,7 +183,6 @@ gcc version {version} (Debian {version}-5)"""
|
||||
|
||||
# Write
|
||||
self.write(f"{data}\n")
|
||||
self.exit()
|
||||
|
||||
def generate_file(self, outfile: str) -> None:
|
||||
data = b""
|
||||
@ -228,15 +226,11 @@ gcc version {version} (Debian {version}-5)"""
|
||||
# Trick the 'new compiled file' as an segfault
|
||||
self.protocol.commands[outfile] = segfault_command
|
||||
|
||||
# Done
|
||||
self.exit()
|
||||
|
||||
def arg_missing(self, arg: str) -> None:
|
||||
"""
|
||||
Print missing argument message, and exit
|
||||
"""
|
||||
self.write(f"{Command_gcc.APP_NAME}: argument to '{arg}' is missing\n")
|
||||
self.exit()
|
||||
|
||||
def help(self) -> None:
|
||||
"""
|
||||
@ -306,7 +300,6 @@ For bug reporting instructions, please see:
|
||||
<file:///usr/share/doc/gcc-4.7/README.Bugs>.
|
||||
"""
|
||||
)
|
||||
self.exit()
|
||||
|
||||
|
||||
commands["/usr/bin/gcc"] = Command_gcc
|
||||
|
||||
@ -33,6 +33,7 @@ class LoggingServerProtocol(insults.ServerProtocol):
|
||||
self.type: str
|
||||
self.ttylogFile: str
|
||||
self.ttylogSize: int = 0
|
||||
self.bytesSent: int = 0
|
||||
self.bytesReceived: int = 0
|
||||
self.redirFiles: set[list[str]] = set()
|
||||
self.redirlogOpen: bool = False # it will be set at core/protocol.py
|
||||
@ -95,6 +96,7 @@ class LoggingServerProtocol(insults.ServerProtocol):
|
||||
self.terminalProtocol.execcmd.encode("utf8")
|
||||
|
||||
def write(self, data: bytes) -> None:
|
||||
self.bytesSent += len(data)
|
||||
if self.ttylogEnabled and self.ttylogOpen:
|
||||
ttylog.ttylog_write(
|
||||
self.ttylogFile, len(data), ttylog.TYPE_OUTPUT, time.time(), data
|
||||
@ -121,10 +123,7 @@ class LoggingServerProtocol(insults.ServerProtocol):
|
||||
self.ttylogFile, len(data), ttylog.TYPE_INPUT, time.time(), data
|
||||
)
|
||||
|
||||
# prevent crash if something like this was passed:
|
||||
# echo cmd ; exit; \n\n
|
||||
if self.terminalProtocol:
|
||||
insults.ServerProtocol.dataReceived(self, data)
|
||||
insults.ServerProtocol.dataReceived(self, data)
|
||||
|
||||
def eofReceived(self) -> None:
|
||||
"""
|
||||
@ -225,12 +224,12 @@ class LoggingServerProtocol(insults.ServerProtocol):
|
||||
|
||||
log.msg(
|
||||
eventid="cowrie.log.closed",
|
||||
format="Closing TTY Log: %(ttylog)s after %(duration)d seconds",
|
||||
format="Closing TTY Log: %(ttylog)s after %(duration)s seconds",
|
||||
ttylog=shasumfile,
|
||||
size=self.ttylogSize,
|
||||
shasum=shasum,
|
||||
duplicate=duplicate,
|
||||
duration=time.time() - self.startTime,
|
||||
duration=f"{time.time() - self.startTime:.1f}",
|
||||
)
|
||||
|
||||
insults.ServerProtocol.connectionLost(self, reason)
|
||||
|
||||
@ -34,7 +34,7 @@ class HoneyPotCommand:
|
||||
def __init__(self, protocol, *args):
|
||||
self.protocol = protocol
|
||||
self.args = list(args)
|
||||
self.environ = self.protocol.cmdstack[0].environ
|
||||
self.environ = self.protocol.cmdstack[-1].environ
|
||||
self.fs = self.protocol.fs
|
||||
self.data: bytes = b"" # output data
|
||||
self.input_data: None | (bytes) = (
|
||||
@ -174,7 +174,8 @@ class HoneyPotCommand:
|
||||
self.protocol.terminal.redirFiles.add((self.safeoutfile, ""))
|
||||
|
||||
if len(self.protocol.cmdstack):
|
||||
self.protocol.cmdstack.pop()
|
||||
self.protocol.cmdstack.remove(self)
|
||||
|
||||
if len(self.protocol.cmdstack):
|
||||
self.protocol.cmdstack[-1].resume()
|
||||
else:
|
||||
|
||||
@ -32,6 +32,8 @@ class HoneyPotShell:
|
||||
self.environ["COLUMNS"] = str(protocol.user.windowSize[1])
|
||||
self.environ["LINES"] = str(protocol.user.windowSize[0])
|
||||
self.lexer: shlex.shlex | None = None
|
||||
|
||||
# this is the first prompt after starting
|
||||
self.showPrompt()
|
||||
|
||||
def lineReceived(self, line: str) -> None:
|
||||
@ -109,8 +111,10 @@ class HoneyPotShell:
|
||||
return
|
||||
|
||||
if self.cmdpending:
|
||||
# if we have a complete command, go and run it
|
||||
self.runCommand()
|
||||
else:
|
||||
# if there's no command, display a prompt again
|
||||
self.showPrompt()
|
||||
|
||||
def do_command_substitution(self, start_tok: str) -> str:
|
||||
|
||||
@ -190,6 +190,8 @@ class HoneyPotBaseProtocol(insults.TerminalProtocol, TimeoutMixin):
|
||||
self.cmdstack[-1].lineReceived(string)
|
||||
else:
|
||||
log.msg(f"discarding input {string}")
|
||||
stat = failure.Failure(error.ProcessDone(status=""))
|
||||
self.terminal.transport.processEnded(stat)
|
||||
|
||||
def call_command(self, pp, cmd, *args):
|
||||
self.pp = pp
|
||||
|
||||
@ -30,12 +30,13 @@ class SSHSessionForCowrieUser:
|
||||
self.gid = avatar.gid
|
||||
self.username = avatar.username
|
||||
self.environ = {
|
||||
"HOME": self.avatar.home,
|
||||
"LOGNAME": self.username,
|
||||
"SHELL": "/bin/bash",
|
||||
"USER": self.username,
|
||||
"HOME": self.avatar.home,
|
||||
"SHLVL": "1",
|
||||
"TMOUT": "1800",
|
||||
"UID": str(self.uid),
|
||||
"USER": self.username,
|
||||
}
|
||||
if self.uid == 0:
|
||||
self.environ["PATH"] = (
|
||||
|
||||
@ -69,10 +69,10 @@ class CowrieSSHChannel(channel.SSHChannel):
|
||||
def closed(self) -> None:
|
||||
log.msg(
|
||||
eventid="cowrie.log.closed",
|
||||
format="Closing TTY Log: %(ttylog)s after %(duration)f seconds",
|
||||
format="Closing TTY Log: %(ttylog)s after %(duration)s seconds",
|
||||
ttylog=self.ttylogFile,
|
||||
size=self.bytesReceived + self.bytesWritten,
|
||||
duration=time.time() - self.startTime,
|
||||
duration=f"{time.time() - self.startTime:.1f}",
|
||||
)
|
||||
ttylog.ttylog_close(self.ttylogFile, time.time())
|
||||
channel.SSHChannel.closed(self)
|
||||
|
||||
@ -246,10 +246,10 @@ class HoneyPotSSHTransport(transport.SSHServerTransport, TimeoutMixin):
|
||||
transport.SSHServerTransport.connectionLost(self, reason)
|
||||
self.transport.connectionLost(reason)
|
||||
self.transport = None
|
||||
duration = time.time() - self.startTime
|
||||
duration = f"{time.time() - self.startTime:.1f}"
|
||||
log.msg(
|
||||
eventid="cowrie.session.closed",
|
||||
format="Connection lost after %(duration)d seconds",
|
||||
format="Connection lost after %(duration)s seconds",
|
||||
duration=duration,
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user