oss-sec mailing list archives

Bugs in iscsiuio


From: Qualys Security Advisory <qsa () qualys com>
Date: Wed, 13 Dec 2017 11:21:55 -0800

Hi all,

We took a quick look at iscsiuio (from the iscsi-initiator-utils),
discovered several bugs, and sent a brief report of our findings to
linux-distros (on Monday, December 11). It was then decided that we
should send this report to oss-security on Wednesday, December 13:
please see below.

Notes: we did not have the time to draft a proper advisory, so this is
rather raw material, but hopefully it will be useful and detailed
enough; also, we did not fully investigate the exploitability (or
unexploitability) of these issues; and this was by no means an
exhaustive audit, so there may be more bugs to be found.

Thank you very much! With best regards,

-- the Qualys Security Advisory team

========================================================================

Source: iscsi-initiator-utils-6.2.0.874-6.git86e8892.fc27.src.rpm

------------------------------------------------------------------------

In iscsid_loop(), new connections are accepted on the abstract socket
@ISCSID_UIP_ABSTRACT_NAMESPACE, but the source of the connection is not
checked: any local user can connect and send messages, thus triggering
the bugs below; this may or may not be the intended behavior. (iscsid
for example processes only root's messages, in mgmt_ipc_handle()).

------------------------------------------------------------------------

In process_iscsid_broadcast(), payload_len is read directly from the
user's message, without checks, and then used in an fread() to a fixed-
size buffer: a straight heap overflow (arbitrary size, arbitrary bytes).
This would probably be exploitable, but every distribution we checked
compiles iscsiuio with FORTIFY, and __fread_chk() catches the heap
overflow before it happens, and aborts:

$ echo -e '\1\0\0\0\x41\x41\x41\x41' | socat - ABSTRACT-CONNECT:ISCSID_UIP_ABSTRACT_NAMESPACE

Process 32442 (iscsiuio) of user 0 dumped core.

Stack trace of thread 32445:
#0  0x00007f15redacted raise (libc.so.6)
#1  0x00007f15redacted abort (libc.so.6)
#2  0x00007f15redacted __libc_message (libc.so.6)
#3  0x00007f15redacted __fortify_fail (libc.so.6)
#4  0x00007f15redacted __chk_fail (libc.so.6)
#5  0x00007f15redacted __fread_chk (libc.so.6)
#6  0x0000555dredacted process_iscsid_broadcast (iscsiuio)
#7  0x0000555dredacted iscsid_loop (iscsiuio)
#8  0x00007f15redacted start_thread (libpthread.so.0)
#9  0x00007f15redacted __clone (libc.so.6)

Stack trace of thread 32443:
#0  0x00007f15redacted sigwait (libpthread.so.0)
#1  0x0000555dredacted signal_handle_thread (iscsiuio)
#2  0x00007f15redacted start_thread (libpthread.so.0)
#3  0x00007f15redacted __clone (libc.so.6)

Stack trace of thread 32442:
#0  0x00007f15redacted socket (libc.so.6)
#1  0x0000555dredacted nic_nl_open (iscsiuio)
#2  0x0000555dredacted main (iscsiuio)
#3  0x00007f15redacted __libc_start_main (libc.so.6)
#4  0x0000555dredacted _start (iscsiuio)

------------------------------------------------------------------------

In process_iscsid_broadcast(), rsp is not initialized, and the
ISCSID_UIP_IPC_GET_IFACE and default cases do not initialize its ping_sc
member: because rsp is then sent back to the connected user, this may
leak useful information to an attacker.

------------------------------------------------------------------------

At the end of process_iscsid_broadcast() there is an fclose(fd) where fd
is fdopen(s2); but after the return, iscsid_loop() calls close(s2): this
double-closes s2, which may not always be a problem, but here, iscsiuio
is multi-threaded and it is theoretically possible that another thread
recycles the file descriptor s2 after the fclose(fd) but before the
close(s2), which would therefore close the wrong file.

Note: at the beginning of process_iscsid_broadcast() there is a return
that does not close s2; care must be taken with the double-close patch,
or it may leak a file descriptor instead.

------------------------------------------------------------------------

At the beginning of decode_cidr(), where in_ipaddr_str is from the
message sent by the user/attacker:

        char                    ipaddress[NI_MAXHOST];

the following code has two problems:

        char ipaddr_str[NI_MAXHOST];
        ...
        if (strlen(in_ipaddr_str) > NI_MAXHOST)
                strncpy(ipaddr_str, in_ipaddr_str, NI_MAXHOST);
        else
                strcpy(ipaddr_str, in_ipaddr_str);

1/ strlen(in_ipaddr_str) > NI_MAXHOST is possible, because the user's
ipaddress is not necessarily null-terminated, but in that case strncpy()
does not null-terminate ipaddr_str;

Note: other "strings" in the message sent by the user/attacker also
suffer from this (non) null-termination problem.

2/ if strlen(in_ipaddr_str) == NI_MAXHOST, the strcpy() triggers an
off-by-one overflow.

Note: this strncpy()/strcpy() pattern of bugs is repeated many times
throughout the codebase.

------------------------------------------------------------------------

In decode_cidr(), the strcpy(ipaddr_str, tok) is invalid, because tok
points to ipaddr_str, and "strings may not overlap" (man strcpy).

------------------------------------------------------------------------

In decode_cidr(), int keepbits = atoi(tmp) may be negative (it comes
from the message sent by the user/attacker), which may bypass checks and
have other consequences in other parts of the code.

------------------------------------------------------------------------

In perform_ping(), int datalen comes from the message sent by the
user/attacker, but it is not checked at all: it looks like this could
lead to a heap overflow in fill_payload_data() (through
do_ping_from_nic_iface() -> prepare_icmpv6_req_pkt()).

(It seems that packet buffers are malloc()ated by alloc_packet(), called
only with alloc_packet(1500, 1500), and uip_slen in fill_payload_data()
is an u16_t, originally from the attacker-controlled int datalen, so it
may very well overflow this 1500-byte buffer -- but we did not double-
check this conclusion, and the lack of datalen checks may have other
consequences as well, but we did not explore them fully.)

========================================================================


Current thread: