Lucene search

K
zdtAntti Levomäki1337DAY-ID-29067
HistoryNov 27, 2017 - 12:00 a.m.

Wget HTTP integer overflow Exploit

2017-11-2700:00:00
Antti Levomäki
0day.today
38

0.707 High

EPSS

Percentile

97.7%

Exploit for linux platform in category dos / poc

wget HTTP integer overflow Exploit

https://xorl.wordpress.com/2017/11/11/cve-2017-13089-wget-http-integer-overflow/

That’s an interesting vulnerability in GNU wget. According to the wget project, this was reported by Antti Levomäki, Christian Jalio, Joonas Pihlaja of Forcepoint as well as Juhani Eronen of the Finnish National Cyber Security Centre. The vulnerability is in src/http.c source code file and more precisely in skip_short_body() function.

/* Read the body of the request, but don't store it anywhere and don't
   display a progress gauge.  This is useful for reading the bodies of
   administrative responses to which we will soon issue another
   request.  The response is not useful to the user, but reading it
   allows us to continue using the same connection to the server.

   If reading fails, false is returned, true otherwise.  In debug
   mode, the body is displayed for debugging purposes.  */

static bool
skip_short_body (int fd, wgint contlen, bool chunked)
{
  enum {
    SKIP_SIZE = 512,                /* size of the download buffer */
    SKIP_THRESHOLD = 4096        /* the largest size we read */
  };
  wgint remaining_chunk_size = 0;
    ...
  return true;
}

The description in the comment is pretty clear but what we care about here is the “remaining_chunk_size” variable which has data type of “wgint”. This is a data type defined in src/wget.h header file based on the architecture and operating system.

/* Pick an integer type large enough for file sizes, content lengths,
   and such.  Because today's files can be very large, it should be a
   signed integer at least 64 bits wide.  This can't be typedeffed to
   off_t because: a) off_t is always 32-bit on Windows, and b) we
   don't necessarily want to tie having a 64-bit type for internal
   calculations to having LFS support.  */

#ifdef WINDOWS
  /* nothing to do, see mswindows.h */
#elif SIZEOF_LONG >= 8
  /* long is large enough, so use it. */
  typedef long wgint;
# define SIZEOF_WGINT SIZEOF_LONG
#elif SIZEOF_LONG_LONG >= 8
  /* long long is large enough and available, use that */
  typedef long long wgint;
# define SIZEOF_WGINT SIZEOF_LONG_LONG
#elif HAVE_INT64_T
  typedef int64_t wgint;
# define SIZEOF_WGINT 8
#elif SIZEOF_OFF_T >= 8
  /* In case off_t is typedeffed to a large non-standard type that our
     tests don't find. */
  typedef off_t wgint;
# define SIZEOF_WGINT SIZEOF_OFF_T
#else
  /* Fall back to using long, which is always available and in most
     cases large enough. */
  typedef long wgint;
# define SIZEOF_WGINT SIZEOF_LONG
#endif

What is worth noting is all of the type definitions are using signed data types. This means that “wgint” variables can get both positive and negative values. Now that this is clear, let’s move back to http.c and skip_short_body() function.

static bool
skip_short_body (int fd, wgint contlen, bool chunked)
{
    ...
    SKIP_SIZE = 512,                /* size of the download buffer */
    ...
  wgint remaining_chunk_size = 0;
  char dlbuf[SKIP_SIZE + 1];
    ...
  while (contlen > 0 || chunked)
    {
      int ret;
      if (chunked)
        {
          if (remaining_chunk_size == 0)
            {
              char *line = fd_read_line (fd);
              char *endl;
              if (line == NULL)
                break;

              remaining_chunk_size = strtol (line, &endl, 16);
              xfree (line);
    ...
          contlen = MIN (remaining_chunk_size, SKIP_SIZE);
    ...
      ret = fd_read (fd, dlbuf, MIN (contlen, SKIP_SIZE), -1);
    ...
}

So, when wget processes chunked responses it will enter this “while” loop (content length greater than zero or the response is chunked). When the chunk size gets to 0, it will read the next line using fd_read_line() and then attempt to retrieve the remaining chunk size using strtol() in hexadecimal. This value is 100% controlled by the response header and it could be anything, including so large that it will wrap around this signed integer into a negative value. Then MIN() macro will be used to compare that value with SKIP_SIZE (which is 512) and use this to initialize “contlen” signed integer. If “remaining_chunk_size” had a negative value it means that this will now be stored in “contlen” which is then used in fd_read() leading to a stack based buffer overflow as the attacker completely controls the size argument that is used to copy data from “fd” (the HTTP page) to “dlbuf” (stack based buffer with size of 513 bytes). The fix was relatively simple as you can see below.

               remaining_chunk_size = strtol (line, &endl, 16);
               xfree (line);

+              if (remaining_chunk_size < 0)
+                return false;
+
               if (remaining_chunk_size == 0)

The fix was a simple bound check after the strtol() call to ensure that the value of “remaining_chunk_size” was not set to a negative value before continuing with the processing.


#  0day.today [2018-02-17]  #