z3v2cicidi / impacket

Automatically exported from code.google.com/p/impacket
Other
0 stars 0 forks source link

On Windows 2012R2 reports Broken Pipe #46

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
Starting from MS Windows 2012R2 the lib fails with the errors:

'_smb_upload' failed with exception 'error(32, 'Broken pipe')'

The _smb_upload is our wrapper around the lib.

@contextlib.contextmanager
def smbconnect(self, smbport=139):
    smbconn = impacket.smbconnection.SMBConnection("*SMBSERVER", self.host, sess_port=smbport)
    self.log.debug("SMB connection to {} port {}".format(self.host, smbport))
    smbconn.login(self.creds.username, self.creds.password.plaintext)
    yield smbconn
    smbconn.logoff()

    with self.smbconnect(smbport) as smbconn:
        ...
        smbconn.putFile(share, upload_to_path, file_to_upload.read)

Any suggestions are appreciated.

Thank you,

Arthur

Original issue reported on code.google.com by aamshu...@rivermeadow.com on 29 Jul 2014 at 6:46

GoogleCodeExporter commented 9 years ago
Hey Arthur:

1) I would do the communication against port 445, not 139. The likelihood of 
failing with 139 is higher since you need to establish a NetBIOS connection 
first, and for that you need the remote NetBIOS name. '*SMBSERVER' used to work 
on older Windows, but that alias doesn't exist anymore in newer versions. The 
library itself would try to gather the target NetBIOS name, but that's not 
always possible. So.. try 445 (which is the default behaviour).

2) What Impacket version are you using?

3) Did you try doing the same procedure outside your lib, using the 
smbclient.py script?

4) A wireshark export would be very useful if the problem persists.

cheers
beto

Original comment by bet...@gmail.com on 29 Jul 2014 at 6:54

GoogleCodeExporter commented 9 years ago
Thanks Beto,

I checked what you suggested with the latest version (0.9.12) of the lib.
The smbclient.py works fine. I also changed the method to use 445:

smbconn = impacket.smbconnection.SMBConnection(remoteName="192...",
remoteHost="192...", sess_port=445)

The issue is still there. But if I add these lines everything works fine
too:

rpctransport = transport.SMBTransport(smbconn.getServerName(),
smbconn.getRemoteHost(), filename = r'\srvsvc', smb_connection = smbconn)
dce = rpctransport.get_dce_rpc()
dce.connect()

Do I really need to keep this connection?

Thanks again,

Arthur

Original comment by aamshu...@rivermeadow.com on 29 Jul 2014 at 8:17

GoogleCodeExporter commented 9 years ago
Hey Arthur:

1) Okey, so smbclient.py is working. That's good to know :)

2) The lines you're adding there are going to use your existing connection to 
connect to the '\srvsvc' pipe, which has nothing to do with uploading a file to 
a target server. 

Don't you wanna share a little bit more of your code?.. 'cause I'm kind of 
confused now. If you say it worked with smbclient.py the problem might be in 
your code.. I can try to help you

cheers
beto

Original comment by bet...@gmail.com on 29 Jul 2014 at 8:32

GoogleCodeExporter commented 9 years ago
I stripped the code a little bit ...

# built-in imports
import contextlib
import ntpath
import os
import re
import time
import socket
import pipes

# 3rd party
import sh
import impacket.smbconnection
from impacket.smb3 import SessionError
from phore.util import retryable, trace

@register(SourceConnection, 'windows')
class WindowsSourceConnection(SourceConnection):

    DEFAULT_SMB_PORT = 445

    @contextlib.contextmanager
    def smbconnect(self, smbport=445):
        """
        @type smbport: int
        @return: context manager
        """
        smbconn = impacket.smbconnection.SMBConnection(remoteName=self.host,
# attempt to get hostname
                                                       remoteHost=self.host,
# ip address
                                                       sess_port=smbport)
        self.log.debug("SMB connection to {} port {}".format(self.host,
smbport))
        smbconn.login(self.creds.username, self.creds.password.plaintext)
        # dialect = smbconn.getDialect()
        # self.log.debug(dialect)
        #
        # if smbconn.isGuestSession() > 0:
        #     self.log.debug("GUEST Session Granted")
        # else:
        #     self.log.debug("USER Session Granted")

        # ADDING THESE LINES MAKES THE CODE WORK
        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 *

        rpctransport = transport.SMBTransport(smbconn.getServerName(),
smbconn.getRemoteHost(), filename = r'\srvsvc', smb_connection = smbconn)
        dce = rpctransport.get_dce_rpc()
        dce.connect()

        yield smbconn
        smbconn.logoff()

    # TODO: there might be some windows machines that have SMB exposed on a
    # different port. At the moment, winexe (used elsewhere) is always
    # using port 139, so there's not much we can do about this. But here's
    # where configuration could be added if they- for whatever reason- have
    # SMB working on another port.
    @trace
    def upload(self, file_names, share="ADMIN$", upload_to=None,
smbport=DEFAULT_SMB_PORT):
        """
        Uploads software to ADMIN$ on the remote Windows machine. Copies
        software over to the Windows temp collection path.

        This strategy is legacy from encloud, but the implementation is pure
        python whereas before there was a samba share mounted on the Windows
        source machine.

        @param file_names: names of files to upload
        @type file_names: list[str]

        @param share: the share name to upload. defaults to ADMIN$ which
"comes"
                      with Windows by default.
        @type share: str

        @param upload_to: directory to upload to, defaults to "None" but is
                        set to "collection_dir" inside this method
        @type upload_to: str

        @param smbport: port on which to connect to host via the SMB
protocol.
                        defaults to DEFAULT_SMB_PORT.
        @type smbport: int
        """

        if not upload_to:
            upload_to = ntpath.normpath(ntpath.join('TEMP',
                                                    COLLECTION_DIR_NAME))

        with self.smbconnect(smbport) as smbconn:
            stime = time.time()
            n_files = 0
            for n_files, filename in enumerate(file_names, 1):
                # get a reference to filename. e.g. cygwin.exe. If the
filename
                # referred to is part of WST, select that, if not try to
upload
                # filename from phoenix machine.
                with open(windows_source_tools.paths.get(filename,
                          filename)) as file_to_upload:
                    upload_path = ntpath.join(upload_to,
os.path.basename(filename))
                    self.log.info('SEND {0} => {1}'.format(filename,
upload_to))
                    if self._smb_upload(smbconn, upload_path,
file_to_upload, share):
                        self._smb_uploaded.add(upload_path)

            self.log.debug('{} files transferred in {:1.1f}s.'.format(
                n_files, time.time() - stime))

    @retryable(retries=SMB_RETRIES, no_retry_list=[SessionError],
               retry_backoff=SMB_RETRY_BACKOFF,
caller_log=SourceConnection.log)
    def _smb_upload(self, smbconn, upload_to_path, file_to_upload, share):
        """Upload a file to the remote machine over SMBConnection
        @param smbconn: A connected SMBConnection
        @type smbconn: SMBConnection
        @param upload_path: the uploaded file's absolute path
        @type upload_path: str
        @param file_to_upload: the file to stream from
        @type file_to_upload: file
        @param share: the share name to upload
        @type share: str
        @raise SMBSessionRetryableError
        @raise SessionError
        """
        try:
            smbconn.putFile(share, upload_to_path, file_to_upload.read)
        except SessionError, se:
            self.log.debug('Error uploading {}'.format(file_to_upload.name))
            self.log.exception(se)
            if RETRYABLE_SMB_SESSION_ERRORS.search(str(se)):
                raise SMBSessionRetryableError()
            else:
                raise
        return True

Original comment by aamshu...@rivermeadow.com on 29 Jul 2014 at 8:45

GoogleCodeExporter commented 9 years ago
Arthur:

I'll take a look at the code.. Please write me (or chat) @ bethus at gmail dot 
com.

No need to continue on this ticket.

Original comment by bet...@gmail.com on 29 Jul 2014 at 8:56

GoogleCodeExporter commented 9 years ago
Looks like it's not an impacket issue.

Original comment by bet...@gmail.com on 30 Jul 2014 at 4:06

GoogleCodeExporter commented 9 years ago
Reopening this issue.

After further research, looks like the problem is the MaxWriteSize change in 
Windows 8.1/2012 R2.

From http://msdn.microsoft.com/en-us/library/cc246805.aspx:
<216> Section 3.3.5.3.1: If the underlying transport is NETBIOS over TCP, 
Windows servers set MaxWriteSize to 65536. Otherwise, MaxWriteSize is based on 
the following table.
Windows version\Connection.Dialect: MaxWriteSize
Windows 7/Windows Server 2008 R2: 1048576
Windows 8/Windows Server 2012: 1048576
All other SMB2 servers: 8388608

Looks like when having such a big MaxWriteSize and uploading a file that is 
bigger than that, the connection gets closed by the server. 

Temporary fix is to force the MaxWriteSize to be smaller until we figure out 
why the server is closing the connection:

===================================================================
--- smb3.py (revision 1251)
+++ smb3.py (working copy)
@@ -360,7 +360,7 @@
             negResp = SMB2Negotiate_Response(ans['Data'])
             self._Connection['MaxTransactSize']   = negResp['MaxTransactSize']
             self._Connection['MaxReadSize']       = negResp['MaxReadSize']
-            self._Connection['MaxWriteSize']      = negResp['MaxWriteSize']
+            self._Connection['MaxWriteSize']      = 
min(65536,negResp['MaxWriteSize'])
             self._Connection['ServerGuid']        = negResp['ServerGuid']
             self._Connection['GSSNegotiateToken'] = negResp['Buffer']
             self._Connection['Dialect']           = negResp['DialectRevision']

Original comment by bet...@gmail.com on 31 Jul 2014 at 1:28