Abstract

The worm architecture is divided into two parts: the core and the plugins. The only goal of the core is to provide a way of communication for the plugins. Each plugin represent a feature. It can be an exploit or an interface, as a network one or a file system one. Plugins are shared objects, which mean we can load/unload them at the runtime. The worm can be present several times into a single one device. It can be useful to have an active version and an harmless one that only wait that the first one is compromised to activate itself.

Core design

The core part is an executable binary. The plugins are shared libraries. The core provides an API to the plugins. This API allows to plugin to send and receive messages to and from other plugins, establish a list of the available features, and control its own usage.

Presentation

The core is an executable file that is written in C, which is compiled, fast and easily allows to load shared objects with the dlopen, dlsym and dlclose functions. The usage of shared libraries makes the worm very modulable and easy to modify at runtime because the code is not stored in only one file.

Core versioning and compatibility

The core has a versioning system, which defines X.Y (a major version X for compatibility and a minor version Y for bug and features). The version number is encoded on 15 bits, 7 bits for the X number and 8 bits for the Y one.

Internal communication API for plugins

The core has a versioning system, which defines X.Y (a major version X for compatibility and a minor version Y for bug and features). The version number is encoded on 15 bits, 7 bits for the X number and 8 bits for the Y one. Each plugin has a message queue, which is managed by the core. When a plugin send a message to another plugin, it will be put in the recipient's queue. All the memory allocation is managed by the core, which means the messages will not last forever once they are read. To store data and retrieve them later, a database plugin will be available. This system jails the plugins in different abstractions and protects them against each other. It allows easy concurrent and parallel programming.

On launch, the core has to load a set of available plugins.
The core will act as an infinite loop, which will call the executable part of each plugins. It provides a set of functions (the core API) that will be available to the plugins: - send_message, used to send an asynchronous message to another plugin. It allows plugins to communicate data between them. - send_message_instant, used to send a synchronous message to another plugin, and get an instant response from it. It allows faster procedures with some limitations (stack limit, timeout). - receive_message, used to retrieve messages from a specified plugin - receive_message_instant, which is called when another plugin send a synchronous message to this plugin. - list_messages, to get a list of metadata about the messages present in the plugin's message queue. - list_plugins, to get the list of available and loaded plugins. This is a features' discovering feature. - disable, to disable the current plugin for a specified amount of time (depending on the duration, simply not call the plugin or detach it from the core). The goal is to make the plugins stealthier and speed up the main loop.


Plugin design

Meta-data and identification

Each plugin will be defined by several informations: - its id, which will be unique to each plugin. Minimum value is 1. - its version number, on 15 bits, like the core one - its compatible core version required, on 15 bits + 1 bit for a flag, the flag containing additional requirement information.

Stealth and obfuscated code

Two main issues have been found for plugins:

Signature and behaviour obfuscation

Each plugin is written following some rules. There are implemented with macro in C, which are generating junk code regenerated randomly at each compilation. This junk code obfuscate the traces of the plugin (inject syscalls, blocks, logic, etc.) at every step of its execution. This could be written:

for (int i = 0; i < 10; i++) {
  call_some_internal_function(i); // some code
  JUNK_MACRO(i) // random code generated at each iteration, with optional variadic parameters
  // ...
}

The goal is to avoid:

Depending on each plugin's implementation, it junk code might differs (for a complex system exploit on memory it could only insert short junk between each operation, in other it could be a set of algorithm evaluation, like a sort, etc.).

Junk injection requires careful programming and is a generic tool that must be evaluated each time it is used.

Shared Library injection

Injecting plugins bytecode into a legit shared library could be a way to avoid anti-virus detection, because the legit files might already be in some whitelist.

Potential issues: - Anti-virus may detect modifications of executables or shared libraries. But it is not obvious because a lot a software are using update systems thus are modified by an update software or update themselves.

Worm spreading

Scanner, Command center

The first characteristic of a worm is its capability of self-spreading without any user action. The worm should be able to take decision in order to choose targets and the way to infect targeted systems. This capability will be provided by two services, so two modules:

Vulnerability exploitation and privilege escalation

Several modules will be designed to attack a service (TCP/UDP endpoint, ...) and some other to attack the current system.

Future and potential work