8
AUTOMATING CUCKOO SANDBOX

image

Cuckoo Sandbox is an open source project that allows you to run malware samples within the safety of virtual machines, and then analyze and report on how the malware behaved in a virtual sandbox without the threat of the malware infecting your real machine. Written in Python, Cuckoo Sandbox also offers a REST API that allows a programmer using any language to fully automate many of Cuckoo’s features, such as spinning up sandboxes, running malware, and grabbing reports. In this chapter, we’ll do all of this with easy-to-use C# libraries and classes. However, there is a lot of work to do, like setting up the virtual environment for Cuckoo to use, before we can begin testing and running malware samples with C#. You can find more information about and download Cuckoo Sandbox at https://www.cuckoosandbox.org/.

Setting Up Cuckoo Sandbox

We won’t cover setting up Cuckoo Sandbox in this chapter because the instructions can vary greatly between different operating systems—and even based on which version of Windows you use as the virtual machine sandbox. This chapter will assume that you correctly set up Cuckoo Sandbox with a Windows guest and that Cuckoo is completely functional. Be sure to follow the directions on the main Cuckoo Sandbox website (http://docs.cuckoosandbox.org/en/latest/installation/), which provides up-to-date and thorough documentation on setting up and configuring the software.

In the conf/cuckoo.conf file that ships with Cuckoo Sandbox, I recommend making an adjustment to the default timeout configuration so that it is shorter (I set mine to 15 seconds) before you begin working with the API. This will make things easier and faster during testing. In your cuckoo.conf file, you will see a section toward the bottom that looks like Listing 8-1.

[timeouts]
# Set the default analysis timeout expressed in seconds. This value will be
# used to define after how many seconds the analysis will terminate unless
# otherwise specified at submission.
default = 120

Listing 8-1: The default timeout configuration section in cuckoo.conf

The default timeout for Cuckoo testing is set to 120 seconds . A long timeout can make you quite impatient to see if you fixed a problem during debugging, since you must wait for the timeout to be reached before a report is ready, but setting this value between 15 and 30 seconds should be good for our purposes.

Manually Running the Cuckoo Sandbox API

Like Nessus, the Cuckoo Sandbox follows a REST pattern (see the description of REST in Chapter 5 if you need a refresher). However, the Cuckoo Sandbox API is far simpler than the Nessus API, since we only need to communicate with a couple of API endpoints. To do this, we’ll continue to use the session/manager pattern and implement the CuckooSession class first, which encompasses how we will communicate with the Cuckoo Sandbox API. Let’s check whether you set up Cuckoo Sandbox correctly, though, before we get started writing code.

Starting the API

With Cuckoo Sandbox successfully installed, you should be able to start it locally with the command ./cuckoo.py, as in Listing 8-2. If you receive an error, ensure the VM you’re using for testing is running.

$ ./cuckoo.py

  eeee e   e eeee e   e  eeeee eeeee
  8  8 8   8 8  8 8   8  8  88 8  88
  8e   8e  8 8e   8eee8e 8   8 8   8
  88   88  8 88   88  8  8   8 8   8
  88e8 88ee8 88e8 88  8  8eee8 8eee8

 Cuckoo Sandbox 2.0-rc2
 www.cuckoosandbox.org
 Copyright (c) 2010-2015

 Checking for updates...
 Good! You have the latest version available.

2016-05-19 16:17:06,146 [lib.cuckoo.core.scheduler] INFO: Using "virtualbox" as machine manager
2016-05-19 16:17:07,484 [lib.cuckoo.core.scheduler] INFO: Loaded 1 machine/s
2016-05-19 16:17:07,495 [lib.cuckoo.core.scheduler] INFO: Waiting for analysis tasks...

Listing 8-2: Starting the Cuckoo Sandbox manager

Starting Cuckoo successfully should yield a fun ASCII art banner, followed by quick informational lines about how many VMs have been loaded. After starting the main Cuckoo script, you need to start the API that we’ll communicate with. Both of these Python scripts must be running at the same time! The cuckoo.py Python script is the engine behind Cuckoo Sandbox. If we start the api.py script without starting the cuckoo.py script, as in Listing 8-3, then our API requests won’t do anything. For us to use the Cuckoo Sandbox from the API, both cuckoo.py and api.py must be running. By default, the Cuckoo Sandbox API listens on port 8090, as Listing 8-3 shows.

$ utils/api.py -H 0.0.0.0
 * Running on http://0.0.0.0:8090/ (Press CTRL+C to quit)

Listing 8-3: Running the HTTP API for Cuckoo Sandbox

To specify an IP address to listen on (the default is localhost), you can pass the utils/api.py script the -H argument , which tells the API which IP address to use when listening for API requests. In this case, we have set 0.0.0.0 as the IP address to listen on, which means all network interfaces (both internal and external IP addresses for the system) will have port 8090 available for communication since we are using the default port. The URL that the Cuckoo API is listening on is also printed to the screen after starting. This URL is how we’ll communicate with the API to drive Cuckoo Sandbox in the rest of the chapter.

Checking Cuckoo’s Status

We can test the API to ensure it has been set up correctly using the curl command line tool, as we have in previous chapters for other APIs. Later in the chapter, we make similar API requests to create a task, watch the task until completed, and report on the file to see how it behaved when it ran. But to get started, Listing 8-4 shows how to use curl to retrieve the Cuckoo Sandbox status information in JSON format with the HTTP API.

$ curl http://127.0.0.1:8090/cuckoo/status
{
  "cpuload": [
    0.0,
    0.02,
    0.05
  ],
  "diskspace": {
    "analyses": {
      "free": 342228357120,
      "total": 486836101120,
      "used": 144607744000
    },
    "binaries": {
      "free": 342228357120,
      "total": 486836101120,
      "used": 144607744000
    }
  },
  "hostname": "fdsa-E7450",
 "machines": {
    "available": 1,
    "total": 1
  },
  "memory": 82.06295645686164,
 "tasks": {
    "completed": 0,
    "pending": 0,
    "reported": 3,
    "running": 0,
    "total": 13
  },
 "version": "2.0-rc2"
}

Listing 8-4: Using curl to retrieve the Cuckoo Sandbox status via the HTTP API

The status information is quite useful, detailing many aspects of the Cuckoo Sandbox system. Of note is the aggregate task information , with the number of tasks that have been run or are running by Cuckoo, listed by status. A task could be analyzing a file that is running or opening a web page with a URL, though we’ll only cover submitting a file for analysis in this chapter. You can also see the number of VMs you have available for analysis and the current version of Cuckoo .

Great, the API is up and running! We’ll use this same status API endpoint later to test our code as we write it and to discuss the JSON it returns more thoroughly. At the moment, we only need to confirm the API is up and running.

Creating the CuckooSession Class

Now that we know the API works and we can make HTTP requests and get the JSON responses, we can start writing our code to drive Cuckoo Sandbox programmatically. Once we have the base classes built, we can submit a file that will be analyzed as it runs and then report on the results. We’ll start with the CuckooSession class, which begins in Listing 8-5.

public class CuckooSession
{
  public CuckooSession(string host, int port)
  {
    this.Host = host;
    this.Port = port;
  }

  public string Host { get; set; }
  public int Port { get; set; }

Listing 8-5: Starting the CuckooSession class

Keeping things simple to start with, we create the CuckooSession class as well as the CuckooSession constructor. The constructor takes two arguments . The first is the host to connect to, and the second is the port on the host on which the API will be listening. In the constructor, the two values passed as arguments are assigned to their respective properties, Host and Port , which are defined below the constructor. Next, we need to implement the methods available for the CuckooSession class.

Writing the ExecuteCommand() Methods to Handle HTTP Requests

Cuckoo expects two kinds of HTTP requests when API requests are made: a traditional HTTP request and a more complex HTTP multipart form request used for sending files to Cuckoo for analysis. We’ll implement two ExecuteCommand() methods to cover these types of requests: first, we’ll use a simpler ExecuteCommand() method that accepts two arguments for the traditional request, and then we’ll overload it with an ExecuteCommand() method that takes three arguments for the multipart request. Creating two methods with the same name but with different arguments, or method overloading, is allowed in C#. This is a good example of when you would use method overloading instead of a single method that accepts optional arguments because the methods for each request are relatively different, despite sharing the same name. Listing 8-6 details the simpler ExecuteCommand() method.

   public JObject ExecuteCommand(string uri, string method)
   {
     HttpWebRequest req = (HttpWebRequest)WebRequest
               .Create("http://" + this.Host + ":" + this.Port + uri);
     req.Method = method;

     string resp = string.Empty;
     using (Stream str = req.GetResponse().GetResponseStream())
     using (StreamReader rdr = new StreamReader(str))
       resp = rdr.ReadToEnd();

   JObject obj = JObject.Parse(resp);
   return obj;
 }

Listing 8-6: The simpler ExecuteCommand() method that accepts just a URI and the HTTP method as arguments

The first ExecuteCommand() method takes two arguments: the URI to request and the HTTP method to use (GET, POST, PUT, and so on). After using Create() to build a new HTTP request and setting the Method property of the request, we make the HTTP request and read the response into a string. Finally, we parse the returned string as JSON and return the new JSON object.

The overloaded ExecuteCommand() method takes three arguments: the URI to request, the HTTP method, and a dictionary of parameters that will be sent in an HTTP multipart request. Multipart requests allow you to send more complex data such as binary files along with other HTTP parameters to a web server, which is exactly how we’ll use it. A full multipart request is shown later in Listing 8-9. How to send this type of request is detailed in Listing 8-7.

  public JObject ExecuteCommand(string uri, string method, IDictionary<string, object> parms)
  {
    HttpWebRequest req = (HttpWebRequest)WebRequest
             .Create("http://" + this.Host + ":" + this.Port + uri);
    req.Method = method;
    string boundary = String.Format("----------{0:N}", Guid.NewGuid());
    byte[] data = GetMultipartFormData(parms, boundary);

    req.ContentLength = data.Length;
    req.ContentType = "multipart/form-data; boundary=" + boundary;

    using (Stream parmStream = req.GetRequestStream())
      parmStream.Write(data, 0, data.Length);

    string resp = string.Empty;
    using (Stream str = req.GetResponse().GetResponseStream())
      using (StreamReader rdr = new StreamReader(str))
        resp = rdr.ReadToEnd();

    JObject obj = JObject.Parse(resp);
    return obj;
  }

Listing 8-7: The overloaded ExecuteCommand() method, which makes a multipart/form-data HTTP request

The second, more complex ExecuteCommand() method takes three arguments, as outlined earlier. After instantiating a new request and setting the HTTP method , we create a boundary that will be used to separate the HTTP parameters in the multipart form request using String.Format() . Once the boundary is created, we call GetMultipartFormData() (which we will implement shortly) to convert the dictionary of parameters passed as the third argument into a multipart HTTP form with the new boundary.

After building the multipart HTTP data, we need to set up the HTTP request by setting the ContentLength and ContentType request properties based on the multipart HTTP data. For the ContentType property, we also append the boundary that will be used to separate the HTTP parameters . Finally, we can write the multipart form data to the HTTP request stream and read the response from the server. With the final response from the server, we parse the response as JSON and then return the JSON object.

Both of these ExecuteCommand() methods will be used to execute API calls against the Cuckoo Sandbox API. But before we can start calling the API endpoints, we need to write a bit more code.

Creating Multipart HTTP Data with the GetMultipartFormData() Method

Although the GetMultipartFormData() method is core to communicating with Cuckoo Sandbox, I’m not going to go over it line by line. This method is actually a good example of a small weakness in the core libraries for C# because it shouldn’t be this complicated to make a multipart HTTP request. Unfortunately, there is no easy-to-use class available that allows us to do this, so we need to create this method to build the HTTP multipart request from scratch. The raw technical details of building multipart HTTP requests are a bit out of scope for what we are looking to accomplish, so I’ll only gloss over the general flow of this method. The method in full (shown in Listing 8-8, minus in-line comments) was written by Brian Grinstead,1 whose work was then incorporated into the RestSharp client (http://restsharp.org/).

  private byte[] GetMultipartFormData(IDictionary<string, object> postParameters, string boundary)
  {
    System.Text.Encoding encoding = System.Text.Encoding.ASCII;
    Stream formDataStream = new System.IO.MemoryStream();
    bool needsCLRF = false;

    foreach (var param in postParameters)
    {
      if (needsCLRF)
        formDataStream.Write(encoding.GetBytes(" "), 0, encoding.GetByteCount(" "));

      needsCLRF = true;
      if (param.Value is FileParameter)
      {

        FileParameter fileToUpload = (FileParameter)param.Value;
        string header = string.Format("--{0} Content-Disposition: form-data; name="{1}";" +
                    "filename="{2}"; Content-Type: {3} ",
                    boundary,
                    param.Key,
                    fileToUpload.FileName ?? param.Key,
                    fileToUpload.ContentType ?? "application/octet-stream");
       formDataStream.Write(encoding.GetBytes(header), 0, encoding.GetByteCount(header));
       formDataStream.Write(fileToUpload.File, 0, fileToUpload.File.Length);
    }
    else
    {
      string postData = string.Format("--{0} Content-Disposition: form-data;" +
                  "name="{1}" {2}",
                  boundary,
                  param.Key,
                  param.Value);
      formDataStream.Write(encoding.GetBytes(postData), 0, encoding.GetByteCount(postData));
    }
  }

  string footer = " --" + boundary + "-- ";
  formDataStream.Write(encoding.GetBytes(footer), 0, encoding.GetByteCount(footer));

  formDataStream.Position = 0;
  byte[] formData = new byte[formDataStream.Length];
  formDataStream.Read(formData, 0, formData.Length);
  formDataStream.Close();
  return formData;
 }
}

Listing 8-8: The GetMultipartFormData() method

In the GetMultipartFormData() method , we start by accepting two arguments: the first is the dictionary of parameters and their respective values that we’ll turn into a multipart form, and the second is the string that we’ll use to separate the file parameters in the request so they can be parsed out. This second argument is called boundary, and we use it to tell the API to split the HTTP request body using this boundary, and then use each section as a separate parameter and value in the request. This can be hard to visualize, so Listing 8-9 details a sample HTTP multipart form request.

POST / HTTP/1.1
Host: localhost:8000
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:29.0) Gecko/20100101 Firefox/29.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Type: multipart/form-data;
boundary=------------------------9051914041544843365972754266
Content-Length: 554

--------------------------9051914041544843365972754266
Content-Disposition: form-data; name="text"

text default
--------------------------9051914041544843365972754266
Content-Disposition: form-data; name="file1"; filename="a.txt"
Content-Type: text/plain
Content of a.txt.

--------------------------9051914041544843365972754266
Content-Disposition: form-data; name="file2"; filename="a.html"
Content-Type: text/html

<!DOCTYPE html><title>Content of a.html.</title>

--------------------------9051914041544843365972754266--

Listing 8-9: A sample HTTP multipart form request

This HTTP request looks a lot like what we are trying to build, so let’s point out the important parts that were mentioned in GetMultipartFormData(). First, note the Content-Type header is multipart/form-data with a boundary , just like the one we set in Listing 8-7. This boundary is used throughout the HTTP request (, , , ) to separate each HTTP parameter. Each parameter also has a parameter name and value . The GetMultipartFormData() method takes the parameter names and values we pass in the Dictionary argument and the boundary and then turns them into a similar HTTP request using the given boundary to separate each parameter.

Processing File Data with the FileParameter Class

In order to send Cuckoo the file or malware we want to analyze, we need to create a class we can use to store the data for the file, such as the file type, filename, and actual content of the file. The simple FileParameter class wraps a bit of the information we need for the GetMultipartFormData() method. It’s shown in Listing 8-10.

