Implementing a custom Listener Plugin Shellcode (SC)
When creating command plugin shellcode, you have two options:
- Write Native Shellcode: Develop the shellcode directly using assembly language and C.
- Convert Executable to Shellcode: Write the code in C/C++ or C# and convert the resulting executable to shellcode using tools like Donut.
In this example, we will use the second option and write the code in C#.
Shellcode main activities
The created shellcode must perform the following actions to facilitate data transfer between the command and the Tuoni server:
- Open a Named Pipe Connection: Establish a connection to the core agent using a named pipe. The pipe name should be "QQQWWWEEE." It can be overwritten in by plugin itself with name provided by Tuoni server core but does not have to.
- Receive Input TLV: Receive an input TLV (Type-Length-Value) message from the core agent through the named pipe. This is the configuration sent by the plugin part in Tuoni server.
- Relay Communication Between Agent and Listener on Tuoni Server: To facilitate secure and efficient relay communication between the agent and the listener operating on the Tuoni server, several methods are employed. These methods include requesting encrypted metadata and upstream data (command results) from the agent, as well as sending downstream data (commands) to the agent. The shellcode must incorporate these methods alongside the actual communication processes with the Tuoni server, which is managed by the server plugin component on the server side.
Info
For parsing TLV messages, we recommend using our simple TLV library, which can be found at our example plugin Github repository https://github.com/shell-dot/tuoni-example-plugins/blob/main/shellcodes/CommandEcho/TLV.cs.
Alternatively, you can implement your own TLV parsing logic.
Examples
Data packets exchanged between shellcode and agents
A connection is established to a named pipe to facilitate communication between the agent and its plugins. The data exchanged follows the specific format:
- 4 BYTE INTEGER: Specifies the length of the subsequent data blob.
- DATA BLOB: The actual data being transmitted.
When shellcode starts
Upon establishing the connection, the listener shellcode reads the incoming data and parses it as TLV structure
Loaded TLV now contains listener configuration provided by the server plugin
Sequence number on some requests from shellcode to agent
Given that shellcode may request various data from the agent asynchronously, it is crucial to maintain accurate tracking of the agent's current responses. To achieve this, requests for metadata and upstream data include a sequence number. This sequence number is also present in the corresponding responses, enabling association of responses with their respective requests.
Asking metadata
The shellcode requests metadata bytes from the agent, which are received in encrypted form and subsequently decrypted by the server. Please note, this example code is not thread-safe and is provided solely for illustrative purposes.
Asking upstream data to send
The shellcode requests upstream data bytes from the agent. These bytes are received in encrypted form and are decrypted by the server for further processing. Please note, this example code is not thread-safe and is provided solely for illustrative purposes.
Relay data from server to agent
The shellcode transmits data received from the server (which is relayed by the server's plugin component) to the agent.
No response is expected to this request.