mirror of
https://github.com/cowrie/cowrie.git
synced 2025-07-01 18:07:27 -04:00
@ -9,6 +9,7 @@ __all__ = [
|
|||||||
"awk",
|
"awk",
|
||||||
"base",
|
"base",
|
||||||
"base64",
|
"base64",
|
||||||
|
"bash",
|
||||||
"busybox",
|
"busybox",
|
||||||
"cat",
|
"cat",
|
||||||
"chmod",
|
"chmod",
|
||||||
|
|||||||
@ -17,7 +17,6 @@ from twisted.python import failure, log
|
|||||||
|
|
||||||
from cowrie.core import utils
|
from cowrie.core import utils
|
||||||
from cowrie.shell.command import HoneyPotCommand
|
from cowrie.shell.command import HoneyPotCommand
|
||||||
from cowrie.shell.honeypot import HoneyPotShell
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@ -206,19 +205,6 @@ commands["/usr/bin/printf"] = Command_printf
|
|||||||
commands["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):
|
class Command_clear(HoneyPotCommand):
|
||||||
def call(self) -> None:
|
def call(self) -> None:
|
||||||
self.protocol.terminal.reset()
|
self.protocol.terminal.reset()
|
||||||
@ -1003,42 +989,6 @@ commands["/usr/bin/yes"] = Command_yes
|
|||||||
commands["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):
|
class Command_php(HoneyPotCommand):
|
||||||
HELP = (
|
HELP = (
|
||||||
"Usage: php [options] [-f] <file> [--] [args...]\n"
|
"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):
|
class Command_env(HoneyPotCommand):
|
||||||
def call(self) -> None:
|
def call(self) -> None:
|
||||||
# This only show environ vars, not the shell vars. Need just to mimic real systems
|
# This only show environ vars, not the shell vars. Need just to mimic real systems
|
||||||
for i in list(self.protocol.environ.keys()):
|
for i in list(self.environ.keys()):
|
||||||
self.write(f"{i}={self.protocol.environ[i]}\n")
|
self.write(f"{i}={self.environ[i]}\n")
|
||||||
|
|
||||||
|
|
||||||
commands["/usr/bin/env"] = Command_env
|
commands["/usr/bin/env"] = Command_env
|
||||||
|
|||||||
@ -56,7 +56,7 @@ class Command_gcc(HoneyPotCommand):
|
|||||||
|
|
||||||
scheduled: Deferred
|
scheduled: Deferred
|
||||||
|
|
||||||
def start(self) -> None:
|
def call(self) -> None:
|
||||||
"""
|
"""
|
||||||
Parse as much as possible from a GCC syntax and generate the output
|
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)
|
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:
|
def no_files(self) -> None:
|
||||||
"""
|
"""
|
||||||
Notify user there are no input files, and exit
|
Notify user there are no input files
|
||||||
"""
|
"""
|
||||||
self.write(
|
self.write(
|
||||||
"""gcc: fatal error: no input files
|
"""gcc: fatal error: no input files
|
||||||
compilation terminated.\n"""
|
compilation terminated.\n"""
|
||||||
)
|
)
|
||||||
self.exit()
|
|
||||||
|
|
||||||
def version(self, short: bool) -> None:
|
def version(self, short: bool) -> None:
|
||||||
"""
|
"""
|
||||||
Print long or short version, and exit
|
Print long or short version
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Generate version number
|
# Generate version number
|
||||||
@ -184,7 +183,6 @@ gcc version {version} (Debian {version}-5)"""
|
|||||||
|
|
||||||
# Write
|
# Write
|
||||||
self.write(f"{data}\n")
|
self.write(f"{data}\n")
|
||||||
self.exit()
|
|
||||||
|
|
||||||
def generate_file(self, outfile: str) -> None:
|
def generate_file(self, outfile: str) -> None:
|
||||||
data = b""
|
data = b""
|
||||||
@ -228,15 +226,11 @@ gcc version {version} (Debian {version}-5)"""
|
|||||||
# Trick the 'new compiled file' as an segfault
|
# Trick the 'new compiled file' as an segfault
|
||||||
self.protocol.commands[outfile] = segfault_command
|
self.protocol.commands[outfile] = segfault_command
|
||||||
|
|
||||||
# Done
|
|
||||||
self.exit()
|
|
||||||
|
|
||||||
def arg_missing(self, arg: str) -> None:
|
def arg_missing(self, arg: str) -> None:
|
||||||
"""
|
"""
|
||||||
Print missing argument message, and exit
|
Print missing argument message, and exit
|
||||||
"""
|
"""
|
||||||
self.write(f"{Command_gcc.APP_NAME}: argument to '{arg}' is missing\n")
|
self.write(f"{Command_gcc.APP_NAME}: argument to '{arg}' is missing\n")
|
||||||
self.exit()
|
|
||||||
|
|
||||||
def help(self) -> None:
|
def help(self) -> None:
|
||||||
"""
|
"""
|
||||||
@ -306,7 +300,6 @@ For bug reporting instructions, please see:
|
|||||||
<file:///usr/share/doc/gcc-4.7/README.Bugs>.
|
<file:///usr/share/doc/gcc-4.7/README.Bugs>.
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
self.exit()
|
|
||||||
|
|
||||||
|
|
||||||
commands["/usr/bin/gcc"] = Command_gcc
|
commands["/usr/bin/gcc"] = Command_gcc
|
||||||
|
|||||||
@ -33,6 +33,7 @@ class LoggingServerProtocol(insults.ServerProtocol):
|
|||||||
self.type: str
|
self.type: str
|
||||||
self.ttylogFile: str
|
self.ttylogFile: str
|
||||||
self.ttylogSize: int = 0
|
self.ttylogSize: int = 0
|
||||||
|
self.bytesSent: int = 0
|
||||||
self.bytesReceived: int = 0
|
self.bytesReceived: int = 0
|
||||||
self.redirFiles: set[list[str]] = set()
|
self.redirFiles: set[list[str]] = set()
|
||||||
self.redirlogOpen: bool = False # it will be set at core/protocol.py
|
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")
|
self.terminalProtocol.execcmd.encode("utf8")
|
||||||
|
|
||||||
def write(self, data: bytes) -> None:
|
def write(self, data: bytes) -> None:
|
||||||
|
self.bytesSent += len(data)
|
||||||
if self.ttylogEnabled and self.ttylogOpen:
|
if self.ttylogEnabled and self.ttylogOpen:
|
||||||
ttylog.ttylog_write(
|
ttylog.ttylog_write(
|
||||||
self.ttylogFile, len(data), ttylog.TYPE_OUTPUT, time.time(), data
|
self.ttylogFile, len(data), ttylog.TYPE_OUTPUT, time.time(), data
|
||||||
@ -121,9 +123,6 @@ class LoggingServerProtocol(insults.ServerProtocol):
|
|||||||
self.ttylogFile, len(data), ttylog.TYPE_INPUT, time.time(), data
|
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:
|
def eofReceived(self) -> None:
|
||||||
@ -225,12 +224,12 @@ class LoggingServerProtocol(insults.ServerProtocol):
|
|||||||
|
|
||||||
log.msg(
|
log.msg(
|
||||||
eventid="cowrie.log.closed",
|
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,
|
ttylog=shasumfile,
|
||||||
size=self.ttylogSize,
|
size=self.ttylogSize,
|
||||||
shasum=shasum,
|
shasum=shasum,
|
||||||
duplicate=duplicate,
|
duplicate=duplicate,
|
||||||
duration=time.time() - self.startTime,
|
duration=f"{time.time() - self.startTime:.1f}",
|
||||||
)
|
)
|
||||||
|
|
||||||
insults.ServerProtocol.connectionLost(self, reason)
|
insults.ServerProtocol.connectionLost(self, reason)
|
||||||
|
|||||||
@ -34,7 +34,7 @@ class HoneyPotCommand:
|
|||||||
def __init__(self, protocol, *args):
|
def __init__(self, protocol, *args):
|
||||||
self.protocol = protocol
|
self.protocol = protocol
|
||||||
self.args = list(args)
|
self.args = list(args)
|
||||||
self.environ = self.protocol.cmdstack[0].environ
|
self.environ = self.protocol.cmdstack[-1].environ
|
||||||
self.fs = self.protocol.fs
|
self.fs = self.protocol.fs
|
||||||
self.data: bytes = b"" # output data
|
self.data: bytes = b"" # output data
|
||||||
self.input_data: None | (bytes) = (
|
self.input_data: None | (bytes) = (
|
||||||
@ -174,7 +174,8 @@ class HoneyPotCommand:
|
|||||||
self.protocol.terminal.redirFiles.add((self.safeoutfile, ""))
|
self.protocol.terminal.redirFiles.add((self.safeoutfile, ""))
|
||||||
|
|
||||||
if len(self.protocol.cmdstack):
|
if len(self.protocol.cmdstack):
|
||||||
self.protocol.cmdstack.pop()
|
self.protocol.cmdstack.remove(self)
|
||||||
|
|
||||||
if len(self.protocol.cmdstack):
|
if len(self.protocol.cmdstack):
|
||||||
self.protocol.cmdstack[-1].resume()
|
self.protocol.cmdstack[-1].resume()
|
||||||
else:
|
else:
|
||||||
|
|||||||
@ -32,6 +32,8 @@ class HoneyPotShell:
|
|||||||
self.environ["COLUMNS"] = str(protocol.user.windowSize[1])
|
self.environ["COLUMNS"] = str(protocol.user.windowSize[1])
|
||||||
self.environ["LINES"] = str(protocol.user.windowSize[0])
|
self.environ["LINES"] = str(protocol.user.windowSize[0])
|
||||||
self.lexer: shlex.shlex | None = None
|
self.lexer: shlex.shlex | None = None
|
||||||
|
|
||||||
|
# this is the first prompt after starting
|
||||||
self.showPrompt()
|
self.showPrompt()
|
||||||
|
|
||||||
def lineReceived(self, line: str) -> None:
|
def lineReceived(self, line: str) -> None:
|
||||||
@ -109,8 +111,10 @@ class HoneyPotShell:
|
|||||||
return
|
return
|
||||||
|
|
||||||
if self.cmdpending:
|
if self.cmdpending:
|
||||||
|
# if we have a complete command, go and run it
|
||||||
self.runCommand()
|
self.runCommand()
|
||||||
else:
|
else:
|
||||||
|
# if there's no command, display a prompt again
|
||||||
self.showPrompt()
|
self.showPrompt()
|
||||||
|
|
||||||
def do_command_substitution(self, start_tok: str) -> str:
|
def do_command_substitution(self, start_tok: str) -> str:
|
||||||
|
|||||||
@ -190,6 +190,8 @@ class HoneyPotBaseProtocol(insults.TerminalProtocol, TimeoutMixin):
|
|||||||
self.cmdstack[-1].lineReceived(string)
|
self.cmdstack[-1].lineReceived(string)
|
||||||
else:
|
else:
|
||||||
log.msg(f"discarding input {string}")
|
log.msg(f"discarding input {string}")
|
||||||
|
stat = failure.Failure(error.ProcessDone(status=""))
|
||||||
|
self.terminal.transport.processEnded(stat)
|
||||||
|
|
||||||
def call_command(self, pp, cmd, *args):
|
def call_command(self, pp, cmd, *args):
|
||||||
self.pp = pp
|
self.pp = pp
|
||||||
|
|||||||
@ -30,12 +30,13 @@ class SSHSessionForCowrieUser:
|
|||||||
self.gid = avatar.gid
|
self.gid = avatar.gid
|
||||||
self.username = avatar.username
|
self.username = avatar.username
|
||||||
self.environ = {
|
self.environ = {
|
||||||
|
"HOME": self.avatar.home,
|
||||||
"LOGNAME": self.username,
|
"LOGNAME": self.username,
|
||||||
"SHELL": "/bin/bash",
|
"SHELL": "/bin/bash",
|
||||||
"USER": self.username,
|
"SHLVL": "1",
|
||||||
"HOME": self.avatar.home,
|
|
||||||
"TMOUT": "1800",
|
"TMOUT": "1800",
|
||||||
"UID": str(self.uid),
|
"UID": str(self.uid),
|
||||||
|
"USER": self.username,
|
||||||
}
|
}
|
||||||
if self.uid == 0:
|
if self.uid == 0:
|
||||||
self.environ["PATH"] = (
|
self.environ["PATH"] = (
|
||||||
|
|||||||
@ -69,10 +69,10 @@ class CowrieSSHChannel(channel.SSHChannel):
|
|||||||
def closed(self) -> None:
|
def closed(self) -> None:
|
||||||
log.msg(
|
log.msg(
|
||||||
eventid="cowrie.log.closed",
|
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,
|
ttylog=self.ttylogFile,
|
||||||
size=self.bytesReceived + self.bytesWritten,
|
size=self.bytesReceived + self.bytesWritten,
|
||||||
duration=time.time() - self.startTime,
|
duration=f"{time.time() - self.startTime:.1f}",
|
||||||
)
|
)
|
||||||
ttylog.ttylog_close(self.ttylogFile, time.time())
|
ttylog.ttylog_close(self.ttylogFile, time.time())
|
||||||
channel.SSHChannel.closed(self)
|
channel.SSHChannel.closed(self)
|
||||||
|
|||||||
@ -246,10 +246,10 @@ class HoneyPotSSHTransport(transport.SSHServerTransport, TimeoutMixin):
|
|||||||
transport.SSHServerTransport.connectionLost(self, reason)
|
transport.SSHServerTransport.connectionLost(self, reason)
|
||||||
self.transport.connectionLost(reason)
|
self.transport.connectionLost(reason)
|
||||||
self.transport = None
|
self.transport = None
|
||||||
duration = time.time() - self.startTime
|
duration = f"{time.time() - self.startTime:.1f}"
|
||||||
log.msg(
|
log.msg(
|
||||||
eventid="cowrie.session.closed",
|
eventid="cowrie.session.closed",
|
||||||
format="Connection lost after %(duration)d seconds",
|
format="Connection lost after %(duration)s seconds",
|
||||||
duration=duration,
|
duration=duration,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user