Coverage for /root/GitHubProjects/impacket/impacket/examples/smbclient.py : 10%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# SECUREAUTH LABS. Copyright 2018 SecureAuth Corporation. All rights reserved.
2#
3# This software is provided under under a slightly modified version
4# of the Apache Software License. See the accompanying LICENSE file
5# for more information.
6#
7# Description: Mini shell using some of the SMB funcionality of the library
8#
9# Author:
10# Alberto Solino (@agsolino)
11#
12#
13# Reference for:
14# SMB DCE/RPC
15#
16from __future__ import division
17from __future__ import print_function
18from io import BytesIO
19import sys
20import time
21import cmd
22import os
23import ntpath
25from six import PY2
26from impacket.dcerpc.v5 import samr, transport, srvs
27from impacket.dcerpc.v5.dtypes import NULL
28from impacket import LOG
29from impacket.smbconnection import SMBConnection, SMB2_DIALECT_002, SMB2_DIALECT_21, SMB_DIALECT, SessionError, \
30 FILE_READ_DATA, FILE_SHARE_READ, FILE_SHARE_WRITE
31from impacket.smb3structs import FILE_DIRECTORY_FILE, FILE_LIST_DIRECTORY
33import chardet
36# If you wanna have readline like functionality in Windows, install pyreadline
37try:
38 import pyreadline as readline
39except ImportError:
40 import readline
42class MiniImpacketShell(cmd.Cmd):
43 def __init__(self, smbClient, tcpShell=None):
44 #If the tcpShell parameter is passed (used in ntlmrelayx),
45 # all input and output is redirected to a tcp socket
46 # instead of to stdin / stdout
47 if tcpShell is not None:
48 cmd.Cmd.__init__(self, stdin=tcpShell.stdin, stdout=tcpShell.stdout)
49 sys.stdout = tcpShell.stdout
50 sys.stdin = tcpShell.stdin
51 sys.stderr = tcpShell.stdout
52 self.use_rawinput = False
53 self.shell = tcpShell
54 else:
55 cmd.Cmd.__init__(self)
56 self.shell = None
58 self.prompt = '# '
59 self.smb = smbClient
60 self.username, self.password, self.domain, self.lmhash, self.nthash, self.aesKey, self.TGT, self.TGS = smbClient.getCredentials()
61 self.tid = None
62 self.intro = 'Type help for list of commands'
63 self.pwd = ''
64 self.share = None
65 self.loggedIn = True
66 self.last_output = None
67 self.completion = []
69 def emptyline(self):
70 pass
72 def precmd(self,line):
73 # switch to unicode
74 if PY2:
75 return line.decode('utf-8')
76 return line
78 def onecmd(self,s):
79 retVal = False
80 try:
81 retVal = cmd.Cmd.onecmd(self,s)
82 except Exception as e:
83 LOG.error(e)
84 LOG.debug('Exception info', exc_info=True)
86 return retVal
88 def do_exit(self,line):
89 if self.shell is not None:
90 self.shell.close()
91 return True
93 def do_shell(self, line):
94 output = os.popen(line).read()
95 print(output)
96 self.last_output = output
98 def do_help(self,line):
99 print("""
100 open {host,port=445} - opens a SMB connection against the target host/port
101 login {domain/username,passwd} - logs into the current SMB connection, no parameters for NULL connection. If no password specified, it'll be prompted
102 kerberos_login {domain/username,passwd} - logs into the current SMB connection using Kerberos. If no password specified, it'll be prompted. Use the DNS resolvable domain name
103 login_hash {domain/username,lmhash:nthash} - logs into the current SMB connection using the password hashes
104 logoff - logs off
105 shares - list available shares
106 use {sharename} - connect to an specific share
107 cd {path} - changes the current directory to {path}
108 lcd {path} - changes the current local directory to {path}
109 pwd - shows current remote directory
110 password - changes the user password, the new password will be prompted for input
111 ls {wildcard} - lists all the files in the current directory
112 rm {file} - removes the selected file
113 mkdir {dirname} - creates the directory under the current path
114 rmdir {dirname} - removes the directory under the current path
115 put {filename} - uploads the filename into the current path
116 get {filename} - downloads the filename from the current path
117 cat {filename} - reads the filename from the current path
118 mount {target,path} - creates a mount point from {path} to {target} (admin required)
119 umount {path} - removes the mount point at {path} without deleting the directory (admin required)
120 list_snapshots {path} - lists the vss snapshots for the specified path
121 info - returns NetrServerInfo main results
122 who - returns the sessions currently connected at the target host (admin required)
123 close - closes the current SMB Session
124 exit - terminates the server process (and this session)
126""")
128 def do_password(self, line):
129 if self.loggedIn is False:
130 LOG.error("Not logged in")
131 return
132 from getpass import getpass
133 newPassword = getpass("New Password:")
134 rpctransport = transport.SMBTransport(self.smb.getRemoteHost(), filename = r'\samr', smb_connection = self.smb)
135 dce = rpctransport.get_dce_rpc()
136 dce.connect()
137 dce.bind(samr.MSRPC_UUID_SAMR)
138 samr.hSamrUnicodeChangePasswordUser2(dce, '\x00', self.username, self.password, newPassword, self.lmhash, self.nthash)
139 self.password = newPassword
140 self.lmhash = None
141 self.nthash = None
143 def do_open(self,line):
144 l = line.split(' ')
145 port = 445
146 if len(l) > 0:
147 host = l[0]
148 if len(l) > 1:
149 port = int(l[1])
152 if port == 139:
153 self.smb = SMBConnection('*SMBSERVER', host, sess_port=port)
154 else:
155 self.smb = SMBConnection(host, host, sess_port=port)
157 dialect = self.smb.getDialect()
158 if dialect == SMB_DIALECT:
159 LOG.info("SMBv1 dialect used")
160 elif dialect == SMB2_DIALECT_002:
161 LOG.info("SMBv2.0 dialect used")
162 elif dialect == SMB2_DIALECT_21:
163 LOG.info("SMBv2.1 dialect used")
164 else:
165 LOG.info("SMBv3.0 dialect used")
167 self.share = None
168 self.tid = None
169 self.pwd = ''
170 self.loggedIn = False
171 self.password = None
172 self.lmhash = None
173 self.nthash = None
174 self.username = None
176 def do_login(self,line):
177 if self.smb is None:
178 LOG.error("No connection open")
179 return
180 l = line.split(' ')
181 username = ''
182 password = ''
183 domain = ''
184 if len(l) > 0:
185 username = l[0]
186 if len(l) > 1:
187 password = l[1]
189 if username.find('/') > 0:
190 domain, username = username.split('/')
192 if password == '' and username != '':
193 from getpass import getpass
194 password = getpass("Password:")
196 self.smb.login(username, password, domain=domain)
197 self.password = password
198 self.username = username
200 if self.smb.isGuestSession() > 0:
201 LOG.info("GUEST Session Granted")
202 else:
203 LOG.info("USER Session Granted")
204 self.loggedIn = True
206 def do_kerberos_login(self,line):
207 if self.smb is None:
208 LOG.error("No connection open")
209 return
210 l = line.split(' ')
211 username = ''
212 password = ''
213 domain = ''
214 if len(l) > 0:
215 username = l[0]
216 if len(l) > 1:
217 password = l[1]
219 if username.find('/') > 0:
220 domain, username = username.split('/')
222 if domain == '':
223 LOG.error("Domain must be specified for Kerberos login")
224 return
226 if password == '' and username != '':
227 from getpass import getpass
228 password = getpass("Password:")
230 self.smb.kerberosLogin(username, password, domain=domain)
231 self.password = password
232 self.username = username
234 if self.smb.isGuestSession() > 0:
235 LOG.info("GUEST Session Granted")
236 else:
237 LOG.info("USER Session Granted")
238 self.loggedIn = True
240 def do_login_hash(self,line):
241 if self.smb is None:
242 LOG.error("No connection open")
243 return
244 l = line.split(' ')
245 domain = ''
246 if len(l) > 0:
247 username = l[0]
248 if len(l) > 1:
249 hashes = l[1]
250 else:
251 LOG.error("Hashes needed. Format is lmhash:nthash")
252 return
254 if username.find('/') > 0:
255 domain, username = username.split('/')
257 lmhash, nthash = hashes.split(':')
259 self.smb.login(username, '', domain,lmhash=lmhash, nthash=nthash)
260 self.username = username
261 self.lmhash = lmhash
262 self.nthash = nthash
264 if self.smb.isGuestSession() > 0:
265 LOG.info("GUEST Session Granted")
266 else:
267 LOG.info("USER Session Granted")
268 self.loggedIn = True
270 def do_logoff(self, line):
271 if self.smb is None:
272 LOG.error("No connection open")
273 return
274 self.smb.logoff()
275 del self.smb
276 self.share = None
277 self.smb = None
278 self.tid = None
279 self.pwd = ''
280 self.loggedIn = False
281 self.password = None
282 self.lmhash = None
283 self.nthash = None
284 self.username = None
286 def do_info(self, line):
287 if self.loggedIn is False:
288 LOG.error("Not logged in")
289 return
290 rpctransport = transport.SMBTransport(self.smb.getRemoteHost(), filename = r'\srvsvc', smb_connection = self.smb)
291 dce = rpctransport.get_dce_rpc()
292 dce.connect()
293 dce.bind(srvs.MSRPC_UUID_SRVS)
294 resp = srvs.hNetrServerGetInfo(dce, 102)
296 print("Version Major: %d" % resp['InfoStruct']['ServerInfo102']['sv102_version_major'])
297 print("Version Minor: %d" % resp['InfoStruct']['ServerInfo102']['sv102_version_minor'])
298 print("Server Name: %s" % resp['InfoStruct']['ServerInfo102']['sv102_name'])
299 print("Server Comment: %s" % resp['InfoStruct']['ServerInfo102']['sv102_comment'])
300 print("Server UserPath: %s" % resp['InfoStruct']['ServerInfo102']['sv102_userpath'])
301 print("Simultaneous Users: %d" % resp['InfoStruct']['ServerInfo102']['sv102_users'])
303 def do_who(self, line):
304 if self.loggedIn is False:
305 LOG.error("Not logged in")
306 return
307 rpctransport = transport.SMBTransport(self.smb.getRemoteHost(), filename = r'\srvsvc', smb_connection = self.smb)
308 dce = rpctransport.get_dce_rpc()
309 dce.connect()
310 dce.bind(srvs.MSRPC_UUID_SRVS)
311 resp = srvs.hNetrSessionEnum(dce, NULL, NULL, 10)
313 for session in resp['InfoStruct']['SessionInfo']['Level10']['Buffer']:
314 print("host: %15s, user: %5s, active: %5d, idle: %5d" % (
315 session['sesi10_cname'][:-1], session['sesi10_username'][:-1], session['sesi10_time'],
316 session['sesi10_idle_time']))
318 def do_shares(self, line):
319 if self.loggedIn is False:
320 LOG.error("Not logged in")
321 return
322 resp = self.smb.listShares()
323 for i in range(len(resp)):
324 print(resp[i]['shi1_netname'][:-1])
326 def do_use(self,line):
327 if self.loggedIn is False:
328 LOG.error("Not logged in")
329 return
330 self.share = line
331 self.tid = self.smb.connectTree(line)
332 self.pwd = '\\'
333 self.do_ls('', False)
335 def complete_cd(self, text, line, begidx, endidx):
336 return self.complete_get(text, line, begidx, endidx, include = 2)
338 def do_cd(self, line):
339 if self.tid is None:
340 LOG.error("No share selected")
341 return
342 p = line.replace('/','\\')
343 oldpwd = self.pwd
344 if p[0] == '\\':
345 self.pwd = line
346 else:
347 self.pwd = ntpath.join(self.pwd, line)
348 self.pwd = ntpath.normpath(self.pwd)
349 # Let's try to open the directory to see if it's valid
350 try:
351 fid = self.smb.openFile(self.tid, self.pwd, creationOption = FILE_DIRECTORY_FILE, desiredAccess = FILE_READ_DATA |
352 FILE_LIST_DIRECTORY, shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE )
353 self.smb.closeFile(self.tid,fid)
354 except SessionError:
355 self.pwd = oldpwd
356 raise
358 def do_lcd(self, s):
359 print(s)
360 if s == '':
361 print(os.getcwd())
362 else:
363 os.chdir(s)
365 def do_pwd(self,line):
366 if self.loggedIn is False:
367 LOG.error("Not logged in")
368 return
369 print(self.pwd)
371 def do_ls(self, wildcard, display = True):
372 if self.loggedIn is False:
373 LOG.error("Not logged in")
374 return
375 if self.tid is None:
376 LOG.error("No share selected")
377 return
378 if wildcard == '':
379 pwd = ntpath.join(self.pwd,'*')
380 else:
381 pwd = ntpath.join(self.pwd, wildcard)
382 self.completion = []
383 pwd = pwd.replace('/','\\')
384 pwd = ntpath.normpath(pwd)
385 for f in self.smb.listPath(self.share, pwd):
386 if display is True:
387 print("%crw-rw-rw- %10d %s %s" % (
388 'd' if f.is_directory() > 0 else '-', f.get_filesize(), time.ctime(float(f.get_mtime_epoch())),
389 f.get_longname()))
390 self.completion.append((f.get_longname(), f.is_directory()))
393 def do_rm(self, filename):
394 if self.tid is None:
395 LOG.error("No share selected")
396 return
397 f = ntpath.join(self.pwd, filename)
398 file = f.replace('/','\\')
399 self.smb.deleteFile(self.share, file)
401 def do_mkdir(self, path):
402 if self.tid is None:
403 LOG.error("No share selected")
404 return
405 p = ntpath.join(self.pwd, path)
406 pathname = p.replace('/','\\')
407 self.smb.createDirectory(self.share,pathname)
409 def do_rmdir(self, path):
410 if self.tid is None:
411 LOG.error("No share selected")
412 return
413 p = ntpath.join(self.pwd, path)
414 pathname = p.replace('/','\\')
415 self.smb.deleteDirectory(self.share, pathname)
417 def do_put(self, pathname):
418 if self.tid is None:
419 LOG.error("No share selected")
420 return
421 src_path = pathname
422 dst_name = os.path.basename(src_path)
424 fh = open(pathname, 'rb')
425 f = ntpath.join(self.pwd,dst_name)
426 finalpath = f.replace('/','\\')
427 self.smb.putFile(self.share, finalpath, fh.read)
428 fh.close()
430 def complete_get(self, text, line, begidx, endidx, include = 1):
431 # include means
432 # 1 just files
433 # 2 just directories
434 p = line.replace('/','\\')
435 if p.find('\\') < 0:
436 items = []
437 if include == 1:
438 mask = 0
439 else:
440 mask = 0x010
441 for i in self.completion:
442 if i[1] == mask:
443 items.append(i[0])
444 if text:
445 return [
446 item for item in items
447 if item.upper().startswith(text.upper())
448 ]
449 else:
450 return items
452 def do_get(self, filename):
453 if self.tid is None:
454 LOG.error("No share selected")
455 return
456 filename = filename.replace('/','\\')
457 fh = open(ntpath.basename(filename),'wb')
458 pathname = ntpath.join(self.pwd,filename)
459 try:
460 self.smb.getFile(self.share, pathname, fh.write)
461 except:
462 fh.close()
463 os.remove(filename)
464 raise
465 fh.close()
467 def do_cat(self, filename):
468 if self.tid is None:
469 LOG.error("No share selected")
470 return
471 filename = filename.replace('/','\\')
472 fh = BytesIO()
473 pathname = ntpath.join(self.pwd,filename)
474 try:
475 self.smb.getFile(self.share, pathname, fh.write)
476 except:
477 raise
478 output = fh.getvalue()
479 encoding = chardet.detect(output)["encoding"]
480 error_msg = "[-] Output cannot be correctly decoded, are you sure the text is readable ?"
481 if encoding:
482 try:
483 print(output.decode(encoding))
484 except:
485 print(error_msg)
486 finally:
487 fh.close()
488 else:
489 print(error_msg)
490 fh.close()
492 def do_close(self, line):
493 self.do_logoff(line)
495 def do_list_snapshots(self, line):
496 l = line.split(' ')
497 if len(l) > 0:
498 pathName= l[0].replace('/','\\')
500 # Relative or absolute path?
501 if pathName.startswith('\\') is not True:
502 pathName = ntpath.join(self.pwd, pathName)
504 snapshotList = self.smb.listSnapshots(self.tid, pathName)
506 if not snapshotList:
507 print("No snapshots found")
508 return
510 for timestamp in snapshotList:
511 print(timestamp)
513 def do_mount(self, line):
514 l = line.split(' ')
515 if len(l) > 1:
516 target = l[0].replace('/','\\')
517 pathName= l[1].replace('/','\\')
519 # Relative or absolute path?
520 if pathName.startswith('\\') is not True:
521 pathName = ntpath.join(self.pwd, pathName)
523 self.smb.createMountPoint(self.tid, pathName, target)
525 def do_umount(self, mountpoint):
526 mountpoint = mountpoint.replace('/','\\')
528 # Relative or absolute path?
529 if mountpoint.startswith('\\') is not True:
530 mountpoint = ntpath.join(self.pwd, mountpoint)
532 mountPath = ntpath.join(self.pwd, mountpoint)
534 self.smb.removeMountPoint(self.tid, mountPath)
536 def do_EOF(self, line):
537 print('Bye!\n')
538 return True