Blog

The State Of The Internet

One great idea behind the internet is to connect devices from nearly every position on earth. Well, this idea sometimes has its drawbacks. In order to get an overview about devices that are actually connected, the University of Michigan regularly scans the internet and publishes its results. So I decided to take a deeper look at the results of the scans of TCP port 502. This port is usually used for industrial related protocols like modbus, which makes them very interesting for our purpose. What did I find?

Getting An Overview

After downloading the zgrab-results of 2015-02-26, one realizes, that 2 mio lines of json data are quite cumbersome to read. Hence, I started writing a little python script to look for interesting things. So in the very begining, I set up a little python class that holds some information about the host.

class Host(object):
  def __init__(self):
    self.addr = ''
    self.product = ''
    self.vendor = ''
    self.firmware = ''

Now we are ready to go over the data and fill some objects.


for line in f:
  finding = json.loads(line)

  h = Host()
  h.addr = finding['host']

  try:
    objects = finding['log'][1]['data']['mei_response']['objects']
    h.product = objects['product_code'].strip()
    h.firmware = objects['revision'].strip()
    h.vendor = objects['vendor'].strip()
  except KeyError:
    continue

  print("{addr}: {vendor} {product} /{firmware}".format(
              addr=h.addr,
              vendor=h.vendor,
              product=h.product,
              firmware=h.firmware))

Let's see, whether this gives some nice information.

...
xxx.xxx.xxx.xxx: Solare Datensysteme GmbH xxxxxx /
xxx.xxx.xxx.xxx: Schneider Electric BMX P34 2020 /v2.5
xxx.xxx.xxx.xxx: Computec Oy CWS06 /v3.181
xxx.xxx.xxx.xxx: TELEMECANIQUE TWDLCAE40DRF /05.20
xxx.xxx.xxx.xxx: Schneider Electric BMX NOE 0100 /V2.30
xxx.xxx.xxx.xxx: Schneider Electric BMX P34 20302 /v2.5
xxx.xxx.xxx.xxx: Computec Oy CWS06 /v3.181
xxx.xxx.xxx.xxx: TELEMECANIQUE SR3 NET01 /V1.8
xxx.xxx.xxx.xxx: Schneider Electric BMX NOE 0100 /V2.30
...

This looks nice, but there are some major things missing. As you might realize, Solare Datensystem GmbH has no information about the firmware. Entering the IP address in the browser, gives some details about that.

"Interface of a Solar-Log device"
"Interface of a Solar-Log device"

As you see, it is no option to visit every web interface manually. Unfortunately just fetching the HTML doesn't work, since the data is printed using Javascript. So I fall back on Selenium. A testing framework that is usually used to test websites. It comes with a nice way of rendering Javascript and gives a handy way to gather firmware information and the plant size automatically. This results in the following code:

def visit_website(host):
    power = ''
    firmware = ''

    try:
        driver = webdriver.PhantomJS()
        driver.implicitly_wait(2)
        driver.set_page_load_timeout(10)
    except:
        return power, firmware

    try:
        driver.get("http://%s" % host)
        power = driver.find_element_by_xpath(
            '//table/tbody/tr[4]/td[2]').text
        firmware = driver.find_element_by_xpath(
            '//table/tbody/tr[5]/td[2]').text

    except Exception as e:
        pass
    finally:
        driver.close()

    return power, firmware

Now the data for Solare Datensysteme GmbH is more meaningful.

...
xxx.xxx.xxx.xxx: Solare Datensysteme GmbH xxxxxx /170 kWp/2.8.3 Build 53 - 08.07.2013
xxx.xxx.xxx.xxx: Solare Datensysteme GmbH xxxxxx /7.6 kWp/2.8.3 Build 53 - 08.07.2013
...

But there is more to get. I think, it is appealing to know in which country each device is located. So let's add some geo information. I quickly found a nice web service that returns proper JSON responses for an IP:

GEOIP = ("www.telize.com", 80)

def get_geo(addr):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(GEOIP)

    try:
        req = 'GET /geoip/%s HTTP/1.0\nHost: %s\n\n' % (addr, GEOIP[0])
        s.send(req.encode())
        data = s.recv(2048).decode()
        payload = data[data.index('{'):]
        geo = json.loads(payload)
    except Exception as e:
        print(e)
    finally:
        s.close()

    return geo

In order to complete the information gathering, it is useful to check, whether some specific ports are open for the corresponding device. So our script now does a simple TCP connect scan, too. The final result of our script returns the host's IP address, open ports, the country, the vendor, the product and firmware information and looks like that:

