The Curious Case of Port 0 (2019) [pdf]
dl.ifip.org
dl.ifip.org
* Port 0 and DDoS: This is usually just a measurement artifact. Reflection-Amplification attacks create UDP packets larger than the current MTU, which leads to fragmentation and hence packet fragments without a UDP header -- however they are still identified as UDP because the ip.proto field is still 17, i.e. UDP). Interpreting such packets often leads to "port 0" instead of "port None", eg in IPFIX.
* Port 0 in the actual traffic. This has to be done via RAW sockets, since this usually signals the OS to use ANY port (especially if binding). This technique has been used ages ago (20 years? see Gobbler tool) to fingerprint operating systems, because they all responded differently to such packets.
"Pretending port zero is a normal one" (Oct. 25, 2014)
https://daniel.haxx.se/blog/2014/10/25/pretending-port-zero-...
I'm suprised no one has used this port as an attack vector yet. I'm sure many even miss this port in there iptables.
But... why? Surely some kind of separate BIND_UNUSED_PORT operation would have been fine, without needlessly overloading the meaning of "port 0".
Now, I assure you that if this "some kind of separate BIND_UNUSED_PORT" operation was available in the Berkeley socket API from the start, it would be mostly unused, people would've just used normal bind() instead (it's way simpler), so the latter option simply becomes impossible: you have to manually pick the free ports for temporary servers. In fact, there are some existing applications that actively refuse to listen on port zero, for example, "ssh -D 0" doesn't work for some reason (patching it to remove the zero check breaks nothing, everything keeps working properly), and they're somewhat annoying to use transiently.
sockaddr_in sockaddr = { 0 };
sockaddr.sin_port = HARDCODED_PORT;
// other fields defaulted...
// option parsing and filling sockaddr structure...
if (has_port_number && flag_use_random_port) {
error(...);
exit(1);
}
if (has_port_number) {
bind(s, sockaddr);
} else {
bind_unused_port(s, sockaddr);
}
vs. sockaddr_in sockaddr = { 0 };
sockaddr.sin_port = HARDCODED_PORT;
// other fields defaulted...
// option parsing and filling sockaddr structure...
bind(s, sockaddr);
Yeah, people would just write the second version. And mind you, the sockaddr argument for bind_unused() still would have a local address to bind to, so what exactly is gained? Difference between 65535 or 65536 ports available doesn't warrant complicating API in such a manner, IMO.Occasionally, some disgruntled player would aim a booter service at us. Most of those DDoS attacks were aimed at port 0.