CVE-2017-8929 (published 2017-05-14T22:29Z)

The sized_string_cmp function in libyara/sizedstr.c in YARA 3.5.0 allows remote attackers to cause a denial of service (use-after-free and application crash) via a crafted rule.

Severity: MEDIUM
CVSS v2 base score: 5.0 (AV:N/AC:L/Au:N/C:N/I:N/A:P)
CVSS v3 base score: 7.5, HIGH (CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H)

Weakness information:
cwe_id: CWE-416
cwe_name: Use After Free
description: The product reuses or references memory after it has been freed. At some point afterward, the memory may be allocated again and saved in another pointer, while the original pointer references a location somewhere within the new allocation. Any operations using the original pointer are no longer valid because the memory "belongs" to the code that operates on the new pointer.
extended_description: 
             
             
             
			 
             
             
         
         

Fix information:
repo_url: https://github.com/VirusTotal/yara
commit_details: repo_url: https://github.com/VirusTotal/yara
author: Victor M. Alvarez
author_date: 2017-05-14 10:27:39+02:00
msg: Fix issue #658
repository: repo_url: https://github.com/VirusTotal/yara
repo_name: VirusTotal/yara
description: The pattern matching swiss knife
repo_language: C
owner: VirusTotal
file_changes: filename: arena.c
change_type: ModificationType.MODIFY
diff: @@ -316,7 +316,7 @@ void yr_arena_destroy(
 //    YR_ARENA* arena  - Pointer to the arena.
 //
 // Returns:
-//    A pointer to the arena's data. NULL if the no data has been written to
+//    A pointer to the arena's data. NULL if no data has been written to
 //    the arena yet.
 //
 

programming_language: C++
filename: exec.c
change_type: ModificationType.MODIFY
diff: @@ -34,6 +34,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <time.h>
 #include <math.h>
 
+#include <yara/arena.h>
 #include <yara/endian.h>
 #include <yara/exec.h>
 #include <yara/limits.h>
@@ -177,6 +178,8 @@ int yr_execute_code(
   YR_RULE* rule;
   YR_MATCH* match;
   YR_OBJECT_FUNCTION* function;
+  YR_OBJECT** obj_ptr;
+  YR_ARENA* obj_arena;
 
   char* identifier;
   char* args_fmt;
@@ -201,6 +204,10 @@ int yr_execute_code(
   if (stack == NULL)
     return ERROR_INSUFFICIENT_MEMORY;
 
+  FAIL_ON_ERROR_WITH_CLEANUP(
+      yr_arena_create(1024, 0, &obj_arena),
+      yr_free(stack));
+
   while(!stop)
   {
     switch(*ip)
@@ -437,6 +444,8 @@ int yr_execute_code(
         rule->clock_ticks += clock() - start;
         start = clock();
         #endif
+
+        assert(sp == 0);
         break;
 
       case OP_OBJ_LOAD:
@@ -577,18 +586,26 @@ int yr_execute_code(
           }
         }
 
+        // if i == MAX_OVERLOADED_FUNCTIONS at this point no matching
+        // prototype was found, but this shouldn't happen.
+
         assert(i < MAX_OVERLOADED_FUNCTIONS);
 
+        // make a copy of the returned object and push the copy into the stack
+        // function->return_obj can't be pushed because it can change in
+        // subsequent calls to the same function.
+
         if (result == ERROR_SUCCESS)
-        {
-          r1.o = function->return_obj;
-          push(r1);
-        }
-        else
-        {
-          stop = TRUE;
-        }
+          result = yr_object_copy(function->return_obj, &r1.o);
+
+        // a pointer to the copied object is stored in a arena in order to
+        // free the object before exiting yr_execute_code
 
+        if (result == ERROR_SUCCESS)
+          result = yr_arena_write_data(obj_arena, &r1.o, sizeof(r1.o), NULL);
+
+        stop = (result != ERROR_SUCCESS);
+        push(r1);
         break;
 
       case OP_FOUND:
@@ -1146,6 +1163,17 @@ int yr_execute_code(
     ip++;
   }
 
+  obj_ptr = (YR_OBJECT**) yr_arena_base_address(obj_arena);
+
+  while (obj_ptr != NULL)
+  {
+    yr_object_destroy(*obj_ptr);
+
+    obj_ptr = (YR_OBJECT**) yr_arena_next_address(
+        obj_arena, obj_ptr, sizeof(YR_OBJECT*));
+  }
+
+  yr_arena_destroy(obj_arena);
   yr_modules_unload_all(context);
   yr_free(stack);
 

programming_language: C
filename: object.h
change_type: ModificationType.MODIFY
diff: @@ -86,6 +86,11 @@ void yr_object_destroy(
     YR_OBJECT* object);
 
 
+int yr_object_copy(
+    YR_OBJECT* object,
+    YR_OBJECT** object_copy);
+
+
 YR_OBJECT* yr_object_lookup_field(
     YR_OBJECT* object,
     const char* field_name);

programming_language: C
filename: sizedstr.h
change_type: ModificationType.MODIFY
diff: @@ -51,7 +51,7 @@ typedef struct _SIZED_STRING
 {
   uint32_t length;
   uint32_t flags;
-  
+
   char c_string[1];
 
 } SIZED_STRING;
@@ -60,7 +60,11 @@ typedef struct _SIZED_STRING
 
 
 int sized_string_cmp(
-  SIZED_STRING* s1,
-  SIZED_STRING* s2);
+    SIZED_STRING* s1,
+    SIZED_STRING* s2);
+
+
+SIZED_STRING* sized_string_dup(
+    SIZED_STRING* s);
 
 #endif

programming_language: C
filename: tests.c
change_type: ModificationType.MODIFY
diff: @@ -88,6 +88,23 @@ define_function(match)
 }
 
 
+define_function(foobar)
+{
+  int64_t arg = integer_argument(1);
+
+  switch (arg)
+  {
+    case 1:
+      return_string("foo");
+      break;
+    case 2:
+      return_string("bar");
+      break;
+  }
+
+  return_string("oops")
+}
+
 begin_declarations;
 
   begin_struct("constants");
@@ -125,6 +142,7 @@ begin_declarations;
   declare_function("fsum", "fff", "f", fsum_3);
   declare_function("length", "s", "i", length);
   declare_function("empty", "", "s", empty);
+  declare_function("foobar", "i", "s", foobar);
 
 end_declarations;
 

programming_language: C
filename: object.c
change_type: ModificationType.MODIFY
diff: @@ -573,11 +573,23 @@ int yr_object_copy(
   switch(object->type)
   {
     case OBJECT_TYPE_INTEGER:
-      ((YR_OBJECT_INTEGER*) copy)->value = UNDEFINED;
+      ((YR_OBJECT_INTEGER*) copy)->value = ((YR_OBJECT_INTEGER*) object)->value;
       break;
 
     case OBJECT_TYPE_STRING:
-      ((YR_OBJECT_STRING*) copy)->value = NULL;
+      if (((YR_OBJECT_STRING*) object)->value != NULL)
+      {
+        ((YR_OBJECT_STRING*) copy)->value = sized_string_dup(
+            ((YR_OBJECT_STRING*) object)->value);
+      }
+      else
+      {
+        ((YR_OBJECT_STRING*) copy)->value = NULL;
+      }
+      break;
+
+    case OBJECT_TYPE_FLOAT:
+      ((YR_OBJECT_DOUBLE*) copy)->value = ((YR_OBJECT_DOUBLE*) object)->value;
       break;
 
     case OBJECT_TYPE_FUNCTION:

programming_language: C
filename: sizedstr.c
change_type: ModificationType.MODIFY
diff: @@ -27,6 +27,8 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
+#include <string.h>
+#include <yara/mem.h>
 #include <yara/sizedstr.h>
 
 
@@ -54,3 +56,21 @@ int sized_string_cmp(
   else
     return 1;
 }
+
+
+SIZED_STRING* sized_string_dup(
+    SIZED_STRING* s)
+{
+  SIZED_STRING* result = (SIZED_STRING*) yr_malloc(
+      sizeof(SIZED_STRING) + s->length);
+
+  if (result == NULL)
+    return NULL;
+
+  result->length = s->length;
+  result->flags = s->flags;
+
+  strncpy(result->c_string, s->c_string, s->length + 1);
+
+  return result;
+}

programming_language: C
filename: test-rules.c
change_type: ModificationType.MODIFY
diff: @@ -1439,6 +1439,20 @@ static void test_modules()
       }",
       NULL);
 
+  assert_true_rule(
+      "import \"tests\" \
+       rule test { \
+        condition: tests.foobar(1) == tests.foobar(1) \
+      }",
+      NULL);
+
+  assert_true_rule(
+      "import \"tests\" \
+       rule test { \
+        condition: tests.foobar(1) != tests.foobar(2) \
+      }",
+      NULL);
+
   assert_true_rule(
       "import \"tests\" \
        rule test { \

programming_language: C