├── .gitignore ├── BloodHound_Custom_Queries ├── add-custom-queries.png ├── README.md └── customqueries.json ├── BloodHound_Custom_Queries_Merger ├── bloodhound_queries.png ├── README.md └── bloodhound-customqueries-downloader ├── README.md ├── Neo4J_Custom_Queries └── README.md └── BloodHound_Loader ├── README.md └── BloodHoundLoader.py /.gitignore: -------------------------------------------------------------------------------- 1 | BloodHound_Custom_Queries_Merger/customqueries.json 2 | -------------------------------------------------------------------------------- /BloodHound_Custom_Queries/add-custom-queries.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/e1abrador/BloodHoundQueries/master/BloodHound_Custom_Queries/add-custom-queries.png -------------------------------------------------------------------------------- /BloodHound_Custom_Queries_Merger/bloodhound_queries.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/e1abrador/BloodHoundQueries/master/BloodHound_Custom_Queries_Merger/bloodhound_queries.png -------------------------------------------------------------------------------- /BloodHound_Custom_Queries/README.md: -------------------------------------------------------------------------------- 1 | # BloodHound Custom Queries 2 | 3 | Here is a simple description of the BloodHound queries in [customqueries.json](customqueries.json). 4 | 5 | ## Installation 6 | 7 | On Linux, you can simply install the queries using this curl command: 8 | ```bash 9 | curl -o ~/.config/bloodhound/customqueries.json "https://raw.githubusercontent.com/CompassSecurity/BloodHoundQueries/master/BloodHound_Custom_Queries/customqueries.json" 10 | ``` 11 | 12 | On Windows, you can simply install the queries using this PowerShell command: 13 | ```powershell 14 | Invoke-WebRequest -Uri "https://raw.githubusercontent.com/CompassSecurity/BloodHoundQueries/master/BloodHound_Custom_Queries/customqueries.json" -OutFile "$env:USERPROFILE\AppData\Roaming\bloodhound\customqueries.json" 15 | ``` 16 | 17 | ## Special Queries 18 | 19 | ### Shortest Paths from no Signing to ... 20 | 21 | Return shortest paths from computers without SMB signing to the Domain. 22 | 23 | The computers without signing have to be imported manually with [BloodHound Loader](../BloodHound_Loader). 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Compass Security BloodHound Resources 2 | 3 | ## About 4 | 5 | This project contains: 6 | 7 | * [BloodHound Custom Queries](BloodHound_Custom_Queries) we often use to see important things in BloodHound 8 | * [Neo4j Custom Queries](Neo4J_Custom_Queries) we use to extract data directly from the Neo4j browser console 9 | * [BloodHound Loader](BloodHoundLoader) script, which allows to make batch modifications to the BloodHound data 10 | * [BloodHound Custom Queries Merger](BloodHound_Custom_Queries_Merger) script to download and merge multiple BloodHound customqueries from different sources. 11 | 12 | ## References 13 | 14 | * Make the most out of BloodHound: https://blog.compass-security.com/2020/07/make-the-most-out-of-bloodhound/ 15 | * Pre-built BloodHound Queries: https://github.com/BloodHoundAD/BloodHound/blob/master/src/components/SearchContainer/Tabs/PrebuiltQueries.json 16 | * Introduction to Cypher Query Language: https://blog.cptjesus.com/posts/introtocypher 17 | * Cypher Cheat Sheet: https://github.com/SadProcessor/Cheats/blob/master/DogWhispererV2.md 18 | * BloodHound Cypher Cheatsheet: https://hausec.com/2019/09/09/bloodhound-cypher-cheatsheet/ 19 | * Handy BloodHound Cypher Queries: https://github.com/mgeeky/Penetration-Testing-Tools/blob/master/red-teaming/bloodhound/Handy-BloodHound-Cypher-Queries.md 20 | -------------------------------------------------------------------------------- /BloodHound_Custom_Queries_Merger/README.md: -------------------------------------------------------------------------------- 1 | # BloodHound Custom Query Merger 2 | 3 | ## Introduction 4 | 5 | This script merges various BloodHound custom queries together into one file 6 | which can then be used on BloodHound. 7 | 8 | The file contains the following BloodHound custom queries: 9 | 10 | - Compass BloodHound Customqueries by [@CompassSecurity](https://github.com/CompassSecurity) 11 | - https://blog.compass-security.com/2020/07/make-the-most-out-of-bloodhound/ 12 | - https://github.com/CompassSecurity/BloodHoundQueries/ 13 | - Certipy BloodHound Customqueries by [@ly4k](https://github.com/ly4k) 14 | - https://research.ifcr.dk/certipy-2-0-bloodhound-new-escalations-shadow-credentials-golden-certificates-and-more-34d1c26f0dc6 15 | - https://github.com/ly4k/Certipy 16 | - Hausec BloodHound Customqueries by [@hausec](https://github.com/hausec) 17 | - https://hausec.com/2019/09/09/bloodhound-cypher-cheatsheet/ 18 | - https://hausec.com/2020/11/23/azurehound-cypher-cheatsheet/ 19 | - https://github.com/hausec/Bloodhound-Custom-Queries 20 | 21 | ## Usage 22 | 23 | Download and various queries: 24 | ```bash 25 | ./bloodhound-customqueries-merger 26 | ``` 27 | 28 | Install the queries: 29 | ```bash 30 | cp customqueries.json ~/.config/bloodhound/customqueries.json 31 | ``` 32 | 33 | You will find the custom queries in BloodHound: 34 | 35 | ![Merged BloodHound Custom Queries](bloodhound_queries.png) 36 | 37 | ## Alternative 38 | 39 | [Bloodhound Query Merger (bqm)](https://github.com/Acceis/bqm) is another tool that deduplicates custom BloudHound queries from different datasets (currently 12) and merge them in one customqueries.json file. 40 | -------------------------------------------------------------------------------- /Neo4J_Custom_Queries/README.md: -------------------------------------------------------------------------------- 1 | # Custom Neo4j Queries 2 | 3 | ## Introduction 4 | 5 | The following queries are to be used in Neo4j Browser directly (by default http://localhost:7474/browser/). 6 | 7 | ## Queries 8 | 9 | ### LAPS 10 | 11 | Show how many computers have LAPS enabled and disabled: 12 | ```cypher 13 | MATCH (c:Computer) RETURN c.haslaps, COUNT(*) 14 | ``` 15 | 16 | ### Local Administrators 17 | 18 | In certain cases, the groups being local administrators are added locally on the computer and not deployed via GPO. In that case, the "AdminTo" edges are not visible in BloodHound. 19 | 20 | If the naming convention allows it, it is possible to find which group has access to which computer and to add the corresponding edges. 21 | 22 | First of all, search for all the groups containing the name of a computer and lists the mapping: 23 | 24 | ```cypher 25 | MATCH (g:Group), (c:Computer) WHERE g.name =~ (".*" + replace(c.name, ("." + c.domain), (".*" + "@" + c.domain))) RETURN g.name AS Group, c.name AS Computer 26 | ``` 27 | 28 | If result is similar to this, you might be lucky and be able to add several new edges to your BloodHound: 29 | ```cypher 30 | Group Computer 31 | PREFIX_COMPUTER1_SUFFIX@DOMAIN.LOCAL COMPUTER1.DOMAIN.LOCAL 32 | PREFIX_COMPUTER2_SUFFIX@DOMAIN.LOCAL COMPUTER2.DOMAIN.LOCAL 33 | PREFIX_COMPUTER3_SUFFIX@DOMAIN.LOCAL COMPUTER3.DOMAIN.LOCAL 34 | ``` 35 | 36 | In order to create the new the edges according to the naming convention, you can use the following query where you have to replace the "PREFIX_" and "_SUFFIX" according to the results above: 37 | ```cypher 38 | MATCH (g:Group), (c:Computer) WHERE g.name =~ ("PREFIX_" + replace(c.name, ("." + c.domain), ("_SUFFIX" + "@" + c.domain))) CREATE (g)-[r:AdminTo]->(c) RETURN g.name AS Group, c.name AS Computer 39 | ``` 40 | -------------------------------------------------------------------------------- /BloodHound_Custom_Queries_Merger/bloodhound-customqueries-downloader: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o errexit 4 | set -o nounset 5 | set -o pipefail 6 | 7 | echo "[*] Creating temporary directory..." 8 | TMPDIR="$(mktemp -d --suffix=_bloodhound-customqueries)" 9 | 10 | # Compass BloodHound Customqueries 11 | echo "[*] Downloading Compass BloodHound customqueries..." 12 | curl -s -o "$TMPDIR/customqueries-compass.json" "https://raw.githubusercontent.com/CompassSecurity/BloodHoundQueries/master/BloodHound_Custom_Queries/customqueries.json" 13 | 14 | echo "[*] Modifying category on Compass BloodHound customqueries..." 15 | jq '.queries[].category |= (sub("^";"🧭 Compass: "))' < "$TMPDIR/customqueries-compass.json" > "$TMPDIR/customqueries-01-compass-modified.json" 16 | 17 | # Certipy BloodHound Customqueries 18 | echo "[*] Downloading Certipy BloodHound customqueries..." 19 | curl -s -o "$TMPDIR/customqueries-certipy.json" "https://raw.githubusercontent.com/ly4k/Certipy/main/customqueries.json" 20 | 21 | echo "[*] Modifying category on Certipy BloodHound customqueries..." 22 | jq '.queries[].category |= (sub("^";"🔏 Certipy: "))' < "$TMPDIR/customqueries-certipy.json" > "$TMPDIR/customqueries-02-certipy-modified.json" 23 | 24 | # Hausec BloodHound Customqueries 25 | echo "[*] Downloading Hausec BloodHound customqueries..." 26 | curl -s -o "$TMPDIR/customqueries-hausec.json" "https://raw.githubusercontent.com/hausec/Bloodhound-Custom-Queries/master/customqueries.json" 27 | 28 | echo "[*] Adding category to Hausec BloodHound customqueries..." 29 | jq '.queries[] |= { "category": "💻 Hausec" } +. ' < "$TMPDIR/customqueries-hausec.json" > "$TMPDIR/customqueries-02-hausec-modified.json" 30 | 31 | echo "[*] Merging queries..." 32 | cat "$TMPDIR/"*-modified.json | jq -s 'add + {queries: map(.queries[])}' > customqueries.json 33 | 34 | echo "[*] Done. Please copy to your config directory:" 35 | echo "cp customqueries.json ~/.config/bloodhound/" 36 | 37 | echo "[*] Bye." 38 | -------------------------------------------------------------------------------- /BloodHound_Loader/README.md: -------------------------------------------------------------------------------- 1 | # BloodHound Loader 2 | 3 | ## Introduction 4 | 5 | [BloodHoundLoader.py](BloodHoundLoader.py) is a tool to set the value of an 6 | attribute in BloodHound (e.g. high value, owned...) for all the items contained 7 | in a file. 8 | 9 | ## Installation 10 | 11 | It should be used with Python 3 and with the Neo4j module installed since it is run directly against the Neo4j database: 12 | ```bash 13 | pip3 install --upgrade neo4j 14 | ``` 15 | 16 | Set all the computers in the file "high_value.txt" to high value targets: 17 | ```bash 18 | python3 BloodHoundLoader.py --dburi bolt://localhost:7687 --dbuser neo4j --dbpassword BloodHound --mode h high_value.txt 19 | ``` 20 | 21 | Set all the computers in the file "owned.txt" to owned principals: 22 | ```bash 23 | python3 BloodHoundLoader.py --mode o owned.txt 24 | ``` 25 | 26 | Set all the computers in the file "no_smb_signing.txt" to "hassigning = false", in order to use them with the queries "All Shortest Paths from no Signing to *": 27 | ```bash 28 | python3 BloodHoundLoader.py --mode s no_smb_signing.txt 29 | ``` 30 | 31 | The names of users and computers in the text file should correspond to the text shown on the GUI, e.g.: 32 | ``` 33 | DC.ACME.COM 34 | COMPUTER.ACME.COM 35 | GUEST@ACME.COM 36 | ``` 37 | 38 | Create new AdminTo edges based on the tuples in the file "adminto.txt": 39 | ```bash 40 | python3 BloodHoundLoader.py --edge AdminTo adminto.txt 41 | ``` 42 | 43 | The names tuples in the text file must be comma separated and ordered (source,destination): 44 | ``` 45 | DOMAIN ADMINS@ACME.COM,DC.ACME.COM 46 | SERVER ADMINS@ACME.COM,COMPUTER1.ACME.COM 47 | EDWARD.NIGMA@ACME.COM,RIDDLE.ACME.COM 48 | ``` 49 | 50 | ## Full Help 51 | 52 | ```bash 53 | python3 BloodHoundLoader.py --help 54 | usage: BloodHoundLoader.py [-h] [--dburi DATABASEURI] [--dbuser DATABASEUSER] [--dbpassword DATABASEPASSWORD] (-m {h,o,s} | -o OPERATION) [-c COMMENT] [-v] filePaths [filePaths ...] 55 | 56 | BloodHoundLoader, tool to set attributes in BloodHound for all the items contained in files 57 | 58 | positional arguments: 59 | filePaths Paths of files the to import 60 | 61 | optional arguments: 62 | -h, --help show this help message and exit 63 | --dburi DATABASEURI Database URI (default: bolt://localhost:7687) 64 | --dbuser DATABASEUSER 65 | Database user (default: neo4j) 66 | --dbpassword DATABASEPASSWORD 67 | Database password (default: BloodHound) 68 | -m {h,o,s}, --mode {h,o,s} 69 | Mode, h = set to high value, o = set to owned, s = set to no SMB signing, u = umark as owned (default: None) 70 | -o OPERATION, --operation OPERATION 71 | Operation to perform if the mode is not set, for instance "highvalue = true" (default: None) 72 | -e EDGE, --edge EDGE 73 | Create the provided edge, file must contain exactly 2 nodes per line, comma separated (default: None) 74 | -c COMMENT, --comment COMMENT 75 | Comment for the log (default: ) 76 | -v, --verbose Verbose mode (default: False) 77 | ``` 78 | -------------------------------------------------------------------------------- /BloodHound_Loader/BloodHoundLoader.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter 4 | import logging 5 | from importlib import util 6 | 7 | if util.find_spec('neo4j') is None: 8 | print('[-] Neo4j library is not installed, please execute the following before: pip3 install --upgrade neo4j') 9 | exit() 10 | 11 | from neo4j import Auth, GraphDatabase 12 | from neo4j.exceptions import ServiceUnavailable 13 | 14 | parser = ArgumentParser(description = 'BloodHoundLoader, tool to set attributes in BloodHound for all the items contained in files', formatter_class = ArgumentDefaultsHelpFormatter) 15 | parser.add_argument('--dburi', dest = 'databaseUri', help = 'Database URI', default = 'bolt://localhost:7687') 16 | parser.add_argument('--dbuser', dest = 'databaseUser', help = 'Database user', default = 'neo4j') 17 | parser.add_argument('--dbpassword', dest = 'databasePassword', help = 'Database password', default = 'BloodHound') 18 | group = parser.add_mutually_exclusive_group(required = True) 19 | group.add_argument('-m', '--mode', dest = 'mode', help = 'Mode, h = set to high value, o = set to owned, s = set to no SMB signing, u = unmark as owned', choices = ['h', 'o', 's', 'u']) 20 | group.add_argument('-o', '--operation', dest = 'operation', help = 'Operation to perform if the mode is not set, for instance "highvalue = true"') 21 | group.add_argument('-e', '--edge', dest = 'edge', help = 'Create the provided edge, file must contain exactly 2 nodes per line, comma separated') 22 | parser.add_argument('-c', '--comment', dest = 'comment', help = 'Comment for the log', default = '') 23 | parser.add_argument('-v', '--verbose', dest = 'verbose', help = 'Verbose mode', action = 'store_true') 24 | parser.add_argument('-b', '--batchsize', dest = 'batchSize', help = 'Number of element to update simultaneously', type = int, default = 10000) 25 | parser.add_argument('filePaths', nargs = '+', help = 'Paths of files the to import') 26 | arguments = parser.parse_args() 27 | 28 | loggingLevel = (logging.DEBUG if arguments.verbose else logging.INFO) 29 | 30 | logger = logging.getLogger('BloodHoundLoader') 31 | logger.setLevel(loggingLevel) 32 | 33 | consoleLogger = logging.StreamHandler() 34 | consoleLogger.setLevel(loggingLevel) 35 | logger.addHandler(consoleLogger) 36 | 37 | logger.debug('[*] Arguments: ' + str(arguments)) 38 | 39 | if arguments.mode == 'h': 40 | operation = 'highvalue = true' 41 | elif arguments.mode == 'o': 42 | operation = 'owned = true' 43 | elif arguments.mode == 'u': 44 | operation = 'owned = false' 45 | elif arguments.mode == 's': 46 | operation = 'hassigning = false' 47 | elif not arguments.edge is None: 48 | operation = 'edge' 49 | else: 50 | operation = arguments.operation 51 | 52 | logger.debug('[*] Operation: ' + operation) 53 | 54 | def handleStandardOperation(filePath, operation, comment, inputSet): 55 | log = '(file: ' + filePath + ', comment: ' + comment + ', operation: ' + operation + ')' 56 | query = 'MATCH (b:Base) WHERE b.name IN $inputSet SET b.' + operation + ', b.BloodHoundLoaderLog = $log RETURN b.name AS name' 57 | results = session.run(query, inputSet = list(inputSet), log = log) 58 | 59 | modifiedSet = set() 60 | for result in results: 61 | modifiedSet.add(result['name']) 62 | errorSet = inputSet - modifiedSet 63 | 64 | modified = len(modifiedSet) 65 | errors = len(errorSet) 66 | total = modified + errors 67 | logger.info('[*] %s%6i%s%6i%s%6i' % ('Modified:', modified, ' Errors:', errors, ' Total:', total)) 68 | 69 | if errors > 0: 70 | logger.debug('[-] Items in error: ' + str(errorSet)) 71 | 72 | try: 73 | driver = GraphDatabase.driver(arguments.databaseUri, auth = Auth(scheme = 'basic', principal = arguments.databaseUser, credentials = arguments.databasePassword)) 74 | with driver.session() as session: 75 | for filePath in arguments.filePaths: 76 | with open(filePath) as file: 77 | logger.info('[*] Opened file: ' + filePath) 78 | 79 | if operation == 'edge': 80 | for line in file: 81 | item = line.strip().split(',') 82 | logger.debug('[*] Current item: ' + item[0] + ' ' + item[1]) 83 | 84 | if item[0] and item[1]: 85 | source = item[0].upper() 86 | destination = item[1].upper() 87 | 88 | log = '(file: ' + filePath + ', comment: ' + arguments.comment + ')' 89 | query = 'MATCH (s),(d) WHERE s.name = "' + source + '" AND d.name = "' + destination + '" CREATE (s)-[r:' + arguments.edge + ']->(d) SET r.BloodHoundLoaderLog = "' + log + '" RETURN COUNT(*) AS count' 90 | logger.debug('[*] Query: ' + query) 91 | results = session.run(query) 92 | 93 | count = results.single()['count'] 94 | if count > 0: 95 | logger.info('[+] Created: ' + source + ' - ' + arguments.edge + ' -> ' + destination) 96 | logger.debug('[*] Number of modified entries: ' + str(count)) 97 | logger.debug('[*] Stored message: ' + log) 98 | else: 99 | logger.error('[-] Could not create: ' + source + ' - ' + arguments.edge + ' -> ' + destination) 100 | 101 | else: 102 | inputSet = set() 103 | for line in file: 104 | item = line.strip() 105 | if item: 106 | name = item.upper() 107 | inputSet.add(name) 108 | 109 | if len(inputSet) >= arguments.batchSize: 110 | handleStandardOperation(filePath, operation, arguments.comment, inputSet) 111 | inputSet = set() 112 | 113 | if len(inputSet) > 0: 114 | handleStandardOperation(filePath, operation, arguments.comment, inputSet) 115 | 116 | except ServiceUnavailable: 117 | logger.exception('[-] Connection to BloodHound Neo4j database failed') 118 | except Exception: 119 | logger.exception('[-] Error') 120 | -------------------------------------------------------------------------------- /BloodHound_Custom_Queries/customqueries.json: -------------------------------------------------------------------------------- 1 | { 2 | "queries": [ 3 | { 4 | "name": "Domains", 5 | "category": "Information Gathering", 6 | "queryList": [ 7 | { 8 | "final": true, 9 | "query": "MATCH (d:Domain) RETURN d" 10 | } 11 | ] 12 | }, 13 | { 14 | "name": "Domain Controllers", 15 | "category": "Information Gathering", 16 | "queryList": [ 17 | { 18 | "final": false, 19 | "title": "Select a Domain Controllers Group...", 20 | "query": "MATCH (n:Group) WHERE n.objectid ENDS WITH \"-516\" RETURN n.name ORDER BY n.name DESC" 21 | }, 22 | { 23 | "final": true, 24 | "query": "MATCH p=(c:Computer)-[:MemberOf*1..]->(n:Group {name: $result}) RETURN p", 25 | "allowCollapse": false 26 | } 27 | ] 28 | }, 29 | { 30 | "name": "High Value Targets", 31 | "category": "Information Gathering", 32 | "queryList": [ 33 | { 34 | "final": false, 35 | "title": "Select a Domain...", 36 | "query": "MATCH (n:Domain) RETURN n.name ORDER BY n.name DESC" 37 | }, 38 | { 39 | "final": true, 40 | "query": "MATCH p = (d:Domain {name: $result})-[r:Contains*1..]->(h {highvalue: true}) RETURN p", 41 | "allowCollapse": false 42 | } 43 | ] 44 | }, 45 | { 46 | "name": "Computers without LAPS", 47 | "category": "Information Gathering", 48 | "queryList": [ 49 | { 50 | "final": false, 51 | "title": "Select a Domain...", 52 | "query": "MATCH (n:Domain) RETURN n.name ORDER BY n.name DESC" 53 | }, 54 | { 55 | "final": true, 56 | "query": "MATCH p = (d:Domain {name: $result})-[r:Contains*1..]->(c:Computer {haslaps: false}) RETURN p" 57 | } 58 | ] 59 | }, 60 | { 61 | "name": "Owned Principals", 62 | "category": "Information Gathering", 63 | "queryList": [ 64 | { 65 | "final": true, 66 | "query": "MATCH p = (d:Domain)-[r:Contains*1..]->(o {owned: true}) RETURN p" 67 | } 68 | ] 69 | }, 70 | { 71 | "name": "Sensitive Principals by Keywords", 72 | "category": "Information Gathering", 73 | "queryList": [ 74 | { 75 | "final": true, 76 | "query": "UNWIND ['admin', 'amministratore', 'empfindlich', 'geheim', 'important', 'azure', 'MSOL', 'kennwort', 'pass', 'secret', 'sensib', 'sensitiv'] AS word MATCH (n) WHERE (toLower(n.name) CONTAINS toLower(word)) OR (toLower(n.description) CONTAINS toLower(word)) RETURN n" 77 | } 78 | ] 79 | }, 80 | { 81 | "name": "Users with Password in AD", 82 | "category": "Accounts", 83 | "queryList": [ 84 | { 85 | "final": false, 86 | "title": "Select a Domain...", 87 | "query": "MATCH (n:Domain) RETURN n.name ORDER BY n.name DESC" 88 | }, 89 | { 90 | "final": true, 91 | "query": "MATCH p = (d:Domain {name: $result})-[r:Contains*1..]->(u:User) WHERE u.userpassword IS NOT NULL RETURN p" 92 | } 93 | ] 94 | }, 95 | { 96 | "name": "Users with \"Pass\" in AD Description", 97 | "category": "Accounts", 98 | "queryList": [ 99 | { 100 | "final": false, 101 | "title": "Select a Domain...", 102 | "query": "MATCH (n:Domain) RETURN n.name ORDER BY n.name DESC" 103 | }, 104 | { 105 | "final": true, 106 | "query": "MATCH p = (d:Domain {name: $result})-[r:Contains*1..]->(u:User) WHERE u.description =~ '(?i).*pass.*' RETURN p" 107 | } 108 | ] 109 | }, 110 | { 111 | "name": "Users with Password not Required", 112 | "category": "Accounts", 113 | "queryList": [ 114 | { 115 | "final": false, 116 | "title": "Select a Domain...", 117 | "query": "MATCH (n:Domain) RETURN n.name ORDER BY n.name DESC" 118 | }, 119 | { 120 | "final": true, 121 | "query": "MATCH p = (d:Domain {name: $result})-[r:Contains*1..]->(u:User {passwordnotreqd: true}) RETURN p" 122 | } 123 | ] 124 | }, 125 | { 126 | "name": "Users with Password never Expiring", 127 | "category": "Accounts", 128 | "queryList": [ 129 | { 130 | "final": false, 131 | "title": "Select a Domain...", 132 | "query": "MATCH (n:Domain) RETURN n.name ORDER BY n.name DESC" 133 | }, 134 | { 135 | "final": true, 136 | "query": "MATCH p = (d:Domain {name: $result})-[r:Contains*1..]->(u:User {pwdneverexpires: True}) WHERE NOT u.name starts with 'KRBTGT' RETURN u" 137 | } 138 | ] 139 | }, 140 | { 141 | "name": "Users with with Same Name in Different Domains", 142 | "category": "Accounts", 143 | "queryList": [ 144 | { 145 | "final": true, 146 | "query": "MATCH (u1:User),(u2:User) WHERE split(u1.name,'@')[0] = split(u2.name,'@')[0] AND u1.domain <> u2.domain AND tointeger(split(u1.objectid,'-')[7]) >= 1000 RETURN u1" 147 | } 148 | ] 149 | }, 150 | { 151 | "name": "Protected Users", 152 | "category": "Privileged Accounts", 153 | "queryList": [ 154 | { 155 | "final": false, 156 | "title": "Select a Protected Users Group...", 157 | "query": "MATCH (n:Group) WHERE n.objectid ENDS WITH \"-525\" RETURN n.name ORDER BY n.name DESC" 158 | }, 159 | { 160 | "final": true, 161 | "query": "MATCH p=(u:User)-[:MemberOf*1..]->(n:Group {name: $result}) RETURN p" 162 | } 163 | ] 164 | }, 165 | { 166 | "name": "AdminTo Relationships", 167 | "category": "Privileged Accounts", 168 | "queryList": [ 169 | { 170 | "final": false, 171 | "title": "Select a Domain...", 172 | "query": "MATCH (n:Domain) RETURN n.name ORDER BY n.name DESC" 173 | }, 174 | { 175 | "final": true, 176 | "query": "MATCH p=(u {domain: $result})-[r:AdminTo]->(c:Computer) RETURN p" 177 | } 178 | ] 179 | }, 180 | { 181 | "name": "Administrators", 182 | "category": "Privileged Accounts", 183 | "queryList": [ 184 | { 185 | "final": false, 186 | "title": "Select a Administrators Group...", 187 | "query": "MATCH (n:Group) WHERE n.objectid ENDS WITH \"-544\" RETURN n.name ORDER BY n.name DESC" 188 | }, 189 | { 190 | "final": true, 191 | "query": "MATCH p=(u:User)-[:MemberOf*1..]->(n:Group {name: $result}) RETURN p" 192 | } 193 | 194 | ] 195 | }, 196 | { 197 | "name": "Computers in Administrators", 198 | "category": "Privileged Accounts", 199 | "queryList": [ 200 | { 201 | "final": false, 202 | "title": "Select a Administrators Group...", 203 | "query": "MATCH (n:Group) WHERE n.objectid ENDS WITH \"-544\" RETURN n.name ORDER BY n.name DESC" 204 | }, 205 | { 206 | "final": true, 207 | "query": "MATCH p = (c:Computer)-[r:MemberOf|HasSIDHistory*1..]->(g:Group {name: $result}) RETURN p", 208 | "endNode": "{}" 209 | } 210 | ] 211 | }, 212 | { 213 | "name": "Computers Local Admin to Another Computer", 214 | "category": "Privileged Accounts", 215 | "queryList": [ 216 | { 217 | "final": false, 218 | "title": "Select a Domain...", 219 | "query": "MATCH (n:Domain) RETURN n.name ORDER BY n.name DESC" 220 | }, 221 | { 222 | "final": true, 223 | "query": "MATCH p = (c1:Computer {domain: $result})-[r1:AdminTo]->(c2:Computer) RETURN p UNION ALL MATCH p = (c3:Computer {domain: $result})-[r2:MemberOf|HasSIDHistory*1..]->(g:Group)-[r3:AdminTo]->(c4:Computer) RETURN p" 224 | } 225 | ] 226 | }, 227 | { 228 | "name": "Sessions of Administrators on non DCs Computers", 229 | "category": "Privileged Accounts", 230 | "queryList": [ 231 | { 232 | "final": false, 233 | "title": "Select a Domain...", 234 | "query": "MATCH (n:Domain) RETURN n.name ORDER BY n.name DESC" 235 | }, 236 | { 237 | "final": true, 238 | "query": "MATCH (dc:Computer {domain: $result})-[r1:MemberOf*0..]->(g1:Group) WHERE g1.objectid =~ \"S-1-5-.*-516\" WITH COLLECT(dc) AS exclude MATCH p = (c:Computer {domain: $result})-[n:HasSession]->(u:User)-[r2:MemberOf*1..]->(g2:Group) WHERE NOT c IN exclude and g2.objectid ENDS WITH \"-544\" RETURN p" 239 | } 240 | ] 241 | }, 242 | { 243 | "name": "DCSync Principals not Administrators", 244 | "category": "Privileged Accounts", 245 | "queryList": [ 246 | { 247 | "final": false, 248 | "title": "Select a Domain...", 249 | "query": "MATCH (n:Domain) RETURN n.name ORDER BY n.name DESC" 250 | }, 251 | { 252 | "final": true, 253 | "query": "MATCH (admins {domain: $result})-[r1:MemberOf*0..]->(g1:Group) WHERE (g1.objectid =~ \"(?i)S-1-5-.*-512\") OR (g1.objectid =~ \"(?i)S-1-5-.*-516\") OR (g1.objectid =~ \"(?i)S-1-5-.*-518\") OR (g1.objectid =~ \"(?i)S-1-5-.*-519\") OR (g1.objectid =~ \"(?i)S-1-5-.*-520\") OR (g1.objectid =~ \"(?i)S-1-5-.*-544\") OR (g1.objectid =~ \"(?i)S-1-5-.*-548\") OR (g1.objectid =~ \"(?i)S-1-5-.*-549\") OR (g1.objectid =~ \"(?i)S-1-5-.*-551\") WITH COLLECT(admins) AS exclude MATCH p=(n1)-[:MemberOf|GetChanges*0..]->(u:Domain {name: $result}) WHERE NOT n1 IN exclude and (n1:Computer or n1:User) RETURN p" 254 | } 255 | ] 256 | }, 257 | { 258 | "name": "AS-REP Roastable Principals", 259 | "category": "Kerberos", 260 | "queryList": [ 261 | { 262 | "final": false, 263 | "title": "Select a Domain...", 264 | "query": "MATCH (n:Domain) RETURN n.name ORDER BY n.name DESC" 265 | }, 266 | { 267 | "final": true, 268 | "query": "MATCH (d:Domain {name: $result})-[r:Contains*1..]->(u {dontreqpreauth: true}) RETURN u" 269 | } 270 | ] 271 | }, 272 | { 273 | "name": "Kerberoastable Principals", 274 | "category": "Kerberos", 275 | "queryList": [ 276 | { 277 | "final": false, 278 | "title": "Select a Domain...", 279 | "query": "MATCH (n:Domain) RETURN n.name ORDER BY n.name DESC" 280 | }, 281 | { 282 | "final": true, 283 | "query": "MATCH (d:Domain {name: $result})-[r:Contains*1..]->(u {hasspn: true}) RETURN u" 284 | } 285 | ] 286 | }, 287 | { 288 | "name": "Kerberoastable Administrators", 289 | "category": "Kerberos", 290 | "queryList": [ 291 | { 292 | "final": false, 293 | "title": "Select a Domain...", 294 | "query": "MATCH (n:Domain) RETURN n.name ORDER BY n.name DESC" 295 | }, 296 | { 297 | "final": true, 298 | "query": "MATCH (admins {domain: $result})-[r1:MemberOf*0..]->(g1:Group) WHERE (g1.objectid =~ \"(?i)S-1-5-.*-512\") OR (g1.objectid =~ \"(?i)S-1-5-.*-516\") OR (g1.objectid =~ \"(?i)S-1-5-.*-518\") OR (g1.objectid =~ \"(?i)S-1-5-.*-519\") OR (g1.objectid =~ \"(?i)S-1-5-.*-520\") OR (g1.objectid =~ \"(?i)S-1-5-.*-544\") OR (g1.objectid =~ \"(?i)S-1-5-.*-548\") OR (g1.objectid =~ \"(?i)S-1-5-.*-549\") OR (g1.objectid =~ \"(?i)S-1-5-.*-551\") WITH COLLECT(admins) AS filter MATCH (d:Domain {name: $result})-[r:Contains*1..]->(u {hasspn: true}) WHERE u IN filter RETURN u" 299 | } 300 | ] 301 | }, 302 | { 303 | "name": "Constrained Delegations", 304 | "category": "Kerberos", 305 | "queryList": [ 306 | { 307 | "final": false, 308 | "title": "Select a Domain...", 309 | "query": "MATCH (n:Domain) RETURN n.name ORDER BY n.name DESC" 310 | }, 311 | { 312 | "final": true, 313 | "query": "MATCH p = (a {domain: $result})-[:AllowedToDelegate]->(c:Computer) RETURN p" 314 | } 315 | ] 316 | }, 317 | { 318 | "name": "Constrained Delegations with Protocol Transition (trustedToAuth)", 319 | "category": "Kerberos", 320 | "queryList": [ 321 | { 322 | "final": false, 323 | "title": "Select a Domain...", 324 | "query": "MATCH (n:Domain) RETURN n.name ORDER BY n.name DESC" 325 | }, 326 | { 327 | "final": true, 328 | "query": "MATCH p = (a {domain: $result, trustedtoauth: true})-[:AllowedToDelegate]->(c:Computer) RETURN p" 329 | } 330 | ] 331 | }, 332 | { 333 | "name": "Computers Allowed to Delegate for Another Computer", 334 | "category": "Kerberos", 335 | "queryList": [ 336 | { 337 | "final": false, 338 | "title": "Select a Domain...", 339 | "query": "MATCH (n:Domain) RETURN n.name ORDER BY n.name DESC" 340 | }, 341 | { 342 | "final": true, 343 | "query": "MATCH p = (c1:Computer {domain: $result})-[:AllowedToDelegate]->(c2:Computer) RETURN p" 344 | } 345 | ] 346 | }, 347 | { 348 | "name": "Unconstrained Delegation Principals", 349 | "category": "Kerberos", 350 | "queryList": [ 351 | { 352 | "final": false, 353 | "title": "Select a Domain...", 354 | "query": "MATCH (n:Domain) RETURN n.name ORDER BY n.name DESC" 355 | }, 356 | { 357 | "final": true, 358 | "query": "MATCH (dca)-[r:MemberOf*0..]->(g:Group) WHERE g.objectid =~ \"S-1-5-.*-516\" OR g.objectid =~ \".*-S-1-5-32-544\" WITH COLLECT(dca) AS exclude MATCH p = (d:Domain {name: $result})-[r:Contains*1..]->(uc {unconstraineddelegation: true}) WHERE (uc:User OR uc:Computer) AND NOT uc IN exclude RETURN p" 359 | } 360 | ] 361 | }, 362 | { 363 | "name": "Resource-Based Constrained Delegation Principals", 364 | "category": "Kerberos", 365 | "queryList": [ 366 | { 367 | "final": false, 368 | "title": "Select a Domain...", 369 | "query": "MATCH (n:Domain) RETURN n.name ORDER BY n.name DESC" 370 | }, 371 | { 372 | "final": true, 373 | "query": "MATCH p=(m)-[r:AllowedToAct]->(n) RETURN p" 374 | } 375 | ] 376 | }, 377 | { 378 | "name": "Configure Resource-Based Constrained Delegation Permissions", 379 | "category": "Kerberos", 380 | "queryList": [ 381 | { 382 | "final": false, 383 | "title": "Select a Domain...", 384 | "query": "MATCH (n:Domain) RETURN n.name ORDER BY n.name DESC" 385 | }, 386 | { 387 | "final": true, 388 | "query": "MATCH p=(m)-[r:AddAllowedToAct]->(n) RETURN p" 389 | } 390 | ] 391 | }, 392 | { 393 | "name": "Interesting GPOs by Keyword", 394 | "category": "Group Policies", 395 | "queryList": [ 396 | { 397 | "final": false, 398 | "title": "Select a Domain...", 399 | "query": "MATCH (n:Domain) RETURN n.name ORDER BY n.name DESC" 400 | }, 401 | { 402 | "final": true, 403 | "query": "UNWIND [\"360totalsecurity\", \"access\", \"acronis\", \"adaware\", \"admin\", \"admin\", \"aegislab\", \"ahnlab\", \"alienvault\", \"altavista\", \"amsi\", \"anti-virus\", \"antivirus\", \"antiy\", \"apexone\", \"applock\", \"arcabit\", \"arcsight\", \"atm\", \"atp\", \"av\", \"avast\", \"avg\", \"avira\", \"baidu\", \"baiduspider\", \"bank\", \"barracuda\", \"bingbot\", \"bitdefender\", \"bluvector\", \"canary\", \"carbon\", \"carbonblack\", \"certificate\", \"check\", \"checkpoint\", \"citrix\", \"clamav\", \"code42\", \"comodo\", \"countercept\", \"countertack\", \"credential\", \"crowdstrike\", \"custom\", \"cyberark\", \"cybereason\", \"cylance\", \"cynet360\", \"cyren\", \"darktrace\", \"datadog\", \"defender\", \"druva\", \"drweb\", \"duckduckbot\", \"edr\", \"egambit\", \"emsisoft\", \"encase\", \"endgame\", \"ensilo\", \"escan\", \"eset\", \"exabot\", \"exception\", \"f-secure\", \"f5\", \"falcon\", \"fidelis\", \"fireeye\", \"firewall\", \"fix\", \"forcepoint\", \"forti\", \"fortigate\", \"fortil\", \"fortinet\", \"gdata\", \"gravityzone\", \"guard\", \"honey\", \"huntress\", \"identity\", \"ikarussecurity\", \"insight\", \"ivanti\", \"juniper\", \"k7antivirus\", \"k7computing\", \"kaspersky\", \"kingsoft\", \"kiosk\", \"laps\", \"lightcyber\", \"logging\", \"logrhythm\", \"lynx\", \"malwarebytes\", \"manageengine\", \"mass\", \"mcafee\", \"microsoft\", \"mj12bot\", \"msnbot\", \"nanoav\", \"nessus\", \"netwitness\", \"office365\", \"onedrive\", \"orion\", \"palo\", \"paloalto\", \"paloaltonetworks\", \"panda\", \"pass\", \"powershell\", \"proofpoint\", \"proxy\", \"qradar\", \"rdp\", \"rsa\", \"runasppl\", \"sandboxe\", \"sap\", \"scanner\", \"scanning\", \"sccm\", \"script\", \"secret\", \"secureage\", \"secureworks\", \"security\", \"sensitive\", \"sentinel\", \"sentinelone\", \"slurp\", \"smartcard\", \"sogou\", \"solarwinds\", \"sonicwall\", \"sophos\", \"splunk\", \"superantispyware\", \"symantec\", \"tachyon\", \"temporary\", \"tencent\", \"totaldefense\", \"transfer\", \"trapmine\", \"trend micro\", \"trendmicro\", \"trusteer\", \"trustlook\", \"uac\", \"vdi\", \"virusblokada\", \"virustotal\", \"virustotalcloud\", \"vpn\", \"vuln\", \"webroot\", \"whitelist\", \"wifi\", \"winrm\", \"workaround\", \"yubikey\", \"zillya\", \"zonealarm\", \"zscaler\"] as word match (n:GPO {domain: $result}) where toLower(n.name) CONTAINS toLower(word) RETURN n" 404 | } 405 | ] 406 | }, 407 | { 408 | "name": "GPO Permissions of Non-Admin Principals", 409 | "category": "Group Policies", 410 | "queryList": [ 411 | { 412 | "final": false, 413 | "title": "Select a Domain...", 414 | "query": "MATCH (n:Domain) RETURN n.name ORDER BY n.name DESC" 415 | }, 416 | { 417 | "final": true, 418 | "query": "MATCH (u1:user {domain: $result})-[r:MemberOf*1..]->(n:Group) WHERE (n.objectid =~ \"(?i)S-1-5-.*-512\") OR (n.objectid =~ \"(?i)S-1-5-.*-516\") OR (n.objectid =~ \"(?i)S-1-5-.*-518\") OR (n.objectid =~ \"(?i)S-1-5-.*-519\") OR (n.objectid =~ \"(?i)S-1-5-.*-520\") OR (n.objectid =~ \"(?i)S-1-5-.*-544\") OR (n.objectid =~ \"(?i)S-1-5-.*-548\") OR (n.objectid =~ \"(?i)S-1-5-.*-549\") OR (n.objectid =~ \"(?i)S-1-5-.*-551\") WITH COLLECT(u1) AS exclude MATCH p = (u2:User)-[r:AddMember|AddSelf|WriteSPN|AddKeyCredentialLink|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|Owns]->(g:GPO) WHERE NOT u2 IN exclude RETURN p" 419 | } 420 | ] 421 | }, 422 | { 423 | "name": "LAPS Passwords Readable by Non-Admin", 424 | "category": "DACL Abuse", 425 | "queryList": [ 426 | { 427 | "final": false, 428 | "title": "Select a Domain...", 429 | "query": "MATCH (n:Domain) RETURN n.name ORDER BY n.name DESC" 430 | }, 431 | { 432 | "final": true, 433 | "query": "MATCH (u1:user {domain: $result})-[r:MemberOf*1..]->(n:Group) WHERE (n.objectid =~ \"(?i)S-1-5-.*-512\") OR (n.objectid =~ \"(?i)S-1-5-.*-516\") OR (n.objectid =~ \"(?i)S-1-5-.*-518\") OR (n.objectid =~ \"(?i)S-1-5-.*-519\") OR (n.objectid =~ \"(?i)S-1-5-.*-520\") OR (n.objectid =~ \"(?i)S-1-5-.*-544\") OR (n.objectid =~ \"(?i)S-1-5-.*-548\") OR (n.objectid =~ \"(?i)S-1-5-.*-549\") OR (n.objectid =~ \"(?i)S-1-5-.*-551\") WITH COLLECT(u1) AS exclude MATCH p = (u2)-[r1:MemberOf*1..]->(g:Group)-[r2:GenericAll]->(t:Computer {haslaps:true}) WHERE NOT u2 IN exclude RETURN p" 434 | } 435 | ] 436 | }, 437 | { 438 | "name": "LAPS Passwords Readable by Owned Principals", 439 | "category": "DACL Abuse", 440 | "queryList": [ 441 | { 442 | "final": true, 443 | "query": "MATCH p = (n {owned: true})-[r1:MemberOf*1..]->(g:Group)-[r2:GenericAll]->(t:Computer {haslaps:true}) RETURN p" 444 | } 445 | ] 446 | }, 447 | { 448 | "name": "ACLs to Computers (excluding High Value Targets)", 449 | "category": "DACL Abuse", 450 | "queryList": [ 451 | { 452 | "final": false, 453 | "title": "Select a Domain...", 454 | "query": "MATCH (n:Domain) RETURN n.name ORDER BY n.name DESC" 455 | }, 456 | { 457 | "final": true, 458 | "query": "MATCH p = (ucg {highvalue: false})-[r {isacl: true}]->(c:Computer {domain: $result}) WHERE (ucg:User OR ucg:Computer OR ucg:Group) RETURN p" 459 | } 460 | ] 461 | }, 462 | { 463 | "name": "Group Delegated Outbound Object Control of Owned Principals", 464 | "category": "DACL Abuse", 465 | "queryList": [ 466 | { 467 | "final": true, 468 | "query": "MATCH p = (n {owned: true})-[r1:MemberOf*1..]->(g:Group)-[r2 {isacl: true}]->(t) RETURN p" 469 | } 470 | ] 471 | }, 472 | { 473 | "name": "Dangerous Rights for Groups under Domain Users", 474 | "category": "DACL Abuse", 475 | "queryList": [ 476 | { 477 | "final": false, 478 | "title": "Select a Domain...", 479 | "query": "MATCH (n:Domain) RETURN n.name ORDER BY n.name DESC" 480 | }, 481 | { 482 | "final": true, 483 | "query": "MATCH p=(m:Group {domain: $result})-[r1:MemberOf*1..]->(g:Group)-[:Owns|WriteDacl|GenericAll|WriteOwner|ExecuteDCOM|GenericWrite|AllowedToDelegate|ForceChangePassword]->(n) WHERE m.objectid ENDS WITH '-513' RETURN p" 484 | } 485 | ] 486 | }, 487 | { 488 | "name": "Set DCSync Principals as High Value Targets", 489 | "category": "Adding High-Value Targets", 490 | "queryList": [ 491 | { 492 | "final": true, 493 | "query": "MATCH (s)-[r:MemberOf|GetChanges*1..]->(d:Domain) WITH s, d MATCH (s)-[r:MemberOf|GetChangesAll*1..]->(d) WITH s, d MATCH p = (s)-[r:MemberOf|GetChanges|GetChangesAll*1..]->(d) WHERE s.highvalue = false SET s.highvalue = true, s.highvaluereason = 'DCSync Principal' RETURN p" 494 | } 495 | ] 496 | }, 497 | { 498 | "name": "Set Unconstrained Delegation Principals as High Value Targets", 499 | "category": "Adding High-Value Targets", 500 | "queryList": [ 501 | { 502 | "final": true, 503 | "query": "MATCH p = (d:Domain)-[r:Contains*1..]->(uc) WHERE (uc:User OR uc:Computer) AND uc.unconstraineddelegation = true AND uc.highvalue = false SET uc.highvalue = true, uc.highvaluereason = 'Unconstrained Delegation Principal' RETURN p" 504 | } 505 | ] 506 | }, 507 | { 508 | "name": "Set Local Admin or Reset Password Principals as High Value Targets", 509 | "category": "Adding High-Value Targets", 510 | "queryList": [ 511 | { 512 | "final": true, 513 | "query": "MATCH (a)-[r:AdminTo|ForceChangePassword]->(b) WHERE a.highvalue = false SET a.highvalue = true, a.highvaluereason = 'Local Admin or Reset Password Principal' RETURN a" 514 | } 515 | ] 516 | }, 517 | { 518 | "name": "Set Principals with Privileges on Computers as High Value Targets", 519 | "category": "Adding High-Value Targets", 520 | "queryList": [ 521 | { 522 | "final": true, 523 | "query": "MATCH (a)-[r:AllowedToDelegate|ExecuteDCOM|ForceChangePassword|GenericAll|GenericWrite|Owns|WriteDacl|WriteOwner]->(n:Computer) WHERE a.highvalue = false SET a.highvalue = true, a.highvaluereason = 'Principal with Privileges on Computers' RETURN a" 524 | } 525 | ] 526 | }, 527 | { 528 | "name": "Set Principals with Privileges on Cert Publishers as High Value Targets", 529 | "category": "Adding High-Value Targets", 530 | "queryList": [ 531 | { 532 | "final": true, 533 | "query": "MATCH (a)-[r:GenericAll|GenericWrite|MemberOf|Owns|WriteDacl|WriteOwner]->(g:Group) WHERE g.objectid =~ 'S-1-5-21-.*-517' AND a.highvalue = false SET a.highvalue = true, a.highvaluereason = 'Principal with Privileges on the Cert Publisher group' RETURN a" 534 | } 535 | ] 536 | }, 537 | { 538 | "name": "Set Members of High Value Targets Groups as High Value Targets", 539 | "category": "Adding High-Value Targets", 540 | "queryList": [ 541 | { 542 | "final": true, 543 | "query": "MATCH (a)-[r:MemberOf*1..]->(g:Group) WHERE a.highvalue = false AND g.highvalue = true SET a.highvalue = true, a.highvaluereason = 'Member of High Value Target Group' RETURN a" 544 | } 545 | ] 546 | }, 547 | { 548 | "name": "Remove Inactive Users and Computers from High Value Targets", 549 | "category": "Adding High-Value Targets", 550 | "queryList": [ 551 | { 552 | "final": true, 553 | "query": "MATCH (uc) WHERE uc.highvalue = true AND ((uc:User AND uc.enabled = false) OR (uc:Computer AND ((uc.enabled = false) OR (uc.lastlogon > 0 AND uc.lastlogon < (TIMESTAMP() / 1000 - 15552000)) OR (uc.lastlogontimestamp > 0 AND uc.lastlogontimestamp < (TIMESTAMP() / 1000 - 15552000))))) SET uc.highvalue = false, uc.nothighvaluereason = 'Inactive' RETURN uc" 554 | } 555 | ] 556 | }, 557 | { 558 | "name": "Shortest Paths to Domain (including Computers)", 559 | "category": "Shortest Paths", 560 | "queryList": [ 561 | { 562 | "final": false, 563 | "title": "Select a Domain...", 564 | "query": "MATCH (d:Domain) RETURN d.name ORDER BY d.name ASC" 565 | }, 566 | { 567 | "final": true, 568 | "query": "MATCH p = allShortestPaths((uc)-[r:{}*1..]->(d:Domain {name: $result})) WHERE (uc:User OR uc:Computer) RETURN p", 569 | "endNode": "{}" 570 | } 571 | ] 572 | }, 573 | { 574 | "name": "Shortest Paths to no LAPS", 575 | "category": "Shortest Paths", 576 | "queryList": [ 577 | { 578 | "final": true, 579 | "query": "MATCH p = allShortestPaths((uc)-[r:{}*1..]->(c:Computer)) WHERE (uc:User OR uc:Computer) AND NOT uc = c AND c.haslaps = false RETURN p" 580 | } 581 | ] 582 | }, 583 | { 584 | "name": "Shortest Paths from Kerberoastable Users to Computers", 585 | "category": "Shortest Paths", 586 | "queryList": [ 587 | { 588 | "final": true, 589 | "query": "MATCH p = allShortestPaths((u:User)-[r:{}*1..]->(c:Computer)) WHERE u.hasspn = true RETURN p" 590 | } 591 | ] 592 | }, 593 | { 594 | "name": "Shortest Paths from Kerberoastable Users to High Value Targets", 595 | "category": "Shortest Paths", 596 | "queryList": [ 597 | { 598 | "final": true, 599 | "query": "MATCH p = allShortestPaths((u:User)-[r:{}*1..]->(h)) WHERE u.hasspn = true AND h.highvalue = true RETURN p" 600 | } 601 | ] 602 | }, 603 | { 604 | "name": "Shortest Paths from Owned Principals (including everything)", 605 | "category": "Shortest Paths", 606 | "queryList": [ 607 | { 608 | "final": true, 609 | "query": "MATCH p = allShortestPaths((u:User)-[r:{}*1..]->(a)) WHERE u.owned = true AND u <> a RETURN p" 610 | } 611 | ] 612 | }, 613 | { 614 | "name": "Shortest Paths from Owned Principals to Domain", 615 | "category": "Shortest Paths", 616 | "queryList": [ 617 | { 618 | "final": false, 619 | "title": "Select a Domain...", 620 | "query": "MATCH (d:Domain) RETURN d.name ORDER BY d.name ASC" 621 | }, 622 | { 623 | "final": true, 624 | "query": "MATCH p = allShortestPaths((o)-[r:{}*1..]->(d:Domain)) WHERE o.owned = true AND d.name = $result RETURN p", 625 | "endNode": "{}" 626 | } 627 | ] 628 | }, 629 | { 630 | "name": "Shortest Paths from Owned Principals to High Value Targets", 631 | "category": "Shortest Paths", 632 | "queryList": [ 633 | { 634 | "final": true, 635 | "query": "MATCH p = allShortestPaths((o)-[r:{}*1..]->(h)) WHERE o.owned = true AND h.highvalue = true RETURN p" 636 | } 637 | ] 638 | }, 639 | { 640 | "name": "Shortest Paths from Owned Principals to no LAPS", 641 | "category": "Shortest Paths", 642 | "queryList": [ 643 | { 644 | "final": true, 645 | "query": "MATCH p = allShortestPaths((o)-[r:{}*1..]->(c:Computer)) WHERE NOT o = c AND o.owned = true AND c.haslaps = false RETURN p" 646 | } 647 | ] 648 | }, 649 | { 650 | "name": "Shortest Paths from no Signing to Domain", 651 | "category": "Shortest Paths", 652 | "queryList": [ 653 | { 654 | "final": false, 655 | "title": "Select a Domain...", 656 | "query": "MATCH (d:Domain) RETURN d.name ORDER BY d.name ASC" 657 | }, 658 | { 659 | "final": true, 660 | "query": "MATCH p = allShortestPaths((c:Computer)-[r:{}*1..]->(d:Domain)) WHERE c.hassigning = false AND d.name = $result RETURN p", 661 | "endNode": "{}" 662 | } 663 | ] 664 | }, 665 | { 666 | "name": "Shortest Paths from no Signing to High Value Targets", 667 | "category": "Shortest Paths", 668 | "queryList": [ 669 | { 670 | "final": true, 671 | "query": "MATCH p = allShortestPaths((c:Computer)-[r:{}*1..]->(h)) WHERE NOT c = h AND c.hassigning = false AND h.highvalue = true RETURN p" 672 | } 673 | ] 674 | }, 675 | { 676 | "name": "Shortest Paths from Domain Users and Domain Computers (including everything)", 677 | "category": "Shortest Paths", 678 | "queryList": [ 679 | { 680 | "final": true, 681 | "query": "MATCH p = allShortestPaths((g:Group)-[r:{}*1..]->(a)) WHERE (g.objectid =~ $domain_users_id OR g.objectid =~ $domain_computers_id) AND g <> a RETURN p", 682 | "props": { 683 | "domain_users_id": "S-1-5-.*-513", 684 | "domain_computers_id": "S-1-5-.*-515" 685 | } 686 | } 687 | ] 688 | } 689 | ] 690 | } 691 | --------------------------------------------------------------------------------