[Home]

Summary:ASTERISK-24469: Security Vulnerability: Mixed IPv4/IPv6 ACLs allow blocked addresses through
Reporter:Matt Jordan (mjordan)Labels:Security
Date Opened:2014-10-29 12:08:12Date Closed:2014-11-20 10:39:28.000-0600
Priority:CriticalRegression?No
Status:Closed/CompleteComponents:Core/Netsock
Versions:1.8.31.1 11.13.1 12.6.1 13.0.0-beta3 Frequency of
Occurrence
Related
Issues:
Environment:Attachments:( 0) ASTERISK-24469-11.diff
Description:From the issue reporter:

{quote}
Relevant excerpt from sip.conf of Asterisk 11.13.0 on Linux (dual stack
system with IPv4 and IPv6 SIP clients):

{noformat}
[general]

bindaddr=::

tcpenable=yes
tcpbindaddr=::

tlsenable=yes
tlsbindaddr=::
{noformat}

And now there are two possible scenarios (no contact* statement except
for the [general] section in the whole sip configuration, only the two
contactdeny statements as shown below in the [general] section).

1. Only IPv6 addresses are denied, IPv4 addresses can register as if
there was no ACL:

{noformat}
contactdeny=::/0
contactdeny=0.0.0.0/0.0.0.0
{noformat}

2. Only IPv4 addresses are denied, IPv6 addresses can register as if
there was no ACL:

{noformat}
contactdeny=0.0.0.0/0.0.0.0
contactdeny=::/0
{noformat}

As the above statements do call into the common ACL code, probably every
statement using the ACL code and referring to IPs or networks of
different address families (e.g. deny) is probably affected, too.

Note: Writing the above as one line, e.g.
contactdeny=::/0,0.0.0.0/0.0.0.0 results in the same behavior, only the
first stated address family is ever matched.

Note: The networks given in the above examples are not relevant, the
behavior solely depends on which address family is stated first.
{quote}

The issue at hand appears to be a bug in {{ast_apply_ha}}. We have a list of addresses that the incoming address is compared against; however, the initial comparison that determines the address to use in the comparison (which does the IPv4/IPv6 family checks) only looks at the first address in the list:

{code}
for (current_ha = ha; current_ha; current_ha = current_ha->next) {
struct ast_sockaddr result;
struct ast_sockaddr mapped_addr;
const struct ast_sockaddr *addr_to_use;
#if 0 /* debugging code */
char iabuf[INET_ADDRSTRLEN];
char iabuf2[INET_ADDRSTRLEN];
/* DEBUG */
ast_copy_string(iabuf, ast_inet_ntoa(sin->sin_addr), sizeof(iabuf));
ast_copy_string(iabuf2, ast_inet_ntoa(ha->netaddr), sizeof(iabuf2));
ast_debug(1, "##### Testing %s with %s\n", iabuf, iabuf2);
#endif

               // This should be &current_ha->addr
if (ast_sockaddr_is_ipv4(&ha->addr)) {
if (ast_sockaddr_is_ipv6(addr)) {
if (ast_sockaddr_is_ipv4_mapped(addr)) {
/* IPv4 ACLs apply to IPv4-mapped addresses */
if (!ast_sockaddr_ipv4_mapped(addr, &mapped_addr)) {
ast_log(LOG_ERROR, "%s provided to ast_sockaddr_ipv4_mapped could not be converted. That shouldn't be possible.\n",
ast_sockaddr_stringify(addr));
continue;
}
addr_to_use = &mapped_addr;
} else {
/* An IPv4 ACL does not apply to an IPv6 address */
continue;
}
} else {
/* Address is IPv4 and ACL is IPv4. No biggie */
addr_to_use = addr;
}
} else {
if (ast_sockaddr_is_ipv6(addr) && !ast_sockaddr_is_ipv4_mapped(addr)) {
addr_to_use = addr;
} else {
/* Address is IPv4 or IPv4 mapped but ACL is IPv6. Skip */
continue;
}
}
{code}

Because we can mix up whether or not the address to use is an IPv4/IPv6 address, applying the netmask fails, leading to the address being accepted.
Comments: