Post

Basic Command and Control

Basic Command and Control

Building Basic Command and Control Server

In order to understand how c2 servers work, I decided to build my on c2 in order to understand the internals of command and control servers. After a few weeks of researching and coding, I came up with a minimal basic c2 and implant.

Goal

Build a working implant in C that connects to a Linux server over TLS and executes remote commands securely.

Please note, Basic-C2 is purely for educational purposes and is not designed for real-world engagements. It’s a learning tool, and its basic functionality reflects that.

You can find the command and control server and implant here

Overview

Server Side

The server is really simple. I wrote it for Linux . It sets up a secure TLS listener, accepts incoming connections, and spawns a thread for each client. From there:

  • Type command
  • It sends it to implant via TLS
  • The implant executes it
  • It sends back the output

The c2 server also has some basic logging functionality.

Thanks to OpenSSL’s abstrations things are a bit simple to make the server use TLS communications

The shell logic reads commands from the operator and forwards them via SSL_write(). The client reads them, runs them, and returns the output.

This is super basic, but it works.

Client Side (Implant)

I wrote the implant for the windows operating system, the implant basically resembles a backdoor (or a basic RAT). the implant uses Schannel API for encrypted TLS communication. Schannel was really hard to deal with, it took me days to get around. How the implant works:

  • Try to connect to c2 server
  • Receive and execute commands using _popen
  • Send output back to c2 server

Unlike OpenSSL, Schannel doesn’t abstract everything. You manually manage the encryption handshake, message buffers, and stream decryption.

This gives more control — and more complexity. But it avoids shipping OpenSSL with the implant, keeping it lightweight and native.

Details

Server


Socket Programming

The server uses socket programming with SSL/TLS to communicate with implants.

The server first creates a socket

1
2
3
4
5
serverSock = socket(AF_INET, SOCK_STREAM, 0);
if (serverSock == -1) {
    perror("Socket creation failed");
    return -1;
}

Then binds address: It binds the socket and address structure together. The address structure contains the following:

  • IP Address
  • Port
  • Transport Protocol (TCP)
  • IP version (4)
1
2
3
4
5
    if (bind(serverSock, (struct sockaddr*)&addr, sizeof(addr))) {
        perror("binding failed");
        close(serverSock);
        return -1;
    }

The server then listens for incoming connections

1
2
3
4
5
    if (listen(serverSock, 20) == -1) {
        perror("Listen Failed");
        close(serverSock);
        return -1;
    }

Another function accepts connections (ONLY SSL/TLS).

The function first ssl certs if they do not exits from a function in the certifications.h header and then it loads them into the program.

1
2
3
4
5
6
if (access("certs/cert.pem", F_OK) != 0 && access("certs/key.pem", F_OK) != 0) {
    generate_key_and_cert();
}

SSL_CTX_use_certificate_file(ctx, "certs/cert.pem", SSL_FILETYPE_PEM);
SSL_CTX_use_PrivateKey_file(ctx, "certs/key.pem", SSL_FILETYPE_PEM);

The server is setup that it will count down from 10 and accept connections in that time and if there is a connection the timer resets, if there are connections the program performs a TLS handshake and accepts connection. Here is how i implanted it

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
    int duration = 10; // default duration is 10 seconds

    start:
    do {
        memset((void*)&client_addr, 0, len);
        memset((void*)&agentSock, 0, sizeof(agentSock));
        printf("[-] Waiting for connections\n");
        // add log "Waiting for connections"

        // wait 
        fd_set read_fds;
        struct timeval timeout;
        FD_ZERO(&read_fds);
        FD_SET(serverSock, &read_fds);

        timeout.tv_sec = duration;
        timeout.tv_usec = 0;

        // Wait for a connection or timeout
        int activity = select(serverSock + 1, &read_fds, NULL, NULL, &timeout);
        if (activity < 0) {
            perror("select error");
            sleep(1);
            goto start;
        }

        if (activity == 0) {
            // Timeout occurred, no connection
            printf("[-] No connection within %d seconds. Continuing...\n", duration);
            sleep(1);
            break;
        } else {
            // Connection is available
            if (FD_ISSET(serverSock, &read_fds)) {
                agentSock = accept(serverSock, (struct sockaddr*)&client_addr, &len);
                if (agentSock == -1) {
                    perror("Accept Failed");
                    return -1;
                }
                
                SSL *ssl = SSL_new(ctx);
                SSL_set_fd(ssl, agentSock);
                // perform tls handshake
                if (SSL_accept(ssl) <= 0) {
                    perror("TLS handshake Failed");
                    ERR_print_errors_fp(stderr);
                    SSL_free(ssl);
                    close(agentSock);
                    return -1;
                }

After accepting connections we increment the connections counter variable and connection to a list (this is all logged from log.h).

1
2
3
4
5
6
7
8
9
10
11
12
                char ip[buffer_len];
                strncpy(ip, inet_ntoa(client_addr.sin_addr), sizeof(ip));
                connections_counter++;
                printf("[+] Connection from %s | shell %d \n", ip, connections_counter);
                log_connections(ip);
                addAgent(agentSock, ip, ssl);
                sleep(1);
                continue;
            }
        }
        } while (true);


Shell


Why I built this

I was curious with how red-team command and control work under the hood. I wanted to understand the lower-level mechanics. Writing this from scratch in C — and with native APIs — gave me deep insight into how TLS, sockets, and remote execution work under the hood.


This is for educational use only.

This post is licensed under CC BY 4.0 by the author.

Trending Tags