Openwall GNU/*/Linux - a small security-enhanced Linux distro for servers
[<prev] [next>] [day] [month] [year] [list]
Date: Tue, 27 Jun 2017 18:58:29 +0100
From: Chris Coulson <chris.coulson@...onical.com>
To: oss-security@...ts.openwall.com
Subject: CVE-2017-9445: Out-of-bounds write in systemd-resolved with crafted
 TCP payload

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.

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/a0166609f782da91710dea9183d1bf138538db37
(v223) and affects all subsequent versions up to and including v233.
----

A patch to resolve this has been provided by Zbigniew
Jędrzejewski-Szmek, along with an additional patch to implement a test.
Both of these are attached.

Many thanks,
Chris

From c67ed7b00f62b3ea6f9476b491fd5db590d04cf4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@...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



From 8587c3351003b1613ad2e439cebbb20fbae07e70 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@...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@...onical.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




[ CONTENT OF TYPE application/pgp-signature SKIPPED ]

Powered by blists - more mailing lists

Your e-mail address:

Please check out the Open Source Software Security Wiki, which is counterpart to this mailing list.

Powered by Openwall GNU/*/Linux - Powered by OpenVZ