Beispiel #1
0
    def __init__(self,
                 server,
                 username=None,
                 password=None,
                 port=445,
                 encrypt=True):
        self.server = server
        self.port = port
        self.pid = os.getpid()
        self.current_host = socket.gethostname()
        self.connection = Connection(uuid.uuid4(), server, port)
        self.session = Session(self.connection,
                               username,
                               password,
                               require_encryption=encrypt)

        self.service_name = "PAExec-%d-%s" % (self.pid, self.current_host)
        log.info("Creating PyPsexec Client with unique name: %s" %
                 self.service_name)
        self._exe_file = "%s.exe" % self.service_name
        self._stdout_pipe_name = "PaExecOut%s%d"\
                                 % (self.current_host, self.pid)
        self._stderr_pipe_name = "PaExecErr%s%d"\
                                 % (self.current_host, self.pid)
        self._stdin_pipe_name = "PaExecIn%s%d" % (self.current_host, self.pid)
        self._unique_id = get_unique_id(self.pid, self.current_host)
        log.info("Generated unique ID for PyPsexec Client: %d" %
                 self._unique_id)
        self._service = Service(self.service_name, self.session)
Beispiel #2
0
    def test_encrypt_ccm(self, monkeypatch):
        def mockurandom(length):
            return b"\xff" * length

        monkeypatch.setattr(os, 'urandom', mockurandom)

        connection = Connection(uuid.uuid4(), "server", 445)
        connection.dialect = Dialects.SMB_3_1_1
        connection.cipher_id = Ciphers.get_cipher(Ciphers.AES_128_CCM)
        session = Session(connection, "user", "pass")
        session.session_id = 1
        session.encryption_key = b"\xff" * 16

        expected = SMB2TransformHeader()
        expected['signature'] = b"\xc8\x73\x0c\x9b\xa7\xe5\x9f\x1c" \
            b"\xfd\x37\x51\xa1\x95\xf2\xb3\xac"
        expected['nonce'] = b"\xff" * 11 + b"\x00" * 5
        expected['original_message_size'] = 4
        expected['flags'] = 1
        expected['session_id'] = 1
        expected['data'] = b"\x21\x91\xe3\x0e"

        actual = connection._encrypt(b"\x01\x02\x03\x04", session)
        assert isinstance(actual, SMB2TransformHeader)
        assert actual.pack() == expected.pack()
Beispiel #3
0
    def test_encrypt_gcm(self, monkeypatch):
        def mockurandom(length):
            return b"\xff" * length

        monkeypatch.setattr(os, 'urandom', mockurandom)

        connection = Connection(uuid.uuid4(), "server", 445)
        connection.dialect = Dialects.SMB_3_1_1
        connection.cipher_id = Ciphers.get_cipher(Ciphers.AES_128_GCM)
        session = Session(connection, "user", "pass")
        session.session_id = 1
        session.encryption_key = b"\xff" * 16

        expected = SMB2TransformHeader()
        expected['signature'] = b"\x39\xd8\x32\x34\xd7\x53\xd0\x8e" \
            b"\xc0\xfc\xbe\x33\x01\x5f\x19\xbd"
        expected['nonce'] = b"\xff" * 12 + b"\x00" * 4
        expected['original_message_size'] = 4
        expected['flags'] = 1
        expected['session_id'] = 1
        expected['data'] = b"\xda\x26\x57\x33"

        actual = connection._encrypt(b"\x01\x02\x03\x04", session)
        assert isinstance(actual, SMB2TransformHeader)
        assert actual.pack() == expected.pack()
Beispiel #4
0
def register_session(server,
                     username=None,
                     password=None,
                     port=445,
                     encrypt=None,
                     connection_timeout=60,
                     connection_cache=None,
                     auth_protocol='negotiate'):
    """
    Creates an active connection and session to the server specified. This can be manually called to register the
    credentials of a specific server instead of defining it on the first function connecting to the server. The opened
    connection is registered in a pool and re-used if a connection is made to the same server with the same
    credentials.

    :param server: The server name to register.
    :param username: Optional username to connect with. Required if no session has been registered for the server and
        Kerberos auth is not being used.
    :param password: Optional password to connect with.
    :param port: The port to connect with.
    :param encrypt: Whether to force encryption or not, once this has been set to True the session cannot be changed
        back to False.
    :param connection_timeout: Override the timeout used for the initial connection.
    :param connection_cache: Connection cache to be used with
    :param auth_protocol: The protocol to use for authentication. Possible values are 'negotiate', 'ntlm' or
        'kerberos'. Defaults to 'negotiate'.
    :return: The Session that was registered or already existed in the pool.
    """
    connection_key = "%s:%s" % (server.lower(), port)

    if connection_cache is None:
        connection_cache = _SMB_CONNECTIONS
    connection = connection_cache.get(connection_key, None)

    # Make sure we ignore any connections that may have had a closed connection
    if not connection or not connection.transport.connected:
        connection = Connection(ClientConfig().client_guid, server, port)
        connection.connect(timeout=connection_timeout)
        connection_cache[connection_key] = connection

    # Find the first session in the connection session list that match the username specified, if not username then
    # just use the first session found or fall back to creating a new one with implicit auth/kerberos.
    session = next((s for s in connection.session_table.values()
                    if username is None or s.username == username), None)
    if not session:
        session = Session(connection,
                          username=username,
                          password=password,
                          require_encryption=(encrypt is True),
                          auth_protocol=auth_protocol)
        session.connect()
    elif encrypt is not None:
        # We cannot go from encryption to no encryption on an existing session but we can do the opposite.
        if session.encrypt_data and not encrypt:
            raise ValueError(
                "Cannot disable encryption on an already negotiated session.")
        elif not session.encrypt_data and encrypt:
            session.encrypt = True

    return session
def test_connection(server, port):
    conn = Connection(uuid.uuid4(), server, port=port)
    print("Opening connection to %s:%d" % (server, port))
    conn.connect(timeout=5)
    try:
        print("Connection successful, sending ECHO request")
        conn.echo()
    finally:
        conn.disconnect(True)
Beispiel #6
0
 def test_verify_message_skip(self, smb_real):
     connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3], True)
     connection.connect()
     try:
         header = SMB2HeaderResponse()
         header['message_id'] = 0xFFFFFFFFFFFFFFFF
         connection.verify_signature(header, 0)
     finally:
         connection.disconnect()
Beispiel #7
0
    def test_broken_message_worker_exception(self, mocker):
        connection = Connection(uuid.uuid4(), 'server', 445, True)

        mock_transport = mocker.MagicMock()
        connection.transport = mock_transport
        connection.transport.recv.side_effect = Exception('test')
        connection._process_message_thread()

        with pytest.raises(Exception, match='test'):
            connection.send(SMB2Echo())

        with pytest.raises(Exception, match='test'):
            connection.receive(None)
Beispiel #8
0
 def test_parse_error_unknown(self):
     connection = Connection(uuid.uuid4(), "server", 445)
     session = Session(connection, "user", "password")
     api = SCMRApi(session)
     with pytest.raises(SCMRException) as exc:
         api._parse_error(999, "function_name")
     assert str(exc.value) == "Exception calling function_name. Code: 999" \
                              ", Msg: ERROR_UNKNOWN"
Beispiel #9
0
    def test_marshal_string_none(self):
        connection = Connection(uuid.uuid4(), "server", 445)
        session = Session(connection, "user", "password")
        api = SCMRApi(session)

        expected = b"\x00\x00\x00\x00"
        actual = api._marshal_string(None)
        assert actual == expected
def bule_screen(IP, username=None, password=None, port=445, encode=None, connectionTimeout=10):
    _SMB_CONNECTIONS = {}
    connection_key = "%s:%s" %(IP, port)
    connection = _SMB_CONNECTIONS.get(connection_key, None)
    if not connection:
        connection = Connection(uuid.uuid4(), IP, port)
        connection.connect(timeout=connectionTimeout)
        _SMB_CONNECTIONS[connection_key] = connection
    session = next((s for s in connection.session_table.values() if username is None or s.username == username), None)
    if not session:
        session = Session(connection, username=username, password=password, require_encryption=(encode is True))
        session.connect()
    elif encode is not None:
        if session.encrypt_data and not encode:
            print("[\033[33m-\033[0m]Cannot disable encryption on an already negotiated session.")
        elif not session.encrypt_data and encode:
            session.encrypt = True
    return session
Beispiel #11
0
 def test_requested_credits_greater_than_available(self, smb_real):
     connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3], True)
     connection.connect()
     try:
         msg = SMB2IOCTLRequest()
         msg['max_output_response'] = 65538  # results in 2 credits required
         with pytest.raises(SMBException) as exc:
             connection._generate_packet_header(msg, None, None, 0)
         assert str(exc.value) == "Request requires 2 credits but only 1 " \
                                  "credits are available"
     finally:
         connection.disconnect()
Beispiel #12
0
 def test_parse_pdu_failure(self):
     connection = Connection(uuid.uuid4(), "server", 445)
     session = Session(connection, "user", "password")
     api = SCMRApi(session)
     fault_pdu = FaultPDU()
     fault_pdu['packed_drep'] = DataRepresentationFormat()
     with pytest.raises(PDUException) as exc:
         api._parse_pdu(fault_pdu.pack(), 10)
     assert "Expecting ResponsePDU for opnum 10 response but got: " \
            "FaultPDU" in str(exc.value)
Beispiel #13
0
 def test_verify_message_skip(self, smb_real):
     connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3], True)
     connection.connect()
     try:
         header = SMB2HeaderResponse()
         header['message_id'] = 0xFFFFFFFFFFFFFFFF
         expected = header.pack()
         connection._verify(header, 0)
         actual = header.pack()
         assert actual == expected
     finally:
         connection.disconnect()
Beispiel #14
0
 def test_parse_pdu_fine(self):
     connection = Connection(uuid.uuid4(), "server", 445)
     session = Session(connection, "user", "password")
     api = SCMRApi(session)
     response_pdu = ResponsePDU()
     response_pdu['packed_drep'] = DataRepresentationFormat()
     response_pdu['stub_data'] = b"\x01\x02\x03\x04"
     expected = b"\x01\x02\x03\x04"
     actual = api._parse_pdu(response_pdu.pack(), 10)
     assert actual == expected
Beispiel #15
0
 def test_verify_fail_no_session(self, smb_real):
     connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3], True)
     connection.connect()
     try:
         header = SMB2HeaderResponse()
         header['message_id'] = 1
         header['flags'].set_flag(Smb2Flags.SMB2_FLAGS_SIGNED)
         with pytest.raises(SMBException) as exc:
             connection._verify(header, 100)
         assert str(exc.value) == "Failed to find session 100 for " \
                                  "message verification"
     finally:
         connection.disconnect()
Beispiel #16
0
 def test_decrypt_invalid_flag(self, smb_real):
     connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3], True)
     session = Session(connection, smb_real[0], smb_real[1])
     connection.connect()
     try:
         session.connect()
         # just get some random message
         header = connection.preauth_integrity_hash_value[-1]
         enc_header = connection._encrypt(header.pack(), session)
         assert isinstance(enc_header, SMB2TransformHeader)
         enc_header['flags'] = 5
         with pytest.raises(SMBException) as exc:
             connection._decrypt(enc_header)
         assert str(exc.value) == "Expecting flag of 0x0001 but got 5 in " \
                                  "the SMB Transform Header Response"
     finally:
         connection.disconnect(True)
Beispiel #17
0
 def test_decrypt_invalid_session_id(self, smb_real):
     connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3], True)
     session = Session(connection, smb_real[0], smb_real[1])
     connection.connect()
     try:
         session.connect()
         # just get some random message
         header = connection.preauth_integrity_hash_value[-1]
         enc_header = connection._encrypt(header.pack(), session)
         assert isinstance(enc_header, SMB2TransformHeader)
         enc_header['session_id'] = 100
         with pytest.raises(SMBException) as exc:
             connection._decrypt(enc_header)
         assert str(exc.value) == "Failed to find valid session 100 for " \
                                  "message decryption"
     finally:
         connection.disconnect(True)
Beispiel #18
0
def register_session(server, username=None, password=None, port=445, encrypt=None, connection_timeout=60):
    """
    Creates an active connection and session to the server specified. This can be manually called to register the
    credentials of a specific server instead of defining it on the first function connecting to the server. The opened
    connection is registered in a pool and re-used if a connection is made to the same server with the same
    credentials.

    :param server: The server name to register.
    :param username: Optional username to connect with. Required if no session has been registered for the server and
        Kerberos auth is not being used.
    :param password: Optional password to connect with.
    :param port: The port to connect with.
    :param encrypt: Whether to force encryption or not, once this has been set to True the session cannot be changed
        back to False.
    :param connection_timeout: Override the timeout used for the initial connection.
    :return: The Session that was registered or already existed in the pool.
    """
    connection_key = "%s:%s" % (server, port)

    global _SMB_CONNECTIONS
    connection = _SMB_CONNECTIONS.get(connection_key, None)

    if not connection:
        connection = Connection(_CLIENT_GUID, server, port)
        connection.connect(timeout=connection_timeout)
        _SMB_CONNECTIONS[connection_key] = connection

    # Find the first session in the connection session list that match the username specified, if not username then
    # just use the first session found or fall back to creating a new one with implicit auth/kerberos.
    session = next((s for s in connection.session_table.values() if username is None or s.username == username), None)
    if not session:
        session = Session(connection, username=username, password=password, require_encryption=(encrypt is True))
        session.connect()
    elif encrypt is not None:
        # We cannot go from encryption to no encryption on an existing session but we can do the opposite.
        if session.encrypt_data and not encrypt:
            raise ValueError("Cannot disable encryption on an already negotiated session.")
        elif not session.encrypt_data and encrypt:
            session.encrypt = True

    return session
Beispiel #19
0
 def __init__(self, server, username, password, port=445):
     connection_id = uuid.uuid4()
     addr = socket.gethostbyname(server)
     self.server = server
     connection = Connection(connection_id,
                             addr,
                             port,
                             require_signing=True)
     self.session = Session(connection,
                            username,
                            password,
                            require_encryption=False)
Beispiel #20
0
 def test_connection_echo(self, smb_real):
     connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3])
     connection.connect()
     session = Session(connection, smb_real[0], smb_real[1])
     session.connect()
     try:
         actual = connection.echo(sid=session.session_id, credit_request=2)
         assert actual == 2
     finally:
         connection.disconnect(True)
Beispiel #21
0
    def test_dialect_implicit_require_signing(self, smb_real):
        connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3], True)
        connection.connect()
        try:
            assert connection.dialect == Dialects.SMB_3_1_1
            assert connection.negotiated_dialects == [
                Dialects.SMB_2_0_2, Dialects.SMB_2_1_0, Dialects.SMB_3_0_0,
                Dialects.SMB_3_0_2, Dialects.SMB_3_1_1
            ]
            assert connection.gss_negotiate_token is not None
            assert len(connection.preauth_integrity_hash_value) == 2
            assert len(connection.salt) == 32
            assert connection.sequence_window['low'] == 1
            assert connection.sequence_window['high'] == 2
            assert connection.client_security_mode == \
                SecurityMode.SMB2_NEGOTIATE_SIGNING_REQUIRED

            # server settings override the require signing
            assert connection.server_security_mode == \
                SecurityMode.SMB2_NEGOTIATE_SIGNING_REQUIRED | \
                SecurityMode.SMB2_NEGOTIATE_SIGNING_ENABLED
            assert connection.supports_encryption
            assert connection.require_signing
        finally:
            connection.disconnect()
    def test_change_notify_on_a_file(self, smb_real):
        connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3])
        connection.connect()
        session = Session(connection, smb_real[0], smb_real[1])
        tree = TreeConnect(session, smb_real[4])
        open = Open(tree, "file-watch.txt")
        try:
            session.connect()
            tree.connect()

            open.create(
                ImpersonationLevel.Impersonation,
                FilePipePrinterAccessMask.MAXIMUM_ALLOWED,
                FileAttributes.FILE_ATTRIBUTE_NORMAL,
                ShareAccess.FILE_SHARE_READ | ShareAccess.FILE_SHARE_WRITE
                | ShareAccess.FILE_SHARE_DELETE,
                CreateDisposition.FILE_OPEN_IF,
                CreateOptions.FILE_NON_DIRECTORY_FILE)

            watcher = FileSystemWatcher(open)
            watcher.start(CompletionFilter.FILE_NOTIFY_CHANGE_FILE_NAME)
            expected = "Received unexpected status from the server: (3221225485) STATUS_INVALID_PARAMETER"
            with pytest.raises(SMBResponseException,
                               match=re.escape(expected)):
                watcher.wait()
        finally:
            connection.disconnect(True)
Beispiel #23
0
 def test_send_invalid_tree_id(self, smb_real):
     connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3])
     session = Session(connection, smb_real[0], smb_real[1])
     connection.connect()
     try:
         session.connect()
         msg = SMB2IOCTLRequest()
         msg['file_id'] = b"\xff" * 16
         with pytest.raises(SMBException) as exc:
             connection.send(msg, session.session_id, 10)
         assert str(exc.value) == "Cannot find Tree with the ID 10 in " \
                                  "the session tree table"
     finally:
         connection.disconnect()
    def __init__(self,
                 server,
                 username=None,
                 password=None,
                 port=445,
                 encrypt=True,
                 obscure=True,
                 sharename="ADMIN$"):
        self.server = server
        self.port = port
        self.pid = os.getpid()
        self.current_host = socket.gethostname()
        self.connection = Connection(uuid.uuid4(), server, port)
        self.session = Session(self.connection,
                               username,
                               password,
                               require_encryption=encrypt)
        self.service_name = ""
        #added

        if obscure:  #added
            self.service_name = Client.obscure_service_name()
            #added
        else:  #added
            self.service_name = "PAExec"
            # This doesn't need a new name everytime the service is created; makes a mess in the directories # OLD = "PAExec-%d-%s" % (self.pid, self.current_host)

        log.info("Creating PyPsexec Client with unique name: %s" %
                 self.service_name)
        self._exe_file = "%s.exe" % self.service_name
        self._stdout_pipe_name = "PaExecOut%s%d" % (self.current_host,
                                                    self.pid)
        self._stderr_pipe_name = "PaExecErr%s%d" % (self.current_host,
                                                    self.pid)
        self._stdin_pipe_name = "PaExecIn%s%d" % (self.current_host, self.pid)
        self._unique_id = get_unique_id(self.pid, self.current_host)
        log.info("Generated unique ID for PyPsexec Client: %d" %
                 self._unique_id)
        self._service = Service(self.service_name, self.session)
        self._share = sharename
Beispiel #25
0
 def test_dialect_3_1_1(self, smb_real):
     connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3])
     connection.connect(Dialects.SMB_3_1_1)
     session = Session(connection, smb_real[0], smb_real[1])
     try:
         session.connect()
         assert len(session.application_key) == 16
         assert session.application_key != session.session_key
         assert len(session.decryption_key) == 16
         assert session.decryption_key != session.session_key
         assert session.encrypt_data
         assert len(session.encryption_key) == 16
         assert session.encryption_key != session.session_key
         assert len(session.connection.preauth_integrity_hash_value) == 2
         assert len(session.preauth_integrity_hash_value) == 3
         assert session.require_encryption
         assert session.session_id is not None
         assert len(session.session_key) == 16
         assert len(session.signing_key) == 16
         assert session.signing_key != session.session_key
         assert not session.signing_required
     finally:
         connection.disconnect(True)
         # test that disconnect can be run multiple times
         session.disconnect()
    def test_change_notify_underlying_close(self, smb_real):
        connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3])
        connection.connect()
        session = Session(connection, smb_real[0], smb_real[1])
        tree = TreeConnect(session, smb_real[4])
        open = Open(tree, "directory-watch")
        try:
            session.connect()
            tree.connect()

            open.create(
                ImpersonationLevel.Impersonation,
                DirectoryAccessMask.MAXIMUM_ALLOWED,
                FileAttributes.FILE_ATTRIBUTE_DIRECTORY,
                ShareAccess.FILE_SHARE_READ | ShareAccess.FILE_SHARE_WRITE
                | ShareAccess.FILE_SHARE_DELETE,
                CreateDisposition.FILE_OPEN_IF,
                CreateOptions.FILE_DIRECTORY_FILE)

            watcher = FileSystemWatcher(open)
            watcher.start(CompletionFilter.FILE_NOTIFY_CHANGE_FILE_NAME)
            assert watcher.result is None
            assert watcher.response_event.is_set() is False

            open.close()

            expected = "Received unexpected status from the server: (267) STATUS_NOTIFY_CLEANUP"
            with pytest.raises(SMBResponseException,
                               match=re.escape(expected)):
                watcher.wait()
        finally:
            connection.disconnect(True)
Beispiel #27
0
    def test_setup_session_with_ntlm_only(self, smb_real):
        connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3])
        connection.connect()

        session = Session(connection,
                          smb_real[0],
                          smb_real[1],
                          False,
                          auth_protocol='ntlm')
        try:
            session.connect()
            assert len(session.application_key) == 16
            assert session.application_key != session.session_key
            assert len(session.decryption_key) == 16
            assert session.decryption_key != session.session_key
            assert not session.encrypt_data
            assert len(session.encryption_key) == 16
            assert session.encryption_key != session.session_key
            assert len(session.connection.preauth_integrity_hash_value) == 2
            assert len(session.preauth_integrity_hash_value) == 3
            assert not session.require_encryption
            assert session.session_id is not None
            assert len(session.session_key) == 16
            assert len(session.signing_key) == 16
            assert session.signing_key != session.session_key
            assert session.signing_required
        finally:
            connection.disconnect()
Beispiel #28
0
    def test_change_notify_on_a_file(self, smb_real):
        connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3])
        connection.connect()
        session = Session(connection, smb_real[0], smb_real[1])
        tree = TreeConnect(session, smb_real[4])
        open = Open(tree, "file-watch.txt")
        try:
            session.connect()
            tree.connect()

            open.create(
                ImpersonationLevel.Impersonation,
                FilePipePrinterAccessMask.MAXIMUM_ALLOWED,
                FileAttributes.FILE_ATTRIBUTE_NORMAL,
                ShareAccess.FILE_SHARE_READ | ShareAccess.FILE_SHARE_WRITE
                | ShareAccess.FILE_SHARE_DELETE,
                CreateDisposition.FILE_OPEN_IF,
                CreateOptions.FILE_NON_DIRECTORY_FILE)

            watcher = FileSystemWatcher(open)
            watcher.start(CompletionFilter.FILE_NOTIFY_CHANGE_FILE_NAME)
            with pytest.raises(InvalidParameter):
                watcher.wait()
        finally:
            connection.disconnect(True)
Beispiel #29
0
    def check(self, ip_addr, port=445):
        """Checks whether a SMB server allows login without credentials"""

        try:
            # connect and attempt authentication
            connection = Connection(uuid.uuid4(),
                                    ip_addr,
                                    port,
                                    require_signing=False)
            connection.connect(Dialects.SMB_2_0_2, timeout=TIMEOUT)
            try:
                session = Session(
                    connection, "", "", require_encryption=False
                )  # dont require encryption or signing to support o2 HomeBox

                try:
                    session.connect()
                except SMBAuthenticationError:
                    return False

                return ("", "")
            finally:
                connection.disconnect()
        except Exception as e:
            print("SMB connection failed")
            print(e)

            return False
Beispiel #30
0
 def __init__(self, server, username, password, port=445):
     connection_id = uuid.uuid4()
     addr = socket.getaddrinfo(server, None, 0, 0,
                               socket.IPPROTO_TCP)[0][4][0]
     self.server = server
     connection = Connection(connection_id,
                             addr,
                             port,
                             require_signing=True)
     self.session = Session(connection,
                            username,
                            password,
                            require_encryption=False)