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

Memory allocation failure caused by the missing boundary check in parseSWF_DEFINELOSSLESS2 #235

Open
0xdd96 opened this issue Dec 1, 2021 · 0 comments

Comments

@0xdd96
Copy link

0xdd96 commented Dec 1, 2021

version: master(commit 04aee52 )
command: listswf $FILE

root:/path_to_libming/build/bin# ./listswf poc
==26225==WARNING: AddressSanitizer failed to allocate 0xfffffffffffffff9 bytes
==26225==AddressSanitizer's allocator is terminating the process instead of returning 0
==26225==If you don't like this behavior set allocator_may_return_null=1
==26225==AddressSanitizer CHECK failed: /mnt/d/CLib/llvm-6.0.1/projects/compiler-rt/lib/sanitizer_common/sanitizer_allocator.cc:225 "((0)) != (0)" (0x0, 0x0)
    #0 0x4e3385 in __asan::AsanCheckFailed(char const*, int, char const*, unsigned long long, unsigned long long) /mnt/d/CLib/llvm-6.0.1/projects/compiler-rt/lib/asan/asan_rtl.cc:69
    #1 0x500c45 in __sanitizer::CheckFailed(char const*, int, char const*, unsigned long long, unsigned long long) /mnt/d/CLib/llvm-6.0.1/projects/compiler-rt/lib/sanitizer_common/sanitizer_termination.cc:79
    #2 0x4e9786 in __sanitizer::ReportAllocatorCannotReturnNull() /mnt/d/CLib/llvm-6.0.1/projects/compiler-rt/lib/sanitizer_common/sanitizer_allocator.cc:225
    #3 0x4e97c6 in __sanitizer::ReturnNullOrDieOnFailure::OnBadRequest() /mnt/d/CLib/llvm-6.0.1/projects/compiler-rt/lib/sanitizer_common/sanitizer_allocator.cc:241
    #4 0x41f676 in __asan::asan_malloc(unsigned long, __sanitizer::BufferedStackTrace*) /mnt/d/CLib/llvm-6.0.1/projects/compiler-rt/lib/asan/asan_allocator.cc:856
    #5 0x4da20b in malloc /mnt/d/CLib/llvm-6.0.1/projects/compiler-rt/lib/asan/asan_malloc_linux.cc:89
    #6 0x5b7e7e in readBytes /path_to_libming/util/read.c:252:17
    #7 0x59b963 in parseSWF_DEFINELOSSLESS2 /path_to_libming/util/parser.c:2168:38
    #8 0x546cf9 in blockParse /path_to_libming/util/blocktypes.c:145:14
    #9 0x53bc13 in readMovie /path_to_libming/util/main.c:269:11
    #10 0x53a0d6 in main /path_to_libming/util/main.c:354:2
    #11 0x7fce52d93bf6 in __libc_start_main /build/glibc-S9d2JN/glibc-2.27/csu/../csu/libc-start.c:310
    #12 0x41a2f9 in _start (/path_to_libming/build/bin/listswf+0x41a2f9)

The cause of this bug is the lack of boundary checks. Specifically, in the parseSWF_DEFINELOSSLESS2 function, the size of end and fileOffset is not compared when readBytes is called. As a result, end-fileOffset may be a negative integer, which eventually leads to allocation failure.

The detailed call chain analysis is as follows.
Download poc

static void readMovie(FILE *f)
{
	int block, type, length, nextFrame=0;
	SWF_Parserstruct *blockp;
	for (;;)
	{
		if(filelen_check_fails(2))
			break;
                // Read 16 bits from the input file, the block is controlled by the attacker
		block = readUInt16 (f);
		type = block >> 6;
                
                 // length = block & 0x3f, which means length<=3f
		length = block & ((1 << 6) - 1);
		if (length == 63)		/* it's a long block. */ 
		{
			if(filelen_check_fails(4))
				break;
			unsigned long real_length = readUInt32 (f);

                        if (real_length > INT_MAX) {
		            SWF_warn(" Could not process long block with length %lu:"
                                     " blocks with length > %d not supported on this system\n",
                                     real_length, INT_MAX);
                            continue;
                        } else {
                            length = (int) real_length;
                        }
		}
		nextFrame = fileOffset+length;
		
		if(filelen_check_fails(length))
			break;
		blockp= blockParse(f, length, type);
                ......
	}
	......
}


SWF_Parserstruct *
blockParse (FILE *f, int length, SWFBlocktype header)
{
  int i;

  for (i = 0; i < numBlocks; i++)
  {
    // Select the corresponding parser to parse
    if (blocks[i].type == header)
    {
      return blocks[i].parser(f,length);
    }
  }
  return parseSWF_UNKNOWNBLOCK(f, length);
}


SWF_Parserstruct *
parseSWF_DEFINELOSSLESS2 (FILE * f, int length)
{
  int end = fileOffset + length;
  PAR_BEGIN (SWF_DEFINELOSSLESS2);

  parserrec->CharacterID = readUInt16 (f); // Read 16 bits from the input file, and fileOffset = fileOffset+2
  parserrec->BitmapFormat = readUInt8 (f); // Read 8 bits from the input file, and fileOffset = fileOffset+1
  parserrec->BitmapWidth = readUInt16 (f); // Read 16 bits from the input file, and fileOffset = fileOffset+2
  parserrec->BitmapHeight = readUInt16 (f); Read 16 bits from the input file, and fileOffset = fileOffset+2
  if( parserrec->BitmapFormat == 3 /* 8-bit */ ) {
      parserrec->BitmapColorTableSize = readUInt8 (f);
  }
  // When length=0, at this moment end=Old_fileOffset, fileOffset=Old_fileOffset+7, then end-fileOffset will be equal to -7 which is a negative integer
  parserrec->ZlibBitmapData = (UI8 *)readBytes (f,end-fileOffset);

  PAR_END;
}


char *readBytes(FILE *f, unsigned long size)
{

  if (size < 1) {
#if DEBUG
    SWF_warn("readBytes: want to read %lu < 1 bytes: Handling a 0\n", size);
#endif
    size = 0;
  }

  unsigned long i;
  char *buf;
  // The parameter size's type is unsigned long. Given a negative integer as input, it will be treated as a large unsigned integer and passed to malloc, causing allocation failures.
  buf = (char *)malloc(sizeof(char)*size);

  if (buf == NULL) {
    fprintf(stderr, "readBytes: Failed to allocate %lu bytes", sizeof(char) * size);
    exit(-1);
  }

  for(i=0;i<size;i++)
  {
    buf[i]=(char)readUInt8(f);
  }

  return buf;
}
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