erebe / wstunnel

Tunnel all your traffic over Websocket or HTTP2 - Bypass firewalls/DPI - Static binary available
Other
3.17k stars 287 forks source link

SOCKS5 UDP support ? #222

Closed bishopfaure closed 3 months ago

bishopfaure commented 3 months ago

Hello,

I am currently benchmarking various tunneling tools especially when it comes to UDP capabilities. I am aware that the does does support single port UDP forwarding but I was wondering if the SOCKS5 stack that you are using supports UDP ?

After successfully mounting a channel with a client and a server, I then used https://github.com/xjasonlyu/tun2socks and https://github.com/heiher/hev-socks5-tunnel to create a virtual interface and a virtual TCP/IP stack to pipe the traffic through wstunnel's SOCKS proxy.

TCP has been working great but I have been unsuccessful forwarding UDP traffic (my test case is a simple DNS query).

I am interested into contributing if needed, but as far as I could see there is definitely UDP code for you SOCKS5 stack, so maybe I am doing it wrong.

Here are the wstunnel logs when I try to perform that UDP port 53 connection over SOCKS:

2024-01-23T03:18:47.105766Z  WARN tunnel{peer="18.68.29.73:36806" forwarded_for="********" id="018d344e-a943-7082-b9b0-2e8037a132e2" remote="127.0.0.1:4000"}: wstunnel::socks5: Rejecting socks5 cnx: Error with reply: Command not supported.

Thank you !

erebe commented 3 months ago

Hello,

Which version are you using ? Socks5 with UDP association should be supported in the latest release. https://github.com/erebe/wstunnel/blob/main/src/socks5.rs#L59

You may want to try with this script https://github.com/erebe/wstunnel/issues/188#issuecomment-1880823558 It seems the log is coming from the server, are you trying to do reverse socks5 tunnel ?

If you don't manage to make it works, would you ming trying to describe the setup, so I can try to reproduce the issue. Also start client and server with --log-lvl=debug to get more logs regarding what is happening

bishopfaure commented 3 months ago

Below the version that I am using:

└─# ./wstunnel -V       
wstunnel 9.2.0

Yes I am using the reverse socks5 tunnel. Both ends of the connections are Kali Linux instances. Here is the setup:

After the last command is launched, the SOCKS5 server spins-up on Box C&C. As you suggested, I tried this DNS UDP test script to see if the SOCKS proxy is working:

import socket
import time

import socks

TARGET = "1.1.1.1"

def check_tcp() -> None:
    s = socks.socksocket(socket.AF_INET, socket.SOCK_STREAM)
    s.set_proxy(socks.SOCKS5, "127.0.0.1", 4000)

    print(f"Sending HTTP request to {TARGET}")
    start = time.time()
    s.connect((TARGET, 80))
    s.send(b"GET / HTTP/1.1\r\nHost: " + TARGET.encode() + b"\r\n\r\n")
    data = s.recv(1024)
    if not data:
        print("No data received")
    elif not data.startswith(b"HTTP/1.1 "):
        print("Invalid response received")
    else:
        print("Response received")
    end = time.time()
    s.close()

    print(f"Time: {round((end - start) * 1000, 2)} ms")

def check_udp() -> None:
    s = socks.socksocket(socket.AF_INET, socket.SOCK_DGRAM)
    s.set_proxy(socks.SOCKS5, "127.0.0.1", 4000)

    req = b"\x12\x34\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x05\x62\x61\x69\x64\x75\x03\x63\x6f\x6d\x00\x00\x01\x00\x01"
    TARGET = "192.168.250.254"
    print(f"Sending DNS request to {TARGET}")
    start = time.time()
    s.sendto(req, (TARGET, 53))
    (rsp, address) = s.recvfrom(4096)
    if address[0] == TARGET and address[1] == 53 and rsp[0] == req[0] and rsp[1] == req[1]:
        print("UDP check passed")
    else:
        print("Invalid response")
    end = time.time()
    s.close()

    print(f"Time: {round((end - start) * 1000, 2)} ms")

if __name__ == "__main__":
    check_tcp()
    check_udp()

Now the fun part:

I think the issue I had was that I forwarded ports with SSH, which only supports TCP, and SOCKS relay UDP via UDP, even though I was pretty sure it was using TCP to pipe UDP datagrams.

If I spin-up the SOCKS5 server on my host, I don't have any issue anymore, and I am able to mount a virtual networking stack to transparently forward UDP through SOCKS5/wstunnel.

Thank you for the answer ! You can close the issue if you don't have any other comment, but I would be interested to learn if UDP actually requires a UDP connection to the SOCKS proxy to perform UDP association. I can't remember where I did read that it was possible.

erebe commented 3 months ago

Perfect then :)

I just saw your email this morning btw, I don't check it often ^^. You may want to use version 9.2.1, I fix a buffer allocation for socks5 udp, even if for tunneling dns traffic it should be fine.

Regarding how socks5 works for UDP, it is a bit weird. You need in fact both protocol, TCP and UDP.

  1. You establish a TCP cnx on your socks5 server asking to forward udp
  2. The server respond you with the ip/port of the udp server where it waits for packets
  3. You send udp packet with extra header, to the server
  4. The socks5 server extract this extra header, and forward your packets for you.
  5. When the established TCP cnx to the socks5 is closed, the socks5 server is supposed to terminate the UDP server

That's the RFC, in the project I cheat a bit, as long as you start a socks5 server there will be a UDP server listening. All udp association are requested to contact the ip/port of this udp server. All that to say, that a valid socks5 client will need TCP and UDP to forward UDP traffic.

Enjoy :)