# Impacket - Collection of Python classes for working with network protocols. # # Copyright (C) 2023 Fortra. All rights reserved. # # This software is provided under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Tested so far: # (h)DhcpGetClientInfoV4 # DhcpV4GetClientInfo # hDhcpEnumSubnetClientsV5 # hDhcpGetOptionValueV5 # Not yet: # DhcpGetSubnetInfo # DhcpEnumSubnets # DhcpGetOptionValue # DhcpEnumOptionValues # DhcpGetOptionValueV5 # DhcpEnumOptionValuesV5 # DhcpGetAllOptionValues # DhcpEnumSubnetClientsV4 # DhcpEnumSubnetElementsV5 # DhcpEnumSubnetClientsVQ # from __future__ import division from __future__ import print_function import socket import struct import pytest import unittest from six import assertRaisesRegex from tests.dcerpc import DCERPCTests from impacket.dcerpc.v5 import dhcpm from impacket.dcerpc.v5.dtypes import NULL from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_LEVEL_PKT_PRIVACY, DCERPCException class DHCPMTests(DCERPCTests): iface_uuid_v1 = dhcpm.MSRPC_UUID_DHCPSRV iface_uuid_v2 = dhcpm.MSRPC_UUID_DHCPSRV2 string_binding = r"ncacn_np:{0.machine}[\PIPE\dhcpserver]" authn = True authn_level = RPC_C_AUTHN_LEVEL_PKT_PRIVACY def test_DhcpGetClientInfoV4(self): dce, rpctransport = self.connect(iface_uuid=self.iface_uuid_v1) request = dhcpm.DhcpGetClientInfoV4() request['ServerIpAddress'] = NULL request['SearchInfo']['SearchType'] = dhcpm.DHCP_SEARCH_INFO_TYPE.DhcpClientName request['SearchInfo']['SearchInfo']['tag'] = dhcpm.DHCP_SEARCH_INFO_TYPE.DhcpClientName request['SearchInfo']['SearchInfo']['ClientName'] = self.serverName + "\0" request.dump() with assertRaisesRegex(self, DCERPCException, "ERROR_DHCP_JET_ERROR"): dce.request(request) #request['SearchInfo']['SearchType'] = dhcpm.DHCP_SEARCH_INFO_TYPE.DhcpClientIpAddress #request['SearchInfo']['SearchInfo']['tag'] = dhcpm.DHCP_SEARCH_INFO_TYPE.DhcpClientIpAddress #ip = struct.unpack("!I", socket.inet_aton(self.machine))[0] #request['SearchInfo']['SearchInfo']['ClientIpAddress'] = ip #request.dump() #with assertRaisesRegex(self, DCERPCException, "ERROR_DHCP_JET_ERROR"): # dce.request(request) def test_hDhcpGetClientInfoV4(self): dce, rpctransport = self.connect(iface_uuid=self.iface_uuid_v1) with assertRaisesRegex(self, DCERPCException, "ERROR_DHCP_JET_ERROR"): dhcpm.hDhcpGetClientInfoV4(dce, dhcpm.DHCP_SEARCH_INFO_TYPE.DhcpClientName, self.serverName + "\0") #ip = struct.unpack("!I", socket.inet_aton(self.machine))[0] #with assertRaisesRegex(self, DCERPCException, "ERROR_DHCP_JET_ERROR"): # dhcpm.hDhcpGetClientInfoV4(dce, dhcpm.DHCP_SEARCH_INFO_TYPE.DhcpClientIpAddress, ip) def test_DhcpV4GetClientInfo(self): dce, rpctransport = self.connect(iface_uuid=self.iface_uuid_v2) request = dhcpm.DhcpV4GetClientInfo() request['ServerIpAddress'] = NULL request['SearchInfo']['SearchType'] = dhcpm.DHCP_SEARCH_INFO_TYPE.DhcpClientName request['SearchInfo']['SearchInfo']['tag'] = dhcpm.DHCP_SEARCH_INFO_TYPE.DhcpClientName request['SearchInfo']['SearchInfo']['ClientName'] = self.serverName + "\0" request.dump() # The DHCP client is probably not created but if we received an invalid DHCP client error # means the search info had no corresponding IPv4 lease records. with assertRaisesRegex(self, DCERPCException, "ERROR_DHCP_INVALID_DHCP_CLIENT"): dce.request(request) #request['SearchInfo']['SearchType'] = dhcpm.DHCP_SEARCH_INFO_TYPE.DhcpClientIpAddress #request['SearchInfo']['SearchInfo']['tag'] = dhcpm.DHCP_SEARCH_INFO_TYPE.DhcpClientIpAddress #ip = struct.unpack("!I", socket.inet_aton(self.machine))[0] #request['SearchInfo']['SearchInfo']['ClientIpAddress'] = ip #request.dump() #with assertRaisesRegex(self, DCERPCException, "ERROR_DHCP_INVALID_DHCP_CLIENT"): # dce.request(request) def test_hDhcpEnumSubnetClientsV5(self): dce, rpctransport = self.connect(iface_uuid=self.iface_uuid_v2) with assertRaisesRegex(self, DCERPCException, "ERROR_NO_MORE_ITEMS"): dhcpm.hDhcpEnumSubnetClientsV5(dce) def test_hDhcpGetOptionValueV5(self): dce, rpctransport = self.connect(iface_uuid=self.iface_uuid_v2) netId = self.machine.split('.')[:-1] netId.append('0') subnet_id = struct.unpack("!I", socket.inet_aton('.'.join(netId)))[0] with assertRaisesRegex(self, DCERPCException, "ERROR_DHCP_SUBNET_NOT_PRESENT"): dhcpm.hDhcpGetOptionValueV5(dce, 3, dhcpm.DHCP_FLAGS_OPTION_DEFAULT, NULL, NULL, dhcpm.DHCP_OPTION_SCOPE_TYPE.DhcpSubnetOptions, subnet_id) @pytest.mark.remote @pytest.mark.skip(reason="Disabled in Windows Server 2008 onwards") class DHCPMTestsSMBTransport(DHCPMTests, unittest.TestCase): transfer_syntax = DCERPCTests.TRANSFER_SYNTAX_NDR @pytest.mark.remote @pytest.mark.skip(reason="Disabled in Windows Server 2008 onwards") class DHCPMTestsSMBTransport64(DHCPMTests, unittest.TestCase): transfer_syntax = DCERPCTests.TRANSFER_SYNTAX_NDR64 @pytest.mark.remote class DHCPMTestsTCPTransport(DHCPMTests, unittest.TestCase): protocol = "ncacn_ip_tcp" iface_uuid = dhcpm.MSRPC_UUID_DHCPSRV2 string_binding_formatting = DCERPCTests.STRING_BINDING_MAPPER transfer_syntax = DCERPCTests.TRANSFER_SYNTAX_NDR @pytest.mark.remote class DHCPMTestsTCPTransport64(DHCPMTests, unittest.TestCase): protocol = "ncacn_ip_tcp" iface_uuid = dhcpm.MSRPC_UUID_DHCPSRV2 string_binding_formatting = DCERPCTests.STRING_BINDING_MAPPER transfer_syntax = DCERPCTests.TRANSFER_SYNTAX_NDR64 @pytest.mark.xfail(reason="NDRUNION without fields as in DhcpSubnetOptions is not implemented with NDR64") def test_hDhcpGetOptionValueV5(self): super(DHCPMTestsTCPTransport64, self).test_hDhcpGetOptionValueV5() # Process command-line arguments. if __name__ == "__main__": unittest.main(verbosity=1)