Visitar URL original
GitHub - beamsearch/ipudp: tunnel IP over UDP, with encryption and random padding · GitHub
Skip to content
beamsearchPublic
forked from Guest0x0/ipudp

About

tunnel IP over UDP, with encryption and random padding

Resources

Stars

0 stars

Watchers

0 watching

Forks

 
 

Repository files navigation

tunneling IP over UDP, with encryption and random padding

Introduction

This tool can be used to hide your traffic and/or pass certain firewalls. You need a remote machine that is secure and has full internet access. Both local client and the remote server should run Linux as the operating systems (although a windows client is possible too, theoretically). Only one client can connect to the server at a time.

Usage

Python 3.6 or newer is required. The Python implementation uses only the standard library.

On the client side, as root or with the required network capabilities:

python3 main.py --key 64BIT_HEX_KEY --connect-to SERVER_IP:SERVER_PORT --tunnel udp \
    [--do-random-padding] \
    [--mtu TUNNEL_MTU_DEFAULT_TO_1300] \
    [--debug] \
    [--tun THE_NAME_OF_THE_TUN_DEVICE]

On the server side, as root or with the required network capabilities:

python3 main.py --key SAME_KEY_AS_CLIENT --listen-at SERVER_PORT --tunnel udp \
    [--do-random-padding] \
    [--mtu SAME_TUNNEL_MTU_AS_CLIENT] \
    [--debug] \
    [--tun THE_NAME_OF_THE_TUN_DEVICE]

You must turn off reverse path filtering on your server system, and make sure that the SERVER_PORT is open. The setup scripts require iproute2, nftables, and sysctl. They configure a point-to-point link using 10.0.1.1 on the client and 10.0.1.2 on the server, with each peer represented as a /32 route. The --mtu value is applied to both TUN interfaces and must match on both peers. The client warns and continues setup if the configured MTU plus the outer IPv4, UDP, nonce, and end-marker overhead exceeds the route MTU to the server (or the interface MTU when no route-specific MTU is available). It keeps the configured tunnel MTU unchanged. A smaller path MTU beyond the directly connected interface may still require selecting a more conservative value manually. For example, a 1500-byte underlay allows a tunnel MTU of up to 1456 bytes without exceeding that limit (20 bytes IPv4 + 8 bytes UDP + 16 bytes framing overhead).

Runtime state is stored under /run/ipudp so cleanup can restore the client's original default routes and the server's original IPv4 forwarding setting. Set IPUDP_STATE_DIR to use another state directory. If setup or cleanup fails, the program exits with an error instead of continuing with partial network configuration.

Without --debug, you should see traffic statistics every 5 seconds, on both sides, if ipudp is running normally. With --debug, every forwarded IP packet produces a line on standard output containing a UTC timestamp, the local client or server role, the packet-flow event, and the source and destination IP addresses.

Overview of Design

ipudp works as follows:

 __________ Local Machine _________      ______________       __________ Remote Server ___________
|                                  |     |  firewall  |      |                                    |
| applications             |--UDP--|-----|------------|------|-UDP--> script                   |--|-----> target
|      |                   |       |     |(encrypted  |      |          |                      |  |
|      | routing           |encrypt|     |UDP traffic)|      |  decrypt |           MASQUERADE |  |
|      V                   |       |     |            |      |          V                      |  |
| TUN virtual device --> script    |     |            |      |  TUN virtual device --nftables--|  |
|__________________________________|     |____________|      |____________________________________|

(and vice versa)

The "imaginary enemy" of this tool are advanced firewalls with the capability of deep packet inspection (DPI), traffic pattern analysis, or even active sniffing.

