Tasks

Each instruction sent over from the server is referred to as a 'task'. Implants receive tasks, interpret and execute them, and return the response.

During development, each task must be assigned a numeric opcode, to discourage the sending of text-based task names over the connection. As these opcodes are specified by the developer, an implant should have a built-in mechanism to efficiently route tasks to their controller. This will be referred to as 'task management'

Task manager

Here is a simple, single-threaded implementation of a task manager from Empress.

package c2

import (
	"github.com/pygrum/Empress/config"
	"github.com/pygrum/Empress/transport"
	"strconv"
)

type Handler func(*transport.Request, *transport.Response)

type Router struct {
	handlers map[int32]Handler
}

func NewRouter() *Router {
	return &Router{
		handlers: make(map[int32]Handler),
	}
}

func (r *Router) HandleFunc(opcode int32, handler Handler) {
	r.handlers[opcode] = handler
}

func (r *Router) handle(req *transport.Request) *transport.Response {
	resp := &transport.Response{
		AgentID:   config.C.AgentID,
		RequestID: req.RequestID,
	}
	handleFunc, ok := r.handlers[req.Opcode]
	if !ok {
		resp.Responses = append(resp.Responses, transport.ResponseDetail{
			Status: transport.StatusErrorWithMessage,
			Dest:   transport.DestStdout,
			Data:   []byte("unknown opcode " + strconv.Itoa(int(req.Opcode))),
		})
		return resp
	}
	handleFunc(req, resp)
	return resp
}

The router has various callback functions registered for each opcode received from the C2. This means that once a client receives a server request (transport.Request), it can simply call the handle function on the router, which will send the request to the appropriate handler and return the response.

Now, whenever a new function is created, the developer simply needs to register the function with the client's internal router. Here is an example of integrating the function ls .

You may notice that CmdLS has the function signature of a router callback. So we can simply go ahead and register it as one of the HandleFunc:

The server integration page goes into more detail about how registration and polling objects are structured.

Last updated