...
xxx.xxx.xxx.xxx/ [TW]: Schneider Electric SAS 140 NOE 771 11 //V4.60
xxx.xxx.xxx.xxx/23,80, [RU]: Schneider Electric SAS TSX P57 4634M //V3.0
xxx.xxx.xxx.xxx/80, [US]: Schneider Electric BMX NOE 0110 //V4.50
xxx.xxx.xxx.xxx/80, [US]: Schneider Electric BMX NOE 0100 //V2.60
xxx.xxx.xxx.xxx/21,80, [PT]: TELEMECANIQUE TSX P57 104M //V0.2
xxx.xxx.xxx.xxx/21,22,23,80, [CZ]: Solare Datensysteme GmbH xxxxxx /170 kWp/2.8.3 Build 53 - 08.07.2013
xxx.xxx.xxx.xxx/23, [RU]: Schneider Electric BMX P34 2020 //v2.5
xxx.xxx.xxx.xxx/23, [RU]: Schneider Electric BMX P34 2020 //v2.5
xxx.xxx.xxx.xxx/ [RU]: Schneider Electric BMX P34 2020 //v2.5
xxx.xxx.xxx.xxx/ [IT]: Schneider Electric BMX P34 2020 //v2.1
xxx.xxx.xxx.xxx/21,80, [US]: Schneider Electric BMX P34 2020 //v2.2
xxx.xxx.xxx.xxx/ [JP]:  OCM Pro CF //V5.07 17/11/11
xxx.xxx.xxx.xxx/21,22,23,80, [CH]: Solare Datensysteme GmbH xxxxxx /7.6 kWp/2.8.3 Build 53 - 08.07.2013
...

Conclusion

When running entirely the script returns 48 different vendors:

...
xxx.xxx.xxx.xxx/ [AT]: TELEMECANIQUE TWDLCAE40DRF //05.20
xxx.xxx.xxx.xxx/ [US]: Schneider Electric BMX NOE 0100 //V01.00 IR22
vendors: 48
>

The top vendors and top products found with an open TCP Port 502, are:

> top vendor
Schneider Electric 1282
TELEMECANIQUE 846
Solare Datensysteme GmbH 550
 416
Schneider Electric SAS 321
Computec Oy 168
SE-Elektronic 123
ABB Stotz Kontakt 120
Rockwell Automation Allen-Bradley 104
CROUZET 48
> top product
BMX P34 2020 (Schneider Electric) 700
TWDLCAE40DRF (TELEMECANIQUE) 658
xxxxxx (Solare Datensysteme GmbH) 550
OCM Pro CF () 409
BMX NOE 0100 (Schneider Electric) 301
TSXETY4103 (Schneider Electric SAS) 249
CWS06 (Computec Oy) 168
G 02 90 00 (SE-Elektronic) 123
141 (Rockwell Automation Allen-Bradley) 85
SR3 NET01 (TELEMECANIQUE) 83
>

As you can see, the top vendor is Schneider Electric. Since we are a german company, we are interested in top vendors and products in Germany. There is a country filter via co:

> co DE
xxx.xxx.xxx.xxx/80, [DE]: Schneider Electric BMX NOE 0100 //V2.60
xxx.xxx.xxx.xxx/21,22,23,80, [DE]: Solare Datensysteme GmbH xxxxxx /4.8 kWp/2.8.3 Build 53 - 08.07.2013
...
> top vendor
Solare Datensysteme GmbH 130
ABB Stotz Kontakt 13
SE-Elektronic 7
Schneider Electric 6
Schneider Electric SAS 3
 2
12345678901234567890 2
PEWEU 2
solvimus GmbH 1
> top product
xxxxxx (Solare Datensysteme GmbH) 130
37 (ABB Stotz Kontakt) 8
G 02 90 00 (SE-Elektronic) 7
29 (ABB Stotz Kontakt) 5
TSXETY4103 (Schneider Electric SAS) 3
 (12345678901234567890) 2
FPWEBD (PEWEU) 2
BMX P34 2020 (Schneider Electric) 2
BMX NOE 0110 (Schneider Electric) 1
OCM Pro CF () 1
>

This reveals, that the most devices accessible in Germany, are solar equipment. As we remember, we grep the power of the solar web interfaces, so let's summarize the accessible power:

> kwp
overall 7902 kWp

As Wolframalpha claims the accesssible power of german Solar-Log devices is enough for roughly ~2500 households. The worldwide accessible solar equipment is almost four times higher which corresponds to almost 30 megawatts (29668 kWp). Note, this only takes into account the Solar-Log devices and ignores devices of other top selled vendors like Schneier Electric. We will have a look at them in another blog post, so be curiuos!

Although this is not a danger to the world's power supply, sabotaging this devices will cause a lot of economic damage. In addition to that, a lot of devices' web interface provide information about SMTP or FTP accounts of their owners.