Brute Force UDP Hole Punch – Is It Possible?

ipnat;Securityudp

So I was just reading about hole punch techniques for UDP and TCP. I read that TCP hole punch can easily be accomplished if a UDP connection has already been established. So here is my theoretical setup. I have two clients A and B both behind a NAT. They do not have a server to help them with the hole punch. Both have dynamic dns set up so that the other client can be hard coded to point to the others external address, also both are hard coded to use the same internal port, which we will say for example is 22321. Now these clients know that if they are both started and not connected that they want to connect so once every minute on the minute they send a UDP datagram to the other client to every single port, starting with the highest ports since my guess is a NAT will be inclined to use ports above 49152 as those are the dynamic ports. After each has done this there will be a record entered into the NATs state. If the clients are lucky then one of the two clients will hit the others external port after it has been opened by the NAT, and the datagram will get through and the listening client could then notify the sending client of the successful connection. If they were not so lucky then the clients just need to repeat the process, this time each starting from opposite ends to cut the time in half, when one gets through to the other then the receiver notifies the sender that the connection has been established. Could this methodology work? Or perhaps even be simplified by tracking the RST responses from the NAT?

We have clients A and B which are behind a NAT and want to communicate. both clients are listening on UDP port 22321 and both clients know the others global IP address, we will say that A has an ip address of 1.1.1.1 and B has a global ip address of 2.2.2.2. So A sends a packet to 2.2.2.2 on every single port, the contents of which need be basically nothing, for every single packet that passes through the NAT the NAT will change the outgoing IP from A's local address i.e. 192.168.1.16 to the global address 1.1.1.1. As the NAT forwards this packet to client B it creates a state entry for each of those packets so that if a packet is recieved from 2.2.2.2 on any of those ports it will know to forward it to client A (192.168.1.16) on port 22321. All of those packets would reach B's NAT at 2.2.2.2 and be discarded as unsolicited packets. Client B does the exact same process targeting client A's global ip address 1.1.1.1 when it hits a port on the NAT that client A has sent a packet out of to client B's NAT (basically any of them at this point) it will forward the packet on to client A. and Client A would be able then respond using that connection from then on out. Now the problem with this is negotiating who goes first and then when the second client should start, so instead they both just start at the exact beginning of a minute and quite likely the first attempt will fail to yield a connection but if they both then immediately repeat the process then there should be 1 or likely even many successful connections, each client could then just discard all but the first connection that succeeded.

Best Answer

Yes. Yes it is possible. The network has to be able to handle all the packets (without exceptions - packet loss means death), the machines have to be able to send out packets fast enough, the other end must not have "flooding protection" and the address has to not change every time you open a new connection. This method managed to connect my laptop behind my University's WiFi with my friends laptop behind his WiFi and it sent a packet from my home WiFi through my University's symmetric NAT. If you're using a phone, you should probably send packets as fast as possible because phones are slow but with laptops you can synchronize the barrage of UDP packets with Thread.sleep()

/**
 * Shoots empty UDP packets at every port between "start" and "end".
 */
public static void sendBarrage(int start, int end, InetAddress target) {

    final ByteBuffer to_send = ByteBuffer.allocate(0);

    // Start at a random value to avoid re-traversing failed paths on repeated attempts.
    final int starting_point = start + generator.nextInt(end - start);

    // Go from starting_point down to 1024. 
    for (int port = starting_point; port >= start; --port) { 
        try {
            if (made_connection == false) {
                myChannel.send(to_send, new InetSocketAddress(target, port));
            } else {
                break;
            }
        } catch (java.nio.channels.ClosedChannelException cce) {
            Application.printerr("Channel closed while on port: " + port);
            break;
        } catch (IOException e) {
            Application.printerr("Error sending in port: " + port);
            continue;
        }
    }

    // Go from 65535 down to say starting_point, not including starting_point.
    for (int port = end - 1; port > starting_point; --port) { 
        try {
            if (made_connection == false) {
                myChannel.send(to_send, new InetSocketAddress(target, port));
            } else {
                break;
            }
        } catch (java.nio.channels.ClosedChannelException cce) {
            Application.printerr("Channel closed while on port: " + port);
            break;
        } catch (IOException e) {
            Application.printerr("Error sending in port: " + port);
            continue;
        }
    }
}

^ Make sure you call this method in a loop, it might take a few attempts. Keep in mind that this method does NOT work with cell phone tower networks (unless you get insanely lucky and the public ip address does not change). It might work better if one side holds the port open while the other guesses rather than having them both brute force both sides.

Also, if only one side holds the ports open, this method might be good: https://www.goto.info.waseda.ac.jp/~wei/file/wei-apan-v10.pdf