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

List:       oss-security
Subject:    [oss-security] Knot Resolver 4.1.0 security release
From:       Vladimír Čunát <vladimir.cunat () nic ! cz>
Date:       2019-07-14 7:27:13
Message-ID: 3464b63f-21cb-c894-a832-63e1a8d07f88 () nic ! cz
[Download RAW message or body]

[Attachment #2 (multipart/mixed)]

[Attachment #4 (multipart/mixed)]


Hello.

This Wednesday there was a Knot Resolver release and embargo lift for
two CVEs, both allowing the server to incorrectly accept DNS records:
CVE-2019-10190 and CVE-2019-10191; more details at the end of this e-mail.

We apologize for forgetting our responsibility to also post to
oss-security on that day.   Thanks to Salvatore Bonaccorso for notifying us.

Minimal patches are attached, but we generally do not recommend
backporting them.   Announcement:
https://lists.nic.cz/pipermail/knot-resolver-users/2019/000189.html

--Vladimir (upstream dev, discovered and fixed)

#### CVE-2019-10190

Impact
======
Under certain circumstances, improper input validation bug in DNS
resolver component of Knot Resolver allows remote attacker to bypass
DNSSEC validation for non-existence answer.

An NXDOMAIN answer would get passed through to the client even if its
DNSSEC validation failed, instead of sending a SERVFAIL packet.
Caching is not affected by this particular bug but see the other CVE.


[Affected version (required)]:
3.2.0 <= Knot Resolver <= 4.0.0

[Vulnerability type (required)]:
CWE-20: Improper Input Validation

[Affected component (required)]:
resolver

[Impact of exploitation (required)]:
Under certain circumstances this bug allows an attacker to hijack
DNS domains.

[Description of vulnerability]:
Under certain circumstances, improper input validation bug in DNS
resolver component of Knot Resolver allows remote attacker to bypass
DNSSEC validation for non-existence answer.

An NXDOMAIN answer would get passed through to the client even if its
DNSSEC validation failed, instead of sending a SERVFAIL packet.
Caching is not affected by this particular bug but see the other CVE.

Attack Vector (AV): Network
Attack Complexity (AC): Low
Privileges Required (PR): None
User Interaction (UI): None
Scope (S): Moderate
Confidentiality (C): None
Integrity (I): Medium
Availability (A): None

Technical Details:
CWE-20



#### CVE-2019-10191

Impact
======
Under certain circumstances this bug allows an network attacker with
ability to spoof packets to downgrade a DNSSEC-secured domain to
DNSSEC-insecure state, thus opening possibilities for further attacks.


[Affected version (required)]:
Knot Resolver <= 4.0.0
(probably since 2.0.0, we did not check older versions thoroughly)

[Vulnerability type (required)]:
CWE-20: Improper Input Validation

[Affected component (required)]:
resolver

[Impact of exploitation (required)]:
Under certain circumstances this bug allows an attacker to downgrade
DNSSEC-secure domains to DNSSEC-insecure state, opening possibility of
domain hijack using attacks against insecure DNS protocol.

[Description of vulnerability]:
Improper input validation bug in DNS resolver component of Knot Resolver
allows remote attacker to poison cache by an unsigned negative answer.

Attack Vector (AV): Network
Attack Complexity (AC): Low
Privileges Required (PR): None
User Interaction (UI): None
Scope (S): All
Confidentiality (C): None
Integrity (I): High
Availability (A): None

Technical Details:
CWE-20


["CVE-2019-10190.patch" (text/x-patch)]

diff --git a/lib/resolve.c b/lib/resolve.c
index 59420238..c39eab41 100644
--- a/lib/resolve.c
+++ b/lib/resolve.c
@@ -555,15 +555,17 @@ static int answer_padding(struct kr_request *request)
 
 static int answer_fail(struct kr_request *request)
 {
+	/* Note: OPT in SERVFAIL response is still useful for cookies/additional info. */
 	knot_pkt_t *answer = request->answer;
+	knot_rrset_t *opt_rr = answer->opt_rr; /* it gets NULLed below */
 	int ret = kr_pkt_clear_payload(answer);
 	knot_wire_clear_ad(answer->wire);
 	knot_wire_clear_aa(answer->wire);
 	knot_wire_set_rcode(answer->wire, KNOT_RCODE_SERVFAIL);
-	if (ret == 0 && answer->opt_rr) {
-		/* OPT in SERVFAIL response is still useful for cookies/additional info. */
+	if (ret == 0 && opt_rr) {
 		knot_pkt_begin(answer, KNOT_ADDITIONAL);
 		answer_padding(request); /* Ignore failed padding in SERVFAIL answer. */
+		answer->opt_rr = opt_rr;
 		ret = edns_put(answer, false);
 	}
 	return ret;
@@ -1592,9 +1594,7 @@ int kr_resolve_finish(struct kr_request *request, int state)
 {
 	/* Finalize answer and construct wire-buffer. */
 	ITERATE_LAYERS(request, NULL, answer_finalize);
-	if (request->state & KR_STATE_FAIL) {
-		state = KR_STATE_FAIL;
-	} else if (answer_finalize(request, state) != 0) {
+	if (answer_finalize(request, state) != 0) {
 		state = KR_STATE_FAIL;
 	}
 

["CVE-2019-10191.patch" (text/x-patch)]

diff --git a/daemon/lua/kres-gen.lua b/daemon/lua/kres-gen.lua
index 1d867682..b0a0e9d8 100644
--- a/daemon/lua/kres-gen.lua
+++ b/daemon/lua/kres-gen.lua
@@ -124,6 +124,7 @@ struct kr_qflags {
 	_Bool DNS64_MARK : 1;
 	_Bool CACHE_TRIED : 1;
 	_Bool NO_NS_FOUND : 1;
+	_Bool PKT_IS_SANE : 1;
 };
 typedef struct {
 	knot_rrset_t **at;
diff --git a/lib/cache/api.c b/lib/cache/api.c
index 4142aa2b..dfcfb116 100644
--- a/lib/cache/api.c
+++ b/lib/cache/api.c
@@ -414,7 +414,7 @@ int cache_stash(kr_layer_t *ctx, knot_pkt_t *pkt)
 	/* LATER(optim.): typically we also have corresponding NS record in the list,
 	 * so we might save a cache operation. */
 
-	if (check_dname_for_lf(knot_pkt_qname(pkt), qry)) {
+	if (qry->flags.PKT_IS_SANE && check_dname_for_lf(knot_pkt_qname(pkt), qry)) {
 		stash_pkt(pkt, qry, req, needs_pkt);
 	}
 
diff --git a/lib/layer/iterate.c b/lib/layer/iterate.c
index ac3b218b..069b34f0 100644
--- a/lib/layer/iterate.c
+++ b/lib/layer/iterate.c
@@ -82,6 +82,8 @@ static bool is_paired_to_query(const knot_pkt_t *answer, struct kr_query *query)
 	uint16_t qtype = query->stype;
 	const knot_dname_t *qname = minimized_qname(query, &qtype);
 
+	/* ID should already match, thanks to session_tasklist_del_msgid()
+	 * in worker_submit(), but it won't hurt to check again. */
 	return query->id      == knot_wire_get_id(answer->wire) &&
 	       knot_wire_get_qdcount(answer->wire) == 1 &&
 	       query->sclass  == knot_pkt_qclass(answer) &&
@@ -1017,6 +1019,7 @@ static int resolve(kr_layer_t *ctx, knot_pkt_t *pkt)
 	if (!query) {
 		return ctx->state;
 	}
+	query->flags.PKT_IS_SANE = false;
 
 	WITH_VERBOSE(query) {
 		if (query->flags.TRACE) {
@@ -1060,6 +1063,10 @@ static int resolve(kr_layer_t *ctx, knot_pkt_t *pkt)
 		return KR_STATE_CONSUME;
 	}
 
+	/* If exiting above here, there's no sense to put it into packet cache.
+	 * The most important part is to check for spoofing: is_paired_to_query() */
+	query->flags.PKT_IS_SANE = true;
+
 #ifndef NOVERBOSELOG
 	const knot_lookup_t *rcode = knot_lookup_by_id(knot_rcode_names, knot_wire_get_rcode(pkt->wire));
 #endif
diff --git a/lib/rplan.h b/lib/rplan.h
index 6e93afc7..15ca5633 100644
--- a/lib/rplan.h
+++ b/lib/rplan.h
@@ -64,6 +64,8 @@ struct kr_qflags {
 	bool DNS64_MARK : 1;     /**< Internal mark for dns64 module. */
 	bool CACHE_TRIED : 1;    /**< Internal to cache module. */
 	bool NO_NS_FOUND : 1;    /**< No valid NS found during last PRODUCE stage. */
+	bool PKT_IS_SANE : 1;    /**< Set by iterator in consume phase to indicate whether
+				  * some basic aspects of the packet are OK, e.g. QNAME. */
 };
 
 /** Combine flags together.  This means set union for simple flags. */

["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