-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathpysharecrawler.py
executable file
·217 lines (189 loc) · 7.38 KB
/
pysharecrawler.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
#!/usr/bin/python
"""
Michael Molho
2015
michael.molho@gmail.com
"""
from impacket.dcerpc import srvsvc
import sys
import string
import time
import logging
from impacket import smb, version, smb3, nt_errors
from impacket.dcerpc.v5 import samr, transport, srvs
from impacket.dcerpc.v5.dtypes import NULL
from impacket.smbconnection import *
from impacket.nmb import NetBIOS
from getpass import getpass
import argparse
import ntpath
import netaddr
from outwriter import CsvOutWriter, SqliteOutWriter, StandardOutWriter
class SmbCrawler():
def __init__(self, verbose=False, out="print"):
self.host = ''
self.nbtname = ''
self.smb = None
self.maxdepth = 999
self.password = None
self.lmhash = None
self.nthash = None
self.username = ''
self.domain = ''
self.verbose = verbose
if len(out) > 4 and out[0:4] == "csv:":
filepath = out.split(':')[1]
self.outwriter = CsvOutWriter(filepath)
elif len(out) > 7 and out[0:7] == "sqlite:":
filepath = out.split(':')[1]
self.outwriter = SqliteOutWriter(filepath)
else:
self.outwriter = StandardOutWriter()
def resolveNbtName(self):
nbt = NetBIOS()
try:
name = nbt.getnetbiosname(self.host)
return name
except:
return ''
def open(self, host, port):
self.host = host
self.nbtname = self.resolveNbtName()
if port == 139:
self.smb = SMBConnection('*SMBSERVER', host, sess_port=port)
else:
self.smb = SMBConnection(host, host, sess_port=port)
dialect = self.smb.getDialect()
if dialect == SMB_DIALECT:
logging.info("SMBv1 dialect used")
elif dialect == SMB2_DIALECT_002:
logging.info("SMBv2.0 dialect used")
elif dialect == SMB2_DIALECT_21:
logging.info("SMBv2.1 dialect used")
else:
logging.info("SMBv3.0 dialect used")
def login(self, domain, username, password):
if self.smb is None:
logging.error("No connection open")
return
try:
self.smb.login(username, password, domain=domain)
except Exception as e:
print ("Authentication failed : " + str(e))
raise
self.username = username
self.domain = domain
if self.smb.isGuestSession() > 0:
logging.info("GUEST Session Granted")
else:
logging.info("USER Session Granted")
def login_hash(self, domain, username, hashes):
if self.smb is None:
logging.error("No connection open")
return
try:
lmhash, nthash = hashes.split(':')
self.smb.login(username, '', domain,lmhash=lmhash, nthash=nthash)
except Exception as e:
print ("Authentication failed : " + str(e))
raise
self.username = username
self.domain = domain
if self.smb.isGuestSession() > 0:
logging.info("GUEST Session Granted")
else:
logging.info("USER Session Granted")
def shares(self):
shares = []
rpctransport = transport.SMBTransport(self.smb.getRemoteHost(), self.smb.getRemoteHost(), filename = r'\srvsvc', smb_connection = self.smb)
dce = rpctransport.get_dce_rpc()
dce.connect()
dce.bind(srvs.MSRPC_UUID_SRVS)
res = srvs.hNetrShareEnum(dce,1)
resp = res['InfoStruct']['ShareInfo']['Level1']['Buffer']
for i in range(len(resp)):
shares += [resp[i]['shi1_netname'][:-1]]
return shares
def ls(self, share, pwd):
files = []
f_pwd = ''
f_pwd = ntpath.join(pwd, '*')
f_pwd = string.replace(f_pwd,'/','\\')
f_pwd = ntpath.normpath(f_pwd)
for f in self.smb.listPath(share, f_pwd):
if f.get_longname() not in ['.', '..']:
files += [f]
return files
def use(self,share):
tid = self.smb.connectTree(share)
self.ls(share, '\\')
return tid
def spider(self, share, root, maxdepth):
if maxdepth < 0:
return []
try:
files = self.ls(share, root)
except Exception as e:
if self.verbose:
print ("Error in ls("+share+","+root+","+str(maxdepth)+") : " + str(e))
return []
for f in files:
new_root = ntpath.join(root, f.get_longname())
new_root = ntpath.normpath(new_root)
self.outwriter.write(self.host, self.nbtname, share, f, new_root)
if f.is_directory():
self.spider(share, root + f.get_longname() + '\\', maxdepth - 1)
def crawl(self, maxdepth, thread = 1):
self.maxdepth = maxdepth
shares = self.shares()
for share in shares:
print ('[+] Spidering ' + share)
try:
tid = self.use(share)
except Exception as e:
if self.verbose:
print ("Error in use("+share+") : " + str(e))
self.spider(share, '\\', maxdepth)
self.outwriter.commit()
if __name__ == "__main__":
rhosts = []
domain = ''
username = ''
parser = argparse.ArgumentParser(description='Complete Windows Samba share crawler.')
parser.add_argument('LOGIN', action="store", help="Can be standalone username for local account or domain/username")
usergroup = parser.add_mutually_exclusive_group(required=True)
usergroup.add_argument('--rhosts', action="store", default=None, help="IP Adress or IP/CIDR")
usergroup.add_argument('--file', action="store", default=None, help="Read IP adresses from input file. One adress per line")
parser.add_argument('--hashes', action="store", default=None, help="NTLM hashes, format is LMHASH:NTHASH")
parser.add_argument('--verbose', action="store_true", default=False, help="Show debug messages")
parser.add_argument('--maxdepth', action="store", type=int, default=1, help="Maximum depth to crawl in shares (default=1)")
parser.add_argument('--out', action="store", default="print", help="Output type: (print, csv:<filepath>, sqlite:<dbpath>)")
if len(sys.argv) == 1:
parser.print_help()
sys.exit(1)
result = parser.parse_args()
cmdargs = dict(result._get_kwargs())
if cmdargs['file'] != None:
rhosts += [line.strip() for line in open(cmdargs['file'], 'r')]
else:
rhosts += [ip.__str__() for ip in list(netaddr.IPNetwork(cmdargs['rhosts']))]
if '/' in cmdargs['LOGIN']:
domain, username = tuple(cmdargs['LOGIN'].split('/'))
else:
domain, username = '', cmdargs['LOGIN']
if cmdargs['hashes'] == None:
password = getpass("Password:")
crawler = SmbCrawler( verbose=cmdargs['verbose'], out=cmdargs['out'] )
for rhost in rhosts:
print ('\n -- ' + rhost + ' -- \n')
try:
crawler.open(rhost,445)
print ('[+] Resolve netbios name : ' + crawler.nbtname + '\n')
if cmdargs['hashes'] != None:
crawler.login_hash(domain, username, cmdargs['hashes'])
else:
crawler.login(domain, username, password)
crawler.crawl(maxdepth = cmdargs['maxdepth'])
except Exception as e:
if crawler.verbose:
print ("Error : " + str(e))