Bug 509845 (CVE-2009-1892) - CVE-2009-1892 dhcp: DoS/abort in some configs with client-identifier and hardware address host specifications
Summary: CVE-2009-1892 dhcp: DoS/abort in some configs with client-identifier and hard...
Alias: CVE-2009-1892
Product: Security Response
Classification: Other
Component: vulnerability
Version: unspecified
Hardware: All
OS: Linux
Target Milestone: ---
Assignee: Red Hat Product Security
QA Contact:
Depends On: 511834
TreeView+ depends on / blocked
Reported: 2009-07-06 14:19 UTC by Tomas Hoger
Modified: 2019-09-29 12:30 UTC (History)
3 users (show)

Fixed In Version:
Doc Type: Bug Fix
Doc Text:
Clone Of:
Last Closed: 2011-10-12 13:46:24 UTC

Attachments (Terms of Use)

Description Tomas Hoger 2009-07-06 14:19:14 UTC
Christoph Biedl reported:

  isc-dchpd aborts when receiving a DHCP request with a "dhcp-client-
  identifier" in certain configurations.  This requires mixing host
  definitions using "dhcp-client-identifier" and "hardware ethernet",
  for a host that is not reachable via the interface the request is
  received from.

Christoph confirmed the problem in versions 3.1.1, 3.1.2, 4.0.1, 4.0.2b1, and
4.1.1b1, and says 3.0.7 is not affected.

Comment 1 Tomas Hoger 2009-07-06 14:20:15 UTC
Full report from Christoph:

isc-dchpd aborts when receiving a DHCP request with a "dhcp-client-
identifier" in certain configurations.  This requires mixing host
definitions using "dhcp-client-identifier" and "hardware ethernet",
for a host that is not reachable via the interface the request is
received from.

Versions affected
3.1.1 (as in Debian lenny = stable)

Versions not affected

How to repeat
Configure dhcpd to listen on (at least) two interfaces.  While this is
not essential (see below), it reflects a real-world scenario.

For the sake of this examples the interfaces are vlan225 and vlan226,
configured as and respectively.

Use the following server configuration that has two host definitions
in vlan226's network:

ddns-update-style none;

default-lease-time 60;
max-lease-time 720;

allow bootp;


log-facility local7;

subnet netmask {
    range dynamic-bootp;

subnet netmask {
    range dynamic-bootp;

host linux {
    option              dhcp-client-identifier "linux";

host ether {
    hardware ethernet   00:10:11:12:13:14;

Start dhcpd, e.g. "/usr/sbin/dhcpd -q vlan225 vlan226"

Configure a client to request an IP address using that identifier,
i.e. add 'send dhcp-client-identifier "linux";' to the dhclient.conf
if using dhcp3-client.  Request an IP via vlan225, the "host linux"
definition will _not_ match since it's in the wrong network.

Expected behavior:
dhcpd issues an address from the pool like as the host
definition is not applicable:

| dhcpd: DHCPDISCOVER from 00:00:0b:ad:be:ef via vlan225
| dhcpd: DHCPOFFER on to 00:00:0b:ad:be:ef via vlan225
| dhcpd: DHCPREQUEST for ( from 00:00:0b:ad:be:ef via vlan225
| dhcpd: DHCPACK on to 00:00:0b:ad:be:ef via vlan225

Observed behavior:
dhcpd terminates, de-facto assertion failure:

| dhcpd: Internal inconsistency: storage value has not been initialized to zero (from dhcp.c:1746).

An attacker can kill the dhcp server using a regular DHCP packet,
causing a denial of service attack.

The problem is in the lookup code that cannot deal with a "dhcp-
client-identifier" host statement in an unreachable network iff both
"dhcp-client-identifier" and "hardware ethernet" host definitions are
given.  The server still terminates if the "subnet"
statement in the example above is missing, and if dhcpd listens on
vlan225 only.  Such a configuration doesn't make sense but may happen
due to a typo.

* Avoid mixing "dhcp-client-identifier" and "hardware ethernet" host
  definitions if possible.
* Separate dhcpd instances for each interface, beware of pidfiles and
  similar (not tried).

The following patch (against 3.1.1, similar for the other versions)
seems to fix that problem but may have unknown side effects.

diff --git a/server/dhcp.c b/server/dhcp.c
index 8b97312..ce95c3b 100644
--- a/server/dhcp.c
+++ b/server/dhcp.c
@@ -1745,6 +1745,8 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
 			if (h)
 				host_reference (&host, h, MDL);
+			else
+				hp = (struct host_decl *)0;
 		if (!host) {
 			find_hosts_by_haddr (&hp,

Comment 2 Tomas Hoger 2009-07-06 14:35:08 UTC
This should not affect dhcp 3.0.x and older, as the abort in this case is triggered by the '*vp != NULL' check, which was added to hash_lookup() in omapip/hash.c, probably some time during the 3.1.x development.  Versions of dhcp with the check is currently only available in Fedora.

This check "missing" in previous versions should mean no harm in this case.  In ack_lease() in server/dhcp.c, hp is first initialized to 0/NULL.

1750         struct host_decl *hp = (struct host_decl *)0;

Then dhcp-client-identifier lookup is performed, that may set hp to non-0.

1765             find_hosts_by_uid (&hp, d1.data, d1.len, MDL);

Returned hp is checked as

1767             for (h = hp; h; h = h -> n_ipaddr) {
1768                 if (!h -> fixed_addr)
1769                     break;
1770             }

and h is set as host if it is non-NULL after this loop

1771             if (h)
1772                 host_reference (&host, h, MDL);

However, is h (and hence host) is 0 / NULL, hardware address search is done

1774         if (!host) {
1775             find_hosts_by_haddr (&hp,

which results in call to hash_lookup(), where abort occurs in recent dhcpd versions.

In old versions, without the check, hp value is either overwritten with new result (if any definition for the hw address is found), or the hp is not changed by find_hosts_by_haddr and hence following for loop will leave h set to 0 (it's identical to the one above)

1780             for (h = hp; h; h = h -> n_ipaddr) {
1781                 if (!h -> fixed_addr)
1782                     break;
1783             }

Comment 10 Tomas Hoger 2009-07-10 06:17:45 UTC
Updated proposed patch based on my discussion with Christoph:

--- a/server/dhcp.c
+++ b/server/dhcp.c
@@ -1747,6 +1747,8 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
                                host_reference (&host, h, MDL);
                if (!host) {
+                       if (hp)
+                               host_dereference (&hp, MDL);
                        find_hosts_by_haddr (&hp,
                                             packet -> raw -> htype,
                                             packet -> raw -> chaddr,

Comment 11 Tomas Hoger 2009-07-15 10:05:40 UTC
Public now via DSA-1833:

Comment 13 Vincent Danen 2009-07-17 17:22:00 UTC
Red Hat Enterprise Linux 5 and earlier provide ISC DHCP <= 3.0.5, which are not vulnerable to this issue.

Comment 14 Jan Lieskovsky 2009-07-18 09:52:55 UTC
MITRE's CVE-2009-1892 entry:

dhcpd in ISC DHCP 3.0.4 and 3.1.1, when the dhcp-client-identifier and
hardware ethernet configuration settings are both used, allows remote
attackers to cause a denial of service (daemon crash) via unspecified


Comment 15 Tomas Hoger 2009-07-20 06:51:33 UTC
(In reply to comment #14)
> dhcpd in ISC DHCP 3.0.4 and 3.1.1

This part is incorrect, this flaw does not affect any 3.0.x version.  Check triggering this abort is not present in 3.0.x code base (it is not in the latest 3.0.x versions - 3.0.7 - and is in the first 3.1.x version available for download from isc.org - 3.1.0a1).

Further discussion is in comment #2.  Additionally, if hp is set during the call to find_hosts_by_uid(), but it is not used (no match in the mentioned for loop), following call to find_hosts_by_haddr() will never change it.

find_hosts_by_haddr() intenally calls hash_lookup().  There are 2 possibilities there - either matching host declaration is found in the hash table or not.

Not found case is trivial and boring, hp value is unchanged, hash_lookup() and find_hosts_by_haddr() returns 0, but this return value is ignored.  Subsequent for loop iterates through old hp, so it can't find anything new and hp is correctly dereferenced later.

If hash_lookup() finds matching entry in the hash table, it checks whether hash has referencer function defined.  host_hw_addr_hash table uses host_reference() referencer, which calls omapi_object_reference(), which does:

        if (*r) {
#if defined (POINTER_DEBUG)
                log_error ("%s(%d): reference store into non-null pointer!",
                           file, line);
                abort ();
                return ISC_R_INVALIDARG; 

Hence r (i.e. hp) is not overwritten when it's non-NULL, and refcount is not incremented.  hash_lookup() does not bother checking return value of the referencer function, but that does not seem to be a big deal here.  So after return from find_hosts_by_haddr(), hp is unchanged and the rest is identical to the not found case.

Note You need to log in before you can comment on or make changes to this bug.