|
| 1 | +# windapsearch |
| 2 | + |
| 3 | +> Utility to enumerate users, groups and computers from a Windows domain through LDAP queries. |
| 4 | +
|
| 5 | +## Getting computer information |
| 6 | + |
| 7 | +### Intro |
| 8 | + |
| 9 | +The `computers` module can be used to enumerate all AD computers and the results can be saved as JSON with `--json`. |
| 10 | + |
| 11 | +``` |
| 12 | +$ windapsearch -d <domain> -u <user> -p <password> --dc <dc_ip> --module computers --json > scans/windapsearch_computers.json |
| 13 | +``` |
| 14 | + |
| 15 | +### Parsing results |
| 16 | + |
| 17 | +Of course the text or JSON results can be used as is but: |
| 18 | + |
| 19 | +1. Having the DNS hostname (`dNSHostName`) can be nice but having the IP address is important and `windapsearch` doesn't offer it because it's not a LDAP information, it has to be retrieved from the DNS server. Having the IP address can be useful to quickly understand in which VLAN the machine is. |
| 20 | +2. The AD is often bloated with old objects like computers that doesn't exist anymore. So `windapsearch` will retrieve a lot of computers that don't have a DNS hostname or have one but that can't be resolved since the DNS entry was removed. As an auditor/pentester we mostly care about computers that are existing and reachable, old entries is noise to us. |
| 21 | + |
| 22 | +So here is a Ruby script that can: |
| 23 | + |
| 24 | +- displays the same output as `windapsearch` (dns hostname, cn, dn, OS infos) but with the IP address(es) on top (`full` command) |
| 25 | +- displays only cn and ip address(es) (`cnip` command) |
| 26 | +- displays only computers with a resolvable DNS host name |
| 27 | + |
| 28 | +```ruby |
| 29 | +require 'resolv' |
| 30 | +require 'json' |
| 31 | + |
| 32 | +data = JSON.load_file('windapsearch_computers.json') |
| 33 | + |
| 34 | +resolver = Resolv::DNS.new(nameserver: ['<dns_1>', '<dns_2>'], search: ['<domain>'], ndots: 1) |
| 35 | + |
| 36 | +def getips(res, name) |
| 37 | + if name.nil? || name.empty? |
| 38 | + 'none' |
| 39 | + else |
| 40 | + ips = res.getaddresses(name) |
| 41 | + if ips.empty? |
| 42 | + 'none' |
| 43 | + else |
| 44 | + ips.join(', ') |
| 45 | + end |
| 46 | + end |
| 47 | +end |
| 48 | + |
| 49 | +if ['cnip','full'].include?(ARGV[0]) |
| 50 | + data.each do |computer| |
| 51 | + ips = getips(resolver, computer['dNSHostName']) |
| 52 | + unless ips == 'none' |
| 53 | + puts "cn: #{computer['cn']}" |
| 54 | + puts "ip(s): #{ips}" |
| 55 | + if ARGV[0] == 'full' |
| 56 | + puts "dNSHostName: #{computer['dNSHostName']}" |
| 57 | + puts "dn: #{computer['dn']}" |
| 58 | + puts "operatingSystem: #{computer['operatingSystem']}" |
| 59 | + puts "operatingSystemVersion: #{computer['operatingSystemVersion']}" |
| 60 | + puts "operatingSystemServicePack: #{computer['operatingSystemServicePack']}" |
| 61 | + end |
| 62 | + puts |
| 63 | + end |
| 64 | + end |
| 65 | +else |
| 66 | + puts "[+] Usage: <cnip|full>\n\n" |
| 67 | + puts 'cnip: displays only cn and ip address(es)' |
| 68 | + puts "full: same as cnip + dns hostname, dn, OS infos\n\n" |
| 69 | + puts 'Note: displays only computers with a resolvable DNS hostname' |
| 70 | +end |
| 71 | +``` |
| 72 | + |
| 73 | +`<dns_1>` and `<dns_2>` need to be replaced with the DNS servers IP address (often the DC) and `<domain>` with the search domain. |
| 74 | + |
| 75 | +Example of output for one computer with the `full` command: |
| 76 | + |
| 77 | +``` |
| 78 | +cn: MACHINE1 |
| 79 | +ip(s): 10.0.0.42 |
| 80 | +dNSHostName: MACHINE1.example.org |
| 81 | +dn: CN=MACHINE1,OU=WSUS,DC=example,DC=org |
| 82 | +operatingSystem: Windows Server 2008 R2 Standard |
| 83 | +operatingSystemVersion: 6.1 (7601) |
| 84 | +operatingSystemServicePack: Service Pack 1 |
| 85 | +``` |
| 86 | + |
| 87 | +Ref.: |
| 88 | + |
| 89 | +- [THTT - windapsearch](https://trove.raw.pm/tools/windapsearch/) |
0 commit comments