Skip to content

Commit

Permalink
add help formatting
Browse files Browse the repository at this point in the history
  • Loading branch information
ZanyMonk committed Jun 16, 2023
1 parent c819ed5 commit 30531b6
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 53 deletions.
25 changes: 21 additions & 4 deletions core/loggers.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import logging.handlers
import logging
import sys
import core.config
import logging.handlers
import os
import xml

from prompt_toolkit import print_formatted_text
from prompt_toolkit.formatted_text import HTML

import core.config
from core import style

log = None
logfile = None

class WeevelyFormatter(logging.Formatter):

FORMATS = {
# logging.DEBUG :"[D][%(module)s.%(funcName)s:%(lineno)d] %(message)s",
logging.DEBUG: "[D][%(module)s] %(message)s",
Expand All @@ -23,6 +27,18 @@ def format(self, record):
return logging.Formatter.format(self, record)


class StderrHandler(logging.Handler):
def __init__(self):
super().__init__()
self.formatter = WeevelyFormatter()

def emit(self, record: logging.LogRecord) -> None:
try:
print_formatted_text(HTML(record.msg), style=style.default_style)
except xml.parsers.expat.ExpatError:
print(record.msg)


if not os.path.isdir(core.config.base_path):
os.makedirs(core.config.base_path)

Expand All @@ -40,6 +56,7 @@ def format(self, record):

"""Initialize the normal handler"""
stream_handler = logging.StreamHandler()
stream_handler = StderrHandler()
stream_handler.setFormatter(WeevelyFormatter())

"""Initialize the standard logger"""
Expand Down
10 changes: 5 additions & 5 deletions core/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,20 +42,20 @@ class terminal:
command replacements to simulate an unrestricted shell.
"""
help_no_shell = """
The system shell interpreter is not available in this session, use the
following command replacements to simulate a unrestricted shell.
<warning>The system shell interpreter is not available in this session, use the
following command replacements to simulate a unrestricted shell.</warning>
"""
welcome_to_s = """
<gutter>[+]</gutter> <label>Weevely</label> <underline><value>${version}</value></underline>
<label>Weevely</label> <underline><value>${version}</value></underline>
<gutter>[+]</gutter> <label>Target:</label>\t<value>${'%s@' % user if user else ''}${hostname}${':%s' % conn_path if conn_path and conn_path != '.' else ''}</value>
<gutter>[+]</gutter> <label>Session:</label>\t<value>${path}</value>
% if default_shell:
<gutter>[+]</gutter> <label>Shell:</label>\t<value>${ 'System shell' if default_shell == 'shell_sh' else 'PHP interpreter'}</value>
% endif
<gutter>[+]</gutter> Browse the filesystem or execute commands starts the connection
<gutter>[+]</gutter> to the target. Type :help for more information.
Browse the filesystem or execute commands to initiate the connection to the target.
Type :help for more information.
"""
set_usage = 'Set session variable (run :show to print). Usage:\n:set <variable> \'<value>\''
unset_usage = 'Unset session variable (run :show to print). Usage:\n:unset <variable>'
Expand Down
25 changes: 25 additions & 0 deletions core/style.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from prompt_toolkit.styles import Style

default_style = Style.from_dict({
'bottom-toolbar': 'noreverse',
'bottom-toolbar.text': 'bg:#ff0000',
'rprompt': 'bg:#ff0066 #000000',
'username': '#a6e22e bold',
'username.root': '#ff4689 underline',
'at': '#ae81ff',
'colon': '#ae81ff',
'pound': '#959077',
'host': '#66d9ef',
'path': 'ansicyan',
'mode': '#ff0000',
'gutter': '#999999',
'label': '#00aa00 bold',
'value': '#33ff66',

'warning': '#ff4689',
'danger': '#ed007e',

'completion-menu': 'bg:#96d0f7 #000000',
'completion-menu.completion.current': 'bg:#0077ff #000000',
'completion-menu.completion.alias': 'bg:#1a9cf2',
})
87 changes: 50 additions & 37 deletions core/terminal.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import shlex
import string
from urllib.parse import urlparse

from mako import template
from prompt_toolkit import PromptSession, HTML
from prompt_toolkit import print_formatted_text
from prompt_toolkit import PromptSession
from prompt_toolkit.completion import Completer, Completion
from prompt_toolkit.history import FileHistory
from prompt_toolkit.key_binding import KeyBindings
from prompt_toolkit.key_binding.bindings import named_commands
from prompt_toolkit.output import ColorDepth
from prompt_toolkit.styles import Style

from core import messages, modules, config
import utils
from core import messages, modules, config, style
from core.loggers import log
from core.module import Status
from core.weexceptions import ChannelException
Expand All @@ -30,10 +30,10 @@ def get_completions(self, document, complete_event):
module = ''
alias = False
if attr.startswith('do_alias_'):
module = ':'+attr[9:]
module = ':' + attr[9:]
alias = True
elif attr.startswith('do_'):
module = ':'+attr[3:]
module = ':' + attr[3:]

classname = ('alias' if alias else 'module')
if module and module.startswith(document.text):
Expand All @@ -47,25 +47,6 @@ def get_completions(self, document, complete_event):
class Terminal:
session = dict()
kb = KeyBindings()
style = Style.from_dict({
'bottom-toolbar': 'noreverse',
'bottom-toolbar.text': 'bg:#ff0000',
'rprompt': 'bg:#ff0066 #000000',
'username': '#999999 bold',
'at': '#00aa00',
'colon': '#ffffff',
'pound': '#00aa00',
'host': '#ff00ff',
'path': 'ansicyan',
'mode': '#ff0000',
'gutter': '#999999',
'label': '#00aa00 bold',
'value': '#33ff66',

'completion-menu': 'bg:#96d0f7 #000000',
'completion-menu.completion.current': 'bg:#0077ff #000000',
'completion-menu.completion.alias': 'bg:#1a9cf2',
})

def __init__(self, session):
self.session = session
Expand All @@ -84,7 +65,7 @@ def cmdloop(self):
reserve_space_for_menu=10,
completer=self.completer,
key_bindings=self.kb,
style=self.style,
style=style.default_style,
)
line = self.precmd(line)
self.onecmd(line)
Expand All @@ -94,6 +75,9 @@ def cmdloop(self):
raise EOFError

def precmd(self, line):
if line == 'exit':
raise EOFError

self.init_default_shell()
return line

Expand Down Expand Up @@ -129,7 +113,7 @@ def default(self, line):
result.endswith('\n')
) else result

print(result)
log.info(result)

def parseline(self, line):
"""Parse the line into a command name and a string containing
Expand Down Expand Up @@ -213,25 +197,30 @@ def init_default_shell(self):
def get_prompt_message(self):
shell = self.session.get('default_shell')
pound = '?'
host = ''
if not shell:
pound = 'weevely>'
host = 'weevely'
pound = '>'
elif shell == 'shell_sh':
pound = '$'
elif shell == 'shell_php':
pound = 'PHP>'

info = self.session.get_connection_info()
user = info.get('user')

if not shell or len(info.get('user')) == 0:
if not shell or len(user) == 0:
return [
('class:host', host),
('class:pound', pound),
('class:space', ' '),
]

msg = []
userclass = 'class:username' + ('.root' if user == 'root' else '')

msg.extend([
('class:username', info.get('user')),
(userclass, user),
('class:at', '@'),
('class:host', info.get('host')),
('class:colon', ':'),
Expand All @@ -243,18 +232,23 @@ def get_prompt_message(self):

return msg

def do_help(self, line, cmd):
self._print_modules(line)

if self.session['shell_sh']['status'] != Status.RUN:
log.info(messages.terminal.help_no_shell)
self._print_command_replacements()

def do_show(self, line, cmd):
"""Command "show" which prints session variables"""

self.session.print_to_user(line)

def do_set(self, line, cmd):
"""Command "set" to set session variables."""

try:
args = shlex.split(line)
except Exception as e:
import traceback;
import traceback
log.debug(traceback.format_exc())
log.warning(messages.generic.error_parsing_command_s % str(e))

Expand All @@ -268,7 +262,6 @@ def do_set(self, line, cmd):

def do_unset(self, line, cmd):
"""Command "unset" to unset session variables."""

# Print all settings that startswith args[0]
if not line:
log.warning(messages.terminal.unset_usage)
Expand All @@ -279,7 +272,6 @@ def do_unset(self, line, cmd):

def _load_modules(self):
"""Load all modules assigning corresponding do_* functions."""

for module_name, module_class in modules.loaded.items():

# Set module.do_terminal_module() function as terminal
Expand Down Expand Up @@ -313,7 +305,7 @@ def _print_intro(self):
else:
hostname = 'undefined host'

print_formatted_text(HTML(template.Template(
log.info(template.Template(
messages.terminal.welcome_to_s
).render(
path=self.session.get('path'),
Expand All @@ -323,7 +315,28 @@ def _print_intro(self):
user=info.get('user'),
hostname=hostname,
conn_path=info.get('path'),
)), style=self.style)
))

def _print_modules(self, term):
for module_group, names in modules.loaded_tree.items():
elements = []
for module_name in names:
if term in module_name:
elements.append([':%s' % module_name, modules.loaded[module_name].info.get('description', '')])

if not term or len(elements):
log.info('<label>%s</label>' % module_group.capitalize())
log.info(utils.prettify.tablify(elements, table_border=False))
print()

def _print_command_replacements(self):
data = []
for module_name, module in modules.loaded.items():
if module.aliases:
data.append([', '.join(module.aliases), module_name])

if data:
log.info(utils.prettify.tablify(data, table_border=False))

@staticmethod
@kb.add('enter')
Expand Down
14 changes: 8 additions & 6 deletions modules/backdoor/tcp.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from core.vectors import PythonCode, ShellCmd, Os
from core.module import Module
from core.loggers import log
from core import messages
import urllib.parse
import telnetlib
import time
import urllib.parse

from core import messages
from core.loggers import log
from core.module import Module
from core.vectors import PythonCode, ShellCmd, Os


class Tcp(Module):
"""Spawn a shell on a TCP port."""
Expand Down Expand Up @@ -72,7 +74,7 @@ def init(self):
{ 'name' : '-vector', 'choices' : self.vectors.get_names() }
])

def run(self):
def run(self, **kwargs):

# Run all the vectors
for vector in self.vectors:
Expand Down
2 changes: 1 addition & 1 deletion modules/shell/ssh.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class Ssh(Module):
py.pexpect | No | Rarely installed
py.subprocess | Yes | Stores clear passwd on disk temporarily
/!\\ When using py.subprocess vector the password is stored in a file at <ASKPASS> /!\\
/!\\ When using py.subprocess vector the password is stored in a file at ASKPASS /!\\
As a result the file gets truncated, chmod and then removed. Becareful !
This file has to be executable, you have to insure it spawns on a partition
that allows execution (ie. not mounted with `noexec`).
Expand Down

0 comments on commit 30531b6

Please sign in to comment.