1# Copyright (c) 2003-2018 CORE Security Technologies 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# 16 17import sys 18import time 19import cmd 20import os 21 22from impacket import LOG 23from impacket.dcerpc.v5 import samr, transport, srvs 24from impacket.dcerpc.v5.dtypes import NULL 25from impacket.smbconnection import * 26 27 28# If you wanna have readline like functionality in Windows, install pyreadline 29try: 30 import pyreadline as readline 31except ImportError: 32 import readline 33 34class MiniImpacketShell(cmd.Cmd): 35 def __init__(self, smbClient,tcpShell=None): 36 #If the tcpShell parameter is passed (used in ntlmrelayx), 37 # all input and output is redirected to a tcp socket 38 # instead of to stdin / stdout 39 if tcpShell is not None: 40 cmd.Cmd.__init__(self,stdin=tcpShell,stdout=tcpShell) 41 sys.stdout = tcpShell 42 sys.stdin = tcpShell 43 sys.stderr = tcpShell 44 self.use_rawinput = False 45 self.shell = tcpShell 46 else: 47 cmd.Cmd.__init__(self) 48 self.shell = None 49 50 self.prompt = '# ' 51 self.smb = smbClient 52 self.username, self.password, self.domain, self.lmhash, self.nthash, self.aesKey, self.TGT, self.TGS = smbClient.getCredentials() 53 self.tid = None 54 self.intro = 'Type help for list of commands' 55 self.pwd = '' 56 self.share = None 57 self.loggedIn = True 58 self.last_output = None 59 self.completion = [] 60 61 def emptyline(self): 62 pass 63 64 def precmd(self,line): 65 # switch to unicode 66 return line.decode('utf-8') 67 68 def onecmd(self,s): 69 retVal = False 70 try: 71 retVal = cmd.Cmd.onecmd(self,s) 72 except Exception, e: 73 #import traceback 74 #traceback.print_exc() 75 LOG.error(e) 76 77 return retVal 78 79 def do_exit(self,line): 80 if self.shell is not None: 81 self.shell.close() 82 return True 83 84 def do_shell(self, line): 85 output = os.popen(line).read() 86 print output 87 self.last_output = output 88 89 def do_help(self,line): 90 print """ 91 open {host,port=445} - opens a SMB connection against the target host/port 92 login {domain/username,passwd} - logs into the current SMB connection, no parameters for NULL connection. If no password specified, it'll be prompted 93 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 94 login_hash {domain/username,lmhash:nthash} - logs into the current SMB connection using the password hashes 95 logoff - logs off 96 shares - list available shares 97 use {sharename} - connect to an specific share 98 cd {path} - changes the current directory to {path} 99 lcd {path} - changes the current local directory to {path} 100 pwd - shows current remote directory 101 password - changes the user password, the new password will be prompted for input 102 ls {wildcard} - lists all the files in the current directory 103 rm {file} - removes the selected file 104 mkdir {dirname} - creates the directory under the current path 105 rmdir {dirname} - removes the directory under the current path 106 put {filename} - uploads the filename into the current path 107 get {filename} - downloads the filename from the current path 108 info - returns NetrServerInfo main results 109 who - returns the sessions currently connected at the target host (admin required) 110 close - closes the current SMB Session 111 exit - terminates the server process (and this session) 112 113""" 114 115 def do_password(self, line): 116 if self.loggedIn is False: 117 LOG.error("Not logged in") 118 return 119 from getpass import getpass 120 newPassword = getpass("New Password:") 121 rpctransport = transport.SMBTransport(self.smb.getRemoteHost(), filename = r'\samr', smb_connection = self.smb) 122 dce = rpctransport.get_dce_rpc() 123 dce.connect() 124 dce.bind(samr.MSRPC_UUID_SAMR) 125 samr.hSamrUnicodeChangePasswordUser2(dce, '\x00', self.username, self.password, newPassword, self.lmhash, self.nthash) 126 self.password = newPassword 127 self.lmhash = None 128 self.nthash = None 129 130 def do_open(self,line): 131 l = line.split(' ') 132 port = 445 133 if len(l) > 0: 134 host = l[0] 135 if len(l) > 1: 136 port = int(l[1]) 137 138 139 if port == 139: 140 self.smb = SMBConnection('*SMBSERVER', host, sess_port=port) 141 else: 142 self.smb = SMBConnection(host, host, sess_port=port) 143 144 dialect = self.smb.getDialect() 145 if dialect == SMB_DIALECT: 146 LOG.info("SMBv1 dialect used") 147 elif dialect == SMB2_DIALECT_002: 148 LOG.info("SMBv2.0 dialect used") 149 elif dialect == SMB2_DIALECT_21: 150 LOG.info("SMBv2.1 dialect used") 151 else: 152 LOG.info("SMBv3.0 dialect used") 153 154 self.share = None 155 self.tid = None 156 self.pwd = '' 157 self.loggedIn = False 158 self.password = None 159 self.lmhash = None 160 self.nthash = None 161 self.username = None 162 163 def do_login(self,line): 164 if self.smb is None: 165 LOG.error("No connection open") 166 return 167 l = line.split(' ') 168 username = '' 169 password = '' 170 domain = '' 171 if len(l) > 0: 172 username = l[0] 173 if len(l) > 1: 174 password = l[1] 175 176 if username.find('/') > 0: 177 domain, username = username.split('/') 178 179 if password == '' and username != '': 180 from getpass import getpass 181 password = getpass("Password:") 182 183 self.smb.login(username, password, domain=domain) 184 self.password = password 185 self.username = username 186 187 if self.smb.isGuestSession() > 0: 188 LOG.info("GUEST Session Granted") 189 else: 190 LOG.info("USER Session Granted") 191 self.loggedIn = True 192 193 def do_kerberos_login(self,line): 194 if self.smb is None: 195 LOG.error("No connection open") 196 return 197 l = line.split(' ') 198 username = '' 199 password = '' 200 domain = '' 201 if len(l) > 0: 202 username = l[0] 203 if len(l) > 1: 204 password = l[1] 205 206 if username.find('/') > 0: 207 domain, username = username.split('/') 208 209 if domain == '': 210 LOG.error("Domain must be specified for Kerberos login") 211 return 212 213 if password == '' and username != '': 214 from getpass import getpass 215 password = getpass("Password:") 216 217 self.smb.kerberosLogin(username, password, domain=domain) 218 self.password = password 219 self.username = username 220 221 if self.smb.isGuestSession() > 0: 222 LOG.info("GUEST Session Granted") 223 else: 224 LOG.info("USER Session Granted") 225 self.loggedIn = True 226 227 def do_login_hash(self,line): 228 if self.smb is None: 229 LOG.error("No connection open") 230 return 231 l = line.split(' ') 232 domain = '' 233 if len(l) > 0: 234 username = l[0] 235 if len(l) > 1: 236 hashes = l[1] 237 else: 238 LOG.error("Hashes needed. Format is lmhash:nthash") 239 return 240 241 if username.find('/') > 0: 242 domain, username = username.split('/') 243 244 lmhash, nthash = hashes.split(':') 245 246 self.smb.login(username, '', domain,lmhash=lmhash, nthash=nthash) 247 self.username = username 248 self.lmhash = lmhash 249 self.nthash = nthash 250 251 if self.smb.isGuestSession() > 0: 252 LOG.info("GUEST Session Granted") 253 else: 254 LOG.info("USER Session Granted") 255 self.loggedIn = True 256 257 def do_logoff(self, line): 258 if self.smb is None: 259 LOG.error("No connection open") 260 return 261 self.smb.logoff() 262 del self.smb 263 self.share = None 264 self.smb = None 265 self.tid = None 266 self.pwd = '' 267 self.loggedIn = False 268 self.password = None 269 self.lmhash = None 270 self.nthash = None 271 self.username = None 272 273 def do_info(self, line): 274 if self.loggedIn is False: 275 LOG.error("Not logged in") 276 return 277 rpctransport = transport.SMBTransport(self.smb.getRemoteHost(), filename = r'\srvsvc', smb_connection = self.smb) 278 dce = rpctransport.get_dce_rpc() 279 dce.connect() 280 dce.bind(srvs.MSRPC_UUID_SRVS) 281 resp = srvs.hNetrServerGetInfo(dce, 102) 282 283 print "Version Major: %d" % resp['InfoStruct']['ServerInfo102']['sv102_version_major'] 284 print "Version Minor: %d" % resp['InfoStruct']['ServerInfo102']['sv102_version_minor'] 285 print "Server Name: %s" % resp['InfoStruct']['ServerInfo102']['sv102_name'] 286 print "Server Comment: %s" % resp['InfoStruct']['ServerInfo102']['sv102_comment'] 287 print "Server UserPath: %s" % resp['InfoStruct']['ServerInfo102']['sv102_userpath'] 288 print "Simultaneous Users: %d" % resp['InfoStruct']['ServerInfo102']['sv102_users'] 289 290 def do_who(self, line): 291 if self.loggedIn is False: 292 LOG.error("Not logged in") 293 return 294 rpctransport = transport.SMBTransport(self.smb.getRemoteHost(), filename = r'\srvsvc', smb_connection = self.smb) 295 dce = rpctransport.get_dce_rpc() 296 dce.connect() 297 dce.bind(srvs.MSRPC_UUID_SRVS) 298 resp = srvs.hNetrSessionEnum(dce, NULL, NULL, 10) 299 300 for session in resp['InfoStruct']['SessionInfo']['Level10']['Buffer']: 301 print "host: %15s, user: %5s, active: %5d, idle: %5d" % ( 302 session['sesi10_cname'][:-1], session['sesi10_username'][:-1], session['sesi10_time'], 303 session['sesi10_idle_time']) 304 305 def do_shares(self, line): 306 if self.loggedIn is False: 307 LOG.error("Not logged in") 308 return 309 resp = self.smb.listShares() 310 for i in range(len(resp)): 311 print resp[i]['shi1_netname'][:-1] 312 313 def do_use(self,line): 314 if self.loggedIn is False: 315 LOG.error("Not logged in") 316 return 317 self.share = line 318 self.tid = self.smb.connectTree(line) 319 self.pwd = '\\' 320 self.do_ls('', False) 321 322 def complete_cd(self, text, line, begidx, endidx): 323 return self.complete_get(text, line, begidx, endidx, include = 2) 324 325 def do_cd(self, line): 326 if self.tid is None: 327 LOG.error("No share selected") 328 return 329 p = string.replace(line,'/','\\') 330 oldpwd = self.pwd 331 if p[0] == '\\': 332 self.pwd = line 333 else: 334 self.pwd = ntpath.join(self.pwd, line) 335 self.pwd = ntpath.normpath(self.pwd) 336 # Let's try to open the directory to see if it's valid 337 try: 338 fid = self.smb.openFile(self.tid, self.pwd, creationOption = FILE_DIRECTORY_FILE \ 339 , desiredAccess = FILE_READ_DATA | FILE_LIST_DIRECTORY \ 340 , shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE \ 341 ) 342 self.smb.closeFile(self.tid,fid) 343 except SessionError: 344 self.pwd = oldpwd 345 raise 346 347 def do_lcd(self, s): 348 print s 349 if s == '': 350 print os.getcwd() 351 else: 352 os.chdir(s) 353 354 def do_pwd(self,line): 355 if self.loggedIn is False: 356 LOG.error("Not logged in") 357 return 358 print self.pwd 359 360 def do_ls(self, wildcard, display = True): 361 if self.loggedIn is False: 362 LOG.error("Not logged in") 363 return 364 if self.tid is None: 365 LOG.error("No share selected") 366 return 367 if wildcard == '': 368 pwd = ntpath.join(self.pwd,'*') 369 else: 370 pwd = ntpath.join(self.pwd, wildcard) 371 self.completion = [] 372 pwd = string.replace(pwd,'/','\\') 373 pwd = ntpath.normpath(pwd) 374 for f in self.smb.listPath(self.share, pwd): 375 if display is True: 376 print "%crw-rw-rw- %10d %s %s" % ( 377 'd' if f.is_directory() > 0 else '-', f.get_filesize(), time.ctime(float(f.get_mtime_epoch())), 378 f.get_longname()) 379 self.completion.append((f.get_longname(), f.is_directory())) 380 381 382 def do_rm(self, filename): 383 if self.tid is None: 384 LOG.error("No share selected") 385 return 386 f = ntpath.join(self.pwd, filename) 387 file = string.replace(f,'/','\\') 388 self.smb.deleteFile(self.share, file) 389 390 def do_mkdir(self, path): 391 if self.tid is None: 392 LOG.error("No share selected") 393 return 394 p = ntpath.join(self.pwd, path) 395 pathname = string.replace(p,'/','\\') 396 self.smb.createDirectory(self.share,pathname) 397 398 def do_rmdir(self, path): 399 if self.tid is None: 400 LOG.error("No share selected") 401 return 402 p = ntpath.join(self.pwd, path) 403 pathname = string.replace(p,'/','\\') 404 self.smb.deleteDirectory(self.share, pathname) 405 406 def do_put(self, pathname): 407 if self.tid is None: 408 LOG.error("No share selected") 409 return 410 src_path = pathname 411 dst_name = os.path.basename(src_path) 412 413 fh = open(pathname, 'rb') 414 f = ntpath.join(self.pwd,dst_name) 415 finalpath = string.replace(f,'/','\\') 416 self.smb.putFile(self.share, finalpath, fh.read) 417 fh.close() 418 419 def complete_get(self, text, line, begidx, endidx, include = 1): 420 # include means 421 # 1 just files 422 # 2 just directories 423 p = string.replace(line,'/','\\') 424 if p.find('\\') < 0: 425 items = [] 426 if include == 1: 427 mask = 0 428 else: 429 mask = 0x010 430 for i in self.completion: 431 if i[1] == mask: 432 items.append(i[0]) 433 if text: 434 return [ 435 item for item in items 436 if item.upper().startswith(text.upper()) 437 ] 438 else: 439 return items 440 441 def do_get(self, filename): 442 if self.tid is None: 443 LOG.error("No share selected") 444 return 445 filename = string.replace(filename,'/','\\') 446 fh = open(ntpath.basename(filename),'wb') 447 pathname = ntpath.join(self.pwd,filename) 448 try: 449 self.smb.getFile(self.share, pathname, fh.write) 450 except: 451 fh.close() 452 os.remove(filename) 453 raise 454 fh.close() 455 456 def do_close(self, line): 457 self.do_logoff(line) 458 459 460