def Run(self): """Runs the proxy. Waits for a TCP connection and passes it forward to the Client class to parse """ logger.Log("INFO", "Waiting for TCP connection") self.tcpSocket.listen(1) sock, addr = self.tcpSocket.accept() logger.Log("INFO", "Received TCP connection") client = Client(self.proxy, sock, self.dst) client.Run()
def main(): """ICMP Tunnel, send TCP over ICMP positional arguments: {client,server} client - Run the client proxy (All flags needed) server - Run the server proxy (No flags needed) optional arguments: -h, --help show this help message and exit -p PROXY_HOST, --proxy-host PROXY_HOST IP of the server tunnel -lh LOCAL_HOST, --local-host LOCAL_HOST Local IP for incoming TCP connections -lp LOCAL_PORT, --local-port LOCAL_PORT Local port for incoming TCP connections -dh DESTINATION_HOST, --destination-host DESTINATION_HOST Remote IP to send TCP connection to -dp DESTINATION_PORT, --destination-port DESTINATION_PORT Remote port to send TCP connection to -v, --verbose Print debug messages """ parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, description="ICMP Tunnel, send TCP over ICMP") # Add server or client parser.add_argument("type", choices=["client", "server"], help="client - Run the client proxy (All flags needed)\nserver - Run the server proxy (No flags needed)") parser.add_argument("-p", "--proxy-host", help="IP of the server tunnel") parser.add_argument("-lh", "--local-host", help="Local IP for incoming TCP connections") parser.add_argument("-lp", "--local-port", type=int, help="Local port for incoming TCP connections") parser.add_argument("-dh", "--destination-host", help="Remote IP to send TCP connection to") parser.add_argument("-dp", "--destination-port", type=int, help="Remote port to send TCP connection to") parser.add_argument("-v", "--verbose", default=False, action="store_true", help="Print debug messages") args = parser.parse_args() # Set the logger verbosity logger.SetVerbosity(args.verbose) if args.type == "server": logger.Log("INFO", "Starting server") Server().Run() else: # Make sure we have all params if args.proxy_host is None or \ args.local_host is None or \ args.local_port is None or \ args.destination_host is None or \ args.destination_port is None: parser.error("client requires proxy,local and destination flags") logger.Log("INFO", "Starting client") ClientProxy(args.proxy_host, args.local_host, args.local_port, args.destination_host, args.destination_port).Run()
def CreateIcmpSocket(): """Create a Raw ICMP socket for sending and receiving Returns: socket: Raw ICMP socket """ # Doesn't handle errors, calling function should logger.Log("DEBUG", "ICMP socket created") return socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
def HandleIcmp(self, socket): """Handle a received ICMP packet (from the client). Reads the ICMP packet and forwards the payload as a TCP packet to the TCP server. Args: socket (socket): ICMP socket that has data to read from """ # Read and parse data packet, address = socket.recvfrom(ICMP_BUFFER_SIZE) try: packet = Icmp.IcmpPacket.Parse(packet) except Exception as x: logger.Log("DEBUG", "Failed parsing packet") return self.src = address[0] self.dst = packet.dst # If this is not an IcmpTunnelPacket ignore it if packet.magic != Icmp.IcmpPacket.MAGIC: return # Skip our packets if packet.type == Icmp.ICMP_ECHO_REPLY and packet.code == 0: logger.Log("DEBUG", "Failed parsing packet") return # Close requested if packet.type == Icmp.ICMP_ECHO_REQUEST and packet.code == 1: self.sockets.remove(self.tcpSocket) self.tcpSocket.close() self.tcpSocket = None logger.Log("INFO", "Client closed") return # Create socket if it doesnt exist if not self.tcpSocket: self.tcpSocket = self.CreateTcpSocket(self.dst) self.sockets.append(self.tcpSocket) logger.Log("INFO", "Client joined") # Send the packet self.tcpSocket.send(packet.payload)
def HandleTcp(self, sock): """Handle a packet from the TCP connection. Reads the packet and forwards it over ICMP to the server ICMP tunnel. Args: sock (socket): Socket that we got the data from """ data = sock.recv(TCP_BUFFER_SIZE) # Build a ICMP packet with our TCP packet as the payload and send it to the server code = 0 if len(data) > 0 else 1 packet = Icmp.IcmpPacket(Icmp.ICMP_ECHO_REQUEST, code, 0, 0, 0, data, self.tcpSocket.getsockname(), self.dst) self.icmpSocket.sendto(packet.Create(), (self.proxy, 1)) # Connection closed, no data if code == 1: logger.Log("INFO", "Connection closed") exit()
def CreateTcpSocket(dst, server=False): """Create a TCP socket with given destination. Binds/Connects to ip depending on params Args: dst ((IP, Port)): Destination to connect to server (bool, optional): If true we bind to dst instead of connecting """ logger.Log("DEBUG", "TCP socket created on {}.{}".format(dst[0], dst[1])) # Create reusable socket sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # If we are a server then bind, else connect to to destination if server: sock.bind(dst) else: sock.connect(dst) return sock
def Parse(cls, packet): """Parses a received network packet into an ICMP packet Args: packet (bytearray): Network packet Returns: IcmpPacket: Parsed packet """ rawIpPacket, rawIcmpPacket = packet[:IcmpPacket. IP_HEADER_SIZE], packet[ IcmpPacket.IP_HEADER_SIZE:] ipPacket = struct.unpack(IcmpPacket.IP_HEADER, rawIpPacket) srcIp = ipPacket[8] # Get the payload payload = "" payloadSize = len(rawIcmpPacket) - IcmpPacket.ICMP_HEADER_SIZE if payloadSize > 0: payload = struct.unpack( "{}s".format(payloadSize), rawIcmpPacket[IcmpPacket.ICMP_HEADER_SIZE:])[0] logger.Log("DEBUG", "Parsing ICMP packet, payload size {}".format(payloadSize)) # Read the packet data type, code, checksum, id, sequence, dstIp, dstPort, magic = struct.unpack( IcmpPacket.ICMP_HEADER, rawIcmpPacket[:IcmpPacket.ICMP_HEADER_SIZE]) # Convert to net data srcIp = socket.inet_ntoa(srcIp) dst = (socket.inet_ntoa(dstIp), dstPort) return cls(type, code, checksum, id, sequence, payload, srcIp, dst, magic)
def Create(self): """Creates a network ready ICMP packet from the saved data Returns: bytearray: Serialized ICMP packet data """ logger.Log("DEBUG", "Creating ICMP packet") packStr = self.ICMP_HEADER packArgs = [ self.type, self.code, 0, self.id, self.sequence, socket.inet_aton(self.dst[0]), self.dst[1], self.magic ] # Add the payload if len(self.payload) > 0: packStr += "{}s".format(len(self.payload)) packArgs.append(self.payload) # Add correct checksum checksum = self.Checksum(struct.pack(packStr, *packArgs)) packArgs[2] = checksum return struct.pack(packStr, *packArgs)