Exploit Development

23 minute read

Approach

This matrial is like a study note of mine, It will contain an advanced matrial as it will cover redicovering vulnrabilities which is some how new, I will Start in each category by mentioning a small brief about it then moving to the discovery tips.

ACID : Attacker Controled Input Data

Stack-based Buffer Overflows

In Stack-based buffer overflows we are in the situation were the data is copied from a buffer to another buffer and the Size of that data is not taken care of, in this situation the distination buffer maybe filled and data continues to write after the buffer limites.

Discovery tips

The Things that you always need to look at when you try to find a use of non-safefunction with parameters that is user controled and with no sanatization.

Also you can look for data writes in a loob that its index or counter and data is controlled by user input.

Unsafe functions:

Here we have a vulnrable code from the readelf binary that assigned CVE-2021-20294


////ACID: filedata, symtab, section, strtab, strtab_size
static void
print_dynamic_symbol (Filedata *filedata, unsigned long si,
          Elf_Internal_Sym *symtab,
          Elf_Internal_Shdr *section,
          char *strtab, size_t strtab_size)
{
  const char *version_string;
  enum versioned_symbol_info sym_info;
  unsigned short vna_other;
  Elf_Internal_Sym *psym = symtab + si;
  
  printf ("%6ld: ", si);
  print_vma (psym->st_value, LONG_HEX);
  putchar (' ');
  print_vma (psym->st_size, DEC_5);
  printf (" %-7s", get_symbol_type (filedata, ELF_ST_TYPE (psym->st_info)));
  printf (" %-6s", get_symbol_binding (filedata, ELF_ST_BIND (psym->st_info)));
  if (filedata->file_header.e_ident[EI_OSABI] == ELFOSABI_SOLARIS)
    printf (" %-7s",  get_solaris_symbol_visibility (psym->st_other));
  else
    {
      unsigned int vis = ELF_ST_VISIBILITY (psym->st_other);

      printf (" %-7s", get_symbol_visibility (vis));
      /* Check to see if any other bits in the st_other field are set.
   Note - displaying this information disrupts the layout of the
   table being generated, but for the moment this case is very rare.  */
      if (psym->st_other ^ vis)
  printf (" [%s] ", get_symbol_other (filedata, psym->st_other ^ vis));
    }
  printf (" %4s ", get_symbol_index_type (filedata, psym->st_shndx));

  bfd_boolean is_valid = VALID_SYMBOL_NAME (strtab, strtab_size,
              psym->st_name);
  const char * sstr = is_valid  ? strtab + psym->st_name : _("");

  version_string
    = get_symbol_version_string (filedata,
         (section == NULL
          || section->sh_type == SHT_DYNSYM),
         strtab, strtab_size, si,
         psym, &sym_info, &vna_other); // Lots of ACID in will yield ACID out
  
  int len_avail = 21;
  if (! do_wide && version_string != NULL) // do_wide is true iff -W option passed
    {
      char buffer[256];

      len_avail -= sprintf (buffer, "@%s", version_string);

      if (sym_info == symbol_undefined)
  len_avail -= sprintf (buffer," (%d)", vna_other);
      else if (sym_info != symbol_hidden)
  len_avail -= 1;
    }

  print_symbol (len_avail, sstr);
// ...
}

Applying the previous mentioned discovery tips to our code we can find the use of “sprintf” with a user controled data “version_string” without sanatization.

version_string
    = get_symbol_version_string (filedata,
         (section == NULL
          || section->sh_type == SHT_DYNSYM),
         strtab, strtab_size, si,
         psym, &sym_info, &vna_other);  
  ...


char buffer[256];

      len_avail -= sprintf (buffer, "@%s", version_string);
      ...

Here we have a vulnrable code from the htmldoc binary that assigned ` CVE-2021-43579`


////ACID: everything read from fp
static int                       /* O - 0 = success, -1 = fail */
image_load_bmp(image_t *img,     /* I - Image to load into */
               FILE    *fp,      /* I - File to read from */
               int     gray,     /* I - Grayscale image? */
               int     load_data)/* I - 1 = load image data, 0 = just info */
{
  int   info_size,  /* Size of info header */
        depth,    /* Depth of image (bits) */
        compression,  /* Type of compression */
        colors_used,  /* Number of colors used */
        x, y,   /* Looping vars */
        color,    /* Color of RLE pixel */
        count,    /* Number of times to repeat */
        temp,   /* Temporary color */
        align;    /* Alignment bytes */
        uchar bit,  /* Bit in image */
        byte;   /* Byte in image */
        uchar *ptr; /* Pointer into pixels */
        uchar   colormap[256][4];/* Colormap */


  // Get the header...
  getc(fp);     /* Skip "BM" sync chars */
  getc(fp);
  read_dword(fp);   /* Skip size */
  read_word(fp);    /* Skip reserved stuff */
  read_word(fp);
  read_dword(fp);

  // Then the bitmap information...
  info_size        = (int)read_dword(fp);
  img->width       = read_long(fp);
  img->height      = read_long(fp);
  read_word(fp);
  depth            = read_word(fp);
  compression      = (int)read_dword(fp);
  read_dword(fp);
  read_long(fp);
  read_long(fp);
  colors_used      = (int)read_dword(fp);
  read_dword(fp);

  if (img->width <= 0 || img->width > 8192 || img->height <= 0 || img->height > 8192)
    return (-1);

  if (info_size > 40)
    for (info_size -= 40; info_size > 0; info_size --)
      getc(fp);

  // Get colormap...
  if (colors_used == 0 && depth <= 8)
    colors_used = 1 << depth;

  fread(colormap, (size_t)colors_used, 4, fp);

  // Setup image and buffers...
  img->depth = gray ? 1 : 3;

  // If this image is indexed and we are writing an encrypted PDF file, bump the use count so
  // we create an image object (Acrobat 6 bug workaround)
  if (depth <= 8 && Encryption)
    img->use ++;

  // Return now if we only need the dimensions...
  if (!load_data)
    return (0);

  img->pixels = (uchar *)malloc((size_t)(img->width * img->height * img->depth));
  if (img->pixels == NULL)
    return (-1);

  if (gray && depth <= 8)
  {
    // Convert colormap to grayscale...
    for (color = colors_used - 1; color >= 0; color --)
      colormap[color][0] = (colormap[color][2] * 31 +
                            colormap[color][1] * 61 +
                            colormap[color][0] * 8) / 100;
  }

  // Read the image data...
  color = 0;
  count = 0;
  align = 0;
  byte  = 0;
  temp  = 0;

  for (y = img->height - 1; y >= 0; y --)
  {
    ptr = img->pixels + y * img->width * img->depth;

    switch (depth)
    {
      case 1 : /* Bitmap */
          for (x = img->width, bit = 128; x > 0; x --)
    {
      if (bit == 128)
        byte = (uchar)getc(fp);

      if (byte & bit)
      {
        if (!gray)
        {
    *ptr++ = colormap[1][2];
    *ptr++ = colormap[1][1];
              }

        *ptr++ = colormap[1][0];
      }
      else
      {
        if (!gray)
        {
    *ptr++ = colormap[0][2];
    *ptr++ = colormap[0][1];
        }

        *ptr++ = colormap[0][0];
      }

      if (bit > 1)
        bit >>= 1;
      else
        bit = 128;
    }

         /*
    * Read remaining bytes to align to 32 bits...
    */

    for (temp = (img->width + 7) / 8; temp & 3; temp ++)
      getc(fp);
          break;

      case 4 : /* 16-color */
          for (x = img->width, bit = 0xf0; x > 0; x --)
    {
     /*
      * Get a new count as needed...
      */

            if (compression != BI_RLE4 && count == 0)
      {
        count = 2;
        color = -1;
            }

      if (count == 0)
      {
        while (align > 0)
        {
          align --;
    getc(fp);
              }

        if ((count = getc(fp)) == 0)
        {
    if ((count = getc(fp)) == 0)
    {
     /*
      * End of line...
      */

                  x ++;
      continue;
    }
    else if (count == 1)
    {
     /*
      * End of image...
      */

      break;
    }
    else if (count == 2)
    {
     /*
      * Delta...
      */

      count = getc(fp) * getc(fp) * img->width;
      color = 0;
    }
    else
    {
     /*
      * Absolute...
      */

      color = -1;
      align = ((4 - (count & 3)) / 2) & 1;
    }
        }
        else
          color = getc(fp);
            }

           /*
      * Get a new color as needed...
      */

      count --;

            if (bit == 0xf0)
      {
              if (color < 0)
    temp = getc(fp) & 255;
        else
    temp = color;

             /*
        * Copy the color value...
        */

              if (!gray)
        {
    *ptr++ = colormap[temp >> 4][2];
    *ptr++ = colormap[temp >> 4][1];
              }

        *ptr++ = colormap[temp >> 4][0];
        bit    = 0x0f;
            }
      else
      {
             /*
        * Copy the color value...
        */

        if (!gray)
        {
          *ptr++ = colormap[temp & 15][2];
          *ptr++ = colormap[temp & 15][1];
        }

        *ptr++ = colormap[temp & 15][0];
        bit    = 0xf0;
      }
    }
          break;

      case 8 : /* 256-color */
          for (x = img->width; x > 0; x --)
    {
     /*
      * Get a new count as needed...
      */

            if (compression != BI_RLE8)
      {
        count = 1;
        color = -1;
            }

      if (count == 0)
      {
        while (align > 0)
        {
          align --;
    getc(fp);
              }

        if ((count = getc(fp)) == 0)
        {
    if ((count = getc(fp)) == 0)
    {
     /*
      * End of line...
      */

                  x ++;
      continue;
    }
    else if (count == 1)
    {
     /*
      * End of image...
      */

      break;
    }
    else if (count == 2)
    {
     /*
      * Delta...
      */

      count = getc(fp) * getc(fp) * img->width;
      color = 0;
    }
    else
    {
     /*
      * Absolute...
      */

      color = -1;
      align = (2 - (count & 1)) & 1;
    }
        }
        else
          color = getc(fp);
            }

           /*
      * Get a new color as needed...
      */

            if (color < 0)
        temp = getc(fp);
      else
        temp = color;

            count --;

           /*
      * Copy the color value...
      */

            if (!gray)
      {
        *ptr++ = colormap[temp][2];
        *ptr++ = colormap[temp][1];
      }

      *ptr++ = colormap[temp][0];
    }
          break;

      case 24 : /* 24-bit RGB */
          if (gray)
    {
            for (x = img->width; x > 0; x --)
      {
        temp = getc(fp) * 8;
        temp += getc(fp) * 61;
        temp += getc(fp) * 31;
        *ptr++ = (uchar)(temp / 100);
      }
    }
    else
    {
            for (x = img->width; x > 0; x --, ptr += 3)
      {
        ptr[2] = (uchar)getc(fp);
        ptr[1] = (uchar)getc(fp);
        ptr[0] = (uchar)getc(fp);
      }
          }

         /*
    * Read remaining bytes to align to 32 bits...
    */

    for (temp = img->width * 3; temp & 3; temp ++)
      getc(fp);
          break;
    }
  }

  return (0);
}

As mentioned, a use of non-safe function “fread” with a user controled parameters “fp” & “colors_used” and as the definetion of “fread tells us”

fread(void *ptr, size_t size, size_t nmemb, FILE *stream)

here we can control how many bytes from our input will be copied to the fixed lingth buffer “colormap”

  fread(colormap, (size_t)colors_used, 4, fp);

Here we have a vulnrable code from the Cellular Baseband (Exynos 980 SoC) samsung firmware

char ** find_tag_end(char **result) {
  char *i;
  unsigned int v2;
  unsigned int cur_char;
  for (i = *result ; ; ++i) {
    cur_char = (unsigned __int8)*i;
    if (cur_char <= 0xD && ((1 << cur_char) & 0x2601) != 0) // \0 \t \n \r
      break;
    v2 = cur_char - 32;
    if (v2 <= 0x1F && ((1 << v2) & (unsigned int)&unk_C0008001) != 0) // space / > ?
      break;
  }
  *result = i;
  return result;
}

int IMSPL_XmlGetNextTagName(char *src, char *dst){
  char * ptr = src;
  // The cut code will:
  // 1. Skip space characters
  // 2. Find the beginning mark '<'
  // 3. Skip comments
  // ...
  char * v8 = ptr + 1;
  char ** v13;
  v13[0] = v8;
  find_tag_end((char **)v13);
  v9 = v13[0];
  if (v8 != v13[0]) {
    memcpy(dst, (int *) ((char *)ptr + 1), v13[0] - v8);
    dst[v9 - v8] = 0;
    V12 = 10601;
    // IMSPL_XmiGetNextTagName: Tag name
    v11 = &log_struct_437f227c;
    Logs((int *)&v11, (int)dst, -1, -20071784);
    * (unsigned __int8 **)src = v13[0];
    LOBYTE(result) = 1;
    return (unsigned __int8) result;
  }
  // ...
}
int IMSPL_XmlParser_ContactLstDecode(int *a1, int *a2) {
  unsigned __int8 *v4;
  int v5;
  log_info_s *v7;
  int v8;
  unsigned __int8 *v9;
  int v10;
  char v11[136];

  bzero(v11, 100);
  v10 = 0;
  v4 = (unsigned __int8 *)*a1;
  v8 = 10597;
  v9 = v4;
  // ----------%s----------
  v7 = &log_struct_4380937c;
  log_0x418ffa6c(&v7, "IMSPL_XmlParser_ContactLstDecode", -20071784) ;
  if (IMSPL_XmlGetNextTagName((char *)&v9, v11) ! = 1) {
  LABEL_8:
    *a1 = (int)v9;
    v8 = 10597;
    // Function END
    v7 = &log_struct_43809448;
    log_0x418ffa6c(&v7, -20071784) ;
    return 1;
  }
// ...
}

Here you can found that the “find_tag_end” function is the one responsible for detrmine what is the size which will be copied using “memcpy” to the fixed buffer “dst” and all that happens with our controlled input data.

Heap Based Overflow

It happens when too much data is written to a buffer stored on the heap, thus overflowing its bounds, and corrupting adjacent data.

Discovery tips

The Things that you always need to look at when you try to find a use of non-safefunction with parameters that is user controled and with no sanatization.

Also you can look for data writes in a loob that its index or counter and data is controlled by user input.

Here we have a vulnrable code from the TCP/IP Stack assigned CVE-2020-25111


////ACID: cp
static uint16_t ScanName(uint8_t * cp, uint8_t ** npp){
  uint8_t len;
  uint16_t rc;
  uint8_t *np;

  if(*npp){
    free(*npp);
    *npp = 0;
  }

  if((*cp & 0xC0) == 0xC0)
    return 2;

  rc = strlen((char *) cp) + 1;
  np = *npp = malloc(rc);
  len = *cp++;
  while(len){
    while (len--)
      *npp++ = *cp++;
    if((len = *cp++) != 0)
      *np++ = '.';
  }
  *np = 0;

  return rc;
}

here we can see that we control the “cp” pointer value that controls the size of the allocation and the loop conditions.

Out Of Bound Write

that’s when ACID isused to calculate a memory location(that is out of bounds of the intended memory), and then ACID is Written to that location.

Discovery Tips

USing ACID for “array index” calculation. “Base + Offset(ACID)”

Here we have a vulnrable code from the Adobe Type 1 font parsing assigned CVE-2020-0938

////ACID: num_master
int SetBlendDesignPositions(void *arg) {
  int num_master;
  Fixed16_16 values[16][15];

  for (num_master = 0; ; num_master++) {
    if (GetToken() != TOKEN_OPEN) {
      break;
    }
    //KC: writes an ACID number (0-15) of ACID values at &values[num_master]
    int values_read = GetOpenFixedArray(&values[num_master], 15);
    SetNumAxes(values_read);
  }

  SetNumMasters(num_master);

  for (int i = 0; i < num_master; i++) {
    procs->BlendDesignPositions(i, &values[i]);
  }

  return 0;
}

Here we can control the “num_master” variable as it will never go to the write part before we provide the “TOKEN_OPEN” So we can write 16 bytes at any index from the “values” array.

Here we have another vulnrable snippest Images (National Imagery Transmission Format (NITF)) parser assigned CVE-2020-13995


// Globals
char Gstr[255];
char sBuffer[1000];
//...
/* V2_0, V2_1 */
int number_of_DESs;
segment_info_type *DES_info;
//...
long read_verify(int fh, char *destination, long length, char *sErrorMessage)
{
    long rc;
    long start;
    long file_len;
    static char sTemp[150];

    rc = read(fh, destination, length);
    if (rc == -1) {
        start = lseek(fh, 0, SEEK_CUR);
        file_len = lseek(fh, 0, SEEK_END);
        sprintf(sTemp, "Error reading, read returned %ld. (start = %ld, \
read length = %ld, file_length = %ld\n%s\n",
                    rc, start, length, file_len, sErrorMessage);
        errmessage(sTemp);
        iQuit(1);
    }
    else if (rc != length) {
        start = lseek(fh, 0, SEEK_CUR) - rc;
        file_len = lseek(fh, 0, SEEK_END);
        sprintf(sTemp, "Error reading, read returned %ld. (start = %ld, \
read length = %ld, file_length = %ld\n%s\n",
                    rc, start, length, file_len, sErrorMessage);
        errmessage(sTemp);
        printf("errno=%d\n", errno);
        iQuit(1);
    }
    return rc;
}

////ACID: hNITF
int main(int argc, char *argv[]){
  //...
    rc = open(sNITFfilename, O_RDONLY| O_BINARY);
  //...
    hNITF = rc;
  //...
  read_verify(hNITF, (char *) sBuffer, 3,
                  "error reading header (# extension segs");
      sBuffer[3] = '\0';
      number_of_DESs = atoi(sBuffer);

      if (number_of_DESs > 0) {
          /* Allocate Space for extension segs information arrays */
          DES_info = (segment_info_type *)
                   malloc(sizeof(segment_info_type) * number_of_DESs);
          if (DES_info == NULL) {
              errmessage("Error allocating memory for DES_info");
              iQuit(1);
          }

          /* Read Image subheader / data lengths */

          read_verify(hNITF, sBuffer, 13 * number_of_DESs,
              "Error reading header / image subheader data lengths");

          temp = sBuffer;

          for (x = 0; x < number_of_DESs; x++) {
              strncpy(Gstr, temp, 4);
              Gstr[4] = '\0';
              DES_info[x].length_of_subheader = atol(Gstr);
              temp += 4;

              strncpy(Gstr, temp, 9);
              Gstr[9] = '\0';
              DES_info[x].length_of_data = atol(Gstr);
              temp += 9;

              DES_info[x].pData = NULL;
              DES_info[x].bFile_written = FALSE;
          }
      }
}

What happend here is that we control the input file “hNIFT” that will be copied to a buffer “sBuffer” and with that we can control the SIze of data read and the size of allocation with controlling “number_of_DESs” after that controlling the “temp” variable that will in the last assigned to a pointer “DES_info” and know we have control over that pointer.

This is for you. Can you find the vulnrability?!


struct flow_action {
  unsigned int               num_entries;
  struct flow_action_entry   entries[];
};

struct flow_rule {
  struct flow_match          match;
  struct flow_action         action;
};

struct nft_flow_rule {
  __be16                     proto;
  struct nft_flow_match      match;
  struct flow_rule           *rule;
};

struct nft_offload_ctx {
  struct {
    enum nft_offload_dep_type   type;
    __be16                      l3num;
    u8                          protonum;
  } dep;
  unsigned int               num_actions;
  struct net                 *net;
  struct nft_offload_reg     regs[NFT_REG32_15 + 1];
};

/**
 * struct_size() - Calculate size of structure with trailing array.
 * @p: Pointer to the structure.
 * @member: Name of the array member.
 * @count: Number of elements in the array.
 *
 * Calculates size of memory needed for structure @p followed by an
 * array of @count number of @member elements.
 *
 * Return: number of bytes needed or SIZE_MAX on overflow.
 */
#define struct_size(p, member, count)         \
  __ab_c_size(count,            \
        sizeof(*(p)->member) + __must_be_array((p)->member),\
        sizeof(*(p)))

#define NFT_OFFLOAD_F_ACTION  (1 << 0)

struct flow_rule *flow_rule_alloc(unsigned int num_actions)
{
  struct flow_rule *rule;
  int i;
  //  allocates space for the rule->action.entries[num_actions] array
  rule = kzalloc(struct_size(rule, action.entries, num_actions),
           GFP_KERNEL);
  if (!rule)
    return NULL;

  rule->action.num_entries = num_actions;
  /* Pre-fill each action hw_stats with DONT_CARE.
   * Caller can override this if it wants stats for a given action.
   */
  for (i = 0; i < num_actions; i++)
    rule->action.entries[i].hw_stats = FLOW_ACTION_HW_STATS_DONT_CARE;

  return rule;
}

static struct nft_flow_rule *nft_flow_rule_alloc(int num_actions)
{
  struct nft_flow_rule *flow;

  flow = kzalloc(sizeof(struct nft_flow_rule), GFP_KERNEL);
  if (!flow)
    return NULL;

  flow->rule = flow_rule_alloc(num_actions);
  if (!flow->rule) {
    kfree(flow);
    return NULL;
  }

  flow->rule->match.dissector = &flow->match.dissector;
  flow->rule->match.mask    = &flow->match.mask;
  flow->rule->match.key   = &flow->match.key;

  return flow;
}

static inline struct nft_expr *nft_expr_first(const struct nft_rule *rule)
{
  return (struct nft_expr *)&rule->data[0];
}

static inline struct nft_expr *nft_expr_last(const struct nft_rule *rule)
{
  return (struct nft_expr *)&rule->data[rule->dlen];
}

static inline bool nft_expr_more(const struct nft_rule *rule,
         const struct nft_expr *expr)
{
  return expr != nft_expr_last(rule) && expr->ops;
}


int nft_fwd_dup_netdev_offload(struct nft_offload_ctx *ctx,
             struct nft_flow_rule *flow,
             enum flow_action_id id, int oif)
{
  struct flow_action_entry *entry;
  struct net_device *dev;

  /* nft_flow_rule_destroy() releases the reference on this device. */
  dev = dev_get_by_index(ctx->net, oif);
  if (!dev)
    return -EOPNOTSUPP;

  entry = &flow->rule->action.entries[ctx->num_actions++];
  entry->id = id;
  entry->dev = dev;

  return 0;
}

static inline void *nft_expr_priv(const struct nft_expr *expr)
{
  return (void *)expr->data;
}

static int nft_dup_netdev_offload(struct nft_offload_ctx *ctx,
          struct nft_flow_rule *flow,
          const struct nft_expr *expr)
{
  const struct nft_dup_netdev *priv = nft_expr_priv(expr); //  assume priv != ACID
  int oif = ctx->regs[priv->sreg_dev].data.data[0];

  return nft_fwd_dup_netdev_offload(ctx, flow, FLOW_ACTION_MIRRED /*5*/, oif);
}

////ACID: rule
struct nft_flow_rule *nft_flow_rule_create(struct net *net,
             const struct nft_rule *rule)
{
  struct nft_offload_ctx *ctx;
  struct nft_flow_rule *flow;
  int num_actions = 0, err;
  struct nft_expr *expr;

  expr = nft_expr_first(rule);
  while (nft_expr_more(rule, expr)) {
    if (expr->ops->offload_flags & NFT_OFFLOAD_F_ACTION)
      num_actions++;

    expr = nft_expr_next(expr);
  }

  if (num_actions == 0)
    return ERR_PTR(-EOPNOTSUPP);

  flow = nft_flow_rule_alloc(num_actions);
  if (!flow)
    return ERR_PTR(-ENOMEM);

  expr = nft_expr_first(rule);

  ctx = kzalloc(sizeof(struct nft_offload_ctx), GFP_KERNEL);
  if (!ctx) {
    err = -ENOMEM;
    goto err_out;
  }
  ctx->net = net;
  ctx->dep.type = NFT_OFFLOAD_DEP_UNSPEC;

  while (nft_expr_more(rule, expr)) {
    if (!expr->ops->offload) {
      err = -EOPNOTSUPP;
      goto err_out;
    }
    err = expr->ops->offload(ctx, flow, expr); // Calls nft_dup_netdev_offload()
    if (err < 0)
      goto err_out;

    expr = nft_expr_next(expr);
  }
  nft_flow_rule_transfer_vlan(ctx, flow);

  flow->proto = ctx->dep.l3num;
  kfree(ctx);

  return flow;
err_out:
  kfree(ctx);
  nft_flow_rule_destroy(flow);

  return ERR_PTR(err);
}

