On Thu, Oct 25, 2018 at 11:35:18AM +0200, Lennart Poettering wrote:
> I guess that settles it from our side.
> 
> Thank you all for working on this!

Thanks everybody!

-- 
You received this bug notification because you are a member of Ubuntu
Touch seeded packages, which is subscribed to systemd in Ubuntu.
https://bugs.launchpad.net/bugs/1795921

Title:
  Out-of-Bounds write in systemd-networkd dhcpv6 option handling

Status in systemd package in Ubuntu:
  Confirmed

Bug description:
  systemd-networkd contains a DHCPv6 client which is written from
  scratch and can be spawned automatically on managed interfaces when
  IPv6 router advertisement are received:

  "Note that DHCPv6 will by default be triggered by Router Advertisement, if 
that is enabled, regardless of this parameter. By enabling DHCPv6 support 
explicitly, the DHCPv6 client will be started regardless of the presence of 
routers on the link, or what flags the routers pass" 
  (https://www.freedesktop.org/software/systemd/man/systemd.network.html)

  
  The function dhcp6_option_append_ia function is used to encode Identity 
Associations received by the server into the options buffer of an outgoing 
DHCPv6 packet:

  // 
https://github.com/systemd/systemd/blob/master/src/libsystemd-network/dhcp6-option.c#L82
  int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) {
          uint16_t len;
          uint8_t *ia_hdr;
          size_t iaid_offset, ia_buflen, ia_addrlen = 0;
          DHCP6Address *addr;
          int r;

          assert_return(buf && *buf && buflen && ia, -EINVAL);

          switch (ia->type) {
          case SD_DHCP6_OPTION_IA_NA:
                  len = DHCP6_OPTION_IA_NA_LEN;
                  iaid_offset = offsetof(DHCP6IA, ia_na);
                  break;

          case SD_DHCP6_OPTION_IA_TA:
                  len = DHCP6_OPTION_IA_TA_LEN;
                  iaid_offset = offsetof(DHCP6IA, ia_ta);
                  break;

          default:
                  return -EINVAL;
          }

  A:      if (*buflen < len)
                  return -ENOBUFS;

          ia_hdr = *buf;
          ia_buflen = *buflen;

          *buf += sizeof(DHCP6Option);
  B:      *buflen -= sizeof(DHCP6Option);

  C:       memcpy(*buf, (char*) ia + iaid_offset, len);

          *buf += len;
  D:      *buflen -= len;

  E:        LIST_FOREACH(addresses, addr, ia->addresses) {
                  r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IAADDR,
                                        sizeof(addr->iaaddr));
                  if (r < 0)
                          return r;

                  memcpy(*buf, &addr->iaaddr, sizeof(addr->iaaddr));

                  *buf += sizeof(addr->iaaddr);
                  *buflen -= sizeof(addr->iaaddr);

                  ia_addrlen += sizeof(DHCP6Option) + sizeof(addr->iaaddr);
          }

          r = option_append_hdr(&ia_hdr, &ia_buflen, ia->type, len + 
ia_addrlen);
          if (r < 0)
                  return r;

          return 0;
  }

  
  The function receives a pointer to the option buffer buf, it's remaining size 
buflen and the IA to be added to the buffer. While the check at (A) tries to 
ensure that the buffer has enough space left to store the IA option, it does 
not take the additional 4 bytes from the DHCP6Option header into account (B). 
Due to this the memcpy at (C) can go out-of-bound and *buflen can underflow in 
(D) giving an attacker a very powerful and largely controlled OOB heap write 
starting at (E).

  The overflow can be triggered relatively easy by advertising a DHCPv6
  server with a server-id >= 493 characters long. This will trigger the
  following code once the client tries to create a REQUEST message:

  
//https://github.com/systemd/systemd/blob/7bcf8123c0305131ace02480763377af974924ef/src/libsystemd-network/sd-dhcp6-client.c#L493
          case DHCP6_STATE_REQUEST:
          case DHCP6_STATE_RENEW:

                  if (client->state == DHCP6_STATE_REQUEST)
                          message->type = DHCP6_REQUEST;
                  else
                          message->type = DHCP6_RENEW;

  A:              r = dhcp6_option_append(&opt, &optlen, 
SD_DHCP6_OPTION_SERVERID,
                                          client->lease->serverid_len,
                                          client->lease->serverid);
                  if (r < 0)
                          return r;

                  if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA)) {
  B:                      r = dhcp6_option_append_ia(&opt, &optlen,
                                                     &client->lease->ia);
                          if (r < 0)
                                  return r;
                  }

  optlen starts with the value 512 and gets decreased to 15 (512 - 493 -
  4) after the call in A. As 15 > DHCP6_OPTION_IA_NA_LEN but 15 <
  (DHCP6_OPTION_IA_NA_LEN + 4) this triggers the memory corruption:

  # journalctl -u systemd-networkd
  Oct 03 14:27:50 ubuntu18 systemd-networkd[45095]: malloc(): memory corruption
  Oct 03 14:27:50 ubuntu18 systemd[1]: systemd-networkd.service: Main process 
exited, code=killed, status=6/ABRT
  Oct 03 14:27:50 ubuntu18 systemd[1]: systemd-networkd.service: Failed with 
result 'signal'.
  Oct 03 14:27:50 ubuntu18 systemd[1]: systemd-networkd.service: Service has no 
hold-off time, scheduling restart.
  Oct 03 14:27:50 ubuntu18 systemd[1]: systemd-networkd.service: Scheduled 
restart job, restart counter is at 225.
  Oct 03 14:27:50 ubuntu18 systemd[1]: Stopped Network Service.
  Oct 03 14:27:50 ubuntu18 systemd[1]: Starting Network Service...
  Oct 03 14:27:50 ubuntu18 systemd-networkd[45118]: ens38: Gained IPv6LL
  Oct 03 14:27:50 ubuntu18 systemd-networkd[45118]: ens33: Gained IPv6LL
  Oct 03 14:27:50 ubuntu18 systemd-networkd[45118]: Enumeration completed
  Oct 03 14:27:50 ubuntu18 systemd[1]: Started Network Service.
  Oct 03 14:27:50 ubuntu18 systemd-networkd[45118]: lo: Link is not managed by 
us
  Oct 03 14:27:50 ubuntu18 systemd-networkd[45118]: ens38: Configured
  Oct 03 14:27:53 ubuntu18 systemd-networkd[45118]: free(): corrupted unsorted 
chunks
  Oct 03 14:27:53 ubuntu18 systemd[1]: systemd-networkd.service: Main process 
exited, code=killed, status=6/ABRT
  Oct 03 14:27:53 ubuntu18 systemd[1]: systemd-networkd.service: Failed with 
result 'signal'.

  An interesting aspect of this bug is that systemd-networkd will
  restart automatically after a crash, which means that even a somewhat
  unstable exploit can work reliably.

  Testing was done on a Ubuntu 18.04 server installation running systemd
  237-3ubuntu10.3

  This bug is subject to a 90 day disclosure deadline. After 90 days elapse
  or a patch has been made broadly available (whichever is earlier), the bug
  report will become visible to the public.

  Please credit Felix Wilhelm from the Google Security Team in all
  releases, patches and advisories related to this issue.

  Let me know if you have any questions.

  Best,
  Felix

To manage notifications about this bug go to:
https://bugs.launchpad.net/ubuntu/+source/systemd/+bug/1795921/+subscriptions

-- 
Mailing list: https://launchpad.net/~touch-packages
Post to     : touch-packages@lists.launchpad.net
Unsubscribe : https://launchpad.net/~touch-packages
More help   : https://help.launchpad.net/ListHelp

Reply via email to