2017-10-25 02:08:19 +00:00
import winrm as pywinrm
import requests
import logging
2019-11-10 21:42:04 +00:00
from io import StringIO
2017-10-25 02:08:19 +00:00
# from winrm.exceptions import InvalidCredentialsError
from impacket . smbconnection import SMBConnection , SessionError
from cme . connection import *
from cme . helpers . logger import highlight
from cme . logger import CMEAdapter
2019-11-10 21:42:04 +00:00
import configparser
2017-10-25 02:08:19 +00:00
# The following disables the InsecureRequests warning and the 'Starting new HTTPS connection' log message
from requests . packages . urllib3 . exceptions import InsecureRequestWarning
requests . packages . urllib3 . disable_warnings ( InsecureRequestWarning )
2020-04-28 11:24:01 +00:00
class SuppressFilter ( logging . Filter ) :
# remove warning https://github.com/diyan/pywinrm/issues/269
def filter ( self , record ) :
return ' wsman ' not in record . getMessage ( )
2017-10-25 02:08:19 +00:00
class winrm ( connection ) :
def __init__ ( self , args , db , host ) :
self . domain = None
connection . __init__ ( self , args , db , host )
@staticmethod
def proto_args ( parser , std_parser , module_parser ) :
winrm_parser = parser . add_parser ( ' winrm ' , help = " own stuff using WINRM " , parents = [ std_parser , module_parser ] )
2019-08-16 13:58:26 +00:00
winrm_parser . add_argument ( " -H " , ' --hash ' , metavar = " HASH " , dest = ' hash ' , nargs = ' + ' , default = [ ] , help = ' NTLM hash(es) or file(s) containing NTLM hashes ' )
2017-10-25 02:08:19 +00:00
dgroup = winrm_parser . add_mutually_exclusive_group ( )
dgroup . add_argument ( " -d " , metavar = " DOMAIN " , dest = ' domain ' , type = str , default = None , help = " domain to authenticate to " )
dgroup . add_argument ( " --local-auth " , action = ' store_true ' , help = ' authenticate locally to each target ' )
cgroup = winrm_parser . add_argument_group ( " Command Execution " , " Options for executing commands " )
cgroup . add_argument ( ' --no-output ' , action = ' store_true ' , help = ' do not retrieve command output ' )
cgroup . add_argument ( " -x " , metavar = " COMMAND " , dest = ' execute ' , help = " execute the specified command " )
cgroup . add_argument ( " -X " , metavar = " PS_COMMAND " , dest = ' ps_execute ' , help = ' execute the specified PowerShell command ' )
return parser
def proto_flow ( self ) :
self . proto_logger ( )
if self . create_conn_obj ( ) :
self . enum_host_info ( )
self . print_host_info ( )
if self . login ( ) :
if hasattr ( self . args , ' module ' ) and self . args . module :
self . call_modules ( )
else :
self . call_cmd_args ( )
def proto_logger ( self ) :
self . logger = CMEAdapter ( extra = { ' protocol ' : ' WINRM ' ,
' host ' : self . host ,
' port ' : ' NONE ' ,
' hostname ' : ' NONE ' } )
def enum_host_info ( self ) :
try :
smb_conn = SMBConnection ( self . host , self . host , None )
try :
smb_conn . login ( ' ' , ' ' )
except SessionError as e :
if " STATUS_ACCESS_DENIED " in e . message :
pass
self . domain = smb_conn . getServerDomain ( )
self . hostname = smb_conn . getServerName ( )
self . logger . extra [ ' hostname ' ] = self . hostname
try :
smb_conn . logoff ( )
except :
pass
except Exception as e :
logging . debug ( " Error retrieving host domain: {} specify one manually with the ' -d ' flag " . format ( e ) )
if self . args . domain :
self . domain = self . args . domain
if self . args . local_auth :
self . domain = self . hostname
def print_host_info ( self ) :
self . logger . info ( self . endpoint )
def create_conn_obj ( self ) :
endpoints = [
' https:// {} :5986/wsman ' . format ( self . host ) ,
' http:// {} :5985/wsman ' . format ( self . host )
]
for url in endpoints :
try :
requests . get ( url , verify = False , timeout = 10 )
self . endpoint = url
if self . endpoint . startswith ( ' https:// ' ) :
self . port = 5986
else :
self . port = 5985
self . logger . extra [ ' port ' ] = self . port
return True
except Exception as e :
if ' Max retries exceeded with url ' not in str ( e ) :
logging . debug ( ' Error in WinRM create_conn_obj: ' + str ( e ) )
return False
def plaintext_login ( self , domain , username , password ) :
try :
2020-04-28 11:24:01 +00:00
from urllib3 . connectionpool import log
log . addFilter ( SuppressFilter ( ) )
2017-10-25 02:08:19 +00:00
self . conn = pywinrm . Session ( self . host ,
auth = ( ' {} \\ {} ' . format ( domain , username ) , password ) ,
transport = ' ntlm ' ,
server_cert_validation = ' ignore ' )
# TO DO: right now we're just running the hostname command to make the winrm library auth to the server
# we could just authenticate without running a command :) (probably)
self . conn . run_cmd ( ' hostname ' )
self . admin_privs = True
2019-11-10 23:12:35 +00:00
self . logger . success ( u ' {} \\ {} : {} {} ' . format ( self . domain ,
username ,
password ,
2018-03-01 19:36:17 +00:00
highlight ( ' ( {} ) ' . format ( self . config . get ( ' CME ' , ' pwn3d_label ' ) ) if self . admin_privs else ' ' ) ) )
2017-10-25 02:08:19 +00:00
return True
except Exception as e :
2019-11-10 23:12:35 +00:00
self . logger . error ( u ' {} \\ {} : {} " {} " ' . format ( self . domain ,
username ,
password ,
2017-10-25 02:08:19 +00:00
e ) )
return False
def parse_output ( self , response_obj ) :
if response_obj . status_code == 0 :
2020-04-28 11:24:01 +00:00
try :
buf = StringIO ( response_obj . std_out . decode ( ' utf-8 ' ) ) . readlines ( )
except UnicodeDecodeError :
logging . debug ( ' Decoding error detected, consider running chcp.com at the target, map the result with https://docs.python.org/3/library/codecs.html#standard-encodings ' )
buf = StringIO ( response_obj . std_out . decode ( ' cp437 ' ) ) . readlines ( )
2017-10-25 02:08:19 +00:00
for line in buf :
2019-11-10 23:12:35 +00:00
self . logger . highlight ( line . strip ( ) )
2017-10-25 02:08:19 +00:00
return response_obj . std_out
else :
2020-04-28 11:24:01 +00:00
try :
buf = StringIO ( response_obj . std_out . decode ( ' utf-8 ' ) ) . readlines ( )
except UnicodeDecodeError :
logging . debug ( ' Decoding error detected, consider running chcp.com at the target, map the result with https://docs.python.org/3/library/codecs.html#standard-encodings ' )
buf = StringIO ( response_obj . std_out . decode ( ' cp437 ' ) ) . readlines ( )
2017-10-25 02:08:19 +00:00
for line in buf :
2019-11-10 23:12:35 +00:00
self . logger . highlight ( line . strip ( ) )
2017-10-25 02:08:19 +00:00
return response_obj . std_err
def execute ( self , payload = None , get_output = False ) :
r = self . conn . run_cmd ( self . args . execute )
self . logger . success ( ' Executed command ' )
self . parse_output ( r )
def ps_execute ( self , payload = None , get_output = False ) :
r = self . conn . run_ps ( self . args . ps_execute )
self . logger . success ( ' Executed command ' )
self . parse_output ( r )