@@ -2077,8 +2077,9 @@ class RidCycleParams:
20772077 "groups", "machines" and/or a domain sid. By default enumerated_input is an empty dict
20782078 and will be filled up during the tool run.
20792079 '''
2080- def __init__ (self , rid_ranges , known_usernames ):
2080+ def __init__ (self , rid_ranges , batch_size , known_usernames ):
20812081 self .rid_ranges = rid_ranges
2082+ self .batch_size = batch_size
20822083 self .known_usernames = known_usernames
20832084 self .enumerated_input = {}
20842085
@@ -2127,7 +2128,7 @@ def run(self):
21272128 # Run...
21282129 for sid in sids_list :
21292130 print_info (f"Trying SID { sid } " )
2130- rid_cycler = self .rid_cycle (sid , self .cycle_params .rid_ranges )
2131+ rid_cycler = self .rid_cycle (sid , self .cycle_params .rid_ranges , self . cycle_params . batch_size )
21312132 for result in rid_cycler :
21322133 # We need the top level key to find out whether we got users, groups, machines or the domain_sid...
21332134 top_level_key = list (result .retval .keys ())[0 ]
@@ -2216,41 +2217,45 @@ def enum_sids(self, users):
22162217 return Result (sids , f"Found { len (sids )} SID(s)" )
22172218 return Result (None , "Could not get any SIDs" )
22182219
2219- def rid_cycle (self , sid , rid_ranges ):
2220+ def rid_cycle (self , sid , rid_ranges , batch_size ):
22202221 '''
22212222 Takes a SID as first parameter well as list of RID ranges (as tuples) as second parameter and does RID cycling.
22222223 '''
22232224 for rid_range in rid_ranges :
22242225 (start_rid , end_rid ) = rid_range
22252226
2226- for rid in range (start_rid , end_rid + 1 ):
2227+ for rid_base in range (start_rid , end_rid + 1 , batch_size ):
2228+ target_sids = " " .join (list (map (lambda x : f'{ sid } -{ x } ' , range (rid_base , rid_base + batch_size ))))
22272229 #FIXME: Could we get rid of error_filter=False?
2228- result = SambaRpcclient (['lookupsids' , f' { sid } - { rid } ' ], self .target , self .creds ).run (log = 'RID Cycling' , error_filter = False )
2230+ result = SambaRpcclient (['lookupsids' , target_sids ], self .target , self .creds ).run (log = 'RID Cycling' , error_filter = False )
22292231
2230- # Example: S-1-5-80-3139157870-2983391045-3678747466-658725712-1004 *unknown*\*unknown* (8)
2231- match = re .search (r"(S-\d+-\d+-\d+-[\d-]+\s+(.*)\s+[^\)]+\))" , result .retmsg )
2232- if match :
2233- sid_and_user = match .group (1 )
2234- entry = match .group (2 )
2235-
2236- # Samba servers sometimes claim to have user accounts
2237- # with the same name as the UID/RID. We don't report these.
2238- if re .search (r"-(\d+) .*\\\1 \(" , sid_and_user ):
2239- continue
2240-
2241- # "(1)" = User, "(2)" = Domain Group,"(3)" = Domain SID,"(4)" = Local Group
2242- # "(5)" = Well-known group, "(6)" = Deleted account, "(7)" = Invalid account
2243- # "(8)" = Unknown, "(9)" = Machine/Computer account
2244- if "(1)" in sid_and_user :
2245- yield Result ({"users" :{str (rid ):{"username" :entry }}}, f"Found user '{ entry } ' (RID { rid } )" )
2246- elif "(2)" in sid_and_user :
2247- yield Result ({"groups" :{str (rid ):{"groupname" :entry , "type" :"domain" }}}, f"Found domain group '{ entry } ' (RID { rid } )" )
2248- elif "(3)" in sid_and_user :
2249- yield Result ({"domain_sid" :f"{ sid } -{ rid } " }, f"Found domain SID { sid } -{ rid } " )
2250- elif "(4)" in sid_and_user :
2251- yield Result ({"groups" :{str (rid ):{"groupname" :entry , "type" :"builtin" }}}, f"Found builtin group '{ entry } ' (RID { rid } )" )
2252- elif "(9)" in sid_and_user :
2253- yield Result ({"machines" :{str (rid ):{"machine" :entry }}}, f"Found machine '{ entry } ' (RID { rid } )" )
2232+ split_result = result .retmsg .splitlines ()
2233+ for line in range (len (split_result )):
2234+ # Example: S-1-5-80-3139157870-2983391045-3678747466-658725712-1004 *unknown*\*unknown* (8)
2235+ match = re .search (r"(S-\d+-\d+-\d+-[\d-]+\s+(.*)\s+[^\)]+\))" , split_result [line ])
2236+ if match :
2237+ sid_and_user = match .group (1 )
2238+ entry = match .group (2 )
2239+ rid = rid_base + line
2240+
2241+ # Samba servers sometimes claim to have user accounts
2242+ # with the same name as the UID/RID. We don't report these.
2243+ if re .search (r"-(\d+) .*\\\1 \(" , sid_and_user ):
2244+ continue
2245+
2246+ # "(1)" = User, "(2)" = Domain Group,"(3)" = Domain SID,"(4)" = Local Group
2247+ # "(5)" = Well-known group, "(6)" = Deleted account, "(7)" = Invalid account
2248+ # "(8)" = Unknown, "(9)" = Machine/Computer account
2249+ if "(1)" in sid_and_user :
2250+ yield Result ({"users" :{str (rid ):{"username" :entry }}}, f"Found user '{ entry } ' (RID { rid } )" )
2251+ elif "(2)" in sid_and_user :
2252+ yield Result ({"groups" :{str (rid ):{"groupname" :entry , "type" :"domain" }}}, f"Found domain group '{ entry } ' (RID { rid } )" )
2253+ elif "(3)" in sid_and_user :
2254+ yield Result ({"domain_sid" :f"{ sid } -{ rid } " }, f"Found domain SID { sid } -{ rid } " )
2255+ elif "(4)" in sid_and_user :
2256+ yield Result ({"groups" :{str (rid ):{"groupname" :entry , "type" :"builtin" }}}, f"Found builtin group '{ entry } ' (RID { rid } )" )
2257+ elif "(9)" in sid_and_user :
2258+ yield Result ({"machines" :{str (rid ):{"machine" :entry }}}, f"Found machine '{ entry } ' (RID { rid } )" )
22542259
22552260### Shares Enumeration
22562261
@@ -2770,7 +2775,7 @@ def run(self):
27702775 # RID Cycling - init parameters
27712776 if self .args .R :
27722777 rid_ranges = self .prepare_rid_ranges ()
2773- self .cycle_params = RidCycleParams (rid_ranges , self .args .users )
2778+ self .cycle_params = RidCycleParams (rid_ranges , self .args .R , self . args . users )
27742779
27752780 # Shares Brute Force - init parameters
27762781 if self .args .shares_file :
@@ -2784,6 +2789,7 @@ def run(self):
27842789 print_info (f"Timeout .......... { self .target .timeout } second(s)" )
27852790 if self .args .R :
27862791 print_info (f"RID Range(s) ..... { self .args .ranges } " )
2792+ print_info (f"RID Req Size ..... { self .args .R } " )
27872793 print_info (f"Known Usernames .. '{ self .args .users } '" )
27882794
27892795 # The enumeration starts with a service scan. Currently this scans for
@@ -3210,7 +3216,7 @@ def check_arguments():
32103216 parser .add_argument ("-O" , action = "store_true" , help = "Get OS information via RPC" )
32113217 parser .add_argument ("-L" , action = "store_true" , help = "Get additional domain info via LDAP/LDAPS (for DCs only)" )
32123218 parser .add_argument ("-I" , action = "store_true" , help = "Get printer information via RPC" )
3213- parser .add_argument ("-R" , action = "store_true" , help = "Enumerate users via RID cycling" )
3219+ parser .add_argument ("-R" , default = 0 , const = 1 , nargs = '?' , type = int , help = "Enumerate users via RID cycling. Optionally, specifies lookup request size. " )
32143220 parser .add_argument ("-N" , action = "store_true" , help = "Do an NetBIOS names lookup (similar to nbtstat) and try to retrieve workgroup from output" )
32153221 parser .add_argument ("-w" , dest = "domain" , default = '' , type = str , help = "Specify workgroup/domain manually (usually found automatically)" )
32163222 parser .add_argument ("-u" , dest = "user" , default = '' , type = str , help = "Specify username to use (default \" \" )" )
0 commit comments