Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decompressing untrusted input is unsafe #7

Open
nemequ opened this issue Jun 21, 2016 · 1 comment
Open

Decompressing untrusted input is unsafe #7

nemequ opened this issue Jun 21, 2016 · 1 comment

Comments

@nemequ
Copy link
Contributor

nemequ commented Jun 21, 2016

wfLZ_Decompress does not allow you to provide a length parameter for the in buffer, so there is no way to prevent it from reading outside the input buffer. Anyone who can control the compressed data can trick the decompressor into reading extra data, possibly causing unwanted information disclosure or a crash.

@nemequ
Copy link
Contributor Author

nemequ commented Jun 22, 2016

I should have mentioned that it doesn't seem to be safe to rely on the compressedSize header field to provide this information to the decoder; we can easily set the length to match the actual buffer size, but wflz is still happy to read past it. It does provide a way to implement this without breaking API compatibility, though…

Here is a quick test:

#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "wfLZ.h"

#define LOREM_IPSUM                                                     \
  "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed vulputate " \
  "lectus nisl, vitae ultricies justo dictum nec. Vestibulum ante ipsum " \
  "primis in faucibus orci luctus et ultrices posuere cubilia Curae; "  \
  "Suspendisse suscipit quam a lectus adipiscing, sed tempor purus "    \
  "cursus. Vivamus id nulla eget elit eleifend molestie. Integer "      \
  "sollicitudin lorem enim, eu eleifend orci facilisis sed. Pellentesque " \
  "sodales luctus enim vel viverra. Cras interdum vel nisl in "         \
  "facilisis. Curabitur sollicitudin tortor vel congue "                \
  "auctor. Suspendisse egestas orci vitae neque placerat blandit.\n"    \
  "\n"                                                                  \
  "Aenean sed nisl ultricies, vulputate lorem a, suscipit nulla. Donec " \
  "egestas volutpat neque a eleifend. Nullam porta semper "             \
  "nunc. Pellentesque adipiscing molestie magna, quis pulvinar metus "  \
  "gravida sit amet. Vestibulum mollis et sapien eu posuere. Quisque "  \
  "tristique dignissim ante et aliquet. Phasellus vulputate condimentum " \
  "nulla in vulputate.\n"                                               \
  "\n"                                                                  \
  "Nullam volutpat tellus at nisi auctor, vitae mattis nibh viverra. Nunc " \
  "vitae lectus tristique, ultrices nibh quis, lobortis elit. Curabitur " \
  "at vestibulum nisi, nec facilisis ante. Nulla pharetra blandit lacus, " \
  "at sodales nulla placerat eget. Nulla congue varius tortor, sit amet " \
  "tempor est mattis nec. Praesent vitae tristique ipsum, rhoncus "     \
  "tristique lorem. Sed et erat tristique ligula accumsan fringilla eu in " \
  "urna. Donec dapibus hendrerit neque nec venenatis. In euismod sapien " \
  "ipsum, auctor consectetur mi dapibus hendrerit.\n"                   \
  "\n"                                                                  \
  "Phasellus sagittis rutrum velit, in sodales nibh imperdiet a. Integer " \
  "vitae arcu blandit nibh laoreet scelerisque eu sit amet eros. Aenean " \
  "odio felis, aliquam in eros at, ornare luctus magna. In semper "     \
  "tincidunt nunc, sollicitudin gravida nunc laoreet eu. Cras eu tempor " \
  "sapien, ut dignissim elit. Proin eleifend arcu tempus, semper erat et, " \
  "accumsan erat. Praesent vulputate diam mi, eget mollis leo "         \
  "pellentesque eget. Aliquam eu tortor posuere, posuere velit sed, "   \
  "suscipit eros. Nam eu leo vitae mauris condimentum lobortis non quis " \
  "mauris. Nulla venenatis fringilla urna nec venenatis. Nam eget velit " \
  "nulla. Proin ut malesuada felis. Suspendisse vitae nunc neque. Donec " \
  "faucibus tempor lacinia. Vivamus ac vulputate sapien, eget lacinia " \
  "nisl.\n"                                                             \
  "\n"                                                                  \
  "Curabitur eu dolor molestie, ullamcorper lorem quis, egestas "       \
  "urna. Suspendisse in arcu sed justo blandit condimentum. Ut auctor, " \
  "sem quis condimentum mattis, est purus pulvinar elit, quis viverra " \
  "nibh metus ac diam. Etiam aliquet est eu dui fermentum consequat. Cras " \
  "auctor diam eget bibendum sagittis. Aenean elementum purus sit amet " \
  "sem euismod, non varius felis dictum. Aliquam tempus pharetra ante a " \
  "sagittis. Curabitur ut urna felis. Etiam sed vulputate nisi. Praesent " \
  "at libero eleifend, sagittis quam a, varius sapien."
#define LOREM_IPSUM_LENGTH ((size_t) 2725)

typedef struct _wfLZ_Header {
  char       sig[4];         // this can be WFLZ for a single compressed block, or ZLFW for a block-compressed stream
  uint32_t   compressedSize;
  uint32_t   decompressedSize;
  /* wfLZ_Block firstBlock; */
} wfLZ_Header;

int main(int argc, char *argv[]) {
  size_t compressed_size = wfLZ_GetMaxCompressedSize ((uint32_t) LOREM_IPSUM_LENGTH);
  uint8_t* compressed = (uint8_t*) malloc(compressed_size);
  uint8_t decompressed[LOREM_IPSUM_LENGTH];
  size_t decompressed_size;

  uint8_t* work_mem = (uint8_t*) malloc (wfLZ_GetWorkMemSize ());

  compressed_size = (size_t) wfLZ_CompressFast (LOREM_IPSUM, (uint32_t) LOREM_IPSUM_LENGTH,
                        compressed, work_mem, 0);

  /* Now try to decompress progressively smaller slices of the
     compressed buffer. */
  while (--compressed_size > 16) {
    decompressed_size = sizeof(decompressed);
    compressed = (uint8_t*) realloc(compressed, compressed_size);
    ((wfLZ_Header*) compressed)->compressedSize = (uint32_t) compressed_size - 16;
    wfLZ_Decompress (compressed, decompressed);
  }

  wfLZ_Decompress (compressed, decompressed);
  assert (memcmp (LOREM_IPSUM, decompressed, LOREM_IPSUM_LENGTH) == 0);

  free (compressed);
  free (work_mem);

  printf("Works\n");

  return 0;
}

Compile with AddressSanitizer, and you get something like:

nemequ@peltast:~/local/src/wflz$ git:(master) 1M 7A gcc -g -O2 -fsanitize=address -o truncate truncate.c wfLZ.c && ./truncate 

=================================================================
==20899==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x61d00001f247 at pc 0x0000004019dd bp 0x7fff1dc58de0 sp 0x7fff1dc58dd0
READ of size 1 at 0x61d00001f247 thread T0
    #0 0x4019dc in wfLZ_Decompress /home/nemequ/local/src/wflz/wfLZ.c:542
    #1 0x400e4e in main /home/nemequ/local/src/wflz/truncate.c:82
    #2 0x7f6ac2291730 in __libc_start_main (/lib64/libc.so.6+0x20730)
    #3 0x400f98 in _start (/home/nemequ/local/src/wflz/truncate+0x400f98)

0x61d00001f247 is located 0 bytes to the right of 1991-byte region [0x61d00001ea80,0x61d00001f247)
allocated by thread T0 here:
    #0 0x7f6ac26fb150 in realloc (/lib64/libasan.so.3+0xc7150)
    #1 0x400e0a in main /home/nemequ/local/src/wflz/truncate.c:80
    #2 0x5769f15c  (<unknown module>)

SUMMARY: AddressSanitizer: heap-buffer-overflow /home/nemequ/local/src/wflz/wfLZ.c:542 in wfLZ_Decompress
Shadow bytes around the buggy address:
  0x0c3a7fffbdf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c3a7fffbe00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c3a7fffbe10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c3a7fffbe20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c3a7fffbe30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c3a7fffbe40: 00 00 00 00 00 00 00 00[07]fa fa fa fa fa fa fa
  0x0c3a7fffbe50: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c3a7fffbe60: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c3a7fffbe70: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c3a7fffbe80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c3a7fffbe90: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
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
  Heap right redzone:      fb
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack partial redzone:   f4
  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
==20899==ABORTING

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant