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