public class FileParameter
{
  public byte[] File { get; set; }
  public string FileName { get; set; }
  public string ContentType { get; set; }

  public FileParameter(byte[] file, string filename, string contenttype)
  {
  File = file;
  FileName = filename;
  ContentType = contenttype;
  }
}

Listing 8-10: The FileParameter class

The FileParameter class represents the data we need to build an HTTP parameter that will contain the file to be analyzed. The constructor for the class accepts three arguments: the byte array containing the file contents, the name of the file, and the content type. Each argument is then assigned to the respective class property (, , ).

Testing the CuckooSession and Supporting Classes

We can test what we have written so far with a short and simple Main() method that requests the status of Cuckoo Sandbox using the API. We did this manually in “Checking Cuckoo’s Status” on page 149. Listing 8-11 shows how we can do this using the new CuckooSession class.

public static void Main(string[] args)
{
  CuckooSession session = new CuckooSession("127.0.0.1", 8090);
  JObject response = session.ExecuteCommand("/cuckoo/status", "GET");
  Console.WriteLine(response.ToString());
}

Listing 8-11: Main() method for retrieving the Cuckoo Sandbox status

With a new Main() method , we first create a CuckooSession object by passing the IP address and the port that Cuckoo Sandbox is running on. If the API is running on your local machine, then 127.0.0.1 for the IP should be fine. The IP and port (8090 by default) should have been set up when we started the API in Listing 8-3. Using the new session, we call the ExecuteCommand() method , passing the URI /cuckoo/status as the first argument and the HTTP method GET as the second method. The response is then printed to the screen using WriteLine() .

Running the Main() method should print a JSON dictionary to the screen with status information about Cuckoo, as detailed in Listing 8-12.

$ ./ch8_automating_cuckoo.exe
{
  "cpuload": [
    0.0,
    0.03,
    0.05
  ],
  "diskspace": {
    "analyses": {
      "free": 342524416000,
      "total": 486836101120,
      "used": 144311685120
    },
    "binaries": {
      "free": 342524416000,
      "total": 486836101120,
      "used": 144311685120
    }
  },
  "hostname": "fdsa-E7450",
  "machines": {
    "available": 1,
    "total": 1
   },
   "memory": 85.542549616647932,
   "tasks": {
     "completed": 0,
     "pending": 0,
     "reported": 2,
     "running": 0,
     "total": 12
   },
   "version": "2.0-rc2"
}

Listing 8-12: Testing the CuckooSession class to print the current status information for the Cuckoo Sandbox

You can see that the JSON information printed here is the same as when we ran the API command manually earlier to check Cuckoo’s status.

writing the CuckooManager Class

With the CuckooSession class and other supporting classes implemented, we can move on to the CuckooManager class, which will wrap a few easy API calls. To start off the CuckooManager class, we need the constructor shown in Listing 8-13.

public class CuckooManager : IDisposable
{
  CuckooSession _session = null;
  public CuckooManager(CuckooSession session)
  {
  _session = session;
  }

Listing 8-13: Starting the CuckooManager class

The CuckooManager class starts by implementing the IDisposable interface , which we’ll use to dispose of our private _session variable when we are finished with the CuckooManager class. The class constructor takes only a single argument: the session to use when communicating with the Cuckoo Sandbox instance. The private _session variable is assigned with the argument passed to the constructor so that the methods we will write shortly can use the session to make their specific API calls.

Writing the CreateTask() Method

The first method in the CuckooManager class is CreateTask(), the most complicated manager method we’ll write. The CreateTask() method implements the HTTP call that will create a new task by determining the type of task we are creating and then making the correct HTTP call, as shown in Listing 8-14.

  public int CreateTask(Task task)
  {
    string param = null, uri = "/tasks/create/";
    object val = null;
    if (task is FileTask)
    {
      byte[] data;
      using (FileStream str = new FileStream((task as FileTask).Filepath,
                                               FileMode.Open,
                                               FileAccess.Read))
      {
        data = new byte[str.Length];
        str.Read(data, 0, data.Length);
      }

      param = "file";
      uri += param;
      val = new FileParameter(data, (task as FileTask).Filepath,
                               "application/binary");
    }

    IDictionary<string, object> parms = new Dictionary<string, object>();
    parms.Add(param, val);
    parms.Add("package", task.Package);
    parms.Add("timeout", task.Timeout.ToString());
    parms.Add("options", task.Options);
    parms.Add("machine", task.Machine);
    parms.Add("platform", task.Platform);
    parms.Add("custom", task.Custom);
    parms.Add("memory", task.EnableMemoryDump.ToString());
    parms.Add("enforce_timeout", task.EnableEnforceTimeout.ToString());

    JObject resp = _session.ExecuteCommand(uri, "POST", parms);

    return (int)resp["task_id"];
  }

Listing 8-14: The CreateTask() method

The CreateTask() method starts by first checking whether the task passed in is a FileTask class (the class for describing a file or malware to be analyzed). Because Cuckoo Sandbox supports more than just analyzing files (such as URLs), the CreateTask() method can easily be extended to create different types of tasks this way. If the task is a FileTask, we open the file to send to Cuckoo Sandbox with a new FileStream() and then read the file into a byte array. Once the file has been read , we create a new FileParameter class with the filename, the file bytes, and the content type application/binary.

Then we set up the HTTP parameters we’ll be sending to Cuckoo Sandbox in a new Dictionary . The HTTP parameters are specified in the Cuckoo Sandbox API documentation and should contain the information required to create a task. These parameters allow us to change default configuration items such as which VM to use . Finally, we create the new task by calling ExecuteCommand() with the parameters in the dictionary and then return the new task ID.

The Task Details and Reporting Methods

A few more API calls need to be supported in order for us to submit our file to be analyzed and reported on, but they are much simpler than CreateTask(), as Listing 8-15 details. We just create a method to show the task details, two methods to report on our tasks, and a method to clean up our sessions.

  public Task GetTaskDetails(int id)
  {
    string uri = "/tasks/view/" + id;
    JObject resp = _session.ExecuteCommand(uri, "GET");
  return TaskFactory.CreateTask(resp["task"]);
  }

  public JObject GetTaskReport(int id)
  {
    return GetTaskReport(id, "json");
  }

  public JObject GetTaskReport(int id, string type)
  {
    string uri = "/tasks/report/" + id + "/" + type;
    return _session.ExecuteCommand(uri, "GET");
  }

  public void Dispose()
  {
    _session = null;
  }
}

Listing 8-15: Supporting methods for retrieving task information and reports

The first method we implement is the GetTaskDetails() method , which takes a task ID for the variable id as its only argument. We first create the URI we’ll make the HTTP request to by appending the ID argument to /tasks/view , and then we call ExecuteCommand() with the new URI. This endpoint returns some information about the task, such as the name of the VM running the task and the task’s current status, which we can use to watch the task until it is finished. Finally, we use the TaskFactory.CreateTask() method to turn the JSON task returned by the API into a C# Task class, which we’ll create in the next section.

The second method is a simple convenience method . Because Cuckoo Sandbox supports multiple types of reports (JSON, XML, and so on), there are two GetTaskReport() methods, and the first is used only for JSON reports. It just accepts the ID of the task you want a report for as an argument and calls its overloaded sister method with the same ID passed, but with a second argument specifying that a JSON report should be returned. In the second GetTaskReport() method , the task ID and report type are passed as arguments and then used to build the URI that will be requested in the API call. The new URI is passed to the ExecuteCommand() method , and the report from Cuckoo Sandbox is returned.

Finally, the Dispose() method , which completes the IDisposable interface, is implemented. This method cleans up the session that we used to communicate with the API, assigning null to the private _session variable.

Creating the Task Abstract Class

Supporting the CuckooSession and CuckooManager classes is the Task class, an abstract class that stores most of the relevant information for a given task so that the information can easily be accessed as properties. Listing 8-16 details the abstract Task class.

public abstract class Task
{
  protected Task(JToken token)
  {
    if (token != null)
    {
      this.AddedOn = DateTime.Parse((string)token["added_on"]);

      if (token["completed_on"].Type != JTokenType.Null)
        this.CompletedOn = DateTime.Parse(token["completed_on"].ToObject<string>());

      this.Machine = (string)token["machine"];
      this.Errors = token["errors"].ToObject<ArrayList>();
      this.Custom = (string)token["custom"];
      this.EnableEnforceTimeout = (bool)token["enforce_timeout"];
      this.EnableMemoryDump = (bool)token["memory"];
      this.Guest = token["guest"];
      this.ID = (int)token["id"];
      this.Options = token["options"].ToString();
      this.Package = (string)token["package"];
      this.Platform = (string)token["platform"];
      this.Priority = (int)token["priority"];
      this.SampleID = (int)token["sample_id"];
      this.Status = (string)token["status"];
      this.Target = (string)token["target"];
      this.Timeout = (int)token["timeout"];
    }
  }

  public string Package { get; set; }
  public int Timeout { get; set; }
  public string Options { get; set; }
  public string Machine { get; set; }
  public string Platform { get; set; }
  public string Custom { get; set; }
  public bool EnableMemoryDump { get; set; }
  public bool EnableEnforceTimeout { get; set; }
  public ArrayList Errors { get; set; }
  public string Target { get; set; }
  public int SampleID { get; set; }
  public JToken Guest { get; set; }
  public int Priority { get; set; }
  public string Status { get; set;}
  public int ID { get; set; }
  public DateTime AddedOn { get; set; }
  public DateTime CompletedOn { get; set; }
}

Listing 8-16: The abstract Task class

Although the abstract Task class looks complex at first, all the class has is a constructor and a dozen or so properties. The constructor accepts a JToken as an argument, which is a special JSON class like JObject. The JToken is used to assign all the task details from the JSON to C# properties in the class. The first property we assign with a value in the constructor is the AddedOn property. Using DateTime.Parse() , the timestamp for when the task was created is parsed from a string to a DateTime class, which is assigned to AddedOn. The same is done for the CompletedOn property, also using DateTime.Parse() , if the task has been completed. The rest of the properties are assigned directly using values from the JSON that was passed as the argument to the constructor.

Sorting and Creating Different Class Types

Cuckoo Sandbox supports more than one type of task, even though we are only implementing one (the file analysis task). The FileTask class will inherit from the abstract Task class, but it adds a new property that stores the path of the file we want to send to Cuckoo to analyze. The other type of task supported by Cuckoo is a URL task that opens a given URL in a web browser and analyzes what happens (in case there is a drive-by exploit or other malware on the site).

Creating the FileTask Class to Make File Analysis Tasks

The FileTask class will be used to store the information we need to kick off an analysis of a file. It’s short and sweet, as Listing 8-17 shows, since it inherits most of its properties from the Task class we just implemented.

public class FileTask : Task
{
  public FileTask() : base(null) { }
  public FileTask(JToken dict) : base(dict) { }
  public string Filepath { get; set; }
}

Listing 8-17: The FileTask class that inherits from Task

The simple FileTask class , which inherits from the previous Task class, uses some advanced inheritance techniques available in C#. The class implements two different constructors, both of which pass their arguments to the base Task constructor as well. For instance, the first constructor accepts no arguments and passes a null value to the base class constructor. This allows us to keep a default constructor for the class that doesn’t require any arguments. The second constructor , which accepts a single JToken class as its only argument, passes the JSON argument straight to the base constructor, which will populate the properties the FileTask class inherits from Task. This makes it easy to set up a FileTask using the JSON returned from the Cuckoo API. The only thing we have in the FileTask class that we don’t have in the generic Task class is the Filepath property , which is only useful for submitting file analysis tasks.

Using the TaskFactory Class to Determine the Task Type to Create

Java developers or others familiar with object-oriented programming may already know about the factory pattern used in object-oriented development. It is a flexible way to have a single class manage the creation of many similar but ultimately different types of classes (usually all inheriting from the same base class, but they could also all be implementing the same interface). The TaskFactory class (shown in Listing 8-18) is used to turn a JSON task returned by Cuckoo Sandbox in an API response into our C# Task class, be it a FileTask or otherwise—that is, if you choose to go the extra step and implement the URL task we described for homework!

public static class TaskFactory
{
  public static Task CreateTask(JToken dict)
  {
    Task task = null;
  switch((string)dict["category"])
    {
      case "file":
        task = new FileTask(dict);
        break;
      default:
        throw new Exception("Don't know category: " + dict["category"]);
    }

    return task;
  }
}

Listing 8-18: The TaskFactory static class, which implements a very simple factory pattern commonly used in object-oriented programming

The final class for us to implement is the TaskFactory static class . This class is the glue that lets us turn JSON tasks from Cuckoo Sandbox into C# FileTask objects—and, if you choose to implement other task types in the future, you can also use TaskFactory to handle the creation of those tasks. The TaskFactory class has only a single static method called CreateTask() , which accepts a JToken as its only argument. In the CreateTask() method, we use a switch statement to test the value of the task category. If the category is a file task , we pass the JToken task to the FileTask constructor and then return the new C# task . Although we won’t use other file types in this book, you can use this switch statement to create a different type of Task, such as a url task based on the category, and then return the result.

Putting It Together

Finally, we have the scaffolding in place to start automating some malware analysis. Listing 8-19 demonstrates using the CuckooSession and CuckooManager classes to create a file analysis task, watch the task until completion, and print the task’s JSON report to the console.

public static void Main(string[] args)
{
  CuckooSession session = new CuckooSession("127.0.0.1", 8090);
  using (CuckooManager manager = new CuckooManager(session))
  {
    FileTask task = new FileTask();
    task.Filepath = "/var/www/payload.exe";

    int taskID = manager.CreateTask(task);
    Console.WriteLine("Created task: " + taskID);

    task = (FileTask)manager.GetTaskDetails(taskID);
    while(task.Status == "pending" || task.Status == "running")
    {
      Console.WriteLine("Waiting 30 seconds..."+task.Status);
      System.Threading.Thread.Sleep(30000);
      task = (FileTask)manager.GetTaskDetails(taskID);
    }

    if (task.Status == "failure")
    {
      Console.Error.WriteLine("There was an error:");
      foreach (var error in task.Errors)
        Console.Error.WriteLine(error);

      return;
    }

    string report = manager.GetTaskReport(taskID).ToString();
    Console.WriteLine(report);
  }
}

Listing 8-19: The Main() method bringing the CuckooSession and CuckooManager classes together

In the Main() method , we first create a new CuckooSession instance , passing the IP address and the port to connect to when making API requests. With the new session created, in the context of a using statement, we create a new CuckooManager object and a new FileTask object as well. We also set the Filepath property on the task to a path on the filesystem with an executable we want to analyze. For testing purposes, you can generate payloads with Metasploit’s msfvenom (as we did in Chapter 4) or use some of the payloads we wrote in Chapter 4. With the FileTask set up with the file to scan, we pass the task to the manager’s CreateTask() method and store the ID returned for later use.

Once the task has been created, we call GetTaskDetails() and pass the task ID returned by CreateTask(). When we call GetTaskDetails(), a status is returned by the method. In this case, we are interested only in two statuses: pending and failure. As long as GetTaskDetails() returns a pending status, we print a friendly message to the user that the task is not done yet and have the application sleep for 30 seconds before calling GetTaskDetails() for the task status again. Once the status is no longer pending, we check whether the status is failure in case something went wrong during analysis. If the status of the task is failure, we print the error message returned by Cuckoo Sandbox.

However, if the status is not failure, we can assume the task successfully completed analysis, and we can create a new report from Cuckoo Sandbox with the findings. We call the GetTaskReport() method , passing the task ID as the only argument, and then print the report to the console screen with WriteLine() .

Testing the Application

With the automation out of the way, we can finally drive our Cuckoo Sandbox instance to run and analyze a potentially nefarious Windows executable and then retrieve a report of the task that was run, as shown in Listing 8-20. Remember to run the instance as an administrator.

$ ./ch8_automating_cuckoo.exe
Waiting 30 seconds...pending
{
  "info": {
    "category": "file",
    "score": 0.0,
    "package": "",
    "started": "2016-05-19 15:56:44",
    "route": "none",
    "custom": "",
    "machine": {
      "status": "stopped",
      "name": "cuckoo1",
      "label": "cuckoo1",
      "manager": "VirtualBox",
      "started_on": "2016-05-19 15:56:44",
      "shutdown_on": "2016-05-19 15:57:09"
    },
    "ended": "2016-05-19 15:57:09",
    "version": "2.0-rc2",
    "platform": "",
    "owner": "",
    "options": "",
    "id": 13,
    "duration": 25
  },
  "signatures": [],
  "target": {
    "category": "file",
    "file": {
      "yara": [],
      "sha1": "f145181e095285feeb6897c9a6bd2e5f6585f294",
      "name": "bypassuac-x64.exe",
      "type": "PE32+ executable (console) x86-64, for MS Windows",
      "sha256": "2a694038d64bc9cfcd8caf6af35b6bfb29d2cb0c95baaeffb2a11cd6e60a73d1",
      "urls": [],
      "crc32": "26FB5E54",
      "path": "/home/bperry/tmp/cuckoo/storage/binaries/2a694038d2cb0c95baaeffb2a11cd6e60a73d1",
      "ssdeep": null,
      "size": 501248,
      "sha512":
"4b09f243a8fcd71ec5bf146002519304fdbaf99f1276da25d8eb637ecbc9cebbc49b580c51e36c96c8548a41c38cc76
595ad1776eb9bd0b96cac17ca109d4d88",
      "md5": "46a695c9a3b93390c11c1c072cf9ef7d"
    }
  },
--snip--

Listing 8-20: The Cuckoo Sandbox analysis JSON report

The analysis report from Cuckoo Sandbox is huge. It contains highly detailed information about what happened on the Windows system while your executable was running. The listing shows the basic metadata about the analysis, such as what machine ran the analysis and common hashes of the executable . Once this report is dumped, we can begin to see what the malware did on an infected system and put together a plan for remediation and cleanup.

Note that only part of the report is included here. What is not shown is the immense number of Windows API and system calls that were made, the files on the filesystem that were touched, and other incredibly detailed system information that allows you to more quickly determine what a malware sample may have done on a client’s machine. More information can be found on what exactly is reported and how to use it on the official Cuckoo Sandbox documentation site: http://docs.cuckoosandbox.org/en/latest/usage/results/.

As an exercise, you can save the full report to a file instead of printing to the console screen, since an output file might be more desirable for future malware analysis!

Conclusion

The Cuckoo Sandbox is a powerful framework for malware analysis, and with the API feature, it can be easily integrated into work processes, infrastructures such as email servers, or even incident response playbooks. With the ability to run both files and arbitrary websites within a sandboxed and contained environment, security professionals can easily and quickly determine whether an attacker may have breached the network with a payload or drive-by exploit.

In this chapter, we were able to drive this functionality of Cuckoo Sandbox programmatically using core C# classes and libraries. We created a handful of classes to communicate with the API and then created tasks and reported on them when they were finished. However, we only implemented support for doing file-based malware analysis. The classes we built, though, are meant to be extensible so that new types of tasks can be added and supported, such as a task that submits a URL to be opened in the web browser.

With such a high-quality and useful framework available freely for all to use, anyone could add this functionality to their organization’s security-critical infrastructure and thus easily cut down the time it takes to discover and remediate potential breaches on home or enterprise networks.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset