and so is 0x7F000001

We all know and are used to write IP addresses in “dot-notation” four octets 0-255, separated by a dot, simple and beautiful 10.3.104.27 – that is how it has always been done and how it should be done.

Some might know that you can shorten dot-notation, and just leave out any zeroes, so 127.0.0.1 becomes 127.1, but there are other ways to write an IP address, like 2130706433 or 0x7F000001.

Should you do it? … No! Can you do it? … Yes!

Let’s have some fun!

Try it…

$ping -c 1 2130706433
PING 2130706433 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.061 ms

--- 2130706433 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.061/0.061/0.061/0.000 ms

or

$ping -c 1 0x7F000001
PING 0x7F000001 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.112 ms

--- 0x7F000001 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.112/0.112/0.112/0.000 ms

But why?

As we can see the number 213070642 is resolved to 127.0.0.1
PING 2130706433 (127.0.0.1). The reason is that the “dot-notation” of IPv4 addresses are generally agreed upon but in fact not defined in any RFC, and IP addresses at their basic are just a 32-bit number, the dot-notation that is generally agreed upon and should always be used used (e.g. 127.0.0.1) is four octets (4x 8-bit) and can be broken down as shown in the image below.

If we convert this binary number to a decimal number (base-10), we get the number 213070642, easy as that.

History

This behavior is nothing new, it has been known since (at least) the late 90’ies and has been classified an issue by Microsoft since IE5 and Mozilla also had it as an issue for 18 years, from 2001 to 2019.

Attempts have been made to define that an IPv4 address has to be written in dot-notation, but there is currently no definite ruling on this matter, thus this behavior remains and I don’t see that this will change. It has been used by malicious actors in the past, and is in fact still being used to obfuscate IP addresses and evade detection.

Calculating it yourself

Let’s calculate the binary representation of a dot-notation IP-Address. In this example we use 10.3.104.27, an address from the private space 10.0.0.0/8.

Split your dot-notated address in each of its octets.

Calculating in base-10

Working in decimal, you can shift bits of an integer by multiplying it by a power of 2 (2, 4, 8, 16, 32 …). So we continue with our example address 10.3.124.27

Shift the bits of the first octet (10) over by 24, by multiplying your number by 1677721610 * 16777216 = 167772160

The second octet (3) needs to be shifted 16 bit, do this by multiplying it by 655363 * 65536 = 196608

The third octet (104) needs to be shifted 8 bits, do this by multiplying it by 256104 * 256 = 26624

The last octet (27) does not need to be shifted. 27 = 27

Now add up the numbers of each octet, this is your decimal representation of the IP address 10.3.104.27

10  *  24 =  167772160 + 
3   *  16 =     196608 +
104 * 256 =      26624 +
27  *  1  =         27
                       = 
               167995419  

What happens in Binary?

When you take the value of the first (10) octet and shift it 24 bits to the left.

It is moved to its “position” in the 32bit number.

00000000 00000000 00000000 00001010 << 24
=        <<<<<<<< <<<<<<<< <<<<<<<<
00001010 00000000 00000000 00000000

Then you can repeat and shift the second octet (3) 16 bits to the left.

00000000 00000000 00000000 00000011 << 16
=                 <<<<<<<< <<<<<<<<
00000000 00000011 00000000 00000000

Shift the third (104) octet 8 bits to the left.

00000000 00000000 00000000 01101000 << 8
=                          <<<<<<<<
00000000 00000000 01101000 00000000

And leave the last (27) octet unchanged.

00000000 00000000 00000000 00011011

Now take your four octets and add them.

00001010 00000000 00000000 00000000
00000000 00000011 00000000 00000000
00000000 00000000 01101000 00000000
00000000 00000000 00000000 00011011
=
00001010 00000011 01101000 00011011

You can even combine things

Because they are just numbers, nothing is stopping you from mixing these methods, as long as the resulting “number” results in the correct binary number.

We can mangle the address 10.3.104.27 in other ways. Leaving the first two octets (10.3), then convert the last two octets (104.27) and convert it to HEX we get 0x681b.
Combining them with a dot . we get this 10.3.0x681b valid IP address.

ping -c 1 10.3.0x681b
PING 10.3.0x681b (10.3.104.27) 56(84) bytes of data.
64 bytes from 10.3.104.27: icmp_seq=1 ttl=64 time=0.087 ms

--- 10.3.0x681b ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.087/0.087/0.087/0.000 ms

You have to finish with HEX bytes, otherwise it will be interpreted as a URL.

Decoding an integer to dot-notation

The first step is to convert the decimal number to either HEX or binary. Then split the result into its four octets, and convert each individually to base-10.

Converting such large numbers from decimal to hex by hand is time consuming, so better use a calculator.

Tools to calculate a “dotless IP Address”

I have written two small python scripts to either get decimal or hex output.

Decimal output

#!/usr/bin/env python3

# `sys` is needed to parse command line argument 
import sys

# Get IP from command line argument as dot-notation
# Split by octets
ip_list = sys.argv[1].split(".")

# Prepare beginning of string 
dec_out = 0

# Shift each octet to its position and add to output value. 
dec_out += int(ip_list[0]) << 24
dec_out += int(ip_list[1]) << 16
dec_out += int(ip_list[2]) << 8
dec_out += int(ip_list[3])

# Output the number
print(dec_out)

Usage:

$ ./dotless_dec.py 10.3.104.27
167995419

Hexadecimal output

#!/usr/bin/env python3

# `sys` is needed to parse command line argument 
import sys

# Get IP from command line argument as dot-notation
# Split by octets
ip_list = sys.argv[1].split(".")

# Function to convert one octet to hexadecimal and zero-pad
def hex_value(inp):
    out = f"{int(inp):X}"
    return out.rjust(2, '0')

# Prepare beginning of string 
out_string = "0x"

# Append the hex representation of each octet to output string.
out_string += hex_value(ip_list[0])
out_string += hex_value(ip_list[1])
out_string += hex_value(ip_list[2])
out_string += hex_value(ip_list[3])

# Print the string.
print(out_string)

Usage:

$ ./dotless_hex.py 10.3.104.27
0x0A03681B

Adam Rabjerg
Adam is a Linux trainer and consultant and has been with B1 Systems since 2017. His topics include automation and configuration management, version control, hard-/software security as well as digital curiosities. At daytime he works as a sysadmin fixing problems and automating anything he can, at night he enjoys the “Maker” life with 3d-design, 3d-printing, PCB-design, micro soldering and more.