Here are some details about the design space of ipudp:

  • the use of TUN virtual device means any IP traffic can be tunneled by ipudp, including TCP, UDP, or even ICMP, without the need to configure applications. But it also means that only one client can connect to the remote server at a time, as port mapping is impossible at the Networking Layer.

  • The protocol for communication between client and server is UDP. As the connectionless property of UDP can reduce tunneling overhead. However, other protocols are possible, too. Again, the tunneling part is designed to be modular. When UDP is not the best option, switching to other protocols like TCP or ICMP should be easy.

    Each UDP payload is the encryption of this complete layout: 8-byte random nonce | IP packet | optional random padding | data-end. The nonce is freshly generated from the operating system's random source for every packet. The final marker is exactly eight ASCII bytes (data-end). The cipher resets before each datagram and uses little-endian 64-bit blocks. After decryption, a missing final marker is treated as a decryption failure. The receiver extracts the IP packet using IPv4's Total Length field or 40 plus IPv6's Payload Length field, both in network byte order, discarding padding. Invalid/truncated headers and lengths exceeding the available data are rejected. IPv6 jumbograms cannot fit within the UDP payload limit and are not supported.

    This format is incompatible with the previous authentication-prefix and explicit-length format. Upgrade both peers together; -auth has been removed.

  • Instead of using complicated ciphers like AES, TLS, etc.. ipudp uses a very simple reactive (stream cipher with key stream dependent on all previous plaintext bytes as well) cipher. This cipher is undoubtedly uncomparable to well-tested industrial ciphers. However, the point is, for any ciphers, once widely used, even if the firewall can't decrypt ciphertext directly, discovering the underlying cipher via various methods, such as machine learning, is possible. However, if every individual user uses a simple but unique cipher, it would be impossible to discover the pattern. The crypto part of ipudp (crypto.py) is designed in such a way, that one can easily implement a different cipher. That's also the encouraged way to use this tool. Just take it as a cryptographic mind-storming ;)

  • To avoid traffic pattern analysis (usually based on packet size), ipudp offers optional random padding. With --do-random-padding, each IP packet smaller than the tunnel MTU receives a uniformly selected number of padding bytes between zero and the remaining space. The padding bytes come from the operating system's random source, and the padded data area never exceeds the tunnel MTU. Padding is disabled by default.

  • The fixed data-end marker provides a simple decryption check, not a cryptographic authentication guarantee. The custom cipher and marker are not production-grade cryptography. Current approach completely eliminates the traffic pattern of hand-shaking.

Function of Files

  • tun.py: interface to TUN virtual devices.
  • crypto.py: cryptographic stuffs. If you want to use an alternative cipher, simply modify the methods of the Encrypter and Decrypter classes. The interface is as follows:
# class Encrypter
__init__(self, key): initializer
reset(self):
    Reset the state of the cipher. Used for stateful (stream) ciphers

encrypt(self, data):
    copy and return an encrypted version of [data].
    You don't need to modify this, as it is derived from [encrypt_in_place].
    
encrypt_in_place(self, data):
    [data] being a mutable bytes-compatible object,
    encrypt [data] in place.

# class Decrypter
__init__(self, key): initializer

reset(self):
    Reset the state of the cipher. Used for stateful (stream) ciphers

decrypt(self, data):
    copy and return an decrypted version of [data].
    You don't need to modify this, as it is derived from [encrypt_in_place].
    
decrypt_in_place(self, data):
    [data] being a mutable bytes-compatible object,
    decrypt [data] in place.
  • logger.py: For logging facilities. Modify this if not satisfied with the UI. Interface:
# module function
packet_event(role, event, packet):
    Return a timestamped debug line containing the role, event, and IP addresses.

# class Logger:
__init__(self, freq, target=sys.stdout):
    [freq] is the frequency to log traffic, in seconds.
    [target] is where the logger logs to

log(self, msg):
    Output [msg]. Should output unconditionally.

log_traffic(self):
    Output current traffic statistics. Should only output after at least
    [freq] seconds since the last output of traffic.

add_traffic(self, mode, size):
    [mode] is 'i' for inbound traffic or 'o' for outbound traffic.
    [size] is the size of new traffic, in bytes.
    Should call [log_traffic] to flush the changes.
  • udp.py: Implementation of the UDP tunnel. Check this if you want to implement an alternative authentication method or padding strategy. Interface:
