Skip to content

Commit

Permalink
Release v2.0
Browse files Browse the repository at this point in the history
  • Loading branch information
p0dalirius committed Feb 18, 2022
1 parent 4fd6298 commit 003d449
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 51 deletions.
Binary file added .github/banner.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed .github/colors.png
Binary file not shown.
Binary file modified .github/example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
66 changes: 45 additions & 21 deletions FindUncommonShares.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@
import threading
import time
import traceback
import dns.resolver, dns.exception


COMMON_SHARES = [
"C$", "D$",
"C$",
"ADMIN$", "IPC$",
"PRINT$", "print$",
"fax$", "FAX$",
Expand Down Expand Up @@ -73,8 +74,9 @@ def STYPE_MASK(stype_value):
return flags


def get_domain_computers(target_dn, ldap_server, ldap_session):
def get_domain_computers(ldap_server, ldap_session):
results = {}
target_dn = ldap_server.info.other["defaultNamingContext"]
ldap_session.search(target_dn, "(objectCategory=computer)", attributes=["dNSHostName", "sAMAccountName"])
for entry in ldap_session.response:
if entry['type'] != 'searchResEntry':
Expand All @@ -87,13 +89,16 @@ def get_domain_computers(target_dn, ldap_server, ldap_session):


def parse_args():
print("FindUncommonShares v2.0 - by @podalirius_\n")

parser = argparse.ArgumentParser(add_help=True, description='Find uncommon SMB shares on remote machines.')
parser.add_argument('-ts', action='store_true', help='Adds timestamp to every logging output')
parser.add_argument('--use-ldaps', action='store_true', help='Use LDAPS instead of LDAP')
parser.add_argument("-q", "--quiet", dest="quiet", action="store_true", default=False, help="show no information at all")
parser.add_argument("-debug", dest="debug", action="store_true", default=False, help="Debug mode")
parser.add_argument("-colors", dest="colors", action="store_true", default=False, help="Colored output mode")
parser.add_argument("-t", "--threads", dest="threads", action="store", type=int, default=5, required=False, help="Number of threads (default: 5)")
parser.add_argument("-q", "--quiet", dest="quiet", action="store_true", default=False, help="Show no information at all.")
parser.add_argument("-debug", dest="debug", action="store_true", default=False, help="Debug mode.")
parser.add_argument("-no-colors", dest="colors", action="store_false", default=True, help="Disables colored output mode")
parser.add_argument("-I", "--ignore-hidden-shares", dest="ignore_hidden_shares", action="store_true", default=False, help="Ignores hidden shares (shares ending with $)")
parser.add_argument("-t", "--threads", dest="threads", action="store", type=int, default=20, required=False, help="Number of threads (default: 20)")
parser.add_argument("-o", "--output-file", dest="output_file", type=str, default="shares.json", required=False, help="Output file to store the results in. (default: shares.json)")

authconn = parser.add_argument_group('authentication & connection')
Expand All @@ -103,10 +108,10 @@ def parse_args():

secret = parser.add_argument_group()
cred = secret.add_mutually_exclusive_group()
cred.add_argument('--no-pass', action="store_true", help='don\'t ask for password (useful for -k)')
cred.add_argument("-p", "--password", dest="auth_password", metavar="PASSWORD", action="store", help="password to authenticate with")
cred.add_argument("--no-pass", action="store_true", help="Don't ask for password (useful for -k)")
cred.add_argument("-p", "--password", dest="auth_password", metavar="PASSWORD", action="store", help="Password to authenticate with")
cred.add_argument("-H", "--hashes", dest="auth_hashes", action="store", metavar="[LMHASH:]NTHASH", help='NT/LM hashes, format is LMhash:NThash')
cred.add_argument('--aes-key', dest="auth_key", action="store", metavar="hex key", help='AES key to use for Kerberos Authentication (128 or 256 bits)')
cred.add_argument("--aes-key", dest="auth_key", action="store", metavar="hex key", help='AES key to use for Kerberos Authentication (128 or 256 bits)')
secret.add_argument("-k", "--kerberos", dest="use_kerberos", action="store_true", help='Use Kerberos authentication. Grabs credentials from .ccache file (KRB5CCNAME) based on target parameters. If valid credentials cannot be found, it will use the ones specified in the command line')

if len(sys.argv) == 1:
Expand Down Expand Up @@ -370,7 +375,7 @@ def init_smb_session(args, target_ip, domain, username, password, address, lmhas


def worker(args, target_name, domain, username, password, address, lmhash, nthash, lock):
target_ip = nslookup.Nslookup(dns_servers=[args.dc_ip]).dns_lookup(target_name).answer
target_ip = nslookup.Nslookup(dns_servers=[args.dc_ip], verbose=args.debug).dns_lookup(target_name).answer
if len(target_ip) != 0:
target_ip = target_ip[0]
try:
Expand All @@ -389,14 +394,26 @@ def worker(args, target_name, domain, username, password, address, lmhash, nthas
if not args.quiet:
if len(sharecomment) != 0:
if args.colors:
print("[>] Found uncommon share '\x1b[93m%s\x1b[0m' on '\x1b[96m%s\x1b[0m' (comment: '\x1b[95m%s\x1b[0m')" % (sharename, address, sharecomment))
if sharename.endswith('$'):
if not args.ignore_hidden_shares:
print("[>] Found '\x1b[94m%s\x1b[0m' on '\x1b[96m%s\x1b[0m' (comment: '\x1b[95m%s\x1b[0m')" % (sharename, address, sharecomment))
else:
print("[>] Found '\x1b[93m%s\x1b[0m' on '\x1b[96m%s\x1b[0m' (comment: '\x1b[95m%s\x1b[0m')" % (sharename, address, sharecomment))
else:
print("[>] Found uncommon share '%s' on '%s' (comment: '%s')" % (sharename, address, sharecomment))
print("[>] Found '%s' on '%s' (comment: '%s')" % (sharename, address, sharecomment))
else:
if args.colors:
print("[>] Found uncommon share '\x1b[93m%s\x1b[0m' on '\x1b[96m%s\x1b[0m'" % (sharename, address))
if sharename.endswith('$'):
if not args.ignore_hidden_shares:
print("[>] Found '\x1b[94m%s\x1b[0m' on '\x1b[96m%s\x1b[0m'" % (sharename, address))
else:
print("[>] Found '\x1b[93m%s\x1b[0m' on '\x1b[96m%s\x1b[0m'" % (sharename, address))
else:
print("[>] Found uncommon share '%s' on '%s'" % (sharename, address))
if sharename.endswith('$'):
if not args.ignore_hidden_shares:
print("[>] Found '%s' on '%s'" % (sharename, address))
else:
print("[>] Found '%s' on '%s'" % (sharename, address))
d = {
"sharename": sharename,
"uncpath": "\\".join(['', '', target_ip, sharename, '']),
Expand All @@ -414,14 +431,24 @@ def worker(args, target_name, domain, username, password, address, lmhash, nthas
elif args.debug and not args.quiet:
if len(sharecomment) != 0:
if args.colors:
print("[>] Skipping common share '\x1b[93m%s\x1b[0m' on '\x1b[96m%s\x1b[0m' (comment: '\x1b[95m%s\x1b[0m')" % (sharename, address, sharecomment))
if sharename.endswith('$') and not args.ignore_hidden_shares:
print("[>] Skipping common share '\x1b[94m%s\x1b[0m' on '\x1b[96m%s\x1b[0m' (comment: '\x1b[95m%s\x1b[0m')" % (sharename, address, sharecomment))
else:
print("[>] Skipping common share '\x1b[93m%s\x1b[0m' on '\x1b[96m%s\x1b[0m' (comment: '\x1b[95m%s\x1b[0m')" % (sharename, address, sharecomment))
else:
print("[>] Skipping common share '%s' on '%s' (comment: '%s')" % (sharename, address, sharecomment))
else:
if args.colors:
print("[>] Skipping common share '\x1b[93m%s\x1b[0m' on '\x1b[96m%s\x1b[0m'" % (sharename, address))
if sharename.endswith('$') and not args.ignore_hidden_shares:
print("[>] Skipping common share '\x1b[94m%s\x1b[0m' on '\x1b[96m%s\x1b[0m'" % (sharename, address))
else:
print("[>] Skipping common share '\x1b[93m%s\x1b[0m' on '\x1b[96m%s\x1b[0m'" % (sharename, address))
else:
print("[>] Skipping common share '%s' on '%s'" % (sharename, address))
if sharename.endswith('$'):
if not args.ignore_hidden_shares:
print("[>] Skipping common share '%s' on '%s'" % (sharename, address))
else:
print("[>] Skipping common share '%s' on '%s'" % (sharename, address))

except Exception as e:
if args.debug:
Expand All @@ -430,8 +457,6 @@ def worker(args, target_name, domain, username, password, address, lmhash, nthas

if __name__ == '__main__':
args = parse_args()
if not args.quiet:
print(version.BANNER)
init_logger(args)

auth_lm_hash = ""
Expand All @@ -454,8 +479,7 @@ def worker(args, target_name, domain, username, password, address, lmhash, nthas

if not args.quiet:
print("[>] Extracting all computers ...")
dn = ','.join(["DC=%s" % part for part in args.auth_domain.split('.')])
computers = get_domain_computers(dn, ldap_server, ldap_session)
computers = get_domain_computers(ldap_server, ldap_session)

if not args.quiet:
print("[+] Found %d computers in the domain." % len(computers.keys()))
Expand Down
63 changes: 33 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,76 +1,81 @@
# FindUncommonShares

<p align="center">
<img alt="GitHub release (latest by date)" src="https://img.shields.io/github/v/release/p0dalirius/FindUncommonShares">
<a href="https://twitter.com/intent/follow?screen_name=podalirius_" title="Follow"><img src="https://img.shields.io/twitter/follow/podalirius_?label=Podalirius&style=social"></a>
<br>
The script <a href="https://github.com/p0dalirius/FindUncommonShares/blob/main/FindUncommonShares.py">FindUncommonShares.py</a> is a Python equivalent of <a href="https://github.com/darkoperator/Veil-PowerView/">PowerView</a>'s <a href="https://github.com/darkoperator/Veil-PowerView/blob/master/PowerView/functions/Invoke-ShareFinder.ps1">Invoke-ShareFinder.ps1</a> allowing to quickly find uncommon shares in vast Windows Active Directory Domains.
<br>
<img alt="GitHub release (latest by date)" src="https://img.shields.io/github/v/release/p0dalirius/FindUncommonShares">
<a href="https://twitter.com/intent/follow?screen_name=podalirius_" title="Follow"><img src="https://img.shields.io/twitter/follow/podalirius_?label=Podalirius&style=social"></a>
<br>
</p>


The script [FindUncommonShares.py](https://github.com/p0dalirius/FindUncommonShares/blob/main/FindUncommonShares.py) is a Python equivalent of [PowerView](https://github.com/darkoperator/Veil-PowerView/)'s [Invoke-ShareFinder.ps1](https://github.com/darkoperator/Veil-PowerView/blob/master/PowerView/functions/Invoke-ShareFinder.ps1) allowing to quickly find uncommon shares in vast Windows Domains.

![](.github/example.png)
![](.github/banner.png)


## Features

- [x] Automatically gets the list of all computers from the domain controller's LDAP.
- [x] Ignore the hidden shares (ending with `$`) with `--ignore-hidden-shares`.
- [x] Multithreaded search.
- [x] JSON export of the found shares, with IP, name, comment, flags and UNC path.

## Usage

```
$ ./FindUncommonShares.py -h
Impacket v0.9.24.dev1+20210906.175840.50c76958 - Copyright 2021 SecureAuth Corporation
$ ./FindUncommonShares.py -h
FindUncommonShares v2.0 - by @podalirius_
usage: FindUncommonShares.py [-h] [-ts] [--use-ldaps] [-q] [-debug] [-t THREADS] [-o OUTPUT_FILE] --dc-ip ip address [-d DOMAIN] [-u USER]
[--no-pass | -p PASSWORD | -H [LMHASH:]NTHASH | --aes-key hex key] [-k]
usage: FindUncommonShares.py [-h] [-ts] [--use-ldaps] [-q] [-debug] [-no-colors] [-I] [-t THREADS] [-o OUTPUT_FILE] --dc-ip ip address
[-d DOMAIN] [-u USER] [--no-pass | -p PASSWORD | -H [LMHASH:]NTHASH | --aes-key hex key] [-k]
Find uncommon SMB shares on remote machines.
optional arguments:
-h, --help show this help message and exit
-ts Adds timestamp to every logging output
--use-ldaps Use LDAPS instead of LDAP
-q, --quiet show no information at all
-debug Debug mode
-q, --quiet Show no information at all.
-debug Debug mode.
-no-colors Disables colored output mode
-I, -ignore-hidden-shares
Ignores hidden shares (shares ending with $)
-t THREADS, --threads THREADS
Number of threads (default: 5)
Number of threads (default: 20)
-o OUTPUT_FILE, --output-file OUTPUT_FILE
Output file to store the results in. (default: shares.json)
authentication & connection:
--dc-ip ip address IP Address of the domain controller or KDC (Key Distribution Center) for Kerberos. If omitted it will use the domain part (FQDN)
specified in the identity parameter
--dc-ip ip address IP Address of the domain controller or KDC (Key Distribution Center) for Kerberos. If omitted it will use the domain part
(FQDN) specified in the identity parameter
-d DOMAIN, --domain DOMAIN
(FQDN) domain to authenticate to
-u USER, --user USER user to authenticate with
--no-pass don't ask for password (useful for -k)
--no-pass Don't ask for password (useful for -k)
-p PASSWORD, --password PASSWORD
password to authenticate with
Password to authenticate with
-H [LMHASH:]NTHASH, --hashes [LMHASH:]NTHASH
NT/LM hashes, format is LMhash:NThash
--aes-key hex key AES key to use for Kerberos Authentication (128 or 256 bits)
-k, --kerberos Use Kerberos authentication. Grabs credentials from .ccache file (KRB5CCNAME) based on target parameters. If valid credentials
cannot be found, it will use the ones specified in the command line
-k, --kerberos Use Kerberos authentication. Grabs credentials from .ccache file (KRB5CCNAME) based on target parameters. If valid
credentials cannot be found, it will use the ones specified in the command line
```

## Examples :

```
[]$ ./FindUncommonShares.py -u 'Administrator' -d 'LAB.local' -p 'Admin123!' --dc-ip 192.168.2.1
Impacket v0.9.24.dev1+20210906.175840.50c76958 - Copyright 2021 SecureAuth Corporation
$ ./FindUncommonShares.py -u 'Administrator' -d 'LAB.local' -p 'Admin123!' --dc-ip 192.168.2.1
FindUncommonShares v2.0 - by @podalirius_
[>] Extracting all computers ...
[+] Found 2 computers.
[>] Enumerating shares ...
[>] Found uncommon share 'Users' on 'DC01.LAB.local'
[>] Found uncommon share 'WeirdShare' on 'DC01.LAB.local' (comment: 'Test comment')
[>] Found uncommon share 'AnotherShare' on 'PC01.LAB.local'
[>] Found uncommon share 'Users' on 'PC01.LAB.local
[]$
[>] Found 'Users' on 'DC01.LAB.local'
[>] Found 'WeirdShare' on 'DC01.LAB.local' (comment: 'Test comment')
[>] Found 'AnotherShare' on 'PC01.LAB.local'
[>] Found 'Users' on 'PC01.LAB.local
$
```

Results are exported in JSON entries:
Expand Down Expand Up @@ -101,11 +106,9 @@ Each JSON entry looks like this:
}
```

## Colored output

You can print results with colors using the `-colors` option:
## Demonstration

![](./.github/colors.png)
![](./.github/example.png)

## Credits

Expand Down

0 comments on commit 003d449

Please sign in to comment.