Ghost Protocol is an educational client/server project for studying TCP socket programming, custom packet framing, session tracking, and controlled remote shell sessions on systems you own or are explicitly authorized to test.
The project consists of:
- A C++ server that accepts agent and operator connections.
- A C++ agent that connects to the server and owns a local persistent Bash process.
- A Python operator console that lists connected agents and opens a shell session for a selected agent.
Authorization required: The agent can execute shell commands on its host. Use this project only in an isolated lab or on systems for which you have explicit permission. Do not expose it to untrusted networks or use it to access systems, accounts, or data without authorization.
- TCP-based agent and operator connections.
- Custom binary packet format using network byte order.
- Server-side registry of connected agent sessions.
- Interactive shell sessions for selected agents.
- One persistent Bash child process per active shell session.
- Shell state that persists between commands: working directory, exported variables, and shell variables.
- Non-blocking stdout and stderr collection.
- Explicit command-completion marker and graceful shell cleanup.
| Area | Technology |
|---|---|
| Server and agent | C++23 |
| Local process management | POSIX (fork, pipe, dup2, poll, waitpid) |
| Operator console | Python 3 |
| Transport | TCP/IP sockets |
| Shell process | /bin/bash -s |
| Build | GNU Make and g++ |
The agent and persistent shell require a POSIX-compatible system such as Linux. Native Windows is not supported.
Ghost_Protocol/
├── agent_main.cpp Agent entry point
├── server_main.cpp Server entry point
├── common/
│ ├── agent.hpp Agent state and message constants
│ ├── common.hpp Shared port configuration
│ └── PersistentShell.hpp Persistent local Bash interface
├── protocol/packet.hpp Packet definition and serialization API
├── src/
│ ├── agent.cpp Agent receive and dispatch loop
│ ├── server.cpp Server listeners and session routing
│ ├── PersistentShell.cpp POSIX persistent-process implementation
│ └── process_demo.cpp Standalone PersistentShell test
├── operator/ Python operator console
└── Makefile
The protocol has a 12-byte header followed by an optional payload:
+----------------+----------------+----------------+-------------------+
| Message type | Session ID | Payload length | Payload |
| 4 bytes | 4 bytes | 4 bytes | N bytes |
+----------------+----------------+----------------+-------------------+
All header fields are unsigned 32-bit values in network byte order.
| Message | Value | Purpose |
|---|---|---|
MESSAGE_COMMAND |
1 | General operator command, including sessions. |
MESSAGE_SHELL_START |
2 | Start an agent shell session. |
MESSAGE_SHELL_DATA |
3 | Send input to the active shell. |
MESSAGE_SHELL_EXIT |
4 | Stop the active shell. |
MESSAGE_OUTPUT |
5 | Return shell output. |
MESSAGE_ERROR |
6 | Return an error. |
MESSAGE_SHELL_ACK |
7 | Confirm shell startup. |
Default ports, defined in common/common.hpp:
Agent listener: TCP 1234
Operator listener: TCP 9000
Agent ── TCP/1234 ──> Server <── TCP/9000 ── Operator console
│
└── routes packets by session ID
The agent initiates its connection to the server. The server does not connect back to the agent.
agent owns one PersistentShell instance. A shell command does not create another Bash process.
MESSAGE_SHELL_START
│
▼
PersistentShell.start()
│
▼
One /bin/bash -s child process
│
▼
MESSAGE_SHELL_DATA
│
▼
PersistentShell.write_input(command)
│
▼
PersistentShell.read_available_output()
│
▼
MESSAGE_OUTPUT
│
▼
MESSAGE_SHELL_EXIT → PersistentShell.stop()
For each command, PersistentShell writes input to the same Bash process and appends an internal completion marker. It polls non-blocking stdout and stderr pipes until the marker arrives, removes the marker from returned output, and sends the result through the existing packet flow. The persistent process is why cd, shell variables, and exported variables survive across inputs.
Shell input is intentionally interpreted by Bash on the authorized agent host. This is remote shell execution by design, not a safe command API. Never send untrusted input to an agent.
- Linux or another POSIX-compatible environment for the agent.
g++with C++23 support.- GNU Make.
- Python 3 for the operator console.
/bin/bashavailable on the agent host.
On Debian-based lab systems:
sudo apt update
sudo apt install build-essential python3Build the server and agent:
make allBuild and run the persistent-shell validation demo:
make demo
./exe/process_demoGenerated executables:
exe/server_main
exe/agent_main
exe/process_demo
Remove generated executables:
make clean-
On the server host, start the server:
./exe/server_main
-
Confirm it listens on both project ports:
ss -ltn | grep -E ':(1234|9000)'
-
On the authorized agent host, connect to the server's reachable LAN address:
./exe/agent_main <SERVER_IP>
-
If the operator console runs on another machine, set
TARGET_IPinoperator/connection.pyto the same server address. The default127.0.0.1works only when the console and server share a host. -
Start the operator console:
python3 operator/main.py
-
Use the console:
ghost$> sessions ghost$> use <SESSION_ID> ghost [<SESSION_ID>]$> shell agent$> pwd agent$> back
back or exit at the agent$> prompt sends MESSAGE_SHELL_EXIT, stops the Bash child, and clears the shell session.
If sessions shows no active agents, the server has not registered an agent TCP connection. Check the following:
-
Start
server_mainbeforeagent_main. -
Pass the server's bridged/LAN address to
agent_main, not the agent VM's own address. -
Verify from the agent machine that the server port is reachable:
ping -c 1 <SERVER_IP> nc -vz <SERVER_IP> 1234
-
Permit inbound TCP
1234and9000on the server host and on any firewall between the systems. -
For bridged VMs, ensure both guests are on a reachable network and guest-to-guest traffic is allowed.
-
For NAT VMs, configure port forwarding or use a network mode that permits the agent to reach the server.
-
Check server output for:
[+] an agent connected -
A failed initial agent connection ends its current connection loop. After fixing reachability, restart
agent_main.
Useful server-side checks:
ip -br addr
ss -ltnp | grep -E ':(1234|9000)'- This is an educational prototype, not a production remote-administration system.
- TCP traffic is plaintext; there is no encryption, authentication, authorization, or integrity protection.
- The agent executes shell input with the privileges of the account that starts it.
- The fixed completion marker can conflict with output containing that exact standalone marker line.
- Slow or long-running commands can return partial output after the polling timeout.
- stdout and stderr are separate pipes, so their relative ordering is not guaranteed.
- The agent does not continuously retry a failed initial server connection.
- There is no durable session storage, audit trail, file transfer control, or production-grade recovery.
- Shell state is reset when the session exits or the agent process terminates.
- Run only in a private, disposable lab network.
- Use a dedicated unprivileged account for the agent.
- Restrict project ports to known lab hosts with host and network firewalls.
- Do not run the server or agent as root.
- Do not expose TCP ports
1234or9000to the public internet. - Treat operator input and agent output as sensitive.
- Add authentication, encryption, authorization, audit logging, message-size limits, and reconnect handling before any legitimate use outside a controlled lab.
Ghost Protocol is provided for educational and authorized testing purposes. The project authors and contributors are not responsible for misuse, unauthorized access, damage, data loss, or legal consequences resulting from its use. You are responsible for complying with applicable laws, organizational policies, and written authorization requirements.