Project

General

Profile

Actions

Security #7393

closed

tcp: segfault on StreamingBufferSlideToOffsetWithRegions

Added by Philippe Antoine 8 months ago. Updated 6 days ago.

Status:
Closed
Priority:
Normal
Target version:
Affected Versions:
Label:
Git IDs:

282509f70c4ce805098e59535af445362e3e9ebd
8900041405dbb5f9584edae994af2100733fb4be
9a53ec43b13f0039a083950511a18bf6f408e432

Severity:
CRITICAL
Disclosure Date:

Description

Triggers on the same callstack
On SMB traffic (app-layer.smb.stream-depth == 200mb)
On a compile of the 7.0.6 tag

(gdb) bt
#0  0x00007f78baad7aa0 in __memset_sse2 () from /lib64/libc.so.6
#1  0x0000559bde7c244d in memset (__len=<optimized out>, __ch=0, __dest=<optimized out>) at /usr/include/bits/string3.h:84
#2  GrowRegionToSize (size=<optimized out>, region=0x7f77f41cee40, cfg=0x559bdf104598 <stream_config+56>, sb=0x7f77f41cee40) at util-streaming-buffer.c:736
#3  StreamingBufferSlideToOffsetWithRegions (slide_offset=37755546, cfg=0x559bdf104598 <stream_config+56>, sb=0x7f77f41cee40) at util-streaming-buffer.c:946
#4  StreamingBufferSlideToOffset (sb=sb@entry=0x7f77f41cee40, cfg=cfg@entry=0x559bdf104598 <stream_config+56>, offset=offset@entry=37755546) at util-streaming-buffer.c:1016
#5  0x0000559bde7a61ff in StreamTcpPruneSession (f=0x7f7786a7a090, flags=<optimized out>) at stream-tcp-list.c:940
#6  0x0000559bde768c89 in FlowWorker (tv=0x559be8a787f0, p=0x7f780117ff70, data=0x7f78011eabf0) at flow-worker.c:657
#7  0x0000559bde6c06bd in TmThreadsSlotVarRun (tv=tv@entry=0x559be8a787f0, p=p@entry=0x7f780117ff70, slot=<optimized out>) at tm-threads.c:135
#8  0x0000559bde793015 in TmThreadsSlotProcessPkt (p=0x7f780117ff70, s=<optimized out>, tv=0x559be8a787f0) at tm-threads.h:200
#9  AFPParsePacketV3 (pbd=<optimized out>, ppd=0x7f77286e1ee0, ptv=0x7f78011809a0) at source-af-packet.c:1013
#10 AFPWalkBlock (pbd=<optimized out>, ptv=0x7f78011809a0) at source-af-packet.c:1032
#11 AFPReadFromRingV3 (ptv=0x7f78011809a0) at source-af-packet.c:1079
#12 0x0000559bde79331b in ReceiveAFPLoop (tv=0x559be8a787f0, data=<optimized out>, slot=<optimized out>) at source-af-packet.c:1431
#13 0x0000559bde6c1eca in TmThreadsSlotPktAcqLoop (td=0x559be8a787f0) at tm-threads.c:318
#14 0x00007f78bc00eea5 in start_thread () from /lib64/libpthread.so.0
#15 0x00007f78bab46b2d in clone () from /lib64/libc.so.6
# The result of ToNextMultipleOf from line 723
(gdb) print grow
grow = 1327104

# The offset in the memory region for the start of the new data, as per line 735
(gdb) print region->buf_size
region->buf_size = 1329152

# The value of diff, as per line 734
(gdb) print grow - region->buf_size
diff = 4294965248

Subtasks 1 (0 open1 closed)

Security #7404: tcp: segfault on StreamingBufferSlideToOffsetWithRegions (7.0.x backport)ClosedPhilippe AntoineActions
Actions #1

Updated by Philippe Antoine 8 months ago

GrowRegionToSize does not seem to check that size is bigger than current region->buf_size

From the caller point of view start->buf_size may be bigger than mem_size even if if (start->buf_size < next->buf_size) as we have @mem_size = ToNextMultipleOf(next_re - slide_offset, cfg->buf_size);@norg

But looks painful to produce a pcap that will get to this part of the code with the right set of conditions/state...

Actions #2

Updated by Philippe Antoine 8 months ago

Side question: how to fuzz efficiently this part of code ?

Actions #3

Updated by Victor Julien 8 months ago

Philippe Antoine wrote in #note-2:

Side question: how to fuzz efficiently this part of code ?

A dedicated fuzz target?

Actions #4

Updated by Philippe Antoine 8 months ago

Victor Julien wrote in #note-3:

Philippe Antoine wrote in #note-2:

Side question: how to fuzz efficiently this part of code ?

A dedicated fuzz target?

Sure, but I wonder how we can have a fuzz target focusing on the TCP engine

Actions #5

Updated by Victor Julien 8 months ago

This wouldn't need the tcp engine at all I think, just the streaming buffer API.

Actions #6

Updated by Philippe Antoine 8 months ago

StreamingBufferConfig cfg = { 8+0xb, 0xff, 8+0x3f, NULL, NULL, NULL };
StreamingBufferInsertAt(sb, &cfg, &seg1, segd, 0x2b, 0x74);
StreamingBufferAppend(sb, &cfg, &seg1, segd, 0xfe);
StreamingBufferSlideToOffset(sb, &cfg, 0x6e);

=================================================================
6386ERROR: AddressSanitizer: heap-buffer-overflow on address 0x606000001cea at pc 0x0001057f7072 bp 0x7ff7bc956ac0 sp 0x7ff7bc956290
WRITE of size 4294967087 at 0x606000001cea thread T0
#0 0x1057f7071 in __asan_memset+0x781 (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0xdb071)
#1 0x103a56596 in GrowRegionToSize util-streaming-buffer.c:735
#2 0x103a49c31 in StreamingBufferSlideToOffset util-streaming-buffer.c:1025
#3 0x1035a9282 in LLVMFuzzerTestOneInput fuzz_streambuf.c:44

What do you think about it Victor ?

Actions #7

Updated by Philippe Antoine 8 months ago

StreamingBufferConfig cfg = { 8+0x2f, 0x9, 8+1, NULL, NULL, NULL };
StreamingBufferInsertAt(sb, &cfg, &seg1, segd, 1, 0x77);
StreamingBufferInsertAt(sb, &cfg, &seg1, segd, 0x72, 0x6b);
StreamingBufferInsertAt(sb, &cfg, &seg1, segd, 0x72, 0x1);
==10459==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x611000001a1c at pc 0x00010b3f7eee bp 0x7ff7b6cb2ae0 sp 0x7ff7b6cb22a0
WRITE of size 114 at 0x611000001a1c thread T0
    #0 0x10b3f7eed in __asan_memmove+0xe4d (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0xdbeed)
    #1 0x1096f4ec0 in StreamingBufferInsertAt util-streaming-buffer.c:1535
Actions #8

Updated by Philippe Antoine 8 months ago

With STREAMING_BUFFER_CONFIG_INITIALIZER values:

    StreamingBufferConfig cfg1 = { 2048, 8, 262144, NULL, NULL, NULL };
    StreamingBuffer *sb = StreamingBufferInit(&cfg1);
    StreamingBufferInsertAt(sb, &cfg1, &seg1, segd, 15832832, 825239);
    StreamingBufferAppend(sb, &cfg1, &seg1, segd, 9934743);
    StreamingBufferSlideToOffset(sb, &cfg1, 9934743);

getting grow 6723584 <= 15832832 bs end thus the overflow

Actions #9

Updated by Philippe Antoine 8 months ago

Better reproducer

    StreamingBufferConfig cfg = { 2048, 8, 262144, NULL, NULL, NULL };
    StreamingBufferSegment seg1;
    StreamingBuffer *sb = StreamingBufferInit(&cfg);
    uint8_t segd[0x100000];
    memset(segd, 'A', 256);

    if (size < 15)
        return 0;

    int r = StreamingBufferInsertAt(sb, &cfg, &seg1, segd, 0x2000, 0);
    if (r != 0)
        return 0;
    r = StreamingBufferInsertAt(sb, &cfg, &seg1, segd, 0x100, 0x221200);
    if (r != 0)
        return 0;
    r = StreamingBufferInsertAt(sb, &cfg, &seg1, segd, 0xFFFF0, 0x1e0000);
    if (r != 0)
        return 0;
    StreamingBufferSlideToOffset(sb, &cfg, 0x221212);

Actions #10

Updated by Philippe Antoine 8 months ago

Ok, I am getting to the root cause of this :
RegionsIntersect is missing a case : the one where the new insertion contains strictly the existing region (regularized with max gap)

    int r = StreamingBufferInsertAt(sb, &cfg, &seg1, segd, 0x2000, 0);
    if (r != 0)
        return 0;
    r = StreamingBufferInsertAt(sb, &cfg, &seg1, segd, 0x100, 0xc0000);
    if (r != 0)
        return 0;
    r = StreamingBufferInsertAt(sb, &cfg, &seg1, segd, 0x80802, 0x7FFFF);
    if (r != 0)
        return 0;
    StreamingBufferSlideToOffset(sb, &cfg, 0xc0000);

First we create a main region, ok

Then, we create a next small region at 0xc0000 = 3 times STREAMING_BUFFER_REGION_GAP_DEFAULT (262144)

We have
[ main offset:0 size:2000 offset:2000 ][ gap:be000 ][ aux offset:c0000 size:800 offset:100 ](max 2, cnt 2, sb->regions 2)

StreamingBufferInsertAt(sb, &cfg, &seg1, segd, 0x80802, 0x7FFFF);. makes things go wrong

It is not merged with [ aux offset:c0000 size:800 offset:100 ]. as it should

Because we ar looking for first region matching 7ffff/100801

And we have @7ffff < 80000==reg_o==r->stream_offset - cfg->region_gap==c0000-40000 < reg_re==r->stream_offset + r->buf_size + cfg->region_gap==c0000+800+40000==100800 < 100801@norg

As we can see, this requires calling StreamingBufferInsertAt with data length >= 2* STREAMING_BUFFER_REGION_GAP_DEFAULT + bufsize=2048 + 2, so 526 338 bytes

Actions #11

Updated by Philippe Antoine 8 months ago

If we want to see if region [C:D] intersects with [A:B] (given c<d and a<b)

There are 6 cases
  1. a b c d : no match
  2. a c b d : match ok
  3. a c d b : should match but does not in current code
  4. c a b d : match ok
  5. c a d b : match ok
  6. c d a b : no match
Actions #12

Updated by Philippe Antoine 8 months ago

But only caller of StreamingBufferInsertAt is using uint16_t as data length, so always smaller than 512 kb !
By the way, it should be reflected in function prototype...

Actions #13

Updated by Philippe Antoine 8 months ago

So TCP reproducer without exploiting bug in ListRegions

int r = StreamingBufferInsertAt(sb, &cfg, &seg1, segd, 0x8000, 0);
if (r != 0)
return 0;
r = StreamingBufferInsertAt(sb, &cfg, &seg1, segd, 0x100, 0xc0000);
if (r != 0)
return 0;
for (size_t i =1; i&lt;14; i++) {
r = StreamingBufferInsertAt(sb, &cfg, &seg1, segd, 0x8000, 0+0x8000*i);
if (r != 0)
return 0;
}
for (size_t i =1; 0x7FF*i&lt;0x50000; i++) {
StreamingBufferSlideToOffset(sb, &cfg, 0x7FF*i);
}
r = StreamingBufferInsertAt(sb, &cfg, &seg1, segd, 0xFFFF, 0x70000);
if (r != 0)
return 0;
StreamingBufferSlideToOffset(sb, &cfg, 0x60000);
Actions #14

Updated by Philippe Antoine 8 months ago

Analysis : we aim to have
start->buf_size > mem_size==next_re - slide_offset

So, we want to maximize start->buf_size while keeping a next region which must keep more than 256kbytes apart

We grow buf_size by multiple appending StreamingBufferInsertAt
Then, we StreamingBufferSlideToOffset while keeping buf_size by sliding just one byte less than cfg->buf_size and enjoying the rounding
But we stop before slide offset + buf_size reach next offset

We then append maximum data without getting the regions merged
We do a big slide, but not so big compared to buf_size, so that we reach next

Actions #15

Updated by Philippe Antoine 8 months ago

  • Status changed from New to In Review
  • Severity changed from MODERATE to CRITICAL
  • Label Needs backport to 7.0 added

Gitlab MR

I assess this critical : default config and crash
I do not think we can turn into RCE as it will memset 4Gbytes with 0...

On the fuzzing side, I am not sure it is worth to add a target which did not find the tricky bug in a few minutes on my laptop...
Also I hit plenty of debug assertions (which I disabled) and I guess caller respects them...

Actions #16

Updated by OISF Ticketbot 8 months ago

  • Subtask #7404 added
Actions #17

Updated by OISF Ticketbot 8 months ago

  • Label deleted (Needs backport to 7.0)
Actions #18

Updated by Philippe Antoine 8 months ago

  • Assignee changed from OISF Dev to Philippe Antoine
Actions #19

Updated by Philippe Antoine 8 months ago

Wondering if we can turn the StreamingBufferr API reproducer into a pcap by
- have a 3 way handshake
- Replace StreamingBufferInsertAt(length, offset) by a TCP packet with length and sequence number as offset
- Replace StreamingBufferSlideToOffset(offset) with TCP ack packet in the other direction with ack number as offset

Actions #21

Updated by Victor Julien 7 months ago

  • Status changed from In Review to Resolved
  • Git IDs updated (diff)
Actions #22

Updated by Victor Julien 7 months ago

  • Status changed from Resolved to Closed
Actions #23

Updated by Juliana Fajardini Reichow 6 days ago

  • Private changed from Yes to No
Actions

Also available in: Atom PDF