[prev in list] [next in list] [prev in thread] [next in thread] 

List:       oss-security
Subject:    [oss-security] CVE-2017-9445: Out-of-bounds write in systemd-resolved with crafted TCP payload
From:       Chris Coulson <chris.coulson () canonical ! com>
Date:       2017-06-27 17:58:29
Message-ID: 03ffaee7-2345-db4b-b16c-b859ed5637cd () canonical ! com
[Download RAW message or body]

[Attachment #2 (multipart/mixed)]

[Attachment #4 (multipart/mixed)]


Hi,

I recently discovered an out-of-bounds write in systemd-resolved in
Ubuntu, which is possible to trigger with a specially crafted TCP payload=
=2E

Details from the Ubuntu bug follow:
https://launchpad.net/bugs/1695546

----
Certain sizes passed to dns_packet_new can cause it to allocate a buffer
that's too small. A page-aligned number - sizeof(DnsPacket) +
sizeof(iphdr) + sizeof(udphdr) will do this - so, on x86 this will be a
page-aligned number - 80. Eg, calling dns_packet_new with a size of 4016
on x86 will result in an allocation of 4096 bytes, but 108 bytes of this
are for the DnsPacket struct.

A malicious DNS server can exploit this by responding with a specially
crafted TCP payload to trick systemd-resolved in to allocating a buffer
that's too small, and subsequently write arbitrary data beyond the end
of it.

I believe this was introduced by
https://github.com/systemd/systemd/commit/a0166609f782da91710dea9183d1bf1=
38538db37
(v223) and affects all subsequent versions up to and including v233.
----

A patch to resolve this has been provided by Zbigniew
J=C4=99drzejewski-Szmek, along with an additional patch to implement a te=
st.
Both of these are attached.

Many thanks,
Chris

["0001-test-resolved-packet-add-a-simple-test-for-our-alloc.patch" (text/x-patch)]

From c67ed7b00f62b3ea6f9476b491fd5db590d04cf4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
Date: Sun, 18 Jun 2017 15:53:15 -0400
Subject: [PATCH 1/2] test-resolved-packet: add a simple test for our
 allocation functions

---
 .gitignore                         |  1 +
 Makefile.am                        | 14 ++++++++++++
 src/resolve/meson.build            |  9 ++++++++
 src/resolve/test-resolved-packet.c | 45 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 69 insertions(+)
 create mode 100644 src/resolve/test-resolved-packet.c

diff --git a/.gitignore b/.gitignore
index 60eda2b8ce..bc47db6481 100644
--- a/.gitignore
+++ b/.gitignore
@@ -271,6 +271,7 @@
 /test-replace-var
 /test-resolve
 /test-resolve-tables
+/test-resolved-packet
 /test-ring
 /test-rlimit-util
 /test-sched-prio
diff --git a/Makefile.am b/Makefile.am
index 3b9ed874e5..59899c65cc 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -5748,6 +5748,7 @@ dist_zshcompletion_data += \
 tests += \
 	test-dns-packet \
 	test-resolve-tables \
+	test-resolved-packet \
 	test-dnssec
 
 manual_tests += \
@@ -5769,6 +5770,19 @@ test_resolve_tables_LDADD = \
 	$(GCRYPT_LIBS) \
 	-lm
 
+test_resolved_packet_SOURCES = \
+	src/resolve/test-resolved-packet.c \
+	$(basic_dns_sources)
+
+test_resolved_packet_CFLAGS = \
+	$(AM_CFLAGS) \
+	$(GCRYPT_CFLAGS)
+
+test_resolved_packet_LDADD = \
+	libsystemd-shared.la \
+	$(GCRYPT_LIBS) \
+	-lm
+
 test_dns_packet_SOURCES = \
 	src/resolve/test-dns-packet.c \
 	$(basic_dns_sources)
diff --git a/src/resolve/meson.build b/src/resolve/meson.build
index f3c411ffee..fe228784fa 100644
--- a/src/resolve/meson.build
+++ b/src/resolve/meson.build
@@ -160,6 +160,15 @@ tests += [
           libm],
          'ENABLE_RESOLVED'],
 
+        [['src/resolve/test-resolved-packet.c',
+          basic_dns_sources,
+          dns_type_headers],
+         [],
+         [libgcrypt,
+          libgpg_error,
+          libm],
+         'ENABLE_RESOLVED'],
+
         [['src/resolve/test-dnssec.c',
           basic_dns_sources,
           dns_type_headers],
diff --git a/src/resolve/test-resolved-packet.c b/src/resolve/test-resolved-packet.c
new file mode 100644
index 0000000000..8b7da1408d
--- /dev/null
+++ b/src/resolve/test-resolved-packet.c
@@ -0,0 +1,45 @@
+/***
+  This file is part of systemd
+
+  Copyright 2017 Zbigniew Jędrzejewski-Szmek
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "log.h"
+#include "resolved-dns-packet.h"
+
+static void test_dns_packet_new(void) {
+        size_t i;
+
+        for (i = 0; i < DNS_PACKET_SIZE_MAX + 2; i++) {
+                _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
+
+                assert_se(dns_packet_new(&p, DNS_PROTOCOL_DNS, i) == 0);
+
+                log_debug("dns_packet_new: %zu → %zu", i, p->allocated);
+                assert_se(p->allocated >= MIN(DNS_PACKET_SIZE_MAX, i));
+        }
+}
+
+int main(int argc, char **argv) {
+
+        log_set_max_level(LOG_DEBUG);
+        log_parse_environment();
+        log_open();
+
+        test_dns_packet_new();
+
+        return 0;
+}
-- 
2.13.0



["0002-resolved-simplify-alloc-size-calculation.patch" (text/x-patch)]

From 8587c3351003b1613ad2e439cebbb20fbae07e70 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
Date: Sun, 18 Jun 2017 16:07:57 -0400
Subject: [PATCH 2/2] resolved: simplify alloc size calculation

The allocation size was calculated in a complicated way, and for values
close to the page size we would actually allocate less than requested.

Reported by Chris Coulson <chris.coulson@canonical.com>.
---
 src/resolve/resolved-dns-packet.c | 8 +-------
 src/resolve/resolved-dns-packet.h | 2 --
 2 files changed, 1 insertion(+), 9 deletions(-)

diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c
index 240ee448f4..821b66e266 100644
--- a/src/resolve/resolved-dns-packet.c
+++ b/src/resolve/resolved-dns-packet.c
@@ -47,13 +47,7 @@ int dns_packet_new(DnsPacket **ret, DnsProtocol protocol, size_t mtu) {
 
         assert(ret);
 
-        if (mtu <= UDP_PACKET_HEADER_SIZE)
-                a = DNS_PACKET_SIZE_START;
-        else
-                a = mtu - UDP_PACKET_HEADER_SIZE;
-
-        if (a < DNS_PACKET_HEADER_SIZE)
-                a = DNS_PACKET_HEADER_SIZE;
+        a = MAX(mtu, DNS_PACKET_HEADER_SIZE);
 
         /* round up to next page size */
         a = PAGE_ALIGN(ALIGN(sizeof(DnsPacket)) + a) - ALIGN(sizeof(DnsPacket));
diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h
index 2c92392e4d..3abcaf8cf3 100644
--- a/src/resolve/resolved-dns-packet.h
+++ b/src/resolve/resolved-dns-packet.h
@@ -66,8 +66,6 @@ struct DnsPacketHeader {
 /* With EDNS0 we can use larger packets, default to 4096, which is what is commonly used */
 #define DNS_PACKET_UNICAST_SIZE_LARGE_MAX 4096
 
-#define DNS_PACKET_SIZE_START 512
-
 struct DnsPacket {
         int n_ref;
         DnsProtocol protocol;
-- 
2.13.0



["signature.asc" (application/pgp-signature)]

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic