> This is the same IP address: 3232271615. You get that by interpreting the 4 bytes of the IP address as a big-endian unsigned 32-bit integer, and print that. This leads to a classic parlor trick: if you try to visit http://3232271615 , Chrome will load http://192.168.140.255.
This was the source of one of my favorite “bugs” ever. I was working on multiple mobile apps for a company, and they had a deep link setup that was incredibly basic: <scheme>://<integer>, which would take you to an article with a simple incrementing ID. This deep link system “just worked” on iOS and Android; take the URL, grab the host, parse it as an int, grab that story ID. Windows Phone, however… the integers we were parsing out were totally wrong, returning incredibly old stories!
Turned out that the host we were given by the frameworks from the URL was auto-converted to an IP in dotted-quad format, and then the int parser was just grabbing the last segment… which meant that we were always getting stories <256, instead of the ~40000 range we were expecting.
These different representations also lead to frequent server side request forgery (SSRF) bypasses - someone might be blocking local IPv4 but you can still access their AWS metadata endpoint at ::ffff:169.254.169.254, etc.
For anyone using Ruby, I'm the author of a gem [1] that comprehensively protects against SSRF bugs. For anyone using Golang I recommend this [2] blog post.
I've found, and reported, a whole bunch of services which take user-supplied URLs and don't filter out access to localhost:8080/server-status, and similar local resources.
A common route to attacking these is to access the AWS metadata URL endpoint. Something at least the Google cloud prevents, by forcing the use of the `Metadata-Flavor: Google` header.
I wonder how many of these bugs are the result of people thinking "Well I've read the spec but most of it is 'cursed' so I'll just implement this subset which fits my idea of 'acceptable'".
Unfortunately the blacklisting approach that works on IPv4 is completely broken for IPv6 since you can't really know where your own services are. I still did not find a good generic way to protect IPv6 and ended up just disallowing it so far everywhere.
Would you be further humbled if the ipad accepted http://CXXVII.I also?
I'm never writing anything that positively accepts 127.1, or 0127.000.000.0001 as a valid address no matter what garbage implementations do.
The issue we have with this are situations when we have to accept only inputs that are domain names which are sure not to be treated as an IP address by some software downstream of us.
I'm now going to change my LAN to use 10.0.0.1 instead of 192.168.0.1 so that I can just type 10.1 This will help not only when testing stuff on mobiles only to have to rewrite the whole adress again because you forgot http:// but also when telling the kids what IP to connect to when setting up LAN games. Or coworkers when telling them them some LAN/router IP. Time server is on 10.36
> I’m on the fence about that last one, the “IPv6 with an embedded dotted decimal” form. My reference parser (Go’s net.ParseIP) understands it, but it’s not really that useful any more in the real world. At the dawn of IPv6, the idea was that you could upgrade an address to IPv6 by prepending a pair of colons, as in ::1.2.3.4, but modern transition mechanisms no longer offer anything as clear-cut as this, so the notation doesn’t really show up in the wild.
I have to disagree with this conclusion. I see it very frequently on Linux. It turns out that programs can bind their listen address to just ::, and the kernel will still allow connections from IPv4, with the address mapped to ::ffff:0.0.0.0/32 -- outbound connections use the same notation.
> It turns out that programs can bind their listen address to just ::, and the kernel will still allow connections from IPv4, with the address mapped to ::ffff:0.0.0.0/32 -- outbound connections use the same notation.
This is only true if the sysctl bindv6only or socket option IPV6_V6ONLY is 0, and is defined by RFC3493.
> At the dawn of IPv6, the idea was that you could upgrade an address to IPv6 by prepending a pair of colons, as in ::1.2.3.4
No, IPv6 explicitly rejected that idea at first. Most of the other IPng proposals did have a backwards compatibility mechanism like that. I'm still sore that the least backwards-compatible proposal was the one that won.
Later the IPv6 cabal admitted their mistake and published NAT64, but at that point it was too late to make it a mandatory required service offered by any default-route router. So now we have all of this crap about dual-stack hosts instead of simply being able to upgrade to IPv6 and trust that you will not lose any connectivity.
This is basically why, twenty years after it was standardized, IPv6 is still merely the "internet of cellphones" and no closer to replacing IPv4.
As usual, DJB saw all of this decades ahead of time:
> It does not process Class A/B notation, or hex or octal notation.
I got to find that notation useful once, to make a shorter one-liner... without even knowing that there were different classes of IPv4 address, and that I was looking at one of them.
It's a tiny function that gives me the IP address of my machine in the LAN, for either Linux and Mac:
# Get main local IP address from the default external route (Internet gateway)
iplan() {
# Note: "1" is shorthand for "1.0.0.0"
case "$OSTYPE" in
linux*) ip -4 -oneline route get 1 | grep -Po 'src \K([\d.]+)' ;;
darwin*) ipconfig getifaddr "$(route -n get 1 | sed -n 's/.*interface: //p')" ;;
esac
}
(sorry to people reading on small screens)
Full disclosure, I got the "1 is shorthand for 1.0.0.0" from here (which didn't get into explaining why it is a shorthand): https://stackoverflow.com/a/25851186
Oh no, that's another shorthand that's different from all the others. A single number should be interpreted as a big-endian uint32, and so "1" should be "0.0.0.1". However, I can confirm that `ip` interprets it as "1.0.0.0", even though you should have to write "1.0" for that.
> So, it’s a de-facto standard that boils down to mostly “what did 4.2BSD understand?“
By the way 4.2BSD was being compatible with older or contemporary implementations, like ITS which was running TCP before any Unix was.
For example plenty of machines back then used octal as a preferred human representation. In fact that’s why octal is the default format of numeric constants in C: C, like Unix, was initially developed for an 18-bit (six octal digits) PDP-7. The smaller 16-bit PDP-11 version came later.
It was a surprising amount of work to figure out all the different formats an IP address can be shown in and convert a given IP into all those formats.
Bigger nitpick: as per RFC 5952, canonically :: is ::. 0000:0000:0000:0000:0000:0000:0000:0000 is a valid way of writing the same address, but it's not the canonical way.
As Go’s net package IP parsing was mentioned, here’s a fun fact: under their API it is impossible to distinguish between an IPv4-mapped IPV6 address and the equivalent normal IPv4 address.
I find this to be a great feature. net.IPNet.Contains takes this into account, so you don’t have to worry about or deal with shenanigans like IPv4 mapped addresses. It makes implementing SSRF protection much easier.
Since I write a Lua-parsed DNS server which works with IPv6, even when compiled for an ancient version of MINGW on Windows XP (which has IPv6 support but no built-in IPv6 parser), I had to write an IPv6 address parser (no inet_pton(), which is what most programs use for IPv6 parsing, on that system).
No, I did not add dotted quad notation to the parser. No, you can not have more than four hex digits in a single quad; 00000001:2::3 is a syntax error. It supports “normal” stuff like ::, ::1, 2001:db8::1, and even non-normal stuff like “2001-0db8-1234-5678 0000-0000-0000-0005” (to be compatible with the really basic IPv6 parser I put in MaraDNS’s recursive resolver nearly two years ago), but does not support any of the IPv6 corner cases in the linked article.
Love it! No conversation about SUS is complete without Theo bashing up the absurdity of some historic bugs being documented as features. :-)
---
I do like the hex specification, though. Especially in the age of /29 and such, it's way easier to deal with space using such notation than the decimal numbers, which make little sense for network boundaries in such case. It looks like ping supports most of these (try `ping 0x08080808`, or `ping 0x08.0x080808`, but note that 0x0808.0x0808 is not valid, only 0x08.0x08.0x0808 would be), but `dig @` doesn't.
BTW, I guess this finally explains why the netmask is often shown as `inet 127.0.0.1 netmask 0xff000000` on the BSDs, which is actually a valid IP address notation, as it turns out!
I'm not convinced these are "cursed". They may be the result of bygone networking conventions, implementation ideas that never came to mainstream fruition, flexibility for use-cases etc. Just because we don't understand something that looks strange, doesn't mean it's cursed, nor that one can simply turn one's nose up and say "I don't understand why these exist so I'll just ignore them when I implement x".
I think they've got Class A/B/C wrong? Or at least they're using it in a way that I never learnt
> The familiar 192.168.140.255 notation is technically the “Class C” notation. You can also write that address in “class B” notation as 192.168.36095, or in “Class A” notation as 192.11046143. What we’re doing is coalescing the final bytes of the address into either a 16-bit or a 24-bit integer field.
> Traditionally, each of the regular classes (A-C) divided the networking and host portions of the address differently to accommodate different sized networks. Class A addresses used the remainder of the first octet to represent the network and the rest of the address to define hosts. This was good for defining a few networks with a lot of hosts each.
but basically it's a weird anachronism. I'm not sure if NTP will actually bind to those addresses using the tcp/ip stack, or if it someone just got lazy and coopted the ip address parser for off-label use.
What is the use-case of a decimal representation of a v6 address or a 32-bit int representation of an ipv4 address?
I’ve never had someone tell me, “see if you can ping 143267841”. I’ve worked in networking for coming up on 30 years now and just haven’t found the use.
I suspect it's actually the other way around. On the wire, a v4 address is four bytes. uint_32 is the natural type for this. So when we start looking at cidr scopes, /24 means the first 24 bits of those 32. "The first 24 bits of 4 bytes" sounds wrong to me, "the first 24 bits of 32 bits" sounds logical.
So as I see it - 143267841 (or 0x88A1801) is the address, and quad-dotted decimal is a (slightly more) human-readable representation of it.
Internally, I would imagine that almost every IPv4 stack uses 32bit ints to represent an address. Its not that crazy to think this would leak out somewhere.
I've written (un)parsers where we would just treat IPv4 addresses as integers because A) that is how they were treated in the binary data and B) given what we were doing with the data, we didn't actually care about the IPv4 field.
IPC at least. If you want to pass an IP address (whose natural native representation is a uint32) from program to program as text, having to format it as dotted decimal would be just unnecessary and inconvenient.
Wow, this.
One thing I didn’t see mentioned was “0”. You mentioned it, but it didn’t grok to something I know to work in some implementations: “ping 0” behaves like “ping 127.0.0.1”.
Maybe ping is treating 0 like 0.0.0.0 aka INADDR_ANY ( https://en.wikipedia.org/wiki/0.0.0.0 ). And interpreting it as all the IPv4 addrs mapped to the local machine (including localhost).
[+] [-] geoffpado|5 years ago|reply
This was the source of one of my favorite “bugs” ever. I was working on multiple mobile apps for a company, and they had a deep link setup that was incredibly basic: <scheme>://<integer>, which would take you to an article with a simple incrementing ID. This deep link system “just worked” on iOS and Android; take the URL, grab the host, parse it as an int, grab that story ID. Windows Phone, however… the integers we were parsing out were totally wrong, returning incredibly old stories!
Turned out that the host we were given by the frameworks from the URL was auto-converted to an IP in dotted-quad format, and then the int parser was just grabbing the last segment… which meant that we were always getting stories <256, instead of the ~40000 range we were expecting.
[+] [-] arkadiyt|5 years ago|reply
For anyone using Ruby, I'm the author of a gem [1] that comprehensively protects against SSRF bugs. For anyone using Golang I recommend this [2] blog post.
[1]: https://github.com/arkadiyt/ssrf_filter
[2]: https://www.agwa.name/blog/post/preventing_server_side_reque...
[+] [-] stevekemp|5 years ago|reply
https://github.com/skx/remotehttp
I've found, and reported, a whole bunch of services which take user-supplied URLs and don't filter out access to localhost:8080/server-status, and similar local resources.
A common route to attacking these is to access the AWS metadata URL endpoint. Something at least the Google cloud prevents, by forcing the use of the `Metadata-Flavor: Google` header.
[+] [-] jamespwilliams|5 years ago|reply
Just to note, this should be ::ffff:169.254.169.254
[+] [-] proactivesvcs|5 years ago|reply
[+] [-] the_mitsuhiko|5 years ago|reply
[+] [-] philsnow|5 years ago|reply
[+] [-] lrossi|5 years ago|reply
I think I will quote this article any time I see someone using regex to validate or parse IPs.
[+] [-] FreshFries|5 years ago|reply
1.1 which to me is the shortest useful IP address I am aware of.
[+] [-] kazinator|5 years ago|reply
I'm never writing anything that positively accepts 127.1, or 0127.000.000.0001 as a valid address no matter what garbage implementations do.
The issue we have with this are situations when we have to accept only inputs that are domain names which are sure not to be treated as an IP address by some software downstream of us.
[+] [-] sunsetMurk|5 years ago|reply
[+] [-] bigiain|5 years ago|reply
[+] [-] z3t4|5 years ago|reply
[+] [-] chungy|5 years ago|reply
I have to disagree with this conclusion. I see it very frequently on Linux. It turns out that programs can bind their listen address to just ::, and the kernel will still allow connections from IPv4, with the address mapped to ::ffff:0.0.0.0/32 -- outbound connections use the same notation.
[+] [-] thwarted|5 years ago|reply
This is only true if the sysctl bindv6only or socket option IPV6_V6ONLY is 0, and is defined by RFC3493.
[+] [-] octoberfranklin|5 years ago|reply
No, IPv6 explicitly rejected that idea at first. Most of the other IPng proposals did have a backwards compatibility mechanism like that. I'm still sore that the least backwards-compatible proposal was the one that won.
Later the IPv6 cabal admitted their mistake and published NAT64, but at that point it was too late to make it a mandatory required service offered by any default-route router. So now we have all of this crap about dual-stack hosts instead of simply being able to upgrade to IPv6 and trust that you will not lose any connectivity.
This is basically why, twenty years after it was standardized, IPv6 is still merely the "internet of cellphones" and no closer to replacing IPv4.
As usual, DJB saw all of this decades ahead of time:
https://cr.yp.to/djbdns/ipv6mess.html
[+] [-] AnthonyMouse|5 years ago|reply
Wait, what? 77.77.88.88 is in dotted decimal. It doesn't correspond to 7777:8888 in hex.
edit: Somebody else already noticed on Twitter:
> And as @alanjmcf noticed, I messed up one of the representations above.
> 1:2:3:4:5:6:77.77.88.88 means 1:2:3:4:5:6:4d4d:5858, not 1:2:3:4:5:6:7777:8888. I missed out a decimal-to-hex conversion in there.
[+] [-] unknown|5 years ago|reply
[deleted]
[+] [-] j1elo|5 years ago|reply
I got to find that notation useful once, to make a shorter one-liner... without even knowing that there were different classes of IPv4 address, and that I was looking at one of them.
It's a tiny function that gives me the IP address of my machine in the LAN, for either Linux and Mac:
(sorry to people reading on small screens)Full disclosure, I got the "1 is shorthand for 1.0.0.0" from here (which didn't get into explaining why it is a shorthand): https://stackoverflow.com/a/25851186
[+] [-] dave_universetf|5 years ago|reply
Ugh.
[+] [-] anderskaseorg|5 years ago|reply
[+] [-] gumby|5 years ago|reply
By the way 4.2BSD was being compatible with older or contemporary implementations, like ITS which was running TCP before any Unix was.
For example plenty of machines back then used octal as a preferred human representation. In fact that’s why octal is the default format of numeric constants in C: C, like Unix, was initially developed for an 18-bit (six octal digits) PDP-7. The smaller 16-bit PDP-11 version came later.
[+] [-] lucb1e|5 years ago|reply
It was a surprising amount of work to figure out all the different formats an IP address can be shown in and convert a given IP into all those formats.
[+] [-] jsrcout|5 years ago|reply
[+] [-] octoberfranklin|5 years ago|reply
[+] [-] phoe-krk|5 years ago|reply
Nitpick: missed a single zero in the middle there.
[+] [-] skissane|5 years ago|reply
[+] [-] Dagger2|5 years ago|reply
[+] [-] egocentric|5 years ago|reply
[+] [-] jpxw|5 years ago|reply
[+] [-] daenney|5 years ago|reply
[+] [-] strenholme|5 years ago|reply
No, I did not add dotted quad notation to the parser. No, you can not have more than four hex digits in a single quad; 00000001:2::3 is a syntax error. It supports “normal” stuff like ::, ::1, 2001:db8::1, and even non-normal stuff like “2001-0db8-1234-5678 0000-0000-0000-0005” (to be compatible with the really basic IPv6 parser I put in MaraDNS’s recursive resolver nearly two years ago), but does not support any of the IPv6 corner cases in the linked article.
The IPv6 test cases in the automated test for the parser are at: https://github.com/samboy/MaraDNS/blob/master/deadwood-githu... (The final three lines are supposed to return errors)
[+] [-] thomashabets2|5 years ago|reply
http://openbsd-archive.7691.n7.nabble.com/inet-net-pton-seem...
[+] [-] cnst|5 years ago|reply
Love it! No conversation about SUS is complete without Theo bashing up the absurdity of some historic bugs being documented as features. :-)
---
I do like the hex specification, though. Especially in the age of /29 and such, it's way easier to deal with space using such notation than the decimal numbers, which make little sense for network boundaries in such case. It looks like ping supports most of these (try `ping 0x08080808`, or `ping 0x08.0x080808`, but note that 0x0808.0x0808 is not valid, only 0x08.0x08.0x0808 would be), but `dig @` doesn't.
BTW, I guess this finally explains why the netmask is often shown as `inet 127.0.0.1 netmask 0xff000000` on the BSDs, which is actually a valid IP address notation, as it turns out!
[+] [-] proactivesvcs|5 years ago|reply
[+] [-] skeletonjelly|5 years ago|reply
> The familiar 192.168.140.255 notation is technically the “Class C” notation. You can also write that address in “class B” notation as 192.168.36095, or in “Class A” notation as 192.11046143. What we’re doing is coalescing the final bytes of the address into either a 16-bit or a 24-bit integer field.
According to this:
https://www.digitalocean.com/community/tutorials/understandi...
Which details my understanding, classes refer to the ranges, not so much grouping the latter part
Happy to be corrected!
[+] [-] voxic11|5 years ago|reply
> Traditionally, each of the regular classes (A-C) divided the networking and host portions of the address differently to accommodate different sized networks. Class A addresses used the remainder of the first octet to represent the network and the rest of the address to define hosts. This was good for defining a few networks with a lot of hosts each.
[+] [-] m463|5 years ago|reply
in the ntp config file, you will have stuff like this:
or: where the "ip address" is of the form: 127.127.<clocktype>.<instance>here's a page explaining the clock types:
https://www.eecis.udel.edu/~mills/ntp/html/refclock.html
but basically it's a weird anachronism. I'm not sure if NTP will actually bind to those addresses using the tcp/ip stack, or if it someone just got lazy and coopted the ip address parser for off-label use.
[+] [-] kortilla|5 years ago|reply
I’ve never had someone tell me, “see if you can ping 143267841”. I’ve worked in networking for coming up on 30 years now and just haven’t found the use.
[+] [-] soneil|5 years ago|reply
So as I see it - 143267841 (or 0x88A1801) is the address, and quad-dotted decimal is a (slightly more) human-readable representation of it.
[+] [-] gizmo686|5 years ago|reply
Internally, I would imagine that almost every IPv4 stack uses 32bit ints to represent an address. Its not that crazy to think this would leak out somewhere.
I've written (un)parsers where we would just treat IPv4 addresses as integers because A) that is how they were treated in the binary data and B) given what we were doing with the data, we didn't actually care about the IPv4 field.
[+] [-] augusto-moura|5 years ago|reply
[+] [-] Sharlin|5 years ago|reply
[+] [-] esnard|5 years ago|reply
[+] [-] jeffbee|5 years ago|reply
[+] [-] tomcooks|5 years ago|reply
https://www.theoryforce.com/fravia/searchlores/obscure
[+] [-] kaoD|5 years ago|reply
Not a boomer myself (I'm just a poor millennial) but I was lucky enough to enjoy the early days of the internet.
May he rest in peace.
[+] [-] alasdair_|5 years ago|reply
[+] [-] abotsis|5 years ago|reply
[+] [-] nealabq|5 years ago|reply