Advertisement
Guest User

Untitled

a guest
Oct 15th, 2019
111
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.77 KB | None | 0 0
  1. # CVE-2018-14054: LibMP4v2 MP4StringProperty Handling Double Free Vulnerability
  2.  
  3. ## Introduction
  4.  
  5. LibMP4v2 is an open source MP4 processing library, designed to create and modify MP4 files as defined by ISO-IEC:14496-1:2001 MPEG-4 Systems.
  6.  
  7. Originally discovered by Ruikai Liu, a double free vulnerability was found in the MP4StringProperty code. While parsing MP4 atoms, it is possible to cause a MP4StringProperty's value to be freed twice due to exception handling, resulting a double free condition. Since this is library code and not actively maintained, many third party applications seem to be affected by this without a fix.
  8.  
  9. ## Technical Analysis
  10.  
  11. In MP4 format, data units are called atoms, which contain information about the video file. One of those is called "mp4v", and this particular one is related to the vulnerability. To understand the problem, we want to learn how atoms are created and destroyed in code, and eventually the walk-through should reveal the double free condition.
  12.  
  13. ### Object Creation
  14.  
  15. First off, atoms are parsed and read from an MP4 file. Our analysis begins with the following:
  16.  
  17. ```cpp
  18. // Line 399 (mp4atom.cpp)
  19. void MP4Atom::ReadChildAtoms() {
  20. // ... code ...
  21. // Line 428
  22. MP4Atom* pChildAtom = MP4Atom::ReadAtom(m_File, this);
  23. // code
  24. }
  25. ```
  26.  
  27. In `MP4Atom::ReadAtom`, an atom object is created:
  28.  
  29. ```cpp
  30. // Line 112 (mp4atom.cpp)
  31. MP4Atom* MP4Atom::ReadAtom(MP4File& file, MP4Atom* pParentAtom) {
  32. MP4Atom* pAtom = CreateAtom(file, pParentAtom, type);
  33. // ... code ...
  34. ```
  35.  
  36. The actual atom object depends on the type specified in the media file. For example, if the type is "mp4v", then this function should return **MP4Mp4vAtom**.
  37.  
  38. When the `MP4Mp4vAtom` object is being prepared, multiple properties are born in the process. One of those is the **compressorName** string property:
  39.  
  40. ```cpp
  41. // Line 46 (atom_mp4v.cpp)
  42. MP4StringProperty* pProp = new MP4StringProperty(*this, "compressorName");
  43. pProp->SetFixedLength(32);
  44. pProp->SetCountedFormat(true);
  45. pProp->SetValue("");
  46. AddProperty(pProp); /* 6 */
  47. ```
  48.  
  49. Basically what this does is setting the `compressName` property to a 32+1 byte allocation, which is tracked in an array named **m_values**. It also sets the default value to empty, and finally that property object is saved by calling `AddProperty`, which is for another array named **m_pProperties**:
  50.  
  51. ```cpp
  52. // Line 350 (mp4property.cpp)
  53. void MP4StringProperty::SetValue(const char* value, uint32_t index)
  54. {
  55. if (m_readOnly) {
  56. ostringstream msg;
  57. msg << "property " << m_name << "is read-only";
  58. throw new PlatformException(msg.str().c_str(), EACCES, __FILE__, __LINE__, __FUNCTION__ );
  59. }
  60.  
  61. MP4Free(m_values[index]);
  62.  
  63. if (m_fixedLength) {
  64. m_values[index] = (char*)MP4Calloc(m_fixedLength + 1);
  65. if (value) {
  66. strncpy(m_values[index], value, m_fixedLength);
  67. }
  68. } else {
  69. if (value) {
  70. m_values[index] = MP4Stralloc(value);
  71. } else {
  72. m_values[index] = NULL;
  73. }
  74. }
  75. }
  76. ```
  77.  
  78. ### Objection Destruction
  79.  
  80. The `MP4StringProperty` class has a destructor that empties all the heap allocations in `m_values`:
  81.  
  82. ```cpp
  83. // Line 331 (mp4property.cpp)
  84. MP4StringProperty::~MP4StringProperty()
  85. {
  86. uint32_t count = GetCount();
  87. for (uint32_t i = 0; i < count; i++) {
  88. MP4Free(m_values[i]);
  89. }
  90. }
  91. ```
  92.  
  93. In order to trigger that destructor, one way is to destroy the atom object, which triggers its own destructor that clears the property array:
  94.  
  95. ```cpp
  96. // Line 61 (mp4atom.cpp)
  97. MP4Atom::~MP4Atom()
  98. {
  99. uint32_t i;
  100.  
  101. for (i = 0; i < m_pProperties.Size(); i++) {
  102. delete m_pProperties[i];
  103. }
  104.  
  105. // ... destroying other things ...
  106. }
  107. ```
  108.  
  109. OK, so `MP4Atom::~MP4Atom` triggers `MP4BytesProperty::~MP4StringProperty`. Got it. And this is where things go wrong.
  110.  
  111. ### The First Free
  112.  
  113. Let's rewind a bit and examine the `MP4Atom::ReadAtom` function again (line 112 in mp4atom.cpp). After an atom is created, it also performs a read operation toward the end of the function:
  114.  
  115. ```cpp
  116. // Line 193 (mp4atom.cpp)
  117. try {
  118. pAtom->Read();
  119. }
  120. catch (Exception* x) {
  121. // delete atom and rethrow so we don't leak memory.
  122. delete pAtom;
  123. throw x;
  124. }
  125. ```
  126.  
  127. The Read is a virtual function, so many atom oriented classes may implement their own. If this isn't overloaded, then the generic version is also available. In this generic function, we just want to focus on how it loads properties:
  128.  
  129. ```cpp
  130. // Line 222 (mp4atom.cpp)
  131. void MP4Atom::Read()
  132. {
  133. // ... code ...
  134. ReadProperties();
  135. // ... code ...
  136. }
  137. ```
  138.  
  139. The ReadProperties basically does this in a loop:
  140.  
  141. ```cpp
  142. // Line 376 (MP4Atom::ReadProperties in mp4atom.cpp)
  143. m_pProperties[i]->Read(m_File);
  144. ```
  145.  
  146. As you remember, one of the properties is **compressorName**, which is a type of `MP4StringProperty`. In this context, we are looking at this Read function:
  147.  
  148. ```cpp
  149. // Line 374 (mp4property.cpp)
  150. void MP4StringProperty::Read( MP4File& file, uint32_t index )
  151. {
  152. // ... code ...
  153.  
  154. for( uint32_t i = begin; i < max; i++ ) {
  155. char*& value = m_values[i];
  156. MP4Free(value);
  157. if( m_useCountedFormat ) {
  158. value = file.ReadCountedString( (m_useUnicode ? 2 : 1), m_useExpandedCount, m_fixedLength );
  159. }
  160. // ... code ...
  161. }
  162. }
  163. ```
  164.  
  165. Notice the MP4Free is our first free, which frees the string property value. We know we will go down to the **ReadCountedString** path, because the m_useCountedFormat flag was set by `SetCountedFormat` while creating the `compressorName` property (line 48 in atom_mp4v.cpp).
  166.  
  167. ### The Second Free
  168.  
  169. The problem with `ReadCountedString` is that it may throw exceptions, which causes the property reading operation to fail, forcing the atom object to be deleted. For example:
  170.  
  171. Line 383 in MP4File::ReadCountedString (mp4file_io.cpp):
  172.  
  173. ```cpp
  174. if (ix > 25) throw new PlatformException("Counted string too long 25 * 255",ERANGE, __FILE__, __LINE__, __FUNCTION__);
  175. ```
  176.  
  177. Line 81 in MP4File::ReadBytes, used by ReadCountedString (mp4file_io.cpp):
  178.  
  179. ```cpp
  180. if( m_memoryBufferPosition + bufsiz > m_memoryBufferSize ) throw new Exception( "not enough bytes, reached end-of-memory", __FILE__, __LINE__, __FUNCTION__ );
  181. ```
  182.  
  183. Line 93 in MP4File::ReadBytes (mp4file_io.cpp):
  184.  
  185. ```cpp
  186. if( file->read( buf, bufsiz, nin )) throw new PlatformException( "read failed", sys::getLastError(), __FILE__, __LINE__, __FUNCTION__ );
  187. ```
  188.  
  189. Line 95 in MP4File::ReadBytes (mp4file_io.cpp):
  190.  
  191. ```cpp
  192. if( nin != bufsiz ) throw new Exception( "not enough bytes, reached end-of-file", __FILE__, __LINE__, __FUNCTION__ );
  193. ```
  194.  
  195. Whatever the exception is, it is handled way back in MP4Atom::ReadAtom, specifically here:
  196.  
  197. ```cpp
  198. // Line 193 (mp4atom.cpp)
  199. try {
  200. pAtom->Read();
  201. }
  202. catch (Exception* x) {
  203. // delete atom and rethrow so we don't leak memory.
  204. delete pAtom;
  205. throw x;
  206. }
  207. ```
  208.  
  209. Notice the `delete` operator, which is our second free. Again, if an atom is deleted, the `MP4Atom::~MP4Atom` destructor is called to clear the properties, and that causes `MP4StringProperty::~MP4StringProperty` to be called as part of the chain of reaction, resulting the `m_values` getting cleared.
  210.  
  211. ### Summary
  212.  
  213. In short, the MP4StringProperty handling is doomed here:
  214.  
  215. ```cpp
  216. void MP4StringProperty::Read( MP4File& file, uint32_t index ) {
  217. // ... code ...
  218.  
  219. // Line 392 (mp4property.cpp)
  220. MP4Free(value);
  221. if( m_useCountedFormat ) {
  222. value = file.ReadCountedString( (m_useUnicode ? 2 : 1), m_useExpandedCount, m_fixedLength );
  223.  
  224. // ... code ...
  225. }
  226. ```
  227.  
  228. The `MP4StringProperty::Read` function wants to update the string property, but never gets a replacement. Instead, it could get an exception, and causing the second free.
  229.  
  230. There are multiple ways to fix this. The easier way is by setting the value to NULL after the first free, so the string reading operation could continue successfully even with the second free. The other way is probably redo the exception handling a bit more strategically.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement