VYPR
High severityNVD Advisory· Published Sep 10, 2018· Updated Aug 5, 2024

CVE-2018-14635

CVE-2018-14635

Description

When using the Linux bridge ml2 driver, non-privileged tenants are able to create and attach ports without specifying an IP address, bypassing IP address validation. A potential denial of service could occur if an IP address, conflicting with existing guests or routers, is then assigned from outside of the allowed allocation pool. Versions of openstack-neutron before 13.0.0.0b2, 12.0.3 and 11.0.5 are vulnerable.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Non-privileged tenants can bypass IP address validation in OpenStack Neutron's Linux bridge driver, leading to potential denial of service via IP conflicts.

Vulnerability

The vulnerability resides in the Linux bridge ml2 driver of OpenStack Neutron. Non-privileged tenants are able to create and attach ports without specifying an IP address, bypassing IP address validation. This allows an IP address to be assigned from outside the allowed allocation pool, potentially conflicting with existing guests or routers. Affected versions are openstack-neutron before 13.0.0.0b2, 12.0.3, and 11.0.5. [1]

Exploitation

An attacker must be a non-privileged tenant with network access to create and attach ports. The attacker can create a port without specifying an IP address, then later an IP address from outside the subnet's allocation pool can be assigned (e.g., via DHCP or manual assignment). This can cause IP conflicts with existing guests or routers. No authentication bypass beyond tenant privileges is required. [1]

Impact

Successful exploitation leads to a denial of service (DoS) condition. The IP conflict can disrupt network connectivity for existing guests or routers, potentially causing service unavailability. The impact is limited to network layer disruption; no data confidentiality or integrity compromise is reported. [1][2]

Mitigation

Red Hat released updates: RHSA-2018:2710 for OpenStack 13 (Queens) with openstack-neutron-12.0.3-5.el7ost, RHSA-2018:3792 for OpenStack 12 (Pike) with openstack-neutron-11.0.4-6.el7ost, and RHSA-2018:2715 for OpenStack 10 (Newton) with openstack-neutron-10.0.5-5.el7ost. [2][3][4] Upstream fixed versions are 13.0.0.0b2, 12.0.3, and 11.0.5. [1] No workaround is documented; upgrading to the fixed version is recommended.

AI Insight generated on May 22, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
neutronPyPI
>= 13.0.0.0b1, < 13.0.0.0b213.0.0.0b2
neutronPyPI
< 11.0.611.0.6
neutronPyPI
>= 12.0.0, < 12.0.412.0.4

Affected products

2
  • ghsa-coords
    Range: >= 13.0.0.0b1, < 13.0.0.0b2
  • The Openstack Project/openstack-neutronv5
    Range: 13.0.0.0b2, 12.0.3, 11.0.5

Patches

1
54aa6e81cb17

Disallow router interface out of subnet IP range

https://github.com/openstack/neutronMiguel LavalleJun 14, 2018via ghsa
2 files changed · +82 0
  • neutron/db/l3_db.py+26 0 modified
    @@ -692,6 +692,27 @@ def _check_router_port(self, context, port_id, device_id):
                 raise n_exc.BadRequest(resource='router', msg=msg)
             return port
     
    +    def _validate_port_in_range_or_admin(self, context, subnets, port):
    +        if context.is_admin:
    +            return
    +        subnets_by_id = {}
    +        for s in subnets:
    +            addr_set = netaddr.IPSet()
    +            for range in s['allocation_pools']:
    +                addr_set.add(netaddr.IPRange(netaddr.IPAddress(range['start']),
    +                                             netaddr.IPAddress(range['end'])))
    +            subnets_by_id[s['id']] = (addr_set, s['project_id'],)
    +        for subnet_id, ip in [(fix_ip['subnet_id'], fix_ip['ip_address'],)
    +                              for fix_ip in port['fixed_ips']]:
    +            if (ip not in subnets_by_id[subnet_id][0] and
    +                    context.project_id != subnets_by_id[subnet_id][1]):
    +                msg = (_('Cannot add interface to router because specified '
    +                         'port %(port)s has an IP address out of the '
    +                         'allocation pool of subnet %(subnet)s, which is not '
    +                         'owned by the project making the request') %
    +                       {'port': port['id'], 'subnet': subnet_id})
    +                raise n_exc.BadRequest(resource='router', msg=msg)
    +
         def _validate_router_port_info(self, context, router, port_id):
             with db_api.autonested_transaction(context.session):
                 # check again within transaction to mitigate race
    @@ -727,6 +748,7 @@ def _validate_router_port_info(self, context, router, port_id):
                     msg = _("Cannot have multiple "
                             "IPv4 subnets on router port")
                     raise n_exc.BadRequest(resource='router', msg=msg)
    +            self._validate_port_in_range_or_admin(context, subnets, port)
                 return port, subnets
     
         def _notify_attaching_interface(self, context, router_db, port,
    @@ -788,6 +810,10 @@ def _add_interface_by_subnet(self, context, router, subnet_id, owner):
             if not subnet['gateway_ip']:
                 msg = _('Subnet for router interface must have a gateway IP')
                 raise n_exc.BadRequest(resource='router', msg=msg)
    +        if subnet['project_id'] != context.project_id and not context.is_admin:
    +            msg = (_('Cannot add interface to router because subnet %s is not '
    +                     'owned by project making the request') % subnet_id)
    +            raise n_exc.BadRequest(resource='router', msg=msg)
             if (subnet['ip_version'] == 6 and subnet['ipv6_ra_mode'] is None and
                     subnet['ipv6_address_mode'] is not None):
                 msg = (_('IPv6 subnet %s configured to receive RAs from an '
    
  • neutron/tests/unit/extensions/test_l3.py+56 0 modified
    @@ -1265,6 +1265,62 @@ def test_router_add_interface_subnet_with_bad_tenant_returns_404(self):
                                                       expected_code=err_code,
                                                       tenant_id='bad_tenant')
     
    +    def test_router_add_interface_by_subnet_other_tenant_subnet_returns_400(
    +            self):
    +        router_tenant_id = _uuid()
    +        with self.router(tenant_id=router_tenant_id, set_context=True) as r:
    +            with self.network(shared=True) as n:
    +                with self.subnet(network=n) as s:
    +                    err_code = exc.HTTPBadRequest.code
    +                    self._router_interface_action('add',
    +                                                  r['router']['id'],
    +                                                  s['subnet']['id'],
    +                                                  None,
    +                                                  expected_code=err_code,
    +                                                  tenant_id=router_tenant_id)
    +
    +    def _test_router_add_interface_by_port_allocation_pool(
    +            self, out_of_pool=False, router_action_as_admin=False,
    +            expected_code=exc.HTTPOk.code):
    +        router_tenant_id = _uuid()
    +        with self.router(tenant_id=router_tenant_id, set_context=True) as r:
    +            with self.network(shared=True) as n:
    +                with self.subnet(network=n) as s1, (
    +                     self.subnet(network=n, cidr='fd00::/64',
    +                                 ip_version=6)) as s2, (
    +                     self.subnet(network=n, cidr='fd01::/64',
    +                                 ip_version=6)) as s3:
    +                    fixed_ips = [{'subnet_id': s1['subnet']['id']},
    +                                 {'subnet_id': s2['subnet']['id']},
    +                                 {'subnet_id': s3['subnet']['id']}]
    +                    if out_of_pool:
    +                        fixed_ips[1] = {'subnet_id': s2['subnet']['id'],
    +                                        'ip_address':
    +                                            s2['subnet']['gateway_ip']}
    +                    with self.port(subnet=s1, fixed_ips=fixed_ips,
    +                                   tenant_id=router_tenant_id,
    +                                   set_context=True) as p:
    +                        kwargs = {'expected_code': expected_code}
    +                        if not router_action_as_admin:
    +                            kwargs['tenant_id'] = router_tenant_id
    +                        self._router_interface_action(
    +                            'add', r['router']['id'], None, p['port']['id'],
    +                            **kwargs)
    +
    +    def test_router_add_interface_by_port_other_tenant_address_in_pool(
    +            self):
    +        self._test_router_add_interface_by_port_allocation_pool()
    +
    +    def test_router_add_interface_by_port_other_tenant_address_out_of_pool(
    +            self):
    +        self._test_router_add_interface_by_port_allocation_pool(
    +            out_of_pool=True, expected_code=exc.HTTPBadRequest.code)
    +
    +    def test_router_add_interface_by_port_admin_address_out_of_pool(
    +            self):
    +        self._test_router_add_interface_by_port_allocation_pool(
    +            out_of_pool=True, router_action_as_admin=True)
    +
         def test_router_add_interface_subnet_with_port_from_other_tenant(self):
             tenant_id = _uuid()
             other_tenant_id = _uuid()
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

11

News mentions

0

No linked articles in our index yet.