NH-RED-TEAM / RustHound

Active Directory data collector for BloodHound written in Rust. 🦀

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Aces missing from Users.json?? - version 2 (BloodhoundCE)

LarsVonZipper opened this issue · comments

Hi,
It's probably me but ...,

When I use Rusthound from the v2-branch, there seems to be numerous 'missing' Aces in the users.json when I compare the results to those captured using rusthound from the main-branch version??

For example, there is a user account that has “Write all properties” access that applies to “Descendant User objects” set on the domain object.

With data collected using the main-branch Rusthound, all users in that domain have an Ace that referenced the said user account as having “IsInherited: true”, “GenericWrite” (correct). Whereas, data collected using the v2-branch Rusthound failed to record this on any user account?

Then, when I took a random user from data collected using the main-branch version, there were 68 Aces recorded. Data for the same user, collected at the same time using the v2-branch version only recorded 52.

Obviously the specific numbers are relative to the domain but the fact there was a large discrepancy worried me a little

Hi @LarsVonZipper,

I'll be analysing this more in detail during the week. Thank you for the information. 👍

Thanks @g0h4n I appreciate it. Let me know if I can help in someway.
Keep up the excellent work!

Hey @g0h4n,
Didn't know if the attached might be of some help??

The .CSV files are a bit redacted but hopefully you get the gist. They are the "Aces" component of the same user collected with the older and newer (v2) version of RustHound.
The screenshot's just show the difference in file sizes of the collected .JSON files comparing the older version of RustHound with the newer (v2)

Regards
Lars

oldRustHound
newRustGound
Old-UserAcesRedacted.csv
New-UserAcesRedacted.csv

Thanks for all the additional information, I appreciate it, it should help me! 😄

@LarsVonZipper,

I was unable to reproduce the issue
I used v1.1.69 and v2.0.0 to generate the JSON files and passed them to the following python script to retrieve only the ACLs. . However, v1 and v2 files have the same ACEs on the MayFly GOAD lab.

Could you please pass the JSON files into the script and send me the two examples?

#!/usr/bin/python3
import json
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('-f', action='store',
                     metavar='<JSON_filename>',
                     help='JSON file',
                     required=True)
parser.add_argument('-V', action='store',
                     metavar='<v1 or v2>',
                     help='Version',
                     required=True)
args = parser.parse_args()
filename = vars(args)['f']
version = vars(args)['V']

print("[?] filename: ",filename)
print("[?] version: ",version)

with open(filename, "r") as f:
	datas = json.load(f)
	for jsonObj in datas["data"]:
		name = jsonObj["Properties"]["name"]
		aces = jsonObj["Aces"]
		print("[+] Name '"+name+"' parsed")
		#print("[?] Aces: ",aces)
		with open(name+"_"+version+".json","w") as f2:
			f2.write(str(aces))
			f2.close
			print("[+] File '"+name+"_"+version+".json"+"' created")

Example:

#v1.1.69
python3 parse_json.py -f v1.1.69/2024xxxxxxxxxx_essos-local_users.json -V v1

#v2.0.0
python3 parse_json.py -f v2.0.0/2024xxxxxxxxxx_essos-local_users.json -V v2

Thanks for the update @g0h4n
On one hand I'm glad you didn't find any discrepancies but on the other.....
I'm more than willing to provide information on the user.json files returned. However, in my scenario I'm dealing with nearly 8000 users; the python script outputs a JSON file for each. Also, I'd need to redact the principalSid information somehow.
Any thoughts?

This version allows you to add a limit on the number of users to parse and I've added a reglex to automatically replace the contents of the SID in order to anonymise it.

#!/usr/bin/python3
import json
import argparse
import re

parser = argparse.ArgumentParser()
parser.add_argument('-f', action='store',
                     metavar='<JSON_filename>',
                     help='JSON file',
                     required=True)
parser.add_argument('-V', action='store',
                     metavar='<v1 or v2>',
                     help='Version',
                     required=True)
parser.add_argument('-n', type=int,
                     action='store',
                     metavar='<integer>',
                     help='Number users like -n 10',
                     required=True)

args = parser.parse_args()
filename = vars(args)['f']
version = vars(args)['V']
number = vars(args)['n']

print("[?] filename: ",filename)
print("[?] version: ",version)
print("[?] limit: ",str(number))

with open(filename, "r") as f:
	datas = json.load(f)
	cpt = 0
	for jsonObj in datas["data"]:
		if cpt == number:
			exit()
		try:
			name = re.sub(r'@([a-zA-Z0-9]+.[a-zA-Z0-9]+)', '@ANONYMOUS.LOCAL', str(jsonObj["Properties"]["name"]))
		except:
			name = jsonObj["Properties"]["name"]
		aces = jsonObj["Aces"]
		aces = re.sub(r'S-1-5-21-(\d+-\d+\-\d+)', 'S-1-5-21-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx', str(aces))
		aces = re.sub(r'([a-zA-Z0-9]+.[a-zA-Z0-9]+)-S-', 'ANONYMOUS.LOCAL-S-', str(aces))		
		print("["+str(cpt+1)+"] Name '"+name+"' parsed")
		#print("[?] Aces: ",aces)
		with open(name+"_"+version+".json","w") as f2:
			f2.write(str(aces))
			f2.close
			print("["+str(cpt+1)+"] File '"+name+"_"+version+".json"+"' created")
		cpt += 1

Examples:

#v1.1.69
python3 parse_json.py -f v1.1.69/2024xxxxxxxxxx_essos-local_users.json -V v1 -n 10

#v2.0.0
python3 parse_json.py -f v2.0.0/2024xxxxxxxxxx_essos-local_users.json -V v2 -n 10

Could you please make sure you send me the users where the ACEs are different?

Please see attached.
I have sent 10 user accounts as an initial sample. Please let me know if you require more or any more details.
Many thanks
Rusthound_v1_Results.tar.gz
Rusthound_v2_Results.tar.gz