From b6681a9d569a0eca260d12c2c70cad632deb1ba2 Mon Sep 17 00:00:00 2001 From: John Hammond Date: Fri, 4 Jun 2021 23:09:56 -0400 Subject: [PATCH] Added an antivirus enumeration for Windows --- .../windows/enumerate/protections/__init__.py | 71 +++++++++++++++++++ .../enumerate/protections/antivirus.py | 61 ++++++++++++++++ pwncat/modules/windows/enumerate/system.py | 71 ------------------- .../windows/manage/powershell/import.py | 11 ++- pwncat/platform/windows.py | 35 ++++----- 5 files changed, 151 insertions(+), 98 deletions(-) create mode 100644 pwncat/modules/windows/enumerate/protections/antivirus.py delete mode 100644 pwncat/modules/windows/enumerate/system.py diff --git a/pwncat/modules/windows/enumerate/protections/__init__.py b/pwncat/modules/windows/enumerate/protections/__init__.py index e69de29..06c03d5 100644 --- a/pwncat/modules/windows/enumerate/protections/__init__.py +++ b/pwncat/modules/windows/enumerate/protections/__init__.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 + +from pwncat.facts import ArchData, DistroVersionData, HostnameData +from pwncat.facts.windows import WindowsUser +from pwncat.modules import ModuleFailed, Status +from pwncat.modules.enumerate import EnumerateModule, Schedule +from pwncat.platform.windows import PowershellError, Windows +from pwncat.util import random_string + + +class Module(EnumerateModule): + """Enumerate windows system information""" + + PROVIDES = ["system.distro", "system.arch", "system.hostname"] + PLATFORM = [Windows] + SCHEDULE = Schedule.ONCE + + def enumerate(self, session: "pwncat.manager.Session"): + + query_system_info = """ + function query_sysinfo { + $os_info = (Get-CimInstance Win32_operatingsystem) + $hostname = [System.Net.Dns]::GetHostName() + + [PsCustomObject]@{ + HostName = $hostname; + BuildNumber = $os_info.BuildNumber; + BuildType = $os_info.BuildType; + CountryCode = $os_info.CountryCode; + TimeZone = $os_info.CurrentTimeZone; + DEP = [PsCustomObject]@{ + Available = $os_info.DataExecutionPrevention_Available; + Available32 = $os_info.DataExecutionPrevention_32bitApplications; + Drivers = $os_info.DataExecutionPrevention_Drivers; + SupportPolicy = $os_info.DataExecutionPrevention_SupportPolicy; + }; + Debug = $os_info.Debug; + Description = $os_info.Description; + InstallDate = $os_info.InstallDate; + LastBootUpTime = $os_info.LastBootUpTime; + Name = $os_info.Name; + Architecture = $os_info.OSArchitecture; + Language = $os_info.OSLanguage; + Suite = $os_info.OSProductSuite; + Type = $os_info.OSType; + ServicePackMajor = $os_info.ServicePackMajorVersion; + ServicePackMinor = $os_info.ServicePackMinorVersion; + Version = $os_info.Version; + } + } + query_sysinfo + """.replace( + "query_sysinfo", random_string(8) + ) + + try: + info = session.platform.powershell(query_system_info)[0] + except PowershellError as exc: + raise ModuleFailed(f"failed to load sysinfo function: {exc}") + + yield DistroVersionData( + self.name, + info["Name"].split("|")[0], + info["BuildType"], + info["BuildNumber"], + info["Version"], + ) + + yield HostnameData(self.name, info["HostName"]) + + yield ArchData(self.name, info["Architecture"]) diff --git a/pwncat/modules/windows/enumerate/protections/antivirus.py b/pwncat/modules/windows/enumerate/protections/antivirus.py new file mode 100644 index 0000000..c8057ed --- /dev/null +++ b/pwncat/modules/windows/enumerate/protections/antivirus.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 + +from typing import Any, Dict, List + +import pwncat +import rich.markup +from pwncat import util +from pwncat.db import Fact +from pwncat.modules import ModuleFailed +from pwncat.modules.enumerate import EnumerateModule, Schedule +from pwncat.platform import PlatformError +from pwncat.platform.windows import PowershellError, Windows + + +class MountedDrive(Fact): + def __init__(self, source, av_name: str, exe_path: str): + super().__init__(source=source, types=["protection.antivirus"]) + + self.av_name: str = av_name + self.exe_path: str = exe_path + + def title(self, session): + return f"Antivirus [red]{rich.markup.escape(self.av_name)}[/red] running from [yellow]{rich.markup.escape(self.exe_path)}[/yellow]" + + +class Module(EnumerateModule): + """Enumerate the current Windows Defender settings on the target""" + + PROVIDES = ["protection.antivirus"] + PLATFORM = [Windows] + + def enumerate(self, session): + + proc = session.platform.Popen( + [ + "wmic.exe", + "/Node:localhost", + "/Namespace:\\\\root\\SecurityCenter2", + "Path", + "AntiVirusProduct", + "Get", + "displayName,pathToSignedReportingExe", + "/Format:csv", + ], + stderr=pwncat.subprocess.DEVNULL, + stdout=pwncat.subprocess.PIPE, + text=True, + ) + + # Process the standard output from the command + with proc.stdout as stream: + for line in stream: + line = line.strip() + + if not line or "displayName,pathToSignedReportingExe" in line: + continue + + _, av_name, exe_path = line.split(",") + yield MountedDrive(self.name, av_name, exe_path) + + proc.wait() diff --git a/pwncat/modules/windows/enumerate/system.py b/pwncat/modules/windows/enumerate/system.py deleted file mode 100644 index fce8a53..0000000 --- a/pwncat/modules/windows/enumerate/system.py +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env python3 - -from pwncat.util import random_string -from pwncat.facts import ArchData, HostnameData, DistroVersionData -from pwncat.modules import Status, ModuleFailed -from pwncat.facts.windows import WindowsUser -from pwncat.platform.windows import Windows, PowershellError -from pwncat.modules.enumerate import Schedule, EnumerateModule - - -class Module(EnumerateModule): - """ Enumerate windows system information """ - - PROVIDES = ["system.distro", "system.arch", "system.hostname"] - PLATFORM = [Windows] - SCHEDULE = Schedule.ONCE - - def enumerate(self, session: "pwncat.manager.Session"): - - query_system_info = """ - function query_sysinfo { - $os_info = (Get-CimInstance Win32_operatingsystem) - $hostname = [System.Net.Dns]::GetHostName() - - [PsCustomObject]@{ - HostName = $hostname; - BuildNumber = $os_info.BuildNumber; - BuildType = $os_info.BuildType; - CountryCode = $os_info.CountryCode; - TimeZone = $os_info.CurrentTimeZone; - DEP = [PsCustomObject]@{ - Available = $os_info.DataExecutionPrevention_Available; - Available32 = $os_info.DataExecutionPrevention_32bitApplications; - Drivers = $os_info.DataExecutionPrevention_Drivers; - SupportPolicy = $os_info.DataExecutionPrevention_SupportPolicy; - }; - Debug = $os_info.Debug; - Description = $os_info.Description; - InstallDate = $os_info.InstallDate; - LastBootUpTime = $os_info.LastBootUpTime; - Name = $os_info.Name; - Architecture = $os_info.OSArchitecture; - Language = $os_info.OSLanguage; - Suite = $os_info.OSProductSuite; - Type = $os_info.OSType; - ServicePackMajor = $os_info.ServicePackMajorVersion; - ServicePackMinor = $os_info.ServicePackMinorVersion; - Version = $os_info.Version; - } - } - query_sysinfo - """.replace( - "query_sysinfo", random_string(8) - ) - - try: - info = session.platform.powershell(query_system_info)[0] - except PowershellError as exc: - raise ModuleFailed(f"failed to load sysinfo function: {exc}") - - yield DistroVersionData( - self.name, - info["Name"].split("|")[0], - info["BuildType"], - info["BuildNumber"], - info["Version"], - ) - - yield HostnameData(self.name, info["HostName"]) - - yield ArchData(self.name, info["Architecture"]) diff --git a/pwncat/modules/windows/manage/powershell/import.py b/pwncat/modules/windows/manage/powershell/import.py index 949cbca..c989e82 100644 --- a/pwncat/modules/windows/manage/powershell/import.py +++ b/pwncat/modules/windows/manage/powershell/import.py @@ -1,11 +1,10 @@ #!/usr/bin/env python3 import os -from io import IOBase, BytesIO +from io import BytesIO, IOBase from pathlib import Path import requests - -from pwncat.modules import Bool, Argument, BaseModule, ModuleFailed +from pwncat.modules import Argument, BaseModule, Bool, ModuleFailed from pwncat.platform.windows import Windows @@ -33,14 +32,14 @@ class Module(BaseModule): self.imported_modules = [] def resolve_psmodule(self, session: "pwncat.manager.Session", path: str): - """ Resolve a module name into a file-like object """ + """Resolve a module name into a file-like object""" if path.startswith("http://") or path.startswith("https://"): # Load from a URL r = requests.get(path, stream=True) if r.status_code != 200: raise PSModuleNotFoundError(path) - return path.split("/")[-1], BytesIO(r.content) + return path.split("/")[-1], BytesIO(r.content + b"\n") orig_path = path path = Path(path) @@ -62,7 +61,7 @@ class Module(BaseModule): if r.status_code != 200: raise PSModuleNotFoundError(orig_path) - return (path.name, BytesIO(r.content + "\n")) + return (path.name, BytesIO(r.content + b"\n")) else: raise PSModuleNotFoundError(orig_path) diff --git a/pwncat/platform/windows.py b/pwncat/platform/windows.py index bfecf95..e30994a 100644 --- a/pwncat/platform/windows.py +++ b/pwncat/platform/windows.py @@ -13,38 +13,31 @@ processes and open multiple files with this platform. However, you should be careful to cleanup all processes and files prior to return from your method or code as the C2 will not attempt to garbage collect file or proces handles. """ -import os -import sys +import base64 import gzip import json -import stat -import time -import base64 -import shutil +import os import pathlib +import readline +import shutil +import stat +import subprocess +import sys import tarfile import termios -import readline import textwrap -import subprocess -from io import ( - BytesIO, - StringIO, - RawIOBase, - TextIOWrapper, - BufferedIOBase, - UnsupportedOperation, -) -from typing import List, Union, BinaryIO, Optional -from subprocess import TimeoutExpired, CalledProcessError +import time from dataclasses import dataclass +from io import (BufferedIOBase, BytesIO, RawIOBase, StringIO, TextIOWrapper, + UnsupportedOperation) +from subprocess import CalledProcessError, TimeoutExpired +from typing import BinaryIO, List, Optional, Union -import requests import pkg_resources - import pwncat -import pwncat.util import pwncat.subprocess +import pwncat.util +import requests from pwncat.platform import Path, Platform, PlatformError INTERACTIVE_END_MARKER = b"INTERACTIVE_COMPLETE\r\n"