Integers Overflows/Underflows

This problem is about making a situation of Under_Allocation or Over_Copy and that is happend by using the math of integers.

Note:
Always bay attension to signed integers it's often causing problems.

So we have to understand how that works.

if we have 8 byte integer that holds 250 after adding 10 to it it will be 5 and that is integer overflow, that if used will be cause the past mentioned situations.

Here we have another vulnrable snippest Windows Kernel Driver (srv2.sys) assigned CVE-2020-0796


////ACID: The date pointed to by request->pNetRawBuffer
signed __int64 __fastcall Srv2DecompressData(SRV2_WORKITEM *workitem)
{
    // declarations omitted
    ...
    request = workitem->psbhRequest;
    if ( request->dwMsgSize < 0x10 )
        return 0xC000090B;
    compressHeader = *(CompressionTransformHeader *)request->pNetRawBuffer;
    ...
   
    newHeader = SrvNetAllocateBuffer((unsigned int)(compressHeader.originalCompressedSegSize + compressHeader.offsetOrLength), 0);
    if ( !newHeader )
        return 0xC000009A;
   
    if ( SmbCompressionDecompress(
                compressHeader.compressionType,
                &workitem->psbhRequest->pNetRawBuffer[compressHeader.offsetOrLength + 16],
                workitem->psbhRequest->dwMsgSize - compressHeader.offsetOrLength - 16,
                &newHeader->pNetRawBuffer[compressHeader.offsetOrLength],
                compressHeader.OriginalCompressedSegSize,
                &finalDecompressedSize) < 0
            || finalDecompressedSize != compressHeader.originalCompressedSegSize) )
    {
        SrvNetFreeBuffer(newHeader);
        return 0xC000090B;
    }
    if ( compressHeader.offsetOrLength )
    {
        memmove(newHeader->pNetRawBuffer, workitem->psbhRequest->pNetRawBuffer + 16, compressHeader.offsetOrLength);
    }
    newHeader->dwMsgSize = compressHeader.OffsetOrLength + fianlDecompressedSize;
    Srv2ReplaceReceiveBuffer(workitem, newHeader);
    return 0;
}

Actually in this code we have not just one overflow, there is two. As we control “pNetRawBuffer” we also controll “compressHeader” with that we can controll the math operation

newHeader = SrvNetAllocateBuffer((unsigned int)(compressHeader.originalCompressedSegSize + compressHeader.offsetOrLength), 0);

So we can allocate a small chunk of memory Under_Allocation then we have two copy operation to that allocated memory with an attecker controlled data in

SmbCompressionDecompress(
                compressHeader.compressionType,
                &workitem->psbhRequest->pNetRawBuffer[compressHeader.offsetOrLength + 16],
                workitem->psbhRequest->dwMsgSize - compressHeader.offsetOrLength - 16,
                &newHeader->pNetRawBuffer[compressHeader.offsetOrLength],
                compressHeader.OriginalCompressedSegSize,
                &finalDecompressedSize)

and

        memmove(newHeader->pNetRawBuffer, workitem->psbhRequest->pNetRawBuffer + 16, compressHeader.offsetOrLength);

Here we have another vulnrable snippest Network Packets (HTTP) assigned CVE-2019-5105


////ACID: param_1
void FUN_00677d70(void **param_1, int param_2, int param_3, int param_4, int param_5 ,uint *param_6)
{
  int header_length;
  size_t _Size;
  int iVar1;
  int iVar2;
  int receiver_length;
  uint sender_length;
  /* Omitted code  */
  void *blkDrvPDUdata;
  /* Omitted code */
  iVar2 = *(int *)(param_2 + 0x128) +  DAT_007a3534;
  if (iVar2 < 0xf) {
     /* Omitted code */
    blkDrvPDUdata = *param_1;
    header_length = (*(byte *)((int)blkDrvPDUdata + 1) & 7) * 2;
    sender_length = *(byte *)((int)blkDrvPDUdata + 5) & 0xf;
    receiver_length = (int)(uint)*(byte *)((int)blkDrvPDUdata + 5) >> 4;
    pvVar3 = (void *)(sender_length + receiver_length + header_length);
    local_20c = header_length;
    if (pvVar3 < param_1[1] || pvVar3 == param_1[1]) {
      pvVar3 = *param_1;
      if ((*(byte *)((int)blkDrvPDUdata + 2) & 0x10) == 0) {
        *param_6 = header_length + (sender_length + receiver_length) * 2;
        if ((*param_6 & 3) != 0) {
          *param_6 = *param_6 + 2;
        }
        _Size = (int)param_1[1] - *param_6;

        /* Omitted  code*/
        if ((local_220 < 0x10) && (local_244 < 0x10)) {      
          /* Omitted  Code*/              
          if (local_20c + _Size_00 + iVar1 + local_214 + _Size < 0x201) {
            memcpy(local_208 + local_214 + iVar1 + _Size_00 + local_20c,
                   (void *)((int)*param_1 + *param_6), _Size );
            param_1[1] = (void *)(local_20c + _Size_00 + iVar1 + local_214 + _Size);
            memcpy(*param_1,local_208,(size_t)param_1[1]);
            *(int *)(param_5 + 0xc) = (int)*param_1 + local_20c;
            *(int *)(param_4 + 0xc) = *(int *)(param_5 + 0xc) + *(int *)(param_5 + 8) * 2;
            *param_6 = local_20c + _Size_00 + iVar1;
            if ((*param_6 & 3) != 0) {
              *param_6 = *param_6 + 2;
            }
          }
        }
      }
    }
  }
  FUN_006ce8f9();
  return;
}

here we controll the “*param_1” variable which is used to calculate many variables that is used to calculate “_size” which is the size of the data will be copied using “mempcy”

memcpy(local_208 + local_214 + iVar1 + _Size_00 + local_20c,
       (void *)((int)*param_1 + *param_6), _Size );

but the true problem here is that the “_size” is an ubsigned integer that is used to store the result of subtraction operation of singed number “param_1” that can lead to make it a large number and that make us able to copy a data out of bound.

 Insinity checks :
    When the sanatization can be bypassed easily like if you checked the variable as signed then used it as unsigned.

Here we have another vulnrable snippest Bluetooth (CC256x and WL18xx chips) assigned CVE-2019-15948


////ACID: where ptr_ll_pkt points after assignment
// Pseudocode from Ghidra decompilation
void process_adv_ind_pdu(int ptr_some_struct)
{
  byte bVar1;
  byte ll_len;
  uint n;
  uint uVar2;
  byte *ptr_ll_pkt;
  undefined local_40;
  byte local_3f;
  undefined auStack62 [0x6];
  undefined local_38;
  undefined stack_buffer [0x1f];
  undefined local_18;

  ptr_ll_pkt = (byte *)(DAT_0005b528 + (uint)*(ushort *)(ptr_some_struct + 0x8));
  bVar1 = *ptr_ll_pkt;
  ll_len = ptr_ll_pkt[0x1];
  uVar2 = (uint)bVar1 & 0xf;
  local_3f = (byte)(((uint)bVar1 << 0x19) >> 0x1f);
  FUN_00067554(auStack62,ptr_ll_pkt + 0x2,0x6);
  n = ((uint)ll_len & 0x3f) - 0x6 & 0xff;
  local_38 = (undefined)n;
  memcpy(stack_buffer,ptr_ll_pkt + 0x8,n);
  local_18 = *(undefined *)(ptr_some_struct + 0xa);
  if ((bVar1 & 0xf) == 0x0) {
    local_40 = 0x0;
  }
  else {
    if (uVar2 == 0x1) {
      local_40 = 0x1;
      local_38 = 0x0;
    }
    else {
      if (uVar2 == 0x2) {
        local_40 = 0x3;
      }
      else {
        if (uVar2 != 0x6) {
          return;
        }
        local_40 = 0x2;
      }
    }
  }
  FUN_000398e2(0x1,&local_40);
  return;
}

As we controll the variable “ptr_ll_pkt” so we can controll the variable “n” which is used as the size to memcpy and we can make it over copy in the following line of code.

n = ((uint)ll_len & 0x3f) - 0x6 & 0xff;

Categories:

Updated: