summarylogtreecommitdiffstats
path: root/0003-evdns-name_parse-fix-remote-stack-overread.patch
blob: 5a4a61598f78593471f195fb4c21f6001c6f5c18 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
From 96f64a022014a208105ead6c8a7066018449d86d Mon Sep 17 00:00:00 2001
From: Azat Khuzhin <a3at.mail@gmail.com>
Date: Mon, 1 Feb 2016 17:32:09 +0300
Subject: [PATCH] evdns: name_parse(): fix remote stack overread

@asn-the-goblin-slayer:
  "the name_parse() function in libevent's DNS code is vulnerable to a buffer overread.

   971         if (cp != name_out) {
   972             if (cp + 1 >= end) return -1;
   973             *cp++ = '.';
   974         }
   975         if (cp + label_len >= end) return -1;
   976         memcpy(cp, packet + j, label_len);
   977         cp += label_len;
   978         j += label_len;
   No check is made against length before the memcpy occurs.

   This was found through the Tor bug bounty program and the discovery should be credited to 'Guido Vranken'."

Reproducer for gdb (https://gist.github.com/azat/e4fcf540e9b89ab86d02):
  set $PROT_NONE=0x0
  set $PROT_READ=0x1
  set $PROT_WRITE=0x2
  set $MAP_ANONYMOUS=0x20
  set $MAP_SHARED=0x01
  set $MAP_FIXED=0x10
  set $MAP_32BIT=0x40

  start

  set $length=202
  # overread
  set $length=2
  # allocate with mmap to have a seg fault on page boundary
  set $l=(1<<20)*2
  p mmap(0, $l, $PROT_READ|$PROT_WRITE, $MAP_ANONYMOUS|$MAP_SHARED|$MAP_32BIT, -1, 0)
  set $packet=(char *)$1+$l-$length
  # hack the packet
  set $packet[0]=63
  set $packet[1]='/'

  p malloc(sizeof(int))
  set $idx=(int *)$2
  set $idx[0]=0
  set $name_out_len=202

  p malloc($name_out_len)
  set $name_out=$3

  # have WRITE only mapping to fail on read
  set $end=$1+$l
  p (void *)mmap($end, 1<<12, $PROT_NONE, $MAP_ANONYMOUS|$MAP_SHARED|$MAP_FIXED|$MAP_32BIT, -1, 0)
  set $m=$4

  p name_parse($packet, $length, $idx, $name_out, $name_out_len)
  x/2s (char *)$name_out

Before this patch:
$ gdb -ex 'source gdb' dns-example
$1 = 1073741824
$2 = (void *) 0x633010
$3 = (void *) 0x633030
$4 = (void *) 0x40200000

Program received signal SIGSEGV, Segmentation fault.
__memcpy_sse2_unaligned () at memcpy-sse2-unaligned.S:33

After this patch:
$ gdb -ex 'source gdb' dns-example
$1 = 1073741824
$2 = (void *) 0x633010
$3 = (void *) 0x633030
$4 = (void *) 0x40200000
$5 = -1
0x633030:       "/"
0x633032:       ""
(gdb) p $m
$6 = (void *) 0x40200000
(gdb) p $1
$7 = 1073741824
(gdb) p/x $1
$8 = 0x40000000
(gdb) quit

P.S. plus drop one condition duplicate.

Fixes: #317
---
 evdns.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/evdns.c b/evdns.c
index 0955a28..c411233 100644
--- a/evdns.c
+++ b/evdns.c
@@ -976,7 +976,6 @@ name_parse(u8 *packet, int length, int *idx, char *name_out, int name_out_len) {
 
 	for (;;) {
 		u8 label_len;
-		if (j >= length) return -1;
 		GET8(label_len);
 		if (!label_len) break;
 		if (label_len & 0xc0) {
@@ -997,6 +996,7 @@ name_parse(u8 *packet, int length, int *idx, char *name_out, int name_out_len) {
 			*cp++ = '.';
 		}
 		if (cp + label_len >= end) return -1;
+		if (j + label_len > length) return -1;
 		memcpy(cp, packet + j, label_len);
 		cp += label_len;
 		j += label_len;
-- 
2.1.4