By Jon Perez & Jonathan Lepore
NOTE: This is part 2 of 3 in our DNS Tunneling series. Be sure to check out part 1 ("Chirp of the PoisonFrog") and part 3 ("The siren song of RogueRobin").
For the second blog post in our series, the IronNet Threat Research Team examines the Glimpse malware that is written in PowerShell and has been associated with OilRig/APT34. Our first post about analyzing malware with DNS tunneling capabilities focuses on how the PoisonFrog malware uses DNS tunneling to send and receive victim information and commands.
Glimpse is a PowerShell script that is executed silently by Visual Basic script. Based on the code, it is unclear what initiates the Visual Basic script itself. However, a variety of typical persistence mechanisms, such as a scheduled task, could serve that purpose. A high-level execution of Glimpse is depicted in the table below:
After Glimpse starts, it checks for the existence of a directory and lock file, as shown in Table 1 High-level execution flow for Glimpse. If no directory or lock file is found, Glimpse creates one. Alternatively, if these do exist and the lock file is older than 10 minutes, the lock file is deleted and the previously running Glimpse script is killed. After the initial checks described above, Glimpse creates a hidden file that contains an agent ID, which is a simple concatenation of a random number 10-99 and the first 8 characters of a GUID without dashes.
Analysis of DNS Tunneling
As with PoisonFrog, Glimpse uses A resource records to communicate with its controller. Additionally, Glimpse can be set to use text mode, which makes use of TXT resource records to receive tasking. While in text mode, Glimpse does not take advantage of the recursion typically observed in DNS communications (i.e., the infected victim sends DNS queries directly to the controller). Communicating directly to the controller, while operationally viable, may not be successful in environments that limit the use of external DNS servers.
DNS Invocation and Resource Records
The methods employed by Glimpse to perform DNS communications are determined by the mode in which it is operating (i.e., text mode or ping mode). In text mode, Glimpse manually builds a DNS query to be transmitted over a UDP socket. In ping mode, Glimpse uses a .NET method. The table below describes the operational mode, record types used, and the method used to send the query.
Query Building Overview
The first DNS query by Glimpse requests the mode to be used in future communications with the controller (i.e., ping mode or text mode). Prior to making any query, a function called AdrGen is used to build a query string. This function takes several parameters, most of which are represented in the subdomain label(s) of the query string. Below is a list of AdrGen parameters.
As mentioned above, one of the parameters passed to the AdrGen function is the action parameter. Table 5: Glimpse action parameters values for the AdrGen function below contains the possible parameters, a brief description, and return values applicable to the action parameter.
The AdrGen function performs several steps to insert random data into the query string it builds. These types of steps have been seen in other malware to avoid DNS caching and assist with signature suppression. Once called, the AdrGen function builds a string of characters representing the hex range, A-F and 0-9, and randomly selects one to seven hex characters. The random selection of hex characters will serve as random padding in later operations. The function then randomly selects two digits from a range of 0-9. Similar to PoisonFrog, the selected digits will be used to determine the indices to insert parameter values into the control data section of the query string. The table below depicts the structure for how the values above are employed. We refer to this section as the management data section due to its primary functionality:
The control data section of the query string is composed of the victim’s agent ID, the action value parameter, and the part_no parameter. While building the control data section, the AdrGen function makes use of the two randomly generated digits described earlier in the process. The first digit is used as the index within the agent ID to insert the part_no parameter. The second digit is used as the index within the agent ID to insert the action value parameter. Below is an example of the control data section of a query string given some of the values described above.
Based on the values in the table above and random index digits of 4 and 8, the table below depicts how the control data section is created.
After creating the control data section of the query string, the sender_or_receiver parameter is checked in order to put the rest of the query string together. The initial communication with the controller passes the M action which is asking the controller what receive mode, either ping or text, the malware should use. In addition to building the control and management data sections, the AdrGen function is responsible for appending the encoded data, encoded file name (if conducting send operations), and controller domain to the control data and management data. The example below represents the complete query string sent to the controller when the AdrGen function is called with the r (receiver) flag. It is worth noting that this query is made using the [System.Net.Dns]::GetHostAddresses method.
The query to set the receive mode expects an A resource record response from the controller. The controller will respond with one of two responses: 126.96.36.199 will set the receive mode to text. Any other IP address will set the receive mode to ping, although the server-side software suggests 188.8.131.52 will be sent.
When set in text receive mode, the malware uses the AdrGen function to create another query string with the r (receiver) flag and a W (wait) action parameter. This query string uses the receiver structure referenced above.
After AdrGen builds the string, a function is called to manually build and send the DNS query packet as opposed to using [System.Net.Dns]::GetHostAddresses. The function responsible for building the packet uses several hardcoded values; however, the most notable is the transaction ID 0xa4a3. While in text mode and receiving, the resource record type requested is TXT. Additionally, when manually building and transmitting the query, a direct connection is made to the controller IP address as opposed to allowing the query to propagate recursively.
The expected TXT record response has the following structure:
There are several expected responses to this query. The table below depicts the possible commands, expected data, what the next query action will be set to, and the function of the command.
In our sample traffic, the TXT resource record returned contained:
This response tells the malware to set a variable for the file name to receivebox\rcvd10100 and set the next query action to D in order to request the next chunk of data. The malware sends another TXT query with the receiver structure. This query is depicted below:
In the case of our sample traffic, the server responded with the following TXT resource record data:
The controller provided the malware with base64-encoded data to be decoded. The data will eventually be written to disk and the malware sets the next query action to D in order to request the next chunk of data. The decoded data shows a command to be executed whoami&ipconfig /all on the victim system. The malware sends another TXT query with the receiver structure, as depicted below. Note the request number parameter is now 0001:
The TXT record returned contained data:
The controller issued the command to write the base64-decoded and modified data to the file name set earlier in the exchange. After the file is written, the malware moves on to process operations.
Glimpse can be set to use ping mode in several ways while performing receive operations. If a query with the M action returns an IP address that is not 184.108.40.206, the malware will use ping mode. It is worth noting that the IP response observed to set ping mode was the reverse of the IP used to set text mode (i.e., 220.127.116.11). Ping mode will also be set if exceptions occur more than three times during text mode. In the latter case, the P action is passed as one of the parameters to AdrGen and the query is made for an A resource record using the [System.Net.Dns]::GetHostAddresses method.
If performing receive operations in ping mode, Glimpse makes a query with the 0 action to contact the controller for tasking. This query uses a receive structure similar to an M action; it is worth noting all of the receiver operation queries made in ping mode use the [System.Net.Dns]::GetHostAddresses method.
Glimpse parses the data returned in the A record for the specific values shown in the table below:
In our sample, after the malware sent the 0 action, the controller responded with an A record containing 18.104.22.168. This response tells the malware to:
- Set the file name for the data that will follow to 10140
- Set the part number to 0
- Parse response data
- Set a 1 action for the next query
The 1 action indicates the malware is ready to receive a file from the controller. The responses to the 1 action queries contain data, structured as an IP address, to be parsed by the malware. The last octet of the IP is checked against the receive counter to ensure the appropriate number of bytes have been received. If the receive counter exceeds 250, it is reset to 0 and continues. The first three octets of the returned IP are accumulated in a buffer until a response contains the IP 1.2.3. where the last octet does not have any use to the process. When the 1.2.3. response is received, the malware will:
- Not expect additional data
- Set a 0 action for the next query
- Check the file for terminating zero(s) and remove them
- Write the file to receivebox with rcvd prepended to the file name.
The table below captures a sample exchange with the net user command being received by the malware.
In this case, the content net user is written to rcvd10140. After writing the data to disk, receiver operations are complete and processor operations begin.
After writing the data received from the controller, a function is called to process the received file. The processor function builds a list of files from the files with content that match rcvd* in the receivebox directory. Similar to PoisonFrog, the last digit of the received file name determines how the content of the file is processed. The table below describes the file markers and operations:
It is worth noting that if the file name ends in a 0 (e.g., 10100), the static string <> is added to the end of the file containing the command output. In the case that a file ends with anything other than 0 or 1, the file is moved to the done directory and a file with the same file name is created in the sendbox directory. The content of that file is a static string 200<> appended to the location of the file in the done directory.
In our sample traffic, after executing the commands sent via cmd.exe, Glimpse writes the output of the commands in the sendbox directory to the appropriate file names (e.g., 10100 or 10140) prepended with proc (e.g., proc10100). Once written, the send operations begin.
At this point in our sample, the malware is still in text mode. The sender function starts operations by building a list of files that are found in sendbox, begin with proc*, and are not empty. It renames the files to their file names without the prepended proc string: this will eventually be encoded and used in the file name section of the query string.
The sender function performs several steps to prepare for transmission. The file to be sent is encoded through the use of a wrapper function “slaber” and encoding function “resolver.” Both of these functions were found in PoisonFrog and are described in the PoisonFrog resolver function data marshalling strings/byte arrays section of our previous blog post. The sender function also creates a file start marker using the hardcoded string COCTab and file name and size information. The name and size of the file to be transmitted is transformed using the structure depicted below. The transformed file information has a fixed size of 27 bytes and is padded with *:
Both PoisonFrog and Glimpse prepare payload data for transmission in the same way. They use a function referred to as resolver to transform the data prior to transmission. The resolver function is described in part one of this blog series.
After transforming the file name and size information, it is encoded and appended to the hardcoded COCTab string. The table below depicts the resolver-encoded file name and size information:
The encoded file content is then appended to the end of the file start marker and the buffer is ready to be chunked for transmission. The chunking operation loops over the encoded content buffer and calls AdrGen with several parameters as depicted below:
When the sender_or_receiver parameter “s” (sender) is passed to AdrGen, the query string follows the structure described below. Note that the sample below represents the first DNS query with the sender flag:
Similar to text mode receiver, after AdrGen builds the string, a function to manually build and send the DNS query packet is called. The text mode sender uses the same hardcoded transaction ID 0xa4a3; however, instead of sending queries for TXT resource records, the malware uses A resource records. As with the text mode receiver, the query is made with a direct connection to the controller IP address as opposed to allowing the query to propagate the native DNS architecture.
If the send function is being invoked in ping mode, the process described above is followed; however, instead of manually building and transmitting the DNS query, the [System.Net.Dns]::GetHostAddresses method is used. With that method, the malware’s query will traverse the native DNS architecture as opposed to the victim making a direct connection to the controller.
The send function expects specific data in the returned A resource records regardless of mode (i.e., ping or text). The table below depicts the expected data:
The send function uses several counters to maintain various pieces of information used to control the flow of execution. An exception counter is used to track the number of exceptions and will exit the send loop if a threshold is hit. The send counter is used to track the number of chunks sent to the controller. An additional counter exists to handle cases where the file being sent is larger than 250 chunks. The send counter is initialized to 0 and read from the fourth octet of the A record returned by the controller. The send counter is also passed to the AdrGen function as the part number parameter and is visible in the query string as depicted below:
When the send loop has fewer than 60 bytes to send (e.g., a small file or the last part of a file), the send function transmits the remaining bytes with a shorter data section. When there are no more bytes to send, a hardcoded file end marker COCTabCOCT is sent in the data section and the send loop will be exited. The controller responds with the 253.25.42.87 A record response.
Once an A record response is received by the malware containing 253.25.42.87, several variables are set in preparation to exit the send operation. After the send operation is complete, the lock file for the current run is deleted and the script exits.
Many of the capabilities discovered in Glimpse were also present in the malware analyzed in part one of this series. Glimpse added the ability to use an alternate DNS resource record type (TXT) as opposed to solely relying on A resource records for DNS queries. Using TXT resource records enabled the actors to provide tasking in fewer transactions due to the amount of data that can be transmitted in a TXT response. To support this capability, the adversaries chose to manually craft the DNS queries and communicate directly with the controller as opposed to using existing .NET DNS libraries. The differences between PoisonFrog and Glimpse highlight the ease at which adversaries can modify their tools to meet their end objectives.
With regard to detection, several methods can be used to identify this type of C2 activity. Performing entropy calculations on subdomain labels can help highlight the amount of randomness in a label, but this is just one of many possible data analysis points, since a standalone feature may not be enough to determine whether traffic is malicious. The IronDefense Network Traffic Analysis platform combines several behavioral detection methods alongside historical network information to detect the C2 techniques used by Glimpse and other malware.
In our next blog post, we will examine the DNS tunneling capability of RogueRobin, which has been linked to the DarkHydrus threat group. Go to DNS Tunneling: part 3 ("The siren song of RogueRobin").
Follow the IronNet Threat Research team @IronNetTR.