jq

Parsing BloodHound Data
@ippsec
put out a pretty cool video on parsing large amounts of data by downloading the BH graph as a Json file to parse with jq
. Below are the queries he demonstrates to get useful data from large query results, which should give you a framework and methodology to experiment:
User Object Queries with jq
jq
To list out the keys inside of a JSON object:
cat users.json | jq '. | keys'
Go into the list based on the key (we only care about data here):
cat users.json | jq '.data[]'
Dump all of the usernames:
cat users.json | jq '.data[].Properties | .name'
Dump all users that are currently enabled:
cat users.json | jq '.data[].Properties | select( .enabled == true ) | .name'
Show all usernames and descriptions for disabled/enabled users (check for passwords in the description field!):
cat users.json | jq '.data[].Properties | select( .enabled == false ) | .name + " " + .description'
Enabled users, where the description is populated with something:
cat users.json | jq '.data[].Properties | select( .enabled == true) | select( .description != null ) | .name + " " + .description'
If you need to convert something from a number to a string, use (.value|tostring)
.
(.value|tostring)
.cat users.json | jq '.data[].Properties | select( .enabled == true) | select( .description != null ) | .name + " " + (.lastlogontimestamp|tostring)'
Show all enabled users and their lastlogin
value (if it’s 1
, the account hasn’t been logged into and you should target it for password spraying!):
lastlogin
value (if it’s 1
, the account hasn’t been logged into and you should target it for password spraying!):cat users.json | jq '.data[].Properties | select( .enabled == true) | select( .description != null ) | .name + " " + (.lastlogin|tostring)'
Show enabled users that have a passwords last set that is greater than the last logon of the computer (password reset but never logged in? password spray it!!!):
cat users.json | jq '.data[].Properties | select( .enabled == true) | select( .pwdlastset > .lastlogontimestamp ) | .name + " " + (.lastlogin|tostring)'
Kerberoastable Users:
cat users.json | jq '.data[].Properties | select( .serviceprincipalnames != [] ) | .name'
Computer Object Queries with jq
jq
Gather hostnames and their operating system:
cat computers.json | jq '.data[].Properties | select(.operatingsystem != null) | .name + ":" + .operatingsystem'
Show all hosts that are not running Windows 10 Pro:
cat computers.json | jq '.data[].Properties | select(.operatingsystem != null) | select(.operatingsystem != "Windows 10 Pro") | .name + ":" + .operatingsystem'
Unsupported Operating Systems
cat computers.json | jq '.data[].Properties | select(.operatingsystem != null) | select(.operatingsystem | contains("2008","2003","XP","NT","2012")) | .name + ":" + .operatingsystem'
Every machine has a password in AD that corresponds to the computer object.
It’s rotated every 30 days. The
lastlogontimestamp
value represents the last time a computer was started.
If the epoch timestamp is from a long time ago, this is a good way of detecting machines that are currently turned off, therefore they will be missed by automated scans and tools. Below is a query to show these systems:
cat computers.json | jq '.data[].Properties | .name + " " + (.lastlogontimestamp|tostring)'
cat computers.json | jq '.data[].Properties | select(.lastlogontimestamp > 1646237212) | .name'
Last updated