# class UDPTun
__init__(
    self,
    mode, # 'c' for client side, 's' for server side
    addr, # (server_ip, server_port) pair for client side, ("", port) for server side
    encrypter, decrypter, # with the interface described above
    MTU,
    do_random_padding, # whether random padding is enabled
    logger # as described above
): initializer

socket:
    The socket used for the tunnel.
    A possible improvement would be to make the whole class selectors-compatible.

send(self, data): Send data over the tunnel. len(data) <= self.MTU.
    Should handle nonce generation, encryption, padding, and end-marker insertion
    
recv(self): Block and return the next received packet.
    Should handle decryption, end-marker verification, and IP-length-based unpadding
  • client.sh: Client initialization script. Would be called after the intialization of TUN device and the tunnel class. Make the TUN virtual device up, configure the 10.0.1.1 to 10.0.1.2 point-to-point link, pin the server endpoint to its original physical route, and replace the client system's default routes with the tunnel route. Available environment variables:
$TUN_NAME: name of the TUN virtual device
$TUN_MTU: MTU configured on the TUN device
$MINIMUM_REQUIRED_UNDERLAY_MTU: minimum route MTU including tunnel overhead
$REMOTE_IP: IP of server
$REMOTE_PORT: target port on server
$IPUDP_STATE_DIR: optional state directory, defaults to /run/ipudp
  • client-cleanup.sh: Would be called on exit of ipudp at client side. Restores the original default routes and any pre-existing exact route to the server, removes the point-to-point address, and takes the TUN device down. With the same set of available environment variables as client.sh
  • server.sh: Server initialization script. Would be called after the intialization of TUN device and the tunnel class. Make the TUN virtual device up, configure the 10.0.1.2 to 10.0.1.1 point-to-point link, enable IP forwarding on the server, and create a dedicated ip ipudp nftables table for IP masquerading. Available environment variables:
$TUN_NAME: name of the TUN virtual device
$TUN_MTU: MTU configured on the TUN device
$LISTEN_PORT: the port which the server listens on
$IPUDP_STATE_DIR: optional state directory, defaults to /run/ipudp
  • server-cleanup.sh: Called on server side exit of ipudp. Deletes the dedicated nftables table, restores the original IPv4 forwarding setting, removes the point-to-point address, and takes the TUN device down. With the same set of available environment variables as server.sh
  • main.py: Entry point. Pretty much just combines the above scripts.

Docker

The development image runs the source from a read-only bind mount. The launcher passes every argument unchanged to main.py:

docker build -f Dockerfile.development -t ipudp-development .

./run-development-container.sh --key 0123456789abcdef \
    --listen-at 48625 --tunnel udp
./run-development-container.sh --key 0123456789abcdef \
    --connect-to SERVER_IP:48625 --tunnel udp

All normal main.py options, including --mtu, --tun, --debug, and --do-random-padding, can be supplied in the same way. A --listen-at SERVER_PORT pair also makes the launcher publish SERVER_PORT/udp on the host. The server container is created with net.ipv4.ip_forward=1; server.sh changes and restores that setting only when running in a namespace where it was initially disabled.

Containers intentionally use an isolated Docker network namespace. A client's default-route replacement affects only its container, leaving the host's Internet route unchanged.

Both launchers configure client and server containers to use Google Public DNS at 8.8.8.8 and 8.8.4.4. These are conventional plaintext DNS resolvers, using UDP or TCP port 53 rather than DNS over HTTPS or DNS over TLS. Client DNS traffic crosses the tunnel after the client route is installed, but it leaves the tunnel server as plaintext DNS.

The deployment image copies the application source and supports either role:

docker build -f Dockerfile.deployment -t ipudp-deployment .

./run-deployment-container.sh --key 0123456789abcdef \
    --listen-at 48625 --tunnel udp
./run-deployment-container.sh --key 0123456789abcdef \
    --connect-to SERVER_IP:48625 --tunnel udp

Inspirations

icmptunnel

About

tunnel IP over UDP, with encryption and random padding

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages